diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index a40a162aa..000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,37 +0,0 @@ -version: 2.1 -setup: true -orbs: - continuation: circleci/continuation@0.2.0 -jobs: - generate-params: - executor: continuation/default - steps: - - checkout - - run: - name: Generate params - # for builds on the ipfs/kubo repo, use 2xlarge for faster builds - # but since this is not available for many contributors, we otherwise use medium - command: | - echo $CIRCLE_REPOSITORY_URL - if [ "$CIRCLE_REPOSITORY_URL" = 'git@github.com:ipfs/kubo.git' ]; then - resource_class=2xlarge - make_jobs=10 - else - resource_class=medium - make_jobs=3 - fi - cat \<<- EOF > params.json - { - "resource_class": "$resource_class", - "make_jobs": "$make_jobs" - } - EOF - cat params.json - - continuation/continue: - parameters: params.json - configuration_path: .circleci/main.yml -workflows: - version: 2 - setup-workflow: - jobs: - - generate-params diff --git a/.circleci/main.yml b/.circleci/main.yml deleted file mode 100644 index 08e176656..000000000 --- a/.circleci/main.yml +++ /dev/null @@ -1,400 +0,0 @@ -version: 2.1 - -parameters: - resource_class: - type: string - default: medium - make_jobs: - type: string - default: 3 - -aliases: - make_out_dirs: &make_out_dirs - run: mkdir -p /tmp/circleci-artifacts /tmp/circleci-workspace /tmp/circleci-test-results/{unit,sharness} - restore_gomod: &restore_gomod - restore_cache: - keys: - - v5-dep-{{ .Branch }}-{{ checksum "~/ipfs/kubo/go.sum" }}-{{ .Environment.CIRCLE_JOB }} - - v5-dep-{{ .Branch }}-{{ checksum "~/ipfs/kubo/go.sum" }}- - - v5-dep-{{ .Branch }}- - - v5-dep-master- - store_gomod: &store_gomod - save_cache: - key: v5-dep-{{ .Branch }}-{{ checksum "~/ipfs/kubo/go.sum" }}-{{ .Environment.CIRCLE_JOB }} - paths: - - ~/go/pkg/mod - - ~/.cache/go-build/ - -default_environment: &default_environment - SERVICE: circle-ci - TRAVIS: 1 - CIRCLE: 1 - CIRCLE_TEST_REPORTS: /tmp/circleci-test-results - CIRCLE_ARTIFACTS: /tmp/circleci-artifacts - GIT_PAGER: cat - -executors: - golang: - docker: - - image: cimg/go:1.19.1 - working_directory: ~/ipfs/kubo - environment: - <<: *default_environment - TEST_NO_DOCKER: 1 - TEST_NO_FUSE: 1 - TEST_VERBOSE: 1 - node: - docker: - - image: circleci/node:14 - working_directory: ~/ipfs/kubo - environment: - <<: *default_environment - node-browsers: - docker: - - image: circleci/node:16.12.0-browsers - working_directory: ~/ipfs/kubo - environment: - <<: *default_environment - NO_SANDBOX: true - LIBP2P_TCP_REUSEPORT: false - LIBP2P_ALLOW_WEAK_RSA_KEYS: 1 - E2E_IPFSD_TYPE: go - dockerizer: - docker: - - image: cimg/go:1.19.1 - environment: - IMAGE_NAME: ipfs/kubo - WIP_IMAGE_TAG: wip - -jobs: - gobuild: - executor: golang - resource_class: 2xlarge+ - steps: - - checkout - - *make_out_dirs - - *restore_gomod - - run: - command: make cmd/ipfs-try-build - environment: - TEST_NO_FUSE: 0 - - run: - command: make cmd/ipfs-try-build - environment: - TEST_NO_FUSE: 1 - - *store_gomod - golint: - executor: golang - steps: - - checkout - - *make_out_dirs - - *restore_gomod - - run: | - make -O test_go_lint - - *store_gomod - gotest: - executor: golang - steps: - - checkout - - *make_out_dirs - - *restore_gomod - - - run: | - make -j 1 test/unit/gotest.junit.xml \ - && [[ ! $(jq -s -c 'map(select(.Action == "fail")) | .[]' test/unit/gotest.json) ]] - - run: - when: always - command: bash <(curl -s https://codecov.io/bash) -cF unittests -X search -f coverage/unit_tests.coverprofile - - run: - command: | - # we want to first test with the kubo version in the go.mod file - go test -v ./... - - # we also want to test the examples against the current version of kubo - # however, that version might be in a fork so we need to replace the dependency - - # backup the go.mod and go.sum files to restore them after we run the tests - cp go.mod go.mod.bak - cp go.sum go.sum.bak - - # make sure the examples run against the current version of kubo - go mod edit -replace github.com/ipfs/kubo=./../../.. - go mod tidy - - go test -v ./... - - # restore the go.mod and go.sum files to their original state - mv go.mod.bak go.mod - mv go.sum.bak go.sum - working_directory: ~/ipfs/kubo/docs/examples/kubo-as-a-library - - - run: - when: always - command: mv "test/unit/gotest.junit.xml" /tmp/circleci-test-results/unit - - - *store_gomod - - - store_test_results: - path: /tmp/circleci-test-results - # Save artifacts - - store_artifacts: - path: /tmp/circleci-artifacts - - store_artifacts: - path: /tmp/circleci-test-results - sharness: - machine: - image: ubuntu-2004:202010-01 - resource_class: << pipeline.parameters.resource_class >> - working_directory: ~/ipfs/kubo - environment: - <<: *default_environment - TEST_NO_DOCKER: 0 - TEST_NO_FUSE: 1 - TEST_VERBOSE: 1 - steps: - - run: sudo apt update - - run: | - mkdir ~/localgo && cd ~/localgo - wget https://golang.org/dl/go1.19.1.linux-amd64.tar.gz - tar xfz go1.19.1.linux-amd64.tar.gz - echo "export PATH=$(pwd)/go/bin:\$PATH" >> ~/.bashrc - - run: go version - - run: sudo apt install socat net-tools fish - - checkout - - - run: - mkdir rb-pinning-service-api && - cd rb-pinning-service-api && - git init && - git remote add origin https://github.com/ipfs-shipyard/rb-pinning-service-api.git && - git fetch --depth 1 origin 773c3adbb421c551d2d89288abac3e01e1f7c3a8 && - git checkout FETCH_HEAD - - run: - cd rb-pinning-service-api && - (for i in {1..3}; do docker-compose pull && break || sleep 5; done) && - docker-compose up -d - - - *make_out_dirs - - *restore_gomod - - - run: - name: Setup Environment Variables - # we need the docker host IP; all ports exported by child containers can be accessed there. - command: echo "export TEST_DOCKER_HOST=$(ip -4 addr show docker0 | grep -Po 'inet \K[\d.]+')" >> $BASH_ENV - - run: - echo TEST_DOCKER_HOST=$TEST_DOCKER_HOST && - make -O -j << pipeline.parameters.make_jobs >> coverage/sharness_tests.coverprofile test/sharness/test-results/sharness.xml TEST_GENERATE_JUNIT=1 CONTINUE_ON_S_FAILURE=1 TEST_DOCKER_HOST=$TEST_DOCKER_HOST - - run: - when: always - command: bash <(curl -s https://codecov.io/bash) -cF sharness -X search -f coverage/sharness_tests.coverprofile - - - run: mv "test/sharness/test-results/sharness.xml" /tmp/circleci-test-results/sharness - # make sure we fail if there are test failures - - run: find test/sharness/test-results -name 't*-*.sh.*.counts' | test/sharness/lib/sharness/aggregate-results.sh | grep 'failed\s*0' - - - *store_gomod - - - store_test_results: - path: /tmp/circleci-test-results - # Save artifacts - - store_artifacts: - path: /tmp/circleci-artifacts - - store_artifacts: - path: /tmp/circleci-test-results - build: - executor: golang - steps: - - checkout - - *make_out_dirs - - *restore_gomod - - run: - name: Building - command: make build - - run: - name: Storing - command: | - mkdir -p /tmp/circleci-workspace/bin - cp cmd/ipfs/ipfs /tmp/circleci-workspace/bin - - persist_to_workspace: - root: /tmp/circleci-workspace - paths: - - bin/ipfs - - *store_gomod - interop: - docker: - - image: cimg/go:1.19.1-node - parallelism: 4 - resource_class: 2xlarge+ - steps: - - *make_out_dirs - - attach_workspace: - at: /tmp/circleci-workspace - - restore_cache: - keys: - - v1-interop-{{ .Branch }}-{{ .Revision }} - - v1-interop-{{ .Branch }}- - - v1-interop- - - run: - name: Installing dependencies - command: | - npm init -y - npm install ipfs@^0.61.0 - npm install ipfs-interop@^8.0.10 - npm install mocha-circleci-reporter@0.0.3 - working_directory: ~/ipfs/kubo/interop - - run: - name: Running tests - command: | - mkdir -p /tmp/test-results/interop/ - export MOCHA_FILE="$(mktemp /tmp/test-results/interop/unit.XXXXXX.xml)" - npx ipfs-interop -- -t node -f $(sed -n -e "s|^import '\(.*\)'$|test/\1|p" node_modules/ipfs-interop/test/node.js | circleci tests split --split-by=timings) -- --reporter mocha-circleci-reporter - working_directory: ~/ipfs/kubo/interop - environment: - LIBP2P_TCP_REUSEPORT: false - LIBP2P_ALLOW_WEAK_RSA_KEYS: 1 - IPFS_GO_EXEC: /tmp/circleci-workspace/bin/ipfs - - store_test_results: - path: /tmp/test-results - - save_cache: - key: v1-interop-{{ .Branch }}-{{ .Revision }} - paths: - - ~/ipfs/kubo/interop/node_modules - go-ipfs-api: - executor: golang - steps: - - *make_out_dirs - - attach_workspace: - at: /tmp/circleci-workspace - - run: - name: Cloning - command: | - git clone https://github.com/ipfs/go-ipfs-api.git - git -C go-ipfs-api log -1 - - run: - name: Starting the daemon - command: /tmp/circleci-workspace/bin/ipfs daemon --init --enable-namesys-pubsub - background: true - - run: - name: Waiting for the daemon - no_output_timeout: 30s - command: | - while ! /tmp/circleci-workspace/bin/ipfs id --api=/ip4/127.0.0.1/tcp/5001 2>/dev/null; do - sleep 1 - done - - restore_cache: - keys: - - v1-go-api-{{ checksum "~/ipfs/kubo/go-ipfs-api/go.sum" }} - - v1-go-api- - - run: - command: go test -count=1 -v ./... - working_directory: ~/ipfs/kubo/go-ipfs-api - - save_cache: - key: v1-go-api-{{ checksum "~/ipfs/kubo/go-ipfs-api/go.sum" }} - paths: - - ~/go/pkg/mod - - ~/.cache/go-build/ - - run: - name: Stopping the daemon - command: /tmp/circleci-workspace/bin/ipfs shutdown - go-ipfs-http-client: - executor: golang - steps: - - *make_out_dirs - - attach_workspace: - at: /tmp/circleci-workspace - - run: - name: Cloning - command: | - git clone https://github.com/ipfs/go-ipfs-http-client.git - git -C go-ipfs-http-client log -1 - - restore_cache: - keys: - - v1-http-client-{{ checksum "~/ipfs/kubo/go-ipfs-http-client/go.sum" }} - - v1-http-client- - - run: - name: go test -count=1 -v ./... - command: | - export PATH=/tmp/circleci-workspace/bin:$PATH - go test -count=1 -v ./... - working_directory: ~/ipfs/kubo/go-ipfs-http-client - - save_cache: - key: v1-http-client-{{ checksum "~/ipfs/kubo/go-ipfs-http-client/go.sum" }} - paths: - - ~/go/pkg/mod - - ~/.cache/go-build/ - ipfs-webui: - executor: node-browsers - resource_class: 2xlarge+ - steps: - - *make_out_dirs - - attach_workspace: - at: /tmp/circleci-workspace - - run: - name: Cloning - command: | - git clone https://github.com/ipfs/ipfs-webui.git - git -C ipfs-webui log -1 - - restore_cache: - keys: - - v1-ipfs-webui-{{ checksum "~/ipfs/kubo/ipfs-webui/package-lock.json" }} - - v1-ipfs-webui- - - run: - name: Installing dependencies - command: | - npm ci --prefer-offline --no-audit --progress=false --cache ~/ipfs/kubo/.cache/npm - npx playwright install - working_directory: ~/ipfs/kubo/ipfs-webui - - run: - name: Running upstream tests (finish early if they fail) - command: | - npm test || circleci-agent step halt - working_directory: ~/ipfs/kubo/ipfs-webui - - run: - name: Running tests with kubo built from current commit - command: npm test - working_directory: ~/ipfs/kubo/ipfs-webui - environment: - IPFS_GO_EXEC: /tmp/circleci-workspace/bin/ipfs - - save_cache: - key: v1-ipfs-webui-{{ checksum "~/ipfs/kubo/ipfs-webui/package-lock.json" }} - paths: - - ~/.cache/ms-playwright - - ~/ipfs/kubo/.cache/npm - # We only run build as a test here. DockerHub images are built and published - # by GitHub Action now: https://github.com/ipfs/kubo/pull/8467 - docker-build: - executor: dockerizer - steps: - - checkout - - setup_remote_docker: - version: "19.03.13" - - run: - name: Build Docker image - command: | - docker build -t $IMAGE_NAME:$WIP_IMAGE_TAG . - -workflows: - version: 2 - - # Runs for all branches, but not on tags - # see: https://circleci.com/docs/2.0/workflows/#executing-workflows-for-a-git-tag - test: - jobs: - - gobuild - - golint - - gotest - - sharness - - build - - interop: - requires: - - build - - go-ipfs-api: - requires: - - build - - go-ipfs-http-client: - requires: - - build - - ipfs-webui: - requires: - - build - - docker-build diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 5151ad178..7d17855e0 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -2,6 +2,9 @@ # request that modifies code that they own. Code owners are not automatically # requested to review draft pull requests. +# Default +* @ipfs/kubo-maintainers + # HTTP Gateway core/corehttp/ @lidel test/sharness/*gateway*.sh @lidel diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..eeba0640d --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +custom: [ipshipyard.gitwallet.co] diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index 9472db123..d89f921b8 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -18,7 +18,7 @@ body: label: Checklist description: Please verify that you've followed these steps options: - - label: This is a bug report, not a question. Ask questions on [discuss.ipfs.io](https://discuss.ipfs.io). + - label: This is a bug report, not a question. Ask questions on [discuss.ipfs.tech](https://discuss.ipfs.tech/c/help/13). required: true - label: I have searched on the [issue tracker](https://github.com/ipfs/kubo/issues?q=is%3Aissue) for my bug. required: true @@ -32,8 +32,9 @@ body: label: Installation method description: Please select your installation method options: + - dist.ipfs.tech or ipfs-update + - docker image - ipfs-desktop - - ipfs-update or dist.ipfs.tech - third-party binary - built from source - type: textarea diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index f3f53fe6c..ec985b0bc 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,7 +1,7 @@ blank_issues_enabled: false contact_links: - name: Getting Help on IPFS - url: https://ipfs.io/help + url: https://ipfs.tech/help about: All information about how and where to get help on IPFS. - name: Kubo configuration reference url: https://github.com/ipfs/kubo/blob/master/docs/config.md#readme @@ -9,9 +9,9 @@ contact_links: - name: Kubo experimental features docs url: https://github.com/ipfs/kubo/blob/master/docs/experimental-features.md#readme about: Documentation on Private Networks, Filestore and other experimental features. - - name: RPC API Reference + - name: Kubo RPC API Reference url: https://docs.ipfs.tech/reference/kubo/rpc/ about: Documentation of all Kubo RPC API endpoints. - - name: IPFS Official Forum - url: https://discuss.ipfs.io + - name: IPFS Official Discussion Forum + url: https://discuss.ipfs.tech about: Please post general questions, support requests, and discussions here. diff --git a/.github/ISSUE_TEMPLATE/enhancement.yml b/.github/ISSUE_TEMPLATE/enhancement.yml index 9bfeba5b5..a0b241b55 100644 --- a/.github/ISSUE_TEMPLATE/enhancement.yml +++ b/.github/ISSUE_TEMPLATE/enhancement.yml @@ -6,11 +6,11 @@ body: - type: markdown attributes: value: | - Suggest an enhancement to Kubo (the program). If you'd like to suggest an improvement to the IPFS protocol, please discuss it on [the forum](https://discuss.ipfs.io). + Suggest an enhancement to Kubo (the program). If you'd like to suggest an improvement to the IPFS protocol, please discuss it on [the forum](https://discuss.ipfs.tech). Issues in this repo must be specific, actionable, and well motivated. They should be starting points for _building_ new features, not brainstorming ideas. - If you have an idea you'd like to discuss, please open a new thread on [the forum](https://discuss.ipfs.io). + If you have an idea you'd like to discuss, please open a new thread on [the forum](https://discuss.ipfs.tech). **Example:** diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml index cf2fa8116..d368588b4 100644 --- a/.github/ISSUE_TEMPLATE/feature.yml +++ b/.github/ISSUE_TEMPLATE/feature.yml @@ -6,11 +6,11 @@ body: - type: markdown attributes: value: | - Suggest a new feature in Kubo (the program). If you'd like to suggest an improvement to the IPFS protocol, please discuss it on [the forum](https://discuss.ipfs.io). + Suggest a new feature in Kubo (the program). If you'd like to suggest an improvement to the IPFS protocol, please discuss it on [the forum](https://discuss.ipfs.tech). Issues in this repo must be specific, actionable, and well motivated. They should be starting points for _building_ new features, not brainstorming ideas. - If you have an idea you'd like to discuss, please open a new thread on [the forum](https://discuss.ipfs.io). + If you have an idea you'd like to discuss, please open a new thread on [the forum](https://discuss.ipfs.tech). **Example:** diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 14f776456..5ace4600a 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,10 +1,6 @@ version: 2 updates: -- package-ecosystem: gomod - directory: "/" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 10 - labels: - - "topic/dependencies" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..4224fd04c --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,5 @@ + diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml new file mode 100644 index 000000000..85f2dbf2b --- /dev/null +++ b/.github/workflows/changelog.yml @@ -0,0 +1,39 @@ +name: Changelog + +on: + pull_request: + types: + - opened + - edited + - synchronize + - reopened + - labeled + - unlabeled + paths: + - '**.go' + - '**/go.mod' + - '**/go.sum' + +jobs: + changelog: + if: contains(github.event.pull_request.title, '[skip changelog]') == false && + contains(github.event.pull_request.labels.*.name, 'skip/changelog') == false + runs-on: ubuntu-latest + name: Changelog + steps: + - id: changelog + env: + GITHUB_TOKEN: ${{ github.token }} + ENDPOINT: repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files + SELECTOR: 'map(select(.filename | startswith("docs/changelogs/"))) | length' + run: gh api "$ENDPOINT" --jq "$SELECTOR" | xargs -I{} echo "modified={}" | tee -a $GITHUB_OUTPUT + - if: steps.changelog.outputs.modified == '0' + env: + MESSAGE: | + docs/changelogs/ was not modified in this PR. Please do one of the following: + - add a changelog entry + - add `[skip changelog]` to the PR title + - label the PR with `skip/changelog` + run: | + echo "::error::${MESSAGE//$'\n'/%0A}" + exit 1 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index af9006adf..8ed324854 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -1,5 +1,5 @@ # 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 -name: "CodeQL" +name: CodeQL on: workflow_dispatch: @@ -8,35 +8,42 @@ on: pull_request: # The branches below must be a subset of the branches above branches: [ master ] + paths-ignore: + - '**/*.md' schedule: - cron: '30 12 * * 2' -jobs: - analyze: - if: github.repository == 'ipfs/kubo' || github.event_name == 'workflow_dispatch' - name: Analyze - runs-on: ubuntu-latest +permissions: + contents: read # to fetch code (actions/checkout) + security-events: write # (github/codeql-action/autobuild) - 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 +concurrency: + group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event_name == 'push' && github.sha || github.ref }} + cancel-in-progress: true + +jobs: + codeql: + if: github.repository == 'ipfs/kubo' || github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + timeout-minutes: 20 steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: 1.23.x # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: - languages: ${{ matrix.language }} + languages: go - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml new file mode 100644 index 000000000..e13a3f882 --- /dev/null +++ b/.github/workflows/docker-build.yml @@ -0,0 +1,34 @@ +# If we decide to run build-image.yml on every PR, we could deprecate this workflow. +name: Docker Build + +on: + workflow_dispatch: + pull_request: + paths-ignore: + - '**/*.md' + push: + branches: + - 'master' + +concurrency: + group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event_name == 'push' && github.sha || github.ref }} + cancel-in-progress: true + +jobs: + docker-build: + if: github.repository == 'ipfs/kubo' || github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + timeout-minutes: 10 + env: + IMAGE_NAME: ipfs/kubo + WIP_IMAGE_TAG: wip + defaults: + run: + shell: bash + steps: + - uses: actions/setup-go@v5 + with: + go-version: 1.23.x + - uses: actions/checkout@v4 + - run: docker build -t $IMAGE_NAME:$WIP_IMAGE_TAG . + - run: docker run --rm $IMAGE_NAME:$WIP_IMAGE_TAG --version diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 76e0c1c11..ca4b5fdd4 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -1,51 +1,135 @@ -name: Publish Docker image +name: Docker Push on: workflow_dispatch: + inputs: + push: + description: 'Push to Docker Hub' + required: true + default: 'false' + tags: + description: 'Custom tags to use for the push' + required: false + default: '' + # # If we decide to build all images on every PR, we should make sure that + # # they are NOT pushed to Docker Hub. + # pull_request: + # paths-ignore: + # - '**/*.md' push: branches: - 'master' + - 'staging' - 'bifrost-*' tags: - 'v*' +permissions: + contents: read # to fetch code (actions/checkout) + jobs: - push_to_registry: + docker-hub: if: github.repository == 'ipfs/kubo' || github.event_name == 'workflow_dispatch' name: Push Docker image to Docker Hub runs-on: ubuntu-latest + timeout-minutes: 15 env: IMAGE_NAME: ipfs/kubo LEGACY_IMAGE_NAME: ipfs/go-ipfs steps: - name: Check out the repo - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up QEMU - uses: docker/setup-qemu-action@v1 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v3 + + - name: Cache Docker layers + uses: actions/cache@v4 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- - name: Get tags id: tags + if: github.event.inputs.tags == '' run: | - TAGS="$(./bin/get-docker-tags.sh $(date -u +%F))" - TAGS="${TAGS//$'\n'/'%0A'}" - echo "::set-output name=value::$(echo $TAGS)" + echo "value<> $GITHUB_OUTPUT + ./bin/get-docker-tags.sh "$(date -u +%F)" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT shell: bash - name: Log in to Docker Hub - uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 + uses: docker/login-action@v3 with: - username: ${{ secrets.DOCKER_USERNAME }} + username: ${{ vars.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - - name: Build Docker image and publish to Docker Hub - uses: docker/build-push-action@v2 + # We have to build each platform separately because when using multi-arch + # builds, only one platform is being loaded into the cache. This would + # prevent us from testing the other platforms. + - name: Build Docker image (linux/amd64) + uses: docker/build-push-action@v6 + with: + platforms: linux/amd64 + context: . + push: false + load: true + file: ./Dockerfile + tags: ${{ env.IMAGE_NAME }}:linux-amd64 + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache-new + + - name: Build Docker image (linux/arm/v7) + uses: docker/build-push-action@v6 + with: + platforms: linux/arm/v7 + context: . + push: false + load: true + file: ./Dockerfile + tags: ${{ env.IMAGE_NAME }}:linux-arm-v7 + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache-new + + - name: Build Docker image (linux/arm64/v8) + uses: docker/build-push-action@v6 + with: + platforms: linux/arm64/v8 + context: . + push: false + load: true + file: ./Dockerfile + tags: ${{ env.IMAGE_NAME }}:linux-arm64-v8 + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache-new + + # We test all the images on amd64 host here. This uses QEMU to emulate + # the other platforms. + - run: docker run --rm $IMAGE_NAME:linux-amd64 --version + - run: docker run --rm $IMAGE_NAME:linux-arm-v7 --version + - run: docker run --rm $IMAGE_NAME:linux-arm64-v8 --version + + # This will only push the previously built images. + - if: github.event_name != 'workflow_dispatch' || github.event.inputs.push == 'true' + name: Publish to Docker Hub + uses: docker/build-push-action@v6 with: platforms: linux/amd64,linux/arm/v7,linux/arm64/v8 context: . push: true file: ./Dockerfile - tags: "${{ steps.tags.outputs.value }}" + tags: "${{ github.event.inputs.tags || steps.tags.outputs.value }}" + cache-from: type=local,src=/tmp/.buildx-cache-new + cache-to: type=local,dest=/tmp/.buildx-cache-new + + # https://github.com/docker/build-push-action/issues/252 + # https://github.com/moby/buildkit/issues/1896 + - name: Move cache to limit growth + run: | + rm -rf /tmp/.buildx-cache + mv /tmp/.buildx-cache-new /tmp/.buildx-cache diff --git a/.github/workflows/gateway-conformance.yml b/.github/workflows/gateway-conformance.yml new file mode 100644 index 000000000..d2f015900 --- /dev/null +++ b/.github/workflows/gateway-conformance.yml @@ -0,0 +1,228 @@ +name: Gateway Conformance + +on: + workflow_dispatch: + push: + branches: + - master + pull_request: + paths-ignore: + - '**/*.md' + +concurrency: + group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event_name == 'push' && github.sha || github.ref }} + cancel-in-progress: true + +defaults: + run: + shell: bash + +env: + # hostnames expected by https://github.com/ipfs/gateway-conformance + GATEWAY_PUBLIC_GATEWAYS: | + { + "example.com": { + "UseSubdomains": true, + "InlineDNSLink": true, + "Paths": ["/ipfs", "/ipns"] + }, + "localhost": { + "UseSubdomains": true, + "InlineDNSLink": true, + "Paths": ["/ipfs", "/ipns"] + } + } + +jobs: + # Testing all gateway features via TCP port specified in Addresses.Gateway + gateway-conformance: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + # 1. Download the gateway-conformance fixtures + - name: Download gateway-conformance fixtures + uses: ipfs/gateway-conformance/.github/actions/extract-fixtures@v0.6 + with: + output: fixtures + + # 2. Build the kubo-gateway + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: 1.23.x + - uses: protocol/cache-go-action@v1 + with: + name: ${{ github.job }} + - name: Checkout kubo-gateway + uses: actions/checkout@v4 + with: + path: kubo-gateway + - name: Build kubo-gateway + run: make build + working-directory: kubo-gateway + + # 3. Init the kubo-gateway + - name: Init kubo-gateway + run: | + ./ipfs init -e + ./ipfs config --json Gateway.PublicGateways "$GATEWAY_PUBLIC_GATEWAYS" + working-directory: kubo-gateway/cmd/ipfs + + # 4. Populate the Kubo gateway with the gateway-conformance fixtures + - name: Import fixtures + run: | + # Import car files + find ./fixtures -name '*.car' -exec kubo-gateway/cmd/ipfs/ipfs dag import --pin-roots=false {} \; + + # Import ipns records + records=$(find ./fixtures -name '*.ipns-record') + for record in $records + do + key=$(basename -s .ipns-record "$record" | cut -d'_' -f1) + kubo-gateway/cmd/ipfs/ipfs routing put --allow-offline "/ipns/$key" "$record" + done + + # Import dnslink records + # the IPFS_NS_MAP env will be used by the daemon + echo "IPFS_NS_MAP=$(cat ./fixtures/dnslinks.IPFS_NS_MAP)" >> $GITHUB_ENV + + # 5. Start the kubo-gateway + - name: Start kubo-gateway + run: | + ./ipfs daemon --offline & + working-directory: kubo-gateway/cmd/ipfs + + # 6. Run the gateway-conformance tests + - name: Run gateway-conformance tests + uses: ipfs/gateway-conformance/.github/actions/test@v0.6 + with: + gateway-url: http://127.0.0.1:8080 + subdomain-url: http://localhost:8080 + args: -skip 'TestGatewayCar/GET_response_for_application/vnd.ipld.car/Header_Content-Length' + json: output.json + xml: output.xml + html: output.html + markdown: output.md + + # 7. Upload the results + - name: Upload MD summary + if: failure() || success() + run: cat output.md >> $GITHUB_STEP_SUMMARY + - name: Upload HTML report + if: failure() || success() + uses: actions/upload-artifact@v4 + with: + name: gateway-conformance.html + path: output.html + - name: Upload JSON report + if: failure() || success() + uses: actions/upload-artifact@v4 + with: + name: gateway-conformance.json + path: output.json + + # Testing trustless gateway feature subset exposed as libp2p protocol + gateway-conformance-libp2p-experiment: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + # 1. Download the gateway-conformance fixtures + - name: Download gateway-conformance fixtures + uses: ipfs/gateway-conformance/.github/actions/extract-fixtures@v0.6 + with: + output: fixtures + + # 2. Build the kubo-gateway + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: 1.23.x + - uses: protocol/cache-go-action@v1 + with: + name: ${{ github.job }} + - name: Checkout kubo-gateway + uses: actions/checkout@v4 + with: + path: kubo-gateway + - name: Build kubo-gateway + run: make build + working-directory: kubo-gateway + + # 3. Init the kubo-gateway + - name: Init kubo-gateway + run: | + ./ipfs init --profile=test + ./ipfs config --json Gateway.PublicGateways "$GATEWAY_PUBLIC_GATEWAYS" + ./ipfs config --json Experimental.GatewayOverLibp2p true + ./ipfs config Addresses.Gateway "/ip4/127.0.0.1/tcp/8080" + ./ipfs config Addresses.API "/ip4/127.0.0.1/tcp/5001" + working-directory: kubo-gateway/cmd/ipfs + + # 4. Populate the Kubo gateway with the gateway-conformance fixtures + - name: Import fixtures + run: | + # Import car files + find ./fixtures -name '*.car' -exec kubo-gateway/cmd/ipfs/ipfs dag import --pin-roots=false {} \; + + # 5. Start the kubo-gateway + - name: Start kubo-gateway + run: | + ( ./ipfs daemon & ) | sed '/Daemon is ready/q' + while [[ "$(./ipfs id | jq '.Addresses | length')" == '0' ]]; do sleep 1; done + working-directory: kubo-gateway/cmd/ipfs + + # 6. Setup a kubo http-p2p-proxy to expose libp2p protocol as a regular HTTP port for gateway conformance tests + - name: Init p2p-proxy kubo node + env: + IPFS_PATH: "~/.kubo-p2p-proxy" + run: | + ./ipfs init --profile=test -e + ./ipfs config --json Experimental.Libp2pStreamMounting true + ./ipfs config Addresses.Gateway "/ip4/127.0.0.1/tcp/8081" + ./ipfs config Addresses.API "/ip4/127.0.0.1/tcp/5002" + working-directory: kubo-gateway/cmd/ipfs + + # 7. Start the kubo http-p2p-proxy + - name: Start kubo http-p2p-proxy + env: + IPFS_PATH: "~/.kubo-p2p-proxy" + run: | + ( ./ipfs daemon & ) | sed '/Daemon is ready/q' + while [[ "$(./ipfs id | jq '.Addresses | length')" == '0' ]]; do sleep 1; done + working-directory: kubo-gateway/cmd/ipfs + + # 8. Start forwarding data from the http-p2p-proxy to the node serving the Gateway API over libp2p + - name: Start http-over-libp2p forwarding proxy + run: | + gatewayNodeId=$(./ipfs --api=/ip4/127.0.0.1/tcp/5001 id -f="") + ./ipfs --api=/ip4/127.0.0.1/tcp/5002 swarm connect $(./ipfs --api=/ip4/127.0.0.1/tcp/5001 swarm addrs local --id | head -n 1) + ./ipfs --api=/ip4/127.0.0.1/tcp/5002 p2p forward --allow-custom-protocol /http/1.1 /ip4/127.0.0.1/tcp/8092 /p2p/$gatewayNodeId + working-directory: kubo-gateway/cmd/ipfs + + # 9. Run the gateway-conformance tests over libp2p + - name: Run gateway-conformance tests over libp2p + uses: ipfs/gateway-conformance/.github/actions/test@v0.6 + with: + gateway-url: http://127.0.0.1:8092 + args: --specs "trustless-gateway,-trustless-ipns-gateway" -skip 'TestGatewayCar/GET_response_for_application/vnd.ipld.car/Header_Content-Length' + json: output.json + xml: output.xml + html: output.html + markdown: output.md + + # 10. Upload the results + - name: Upload MD summary + if: failure() || success() + run: cat output.md >> $GITHUB_STEP_SUMMARY + - name: Upload HTML report + if: failure() || success() + uses: actions/upload-artifact@v4 + with: + name: gateway-conformance-libp2p.html + path: output.html + - name: Upload JSON report + if: failure() || success() + uses: actions/upload-artifact@v4 + with: + name: gateway-conformance-libp2p.json + path: output.json diff --git a/.github/workflows/gobuild.yml b/.github/workflows/gobuild.yml new file mode 100644 index 000000000..5aebfd938 --- /dev/null +++ b/.github/workflows/gobuild.yml @@ -0,0 +1,40 @@ +name: Go Build + +on: + workflow_dispatch: + pull_request: + paths-ignore: + - '**/*.md' + push: + branches: + - 'master' + +concurrency: + group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event_name == 'push' && github.sha || github.ref }} + cancel-in-progress: true + +jobs: + go-build: + if: github.repository == 'ipfs/kubo' || github.event_name == 'workflow_dispatch' + runs-on: ${{ fromJSON(github.repository == 'ipfs/kubo' && '["self-hosted", "linux", "x64", "4xlarge"]' || '"ubuntu-latest"') }} + timeout-minutes: 20 + env: + TEST_DOCKER: 0 + TEST_VERBOSE: 1 + TRAVIS: 1 + GIT_PAGER: cat + IPFS_CHECK_RCMGR_DEFAULTS: 1 + defaults: + run: + shell: bash + steps: + - uses: actions/setup-go@v5 + with: + go-version: 1.23.x + - uses: actions/checkout@v4 + - run: make cmd/ipfs-try-build + env: + TEST_FUSE: 1 + - run: make cmd/ipfs-try-build + env: + TEST_FUSE: 0 diff --git a/.github/workflows/golang-analysis.yml b/.github/workflows/golang-analysis.yml index 3a74e61a2..36a5bba01 100644 --- a/.github/workflows/golang-analysis.yml +++ b/.github/workflows/golang-analysis.yml @@ -1,19 +1,35 @@ -on: [push, pull_request] -name: Go Checks +name: Go Check + +on: + workflow_dispatch: + pull_request: + paths-ignore: + - '**/*.md' + push: + branches: + - 'master' + +permissions: + contents: read # to fetch code (actions/checkout) + +concurrency: + group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event_name == 'push' && github.sha || github.ref }} + cancel-in-progress: true jobs: - unit: + go-check: + if: github.repository == 'ipfs/kubo' || github.event_name == 'workflow_dispatch' runs-on: ubuntu-latest - name: All + timeout-minutes: 10 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: recursive - - uses: actions/setup-go@v2 + - uses: actions/setup-go@v5 with: - go-version: "1.19.x" + go-version: "1.23.x" - name: Check that go.mod is tidy - uses: protocol/multiple-go-modules@v1.2 + uses: protocol/multiple-go-modules@v1.4 with: run: | go mod tidy @@ -33,6 +49,6 @@ jobs: fi - name: go vet if: always() # run this step even if the previous one failed - uses: protocol/multiple-go-modules@v1.2 + uses: protocol/multiple-go-modules@v1.4 with: run: go vet ./... diff --git a/.github/workflows/golint.yml b/.github/workflows/golint.yml new file mode 100644 index 000000000..57b5d46dd --- /dev/null +++ b/.github/workflows/golint.yml @@ -0,0 +1,36 @@ +name: Go Lint + +on: + workflow_dispatch: + pull_request: + paths-ignore: + - '**/*.md' + push: + branches: + - 'master' + +concurrency: + group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event_name == 'push' && github.sha || github.ref }} + cancel-in-progress: true + +jobs: + go-lint: + if: github.repository == 'ipfs/kubo' || github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + timeout-minutes: 10 + env: + TEST_DOCKER: 0 + TEST_FUSE: 0 + TEST_VERBOSE: 1 + TRAVIS: 1 + GIT_PAGER: cat + IPFS_CHECK_RCMGR_DEFAULTS: 1 + defaults: + run: + shell: bash + steps: + - uses: actions/setup-go@v5 + with: + go-version: 1.23.x + - uses: actions/checkout@v4 + - run: make -O test_go_lint diff --git a/.github/workflows/gotest.yml b/.github/workflows/gotest.yml new file mode 100644 index 000000000..c6cac2c78 --- /dev/null +++ b/.github/workflows/gotest.yml @@ -0,0 +1,109 @@ +name: Go Test + +on: + workflow_dispatch: + pull_request: + paths-ignore: + - '**/*.md' + push: + branches: + - 'master' + +concurrency: + group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event_name == 'push' && github.sha || github.ref }} + cancel-in-progress: true + +jobs: + go-test: + if: github.repository == 'ipfs/kubo' || github.event_name == 'workflow_dispatch' + runs-on: ${{ fromJSON(github.repository == 'ipfs/kubo' && '["self-hosted", "linux", "x64", "2xlarge"]' || '"ubuntu-latest"') }} + timeout-minutes: 20 + env: + TEST_DOCKER: 0 + TEST_FUSE: 0 + TEST_VERBOSE: 1 + TRAVIS: 1 + GIT_PAGER: cat + IPFS_CHECK_RCMGR_DEFAULTS: 1 + defaults: + run: + shell: bash + steps: + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: 1.23.x + - name: Check out Kubo + uses: actions/checkout@v4 + - name: Install missing tools + run: sudo apt update && sudo apt install -y zsh + - name: 👉️ If this step failed, go to «Summary» (top left) → inspect the «Failures/Errors» table + env: + # increasing parallelism beyond 2 doesn't speed up the tests much + PARALLEL: 2 + run: | + make -j "$PARALLEL" test/unit/gotest.junit.xml && + [[ ! $(jq -s -c 'map(select(.Action == "fail")) | .[]' test/unit/gotest.json) ]] + - name: Upload coverage to Codecov + uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0 + if: failure() || success() + with: + name: unittests + files: coverage/unit_tests.coverprofile + - name: Test kubo-as-a-library example + run: | + # we want to first test with the kubo version in the go.mod file + go test -v ./... + + # we also want to test the examples against the current version of kubo + # however, that version might be in a fork so we need to replace the dependency + + # backup the go.mod and go.sum files to restore them after we run the tests + cp go.mod go.mod.bak + cp go.sum go.sum.bak + + # make sure the examples run against the current version of kubo + go mod edit -replace github.com/ipfs/kubo=./../../.. + go mod tidy + + go test -v ./... + + # restore the go.mod and go.sum files to their original state + mv go.mod.bak go.mod + mv go.sum.bak go.sum + working-directory: docs/examples/kubo-as-a-library + - name: Create a proper JUnit XML report + uses: ipdxco/gotest-json-to-junit-xml@v1 + with: + input: test/unit/gotest.json + output: test/unit/gotest.junit.xml + if: failure() || success() + - name: Archive the JUnit XML report + uses: actions/upload-artifact@v4 + with: + name: unit + path: test/unit/gotest.junit.xml + if: failure() || success() + - name: Create a HTML report + uses: ipdxco/junit-xml-to-html@v1 + with: + mode: no-frames + input: test/unit/gotest.junit.xml + output: test/unit/gotest.html + if: failure() || success() + - name: Archive the HTML report + uses: actions/upload-artifact@v4 + with: + name: html + path: test/unit/gotest.html + if: failure() || success() + - name: Create a Markdown report + uses: ipdxco/junit-xml-to-html@v1 + with: + mode: summary + input: test/unit/gotest.junit.xml + output: test/unit/gotest.md + if: failure() || success() + - name: Set the summary + run: cat test/unit/gotest.md >> $GITHUB_STEP_SUMMARY + if: failure() || success() diff --git a/.github/workflows/interop.yml b/.github/workflows/interop.yml new file mode 100644 index 000000000..bfa6523de --- /dev/null +++ b/.github/workflows/interop.yml @@ -0,0 +1,136 @@ +name: Interop + +on: + workflow_dispatch: + pull_request: + paths-ignore: + - '**/*.md' + push: + branches: + - 'master' + +env: + GO_VERSION: 1.23.x + +concurrency: + group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event_name == 'push' && github.sha || github.ref }} + cancel-in-progress: true + +defaults: + run: + shell: bash + +jobs: + interop-prep: + if: github.repository == 'ipfs/kubo' || github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + timeout-minutes: 5 + env: + TEST_DOCKER: 0 + TEST_FUSE: 0 + TEST_VERBOSE: 1 + TRAVIS: 1 + GIT_PAGER: cat + IPFS_CHECK_RCMGR_DEFAULTS: 1 + defaults: + run: + shell: bash + steps: + - uses: actions/setup-go@v5 + with: + go-version: ${{ env.GO_VERSION }} + - uses: actions/checkout@v4 + - run: make build + - uses: actions/upload-artifact@v4 + with: + name: kubo + path: cmd/ipfs/ipfs + helia-interop: + needs: [interop-prep] + runs-on: ${{ fromJSON(github.repository == 'ipfs/kubo' && '["self-hosted", "linux", "x64", "2xlarge"]' || '"ubuntu-latest"') }} + timeout-minutes: 20 + defaults: + run: + shell: bash + steps: + - uses: actions/setup-node@v4 + with: + node-version: lts/* + - uses: actions/download-artifact@v4 + with: + name: kubo + path: cmd/ipfs + - run: chmod +x cmd/ipfs/ipfs + - run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT + id: npm-cache-dir + - uses: actions/cache@v4 + with: + path: ${{ steps.npm-cache-dir.outputs.dir }} + key: ${{ runner.os }}-${{ github.job }}-helia-${{ hashFiles('**/package-lock.json') }} + restore-keys: ${{ runner.os }}-${{ github.job }}-helia- + - run: sudo apt update + - run: sudo apt install -y libxkbcommon0 libxdamage1 libgbm1 libpango-1.0-0 libcairo2 # dependencies for playwright + - run: npx --package @helia/interop helia-interop + env: + KUBO_BINARY: ${{ github.workspace }}/cmd/ipfs/ipfs + ipfs-webui: + needs: [interop-prep] + runs-on: ${{ fromJSON(github.repository == 'ipfs/kubo' && '["self-hosted", "linux", "x64", "2xlarge"]' || '"ubuntu-latest"') }} + timeout-minutes: 20 + env: + NO_SANDBOX: true + LIBP2P_TCP_REUSEPORT: false + LIBP2P_ALLOW_WEAK_RSA_KEYS: 1 + E2E_IPFSD_TYPE: go + TRAVIS: 1 + GIT_PAGER: cat + IPFS_CHECK_RCMGR_DEFAULTS: 1 + defaults: + run: + shell: bash + steps: + - uses: actions/setup-node@v4 + with: + node-version: 18.14.0 + - uses: actions/download-artifact@v4 + with: + name: kubo + path: cmd/ipfs + - run: chmod +x cmd/ipfs/ipfs + - uses: actions/checkout@v4 + with: + repository: ipfs/ipfs-webui + path: ipfs-webui + - run: | + echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT + id: npm-cache-dir + - uses: actions/cache@v4 + with: + path: ${{ steps.npm-cache-dir.outputs.dir }} + key: ${{ runner.os }}-${{ github.job }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-${{ github.job }}- + - env: + NPM_CACHE_DIR: ${{ steps.npm-cache-dir.outputs.dir }} + run: | + npm ci --prefer-offline --no-audit --progress=false --cache "$NPM_CACHE_DIR" + npx playwright install --with-deps + working-directory: ipfs-webui + - id: ref + run: echo "ref=$(git rev-parse --short HEAD)" | tee -a $GITHUB_OUTPUT + working-directory: ipfs-webui + - id: state + env: + GITHUB_TOKEN: ${{ github.token }} + ENDPOINT: repos/ipfs/ipfs-webui/commits/${{ steps.ref.outputs.ref }}/status + SELECTOR: .state + KEY: state + run: gh api "$ENDPOINT" --jq "$SELECTOR" | xargs -I{} echo "$KEY={}" | tee -a $GITHUB_OUTPUT + - name: Build ipfs-webui@main (state=${{ steps.state.outputs.state }}) + run: npm run test:build + working-directory: ipfs-webui + - name: Test ipfs-webui@main (state=${{ steps.state.outputs.state }}) E2E against the locally built Kubo binary + run: npm run test:e2e + env: + IPFS_GO_EXEC: ${{ github.workspace }}/cmd/ipfs/ipfs + working-directory: ipfs-webui diff --git a/.github/workflows/sharness.yml b/.github/workflows/sharness.yml new file mode 100644 index 000000000..ac91b1321 --- /dev/null +++ b/.github/workflows/sharness.yml @@ -0,0 +1,123 @@ +name: Sharness + +on: + workflow_dispatch: + pull_request: + paths-ignore: + - '**/*.md' + push: + branches: + - 'master' + +concurrency: + group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event_name == 'push' && github.sha || github.ref }} + cancel-in-progress: true + +jobs: + sharness-test: + if: github.repository == 'ipfs/kubo' || github.event_name == 'workflow_dispatch' + runs-on: ${{ fromJSON(github.repository == 'ipfs/kubo' && '["self-hosted", "linux", "x64", "4xlarge"]' || '"ubuntu-latest"') }} + timeout-minutes: 20 + defaults: + run: + shell: bash + steps: + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: 1.23.x + - name: Checkout Kubo + uses: actions/checkout@v4 + with: + path: kubo + - name: Install missing tools + run: sudo apt update && sudo apt install -y socat net-tools fish libxml2-utils + - uses: actions/cache@v4 + with: + path: test/sharness/lib/dependencies + key: ${{ runner.os }}-test-generate-junit-html-${{ hashFiles('test/sharness/lib/test-generate-junit-html.sh') }} + - name: Run Sharness tests + run: | + make -O -j "$PARALLEL" \ + test_sharness \ + coverage/sharness_tests.coverprofile \ + test/sharness/test-results/sharness.xml + working-directory: kubo + env: + TEST_DOCKER: 1 + TEST_PLUGIN: 0 + TEST_FUSE: 0 + TEST_VERBOSE: 1 + TEST_JUNIT: 1 + TEST_EXPENSIVE: 1 + IPFS_CHECK_RCMGR_DEFAULTS: 1 + CONTINUE_ON_S_FAILURE: 1 + # increasing parallelism beyond 10 doesn't speed up the tests much + PARALLEL: ${{ github.repository == 'ipfs/kubo' && 10 || 3 }} + - name: Upload coverage report + uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0 + if: failure() || success() + with: + name: sharness + files: kubo/coverage/sharness_tests.coverprofile + - name: Aggregate results + run: find kubo/test/sharness/test-results -name 't*-*.sh.*.counts' | kubo/test/sharness/lib/sharness/aggregate-results.sh > kubo/test/sharness/test-results/summary.txt + - name: 👉️ If this step failed, go to «Summary» (top left) → «HTML Report» → inspect the «Failures» column + run: | + cat kubo/test/sharness/test-results/summary.txt && + grep 'failed\s*0' kubo/test/sharness/test-results/summary.txt + - name: Add aggregate results to the summary + if: failure() || success() + run: | + echo "# Summary" >> $GITHUB_STEP_SUMMARY + echo >> $GITHUB_STEP_SUMMARY + cat kubo/test/sharness/test-results/summary.txt >> $GITHUB_STEP_SUMMARY + - name: Generate one-page HTML report + uses: ipdxco/junit-xml-to-html@v1 + if: failure() || success() + with: + mode: no-frames + input: kubo/test/sharness/test-results/sharness.xml + output: kubo/test/sharness/test-results/sharness.html + - name: Upload one-page HTML report to S3 + id: one-page + uses: ipdxco/custom-github-runners/.github/actions/upload-artifact@main + if: github.repository == 'ipfs/kubo' && (failure() || success()) + with: + source: kubo/test/sharness/test-results/sharness.html + destination: sharness.html + - name: Upload one-page HTML report + if: github.repository != 'ipfs/kubo' && (failure() || success()) + uses: actions/upload-artifact@v4 + with: + name: sharness.html + path: kubo/test/sharness/test-results/sharness.html + - name: Generate full HTML report + uses: ipdxco/junit-xml-to-html@v1 + if: failure() || success() + with: + mode: frames + input: kubo/test/sharness/test-results/sharness.xml + output: kubo/test/sharness/test-results/sharness-html + - name: Upload full HTML report to S3 + id: full + uses: ipdxco/custom-github-runners/.github/actions/upload-artifact@main + if: github.repository == 'ipfs/kubo' && (failure() || success()) + with: + source: kubo/test/sharness/test-results/sharness-html + destination: sharness-html/ + - name: Upload full HTML report + if: github.repository != 'ipfs/kubo' && (failure() || success()) + uses: actions/upload-artifact@v4 + with: + name: sharness-html + path: kubo/test/sharness/test-results/sharness-html + - name: Add S3 links to the summary + if: github.repository == 'ipfs/kubo' && (failure() || success()) + run: echo "$MD" >> $GITHUB_STEP_SUMMARY + env: + MD: | + # HTML Reports + + - View the [one page HTML report](${{ steps.one-page.outputs.url }}) + - View the [full HTML report](${{ steps.full.outputs.url }}index.html) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 6f6d895d1..16d65d721 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -2,25 +2,12 @@ name: Close and mark stale issue on: schedule: - - cron: '0 0 * * *' + - cron: '0 0 * * *' + +permissions: + issues: write + pull-requests: write jobs: stale: - - runs-on: ubuntu-latest - permissions: - issues: write - pull-requests: write - - steps: - - uses: actions/stale@v3 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - stale-issue-message: 'Oops, seems like we needed more information for this issue, please comment with more details or this issue will be closed in 7 days.' - close-issue-message: 'This issue was closed because it is missing author input.' - stale-issue-label: 'kind/stale' - any-of-labels: 'need/author-input' - exempt-issue-labels: 'need/triage,need/community-input,need/maintainer-input,need/maintainers-input,need/analysis,status/blocked,status/in-progress,status/ready,status/deferred,status/inactive' - days-before-issue-stale: 6 - days-before-issue-close: 7 - enable-statistics: true + uses: pl-strflt/.github/.github/workflows/reusable-stale-issue.yml@v0.3 diff --git a/.github/workflows/sync-release-assets.yml b/.github/workflows/sync-release-assets.yml index da3ca9582..0d5c8199b 100644 --- a/.github/workflows/sync-release-assets.yml +++ b/.github/workflows/sync-release-assets.yml @@ -1,4 +1,4 @@ -name: Sync github release assets with dist.ipfs.tech +name: Sync GitHub Release Assets on: workflow_dispatch: @@ -9,27 +9,31 @@ concurrency: group: release-assets-dist-sync cancel-in-progress: true +permissions: + contents: write # to upload release asset + jobs: - sync-github-and-dist-ipfs-tech: + dist-ipfs-tech: if: github.repository == 'ipfs/kubo' || github.event_name == 'workflow_dispatch' runs-on: "ubuntu-latest" + timeout-minutes: 15 steps: - uses: ipfs/download-ipfs-distribution-action@v1 - uses: ipfs/start-ipfs-daemon-action@v1 with: args: --init --init-profile=flatfs,server --enable-gc=false - - uses: actions/setup-node@v2 + - uses: actions/setup-node@v4 with: node-version: 14 - name: Sync the latest 5 github releases - uses: actions/github-script@v4 + uses: actions/github-script@v7 with: script: | const fs = require('fs').promises const max_synced = 5 // fetch github releases - resp = await github.repos.listReleases({ + resp = await github.rest.repos.listReleases({ owner: context.repo.owner, repo: context.repo.repo, page: 1, @@ -112,7 +116,7 @@ jobs: } console.log("uploading", file, "to github release", release.tag_name) - const uploadReleaseAsset = async (file) => github.repos.uploadReleaseAsset({ + const uploadReleaseAsset = async (file) => github.rest.repos.uploadReleaseAsset({ owner: context.repo.owner, repo: context.repo.repo, release_id: release.id, diff --git a/.github/workflows/testground-on-push.yml b/.github/workflows/testground-on-push.yml deleted file mode 100644 index 7b55dbdee..000000000 --- a/.github/workflows/testground-on-push.yml +++ /dev/null @@ -1,38 +0,0 @@ ---- -name: Testground PR Checker - -on: - workflow_dispatch: - push: - - -jobs: - testground: - if: github.repository == 'ipfs/kubo' || github.event_name == 'workflow_dispatch' - runs-on: ubuntu-latest - name: ${{ matrix.composition_file }} - strategy: - matrix: - include: - - backend_addr: ci.testground.ipfs.team - backend_proto: https - plan_directory: testplans/bitswap - composition_file: testplans/bitswap/_compositions/small-k8s.toml - - backend_addr: ci.testground.ipfs.team - backend_proto: https - plan_directory: testplans/bitswap - composition_file: testplans/bitswap/_compositions/medium-k8s.toml - - backend_addr: ci.testground.ipfs.team - backend_proto: https - plan_directory: testplans/bitswap - composition_file: testplans/bitswap/_compositions/large-k8s.toml - steps: - - uses: actions/checkout@v2 - - name: testground run - uses: testground/testground-github-action@v1 - timeout-minutes: 5 - with: - backend_addr: ${{ matrix.backend_addr }} - backend_proto: ${{ matrix.backend_proto }} - plan_directory: ${{ matrix.plan_directory }} - composition_file: ${{ matrix.composition_file }} diff --git a/.golangci.yml b/.golangci.yml index 2c46046aa..11772b053 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -4,5 +4,8 @@ linters: linters-settings: stylecheck: + checks: + - all + - '-ST1003' dot-import-whitelist: - github.com/ipfs/kubo/test/cli/testutils diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b5a5769f..621e07d7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Kubo Changelogs +- [v0.32](docs/changelogs/v0.32.md) +- [v0.31](docs/changelogs/v0.31.md) +- [v0.30](docs/changelogs/v0.30.md) +- [v0.29](docs/changelogs/v0.29.md) +- [v0.28](docs/changelogs/v0.28.md) +- [v0.27](docs/changelogs/v0.27.md) +- [v0.26](docs/changelogs/v0.26.md) +- [v0.25](docs/changelogs/v0.25.md) +- [v0.24](docs/changelogs/v0.24.md) +- [v0.23](docs/changelogs/v0.23.md) +- [v0.22](docs/changelogs/v0.22.md) +- [v0.21](docs/changelogs/v0.21.md) +- [v0.20](docs/changelogs/v0.20.md) +- [v0.19](docs/changelogs/v0.19.md) - [v0.18](docs/changelogs/v0.18.md) - [v0.17](docs/changelogs/v0.17.md) - [v0.16](docs/changelogs/v0.16.md) diff --git a/Dockerfile b/Dockerfile index 57e650710..e04b3c666 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,6 @@ -FROM golang:1.19.1-buster -LABEL maintainer="Steven Allen " +FROM --platform=${BUILDPLATFORM:-linux/amd64} golang:1.23 AS builder -# Install deps -RUN apt-get update && apt-get install -y \ - libssl-dev \ - ca-certificates \ - fuse +ARG TARGETOS TARGETARCH ENV SRC_DIR /kubo @@ -20,44 +15,46 @@ COPY . $SRC_DIR # e.g. docker build --build-arg IPFS_PLUGINS="foo bar baz" ARG IPFS_PLUGINS +# Allow for other targets to be built, e.g.: docker build --build-arg MAKE_TARGET="nofuse" +ARG MAKE_TARGET=build + # Build the thing. # Also: fix getting HEAD commit hash via git rev-parse. RUN cd $SRC_DIR \ && mkdir -p .git/objects \ - && GOFLAGS=-buildvcs=false make build GOTAGS=openssl IPFS_PLUGINS=$IPFS_PLUGINS + && GOOS=$TARGETOS GOARCH=$TARGETARCH GOFLAGS=-buildvcs=false make ${MAKE_TARGET} IPFS_PLUGINS=$IPFS_PLUGINS -# Get su-exec, a very minimal tool for dropping privileges, -# and tini, a very minimal init daemon for containers -ENV SUEXEC_VERSION v0.2 -ENV TINI_VERSION v0.19.0 +# Using Debian Buster because the version of busybox we're using is based on it +# and we want to make sure the libraries we're using are compatible. That's also +# why we're running this for the target platform. +FROM debian:stable-slim AS utilities RUN set -eux; \ - dpkgArch="$(dpkg --print-architecture)"; \ - case "${dpkgArch##*-}" in \ - "amd64" | "armhf" | "arm64") tiniArch="tini-static-$dpkgArch" ;;\ - *) echo >&2 "unsupported architecture: ${dpkgArch}"; exit 1 ;; \ - esac; \ - cd /tmp \ - && git clone https://github.com/ncopa/su-exec.git \ - && cd su-exec \ - && git checkout -q $SUEXEC_VERSION \ - && make su-exec-static \ - && cd /tmp \ - && wget -q -O tini https://github.com/krallin/tini/releases/download/$TINI_VERSION/$tiniArch \ - && chmod +x tini + apt-get update; \ + apt-get install -y \ + tini \ + # Using gosu (~2MB) instead of su-exec (~20KB) because it's easier to + # install on Debian. Useful links: + # - https://github.com/ncopa/su-exec#why-reinvent-gosu + # - https://github.com/tianon/gosu/issues/52#issuecomment-441946745 + gosu \ + # This installs fusermount which we later copy over to the target image. + fuse \ + ca-certificates \ + ; \ + rm -rf /var/lib/apt/lists/* # Now comes the actual target image, which aims to be as small as possible. -FROM busybox:1.31.1-glibc -LABEL maintainer="Steven Allen " +FROM busybox:stable-glibc # Get the ipfs binary, entrypoint script, and TLS CAs from the build container. ENV SRC_DIR /kubo -COPY --from=0 $SRC_DIR/cmd/ipfs/ipfs /usr/local/bin/ipfs -COPY --from=0 $SRC_DIR/bin/container_daemon /usr/local/bin/start_ipfs -COPY --from=0 $SRC_DIR/bin/container_init_run /usr/local/bin/container_init_run -COPY --from=0 /tmp/su-exec/su-exec-static /sbin/su-exec -COPY --from=0 /tmp/tini /sbin/tini -COPY --from=0 /bin/fusermount /usr/local/bin/fusermount -COPY --from=0 /etc/ssl/certs /etc/ssl/certs +COPY --from=utilities /usr/sbin/gosu /sbin/gosu +COPY --from=utilities /usr/bin/tini /sbin/tini +COPY --from=utilities /bin/fusermount /usr/local/bin/fusermount +COPY --from=utilities /etc/ssl/certs /etc/ssl/certs +COPY --from=builder $SRC_DIR/cmd/ipfs/ipfs /usr/local/bin/ipfs +COPY --from=builder $SRC_DIR/bin/container_daemon /usr/local/bin/start_ipfs +COPY --from=builder $SRC_DIR/bin/container_init_run /usr/local/bin/container_init_run # Add suid bit on fusermount so it will run properly RUN chmod 4755 /usr/local/bin/fusermount @@ -65,13 +62,6 @@ RUN chmod 4755 /usr/local/bin/fusermount # Fix permissions on start_ipfs (ignore the build machine's permissions) RUN chmod 0755 /usr/local/bin/start_ipfs -# This shared lib (part of glibc) doesn't seem to be included with busybox. -COPY --from=0 /lib/*-linux-gnu*/libdl.so.2 /lib/ - -# Copy over SSL libraries. -COPY --from=0 /usr/lib/*-linux-gnu*/libssl.so* /usr/lib/ -COPY --from=0 /usr/lib/*-linux-gnu*/libcrypto.so* /usr/lib/ - # Swarm TCP; should be exposed to the public EXPOSE 4001 # Swarm UDP; should be exposed to the public @@ -113,7 +103,7 @@ ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/start_ipfs"] # Healthcheck for the container # QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn is the CID of empty folder HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ - CMD ipfs dag stat /ipfs/QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn || exit 1 + CMD ipfs --api=/ip4/127.0.0.1/tcp/5001 dag stat /ipfs/QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn || exit 1 # Execute the daemon subcommand by default CMD ["daemon", "--migrate=true", "--agent-version-suffix=docker"] diff --git a/README.md b/README.md index e734fef66..30a884e96 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,36 @@ +

+
+ Kubo logo +
+ Kubo: IPFS Implementation in GO +
+

-![kubo, an IPFS node in Go](https://ipfs.io/ipfs/bafykbzacecaesuqmivkauix25v6i6xxxsvsrtxknhgb5zak3xxsg2nb4dhs2u/ipfs.go.png) +

The first implementation of IPFS.

-[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square&cacheSeconds=3600)](https://protocol.ai) -[![GoDoc](https://img.shields.io/badge/godoc-reference-5272B4.svg?style=flat-square&cacheSeconds=3600)](https://godoc.org/github.com/ipfs/kubo) -[![CircleCI](https://img.shields.io/circleci/build/github/ipfs/kubo?style=flat-square&cacheSeconds=3600)](https://circleci.com/gh/ipfs/kubo) +

+ Official Part of IPFS Project + Discourse Forum + Matrix + ci + GitHub release + godoc reference +

+ +
## What is Kubo? Kubo was the first IPFS implementation and is the most widely used one today. Implementing the *Interplanetary Filesystem* - the Web3 standard for content-addressing, interoperable with HTTP. Thus powered by IPLD's data models and the libp2p for network communication. Kubo is written in Go. Featureset -- Runs an IPFS-Node as a network service -- [Command Line Interface](https://docs.ipfs.tech/reference/kubo/cli/) to IPFS-Nodes -- Local [Web2-to-Web3 HTTP Gateway functionality](https://github.com/ipfs/specs/tree/main/http-gateways#readme) -- HTTP RPC API (`/api/v0`) to access and control the daemon -- IPFS's internal Webgui can be used to manage the Kubo nodes +- Runs an IPFS-Node as a network service that is part of LAN and WAN DHT +- [HTTP Gateway](https://specs.ipfs.tech/http-gateways/) (`/ipfs` and `/ipns`) functionality for trusted and [trustless](https://docs.ipfs.tech/reference/http/gateway/#trustless-verifiable-retrieval) content retrieval +- [HTTP Routing V1](https://specs.ipfs.tech/routing/http-routing-v1/) (`/routing/v1`) client and server implementation for [delegated routing](./docs/delegated-routing.md) lookups +- [HTTP Kubo RPC API](https://docs.ipfs.tech/reference/kubo/rpc/) (`/api/v0`) to access and control the daemon +- [Command Line Interface](https://docs.ipfs.tech/reference/kubo/cli/) based on (`/api/v0`) RPC API +- [WebUI](https://github.com/ipfs/ipfs-webui/#readme) to manage the Kubo node +- [Content blocking](/docs/content-blocking.md) support for operators of public nodes ### Other implementations @@ -33,7 +49,7 @@ Before opening an issue, consider using one of the following locations to ensure - Exploration of new ideas in [ipfs/notes issues](https://github.com/ipfs/notes/issues). - Ask questions and meet the rest of the community at the [IPFS Forum](https://discuss.ipfs.tech). - Or [chat with us](https://docs.ipfs.tech/community/chat/). - + [![YouTube Channel Subscribers](https://img.shields.io/youtube/channel/subscribers/UCdjsUXJ3QawK4O5L1kqqsew?label=Subscribe%20IPFS&style=social&cacheSeconds=3600)](https://www.youtube.com/channel/UCdjsUXJ3QawK4O5L1kqqsew) [![Follow @IPFS on Twitter](https://img.shields.io/twitter/follow/IPFS?style=social&cacheSeconds=3600)](https://twitter.com/IPFS) ## Next milestones @@ -48,8 +64,8 @@ Before opening an issue, consider using one of the following locations to ensure - [Next milestones](#next-milestones) - [Table of Contents](#table-of-contents) - [Security Issues](#security-issues) +- [Minimal System Requirements](#minimal-system-requirements) - [Install](#install) - - [System Requirements](#system-requirements) - [Docker](#docker) - [Official prebuilt binaries](#official-prebuilt-binaries) - [Updating](#updating) @@ -57,23 +73,24 @@ Before opening an issue, consider using one of the following locations to ensure - [Downloading builds using IPFS](#downloading-builds-using-ipfs) - [Unofficial Linux packages](#unofficial-linux-packages) - [ArchLinux](#arch-linux) + - [Gentoo Linux](#gentoo-linux) - [Nix](#nix) - [Solus](#solus) - [openSUSE](#opensuse) - [Guix](#guix) - [Snap](#snap) + - [Ubuntu PPA](#ubuntu-ppa) - [Unofficial Windows packages](#unofficial-windows-packages) - [Chocolatey](#chocolatey) - [Scoop](#scoop) - [Unofficial MacOS packages](#unofficial-macos-packages) - [MacPorts](#macports) - [Nix](#nix-macos) - - [Homebrew](#homebrew) + - [Homebrew](#homebrew) - [Build from Source](#build-from-source) - [Install Go](#install-go) - [Download and Compile IPFS](#download-and-compile-ipfs) - [Cross Compiling](#cross-compiling) - - [OpenSSL](#openssl) - [Troubleshooting](#troubleshooting) - [Getting Started](#getting-started) - [Usage](#usage) @@ -94,26 +111,38 @@ Before opening an issue, consider using one of the following locations to ensure Please follow [`SECURITY.md`](SECURITY.md). +### Minimal System Requirements + +IPFS can run on most Linux, macOS, and Windows systems. We recommend running it on a machine with at least 4 GB of RAM and 2 CPU cores (kubo is highly parallel). On systems with less memory, it may not be completely stable, and you run on your own risk. + ## Install The canonical download instructions for IPFS are over at: https://docs.ipfs.tech/install/. It is **highly recommended** you follow those instructions if you are not interested in working on IPFS development. -### System Requirements - -IPFS can run on most Linux, macOS, and Windows systems. We recommend running it on a machine with at least 2 GB of RAM and 2 CPU cores (kubo is highly parallel). On systems with less memory, it may not be completely stable. - -If your system is resource-constrained, we recommend: - -1. Installing OpenSSL and rebuilding kubo manually with `make build GOTAGS=openssl`. See the [download and compile](#download-and-compile-ipfs) section for more information on compiling kubo. -2. Initializing your daemon with `ipfs init --profile=lowpower` - ### Docker Official images are published at https://hub.docker.com/r/ipfs/kubo/: [![Docker Image Version (latest semver)](https://img.shields.io/docker/v/ipfs/kubo?color=blue&label=kubo%20docker%20image&logo=docker&sort=semver&style=flat-square&cacheSeconds=3600)](https://hub.docker.com/r/ipfs/kubo/) -More info on how to run Kubo (go-ipfs) inside Docker can be found [here](https://docs.ipfs.tech/how-to/run-ipfs-inside-docker/). +- 🟢 Releases + - `latest` and `release` tags always point at [the latest stable release](https://github.com/ipfs/kubo/releases/latest) + - `vN.N.N` points at a specific [release tag](https://github.com/ipfs/kubo/releases) + - These are production grade images. +- 🟠 We also provide experimental developer builds + - `master-latest` always points at the `HEAD` of the `master` branch + - `master-YYYY-DD-MM-GITSHA` points at a specific commit from the `master` branch + - These tags are used by developers for internal testing, not intended for end users or production use. + +```console +$ docker pull ipfs/kubo:latest +$ docker run --rm -it --net=host ipfs/kubo:latest +``` + +To [customize your node](https://docs.ipfs.tech/install/run-ipfs-inside-docker/#customizing-your-node), +pass necessary config via `-e` or by mounting scripts in the `/container-init.d`. + +Learn more at https://docs.ipfs.tech/install/run-ipfs-inside-docker/ ### Official prebuilt binaries @@ -166,12 +195,18 @@ $ ipfs get /ipns/dist.ipfs.tech/kubo/$VERSION/kubo_$VERSION_windows-amd64.zip ### Unofficial Linux packages + + Packaging status + + - [ArchLinux](#arch-linux) -- [Nix](#nix) +- [Gentoo Linux](#gentoo-linux) +- [Nix](#nix-linux) - [Solus](#solus) - [openSUSE](#opensuse) - [Guix](#guix) - [Snap](#snap) +- [Ubuntu PPA](#ubuntu-ppa) #### Arch Linux @@ -183,15 +218,25 @@ $ ipfs get /ipns/dist.ipfs.tech/kubo/$VERSION/kubo_$VERSION_windows-amd64.zip [![kubo-git via AUR](https://img.shields.io/static/v1?label=kubo-git&message=latest%40master&color=1793d1&logo=arch-linux&style=flat-square&cacheSeconds=3600)](https://aur.archlinux.org/packages/kubo/) +#### Gentoo Linux + +https://wiki.gentoo.org/wiki/Kubo + +```bash +# emerge -a net-p2p/kubo +``` + +https://packages.gentoo.org/packages/net-p2p/kubo + #### Nix With the purely functional package manager [Nix](https://nixos.org/nix/) you can install kubo (go-ipfs) like this: ``` -$ nix-env -i ipfs +$ nix-env -i kubo ``` -You can also install the Package by using its attribute name, which is also `ipfs`. +You can also install the Package by using its attribute name, which is also `kubo`. #### Solus @@ -215,6 +260,31 @@ You can also install it through the Solus software center. No longer supported, see rationale in [kubo#8688](https://github.com/ipfs/kubo/issues/8688). +#### Ubuntu PPA + +[PPA homepage](https://launchpad.net/~twdragon/+archive/ubuntu/ipfs) on Launchpad. + +##### Latest Ubuntu (>= 20.04 LTS) +```sh +sudo add-apt-repository ppa:twdragon/ipfs +sudo apt update +sudo apt install ipfs-kubo +``` + +##### Any Ubuntu version + +```sh +sudo su +echo 'deb https://ppa.launchpadcontent.net/twdragon/ipfs/ubuntu <> main' >> /etc/apt/sources.list.d/ipfs +echo 'deb-src https://ppa.launchpadcontent.net/twdragon/ipfs/ubuntu <> main' >> /etc/apt/sources.list.d/ipfs +exit +sudo apt update +sudo apt install ipfs-kubo +``` +where `<>` is the codename of your Ubuntu distribution (for example, `jammy` for 22.04 LTS). During the first installation the package maintenance script may automatically ask you about which networking profile, CPU accounting model, and/or existing node configuration file you want to use. + +**NOTE**: this method also may work with any compatible Debian-based distro which has `libc6` inside, and APT as a package manager. + ### Unofficial Windows packages - [Chocolatey](#chocolatey) @@ -252,10 +322,10 @@ $ sudo port install ipfs In macOS you can use the purely functional package manager [Nix](https://nixos.org/nix/): ``` -$ nix-env -i ipfs +$ nix-env -i kubo ``` -You can also install the Package by using its attribute name, which is also `ipfs`. +You can also install the Package by using its attribute name, which is also `kubo`. #### Homebrew @@ -313,15 +383,6 @@ Compiling for a different platform is as simple as running: make build GOOS=myTargetOS GOARCH=myTargetArchitecture ``` -##### OpenSSL - -To build go-ipfs with OpenSSL support, append `GOTAGS=openssl` to your `make` invocation. Building with OpenSSL should significantly reduce the background CPU usage on nodes that frequently make or receive new connections. - -Note: OpenSSL requires CGO support and, by default, CGO is disabled when cross-compiling. To cross-compile with OpenSSL support, you must: - -1. Install a compiler toolchain for the target platform. -2. Set the `CGO_ENABLED=1` environment variable. - #### Troubleshooting - Separate [instructions are available for building on Windows](docs/windows.md). @@ -381,7 +442,6 @@ Some places to get you started on the codebase: - libp2p - libp2p: https://github.com/libp2p/go-libp2p - DHT: https://github.com/libp2p/go-libp2p-kad-dht - - PubSub: https://github.com/libp2p/go-libp2p-pubsub - [IPFS : The `Add` command demystified](https://github.com/ipfs/kubo/tree/master/docs/add-code-flow.md) ### Map of Implemented Subsystems @@ -411,9 +471,11 @@ If you make changes to the protocol buffers, you will need to install the [proto Find more documentation for developers on [docs](./docs) ## Maintainer Info -* [Project Board for active and upcoming work](https://pl-strflt.notion.site/Kubo-GitHub-Project-Board-c68f9192e48e4e9eba185fa697bf0570) -* [Release Process](https://pl-strflt.notion.site/Kubo-Release-Process-5a5d066264704009a28a79cff93062c4) -* [Additional PL EngRes Kubo maintainer info](https://pl-strflt.notion.site/Kubo-go-ipfs-4a484aeeaa974dcf918027c300426c05) + +Kubo is maintained by [Shipyard](https://ipshipyard.com/). + +* This repository is part of [Shipyard's GO Triage triage](https://ipshipyard.notion.site/IPFS-Go-Triage-Boxo-Kubo-Rainbow-0ddee6b7f28d412da7dabe4f9107c29a). +* [Release Process](https://ipshipyard.notion.site/Kubo-Release-Process-6dba4f5755c9458ab5685eeb28173778) ## Contributing @@ -424,7 +486,9 @@ We ❤️ all [our contributors](docs/AUTHORS); this project wouldn’t be what This repository falls under the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md). -Please reach out to us in one [chat](https://docs.ipfs.tech/community/chat/) rooms. +Members of IPFS community provide Kubo support on [discussion forum category here](https://discuss.ipfs.tech/c/help/help-kubo/23). + +Need help with IPFS itself? Learn where to get help and support at https://ipfs.tech/help. ## License diff --git a/Rules.mk b/Rules.mk index 3d6f621cc..ef88bee0f 100644 --- a/Rules.mk +++ b/Rules.mk @@ -19,7 +19,7 @@ include mk/golang.mk # extra properties # # -------------------- # -ifeq ($(TEST_NO_FUSE),1) +ifeq ($(TEST_FUSE),0) GOTAGS += nofuse endif export LIBP2P_TCP_REUSEPORT=false @@ -66,6 +66,10 @@ clean: rm -rf $(CLEAN) .PHONY: clean +mod_tidy: + @find . -name go.mod -execdir $(GOCC) mod tidy \; +.PHONY: mod_tidy + coverage: $(COVERAGE) .PHONY: coverage @@ -118,7 +122,8 @@ help: @echo ' all - print this help message' @echo ' build - Build binary at ./cmd/ipfs/ipfs' @echo ' nofuse - Build binary with no fuse support' - @echo ' install - Build binary and install into $$GOPATH/bin' + @echo ' install - Build binary and install into $$GOBIN' + @echo ' mod_tidy - Remove unused dependencies from go.mod files' # @echo ' dist_install - TODO: c.f. ./cmd/ipfs/dist/README.md' @echo '' @echo 'CLEANING TARGETS:' @@ -136,8 +141,7 @@ help: @echo ' test_go_expensive - Run all go tests and compile on all platforms' @echo ' test_go_race - Run go tests with the race detector enabled' @echo ' test_go_lint - Run the `golangci-lint` vetting tool' - @echo ' test_sharness_short - Run short sharness tests' - @echo ' test_sharness_expensive - Run all sharness tests' + @echo ' test_sharness - Run sharness tests' @echo ' coverage - Collects coverage info from unit tests and sharness' @echo .PHONY: help diff --git a/appveyor.yml b/appveyor.yml index 696102ffc..5f2907d00 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,8 +14,8 @@ environment: GOPATH: c:\gopath TEST_VERBOSE: 1 #TEST_NO_FUSE: 1 - #TEST_SUITE: test_sharness_expensive - #GOFLAGS: -tags nofuse + #TEST_SUITE: test_sharness + #GOFLAGS: -tags nofuse global: BASH: C:\cygwin\bin\bash matrix: @@ -23,27 +23,27 @@ environment: GOVERSION: 1.5.1 GOROOT: c:\go DOWNLOADPLATFORM: "x64" - + install: # Enable make #- SET PATH=c:\MinGW\bin;%PATH% #- copy c:\MinGW\bin\mingw32-make.exe c:\MinGW\bin\make.exe - go version - go env - + # Cygwin build script # # NOTES: # # The stdin/stdout file descriptor appears not to be valid for the Appveyor -# build which causes failures as certain functions attempt to redirect +# build which causes failures as certain functions attempt to redirect # default file handles. Ensure a dummy file descriptor is opened with 'exec'. -# +# build_script: - '%BASH% -lc "cd $APPVEYOR_BUILD_FOLDER; exec 0 HTML representation for non-UnixFS DAGs such as DAG-CBOR. diff --git a/assets/dag-index-html/index.go b/assets/dag-index-html/index.go deleted file mode 100644 index 214b06a38..000000000 --- a/assets/dag-index-html/index.go +++ /dev/null @@ -1,81 +0,0 @@ -package dagindexhtml - -import "html/template" - -// TODO: DagIndexTemplate - replace static CSS with shared one with ../dir-index-html - -// DagIndexTemplate is HTML-based template for non-UnixFS DAGs when request was -// made with Accept: text/html (web browsers). -var DagIndexTemplate = template.Must(template.New("redirect").Parse(` - - - - - - - - - - - - - - - - - - {{ .Path }} - - - - -
-
-

CID: {{.CID}}
- Codec: {{.CodecName}} ({{.CodecHex}})

-
-
- - - - - - - -
-

Preview as JSON
(application/json)

-
-

Or download as: -

-

-
-
-
- -`)) - -type DagIndexTemplateData struct { - Path string - CID string - CodecName string - CodecHex string -} diff --git a/assets/dir-index-html/README.md b/assets/dir-index-html/README.md deleted file mode 100644 index 3dd45eb59..000000000 --- a/assets/dir-index-html/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# dir-index-html - -> Directory listing HTML for HTTP gateway - -![](https://user-images.githubusercontent.com/157609/88379209-ce6f0600-cda2-11ea-9620-20b9237bb441.png) - -## Updating - -When making updates to the directory listing page template, please note the following: - -1. Make your changes to the (human-friendly) source documents in the `src` directory and run `npm run build` -3. Before testing or releasing, go to the top-level `./assets` directory and make sure to run the `go generate .` script to update the bindata version - -## Testing - -1. Make sure you have [Go](https://golang.org/dl/) installed -2. Start the test server, which lives in its own directory: - -```bash -> cd test -> go run . -``` -This will listen on [`localhost:3000`](http://localhost:3000/) and reload the template every time you refresh the page. - -If you get a "no such file or directory" error upon trying `go run .`, make sure you ran `npm run build` to generate the minified artifact that the test is looking for. - diff --git a/assets/dir-index-html/dir-index.html b/assets/dir-index-html/dir-index.html deleted file mode 100644 index d861cb657..000000000 --- a/assets/dir-index-html/dir-index.html +++ /dev/null @@ -1,99 +0,0 @@ - -{{ $root := . }} - - - - - - - - - - - - - - - - - -{{ .Path }} - - - - -
-
-
- - Index of - {{ range .Breadcrumbs -}} - /{{ if .Path }}{{ .Name }}{{ else }}{{ .Name }}{{ end }} - {{- else }} - {{ .Path }} - {{ end }} - - {{ if .Hash }} -
- {{ .Hash }} -
- {{ end }} -
- {{ if .Size }} -
-  {{ .Size }} -
- {{ end }} -
-
- - {{ if .BackLink }} - - - - - - - {{ end }} - {{ range .Listing }} - - - - - - - {{ end }} -
-
 
-
- .. -
-
 
-
- {{ .Name }} - - {{ if .Hash }} - - {{ .ShortHash }} - - {{ end }} - {{ .Size }}
-
-
- - diff --git a/assets/dir-index-html/index.go b/assets/dir-index-html/index.go deleted file mode 100644 index 98933e3f6..000000000 --- a/assets/dir-index-html/index.go +++ /dev/null @@ -1 +0,0 @@ -package dirindexhtml diff --git a/assets/dir-index-html/knownIcons.txt b/assets/dir-index-html/knownIcons.txt deleted file mode 100644 index c110530ea..000000000 --- a/assets/dir-index-html/knownIcons.txt +++ /dev/null @@ -1,65 +0,0 @@ -.aac -.aiff -.ai -.avi -.bmp -.c -.cpp -.css -.dat -.dmg -.doc -.dotx -.dwg -.dxf -.eps -.exe -.flv -.gif -.h -.hpp -.html -.ics -.iso -.java -.jpg -.jpeg -.js -.key -.less -.mid -.mkv -.mov -.mp3 -.mp4 -.mpg -.odf -.ods -.odt -.otp -.ots -.ott -.pdf -.php -.png -.ppt -.psd -.py -.qt -.rar -.rb -.rtf -.sass -.scss -.sql -.tga -.tgz -.tiff -.txt -.wav -.wmv -.xls -.xlsx -.xml -.yml -.zip diff --git a/assets/dir-index-html/package.json b/assets/dir-index-html/package.json deleted file mode 100644 index 4b2427574..000000000 --- a/assets/dir-index-html/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "dir-index-html", - "description": "Directory listing HTML for go-ipfs gateways", - "version": "1.3.0", - "private": true, - "homepage": "https://github.com/ipfs/go-ipfs", - "license": "MIT", - "scripts": { - "start": "cd test && go run .", - "build": "npm run build:clean && npm run build:remove-style-links && npm run build:minify-wrap-css && npm run build:combine-html-css && npm run build:remove-unused", - "build:clean": "rm dir-index.html", - "build:remove-style-links": "sed '/ ./base-html.html", - "build:minify-wrap-css": "(echo \"\") > ./minified-wrapped-style.html", - "build:combine-html-css": "sed '/<\\/title>/ r ./minified-wrapped-style.html' ./base-html.html > ./dir-index.html", - "build:remove-unused": "rm ./base-html.html && rm ./minified-wrapped-style.html" - } -} diff --git a/assets/dir-index-html/src/dir-index.html b/assets/dir-index-html/src/dir-index.html deleted file mode 100644 index 109c7afbf..000000000 --- a/assets/dir-index-html/src/dir-index.html +++ /dev/null @@ -1,98 +0,0 @@ - -{{ $root := . }} - - - - - - - - - - - - - - - - - - - -{{ .Path }} - - - -
-
-
- - Index of - {{ range .Breadcrumbs -}} - /{{ if .Path }}{{ .Name }}{{ else }}{{ .Name }}{{ end }} - {{- else }} - {{ .Path }} - {{ end }} - - {{ if .Hash }} -
- {{ .Hash }} -
- {{ end }} -
- {{ if .Size }} -
-  {{ .Size }} -
- {{ end }} -
-
- - {{ if .BackLink }} - - - - - - - {{ end }} - {{ range .Listing }} - - - - - - - {{ end }} -
-
 
-
- .. -
-
 
-
- {{ .Name }} - - {{ if .Hash }} - - {{ .ShortHash }} - - {{ end }} - {{ .Size }}
-
-
- - diff --git a/assets/dir-index-html/src/icons.css b/assets/dir-index-html/src/icons.css deleted file mode 100644 index dcdbd3cd9..000000000 --- a/assets/dir-index-html/src/icons.css +++ /dev/null @@ -1,403 +0,0 @@ -/* Source - fileicons.org */ - -.ipfs-_blank { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAWBJREFUeNqEUj1LxEAQnd1MVA4lyIEWx6UIKEGUExGsbC3tLfwJ/hT/g7VlCnubqxXBwg/Q4hQP/LhKL5nZuBsvuGfW5MGyuzM7jzdvVuR5DgYnZ+f99ai7Vt5t9K9unu4HLweI3qWYxI6PDosdy0fhcntxO44CcOBzPA7mfEyuHwf7ntQk4jcnywOxIlfxOCNYaLVgb6cXbkTdhJXq2SIlNMC0xIqhHczDbi8OVzpLSUa0WebRfmigLHqj1EcPZnwf7gbDIrYVRyEinurj6jTBHyI7pqVrFQqEbt6TEmZ9v1NRAJNC1xTYxIQh/MmRUlmFQE3qWOW1nqB2TWk1/3tgJV0waVvkFIEeZbHq4ElyKzAmEXOx6gnEVJuWBzmkRJBRPYGZBDsVaOlpSgVJE2yVaAe/0kx/3azBRO0VsbMFZE3CDSZKweZfYIVg+DZ6v7h9GDVOwZPw/PoxKu/fAgwALbDAXf7DdQkAAAAASUVORK5CYII=); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-_page { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmhJREFUeNpsUztv01AYPfdhOy/XTZ80VV1VoCqlA2zQqUgwMEErWBALv4GJDfEDmOEHsFTqVCTExAiiSI2QEKJKESVFFBWo04TESRzfy2c7LY/kLtf2d8+555zvM9NaI1ora5svby9OnbUEBxgDlIKiWjXQeLy19/X17sEtcPY2rtHS96/Hu0RvXXLz+cUzM87zShsI29DpHCYt4E6Box4IZzTnbDx7V74GjhOSfwgE0H2638K9h08A3iHGVbjTw7g6YmAyw/BgecHNGGJjvfQhIfmfIFDAXJpjuugi7djIFVI4P0plctgJQ0xnFe5eOO02OwEp2VkhSCnC8WOCdqgwnzFx4/IyppwRVN+XYXsecqZA1pB48ekAnw9/4GZx3L04N/GoTwEjX4cNH5vlPfjtAIYp8cWrQutxrC5Mod3VsXVTMFSqtaE+gl9dhaUxE2tXZiF7nYiiatJ3v5s8R/1yOCNLOuwjkELiTbmC9dJHpIaGASsDkoFQGJQwHWMcHWJYOmUj1OjvQotuytt5nHMLEGkCyx6QU384jwkUAd2sxJbS/QShZtg/8rHzzQOzSaFhxQrA6YgQMQHojCUlgnCAAvKFBoXXaHfArSCZDE0gyWJgFIKmvUFKO4MUNIk2a4+hODtDUVuJ/J732AKS6ZtImdTyAQQB3bZN8l9t75IFh0JMUdVKsohsUPqRgnka0tYgggYpCHkKGTsHI5NOMojB4iTICCepvX53AIEfQta1iUCmoTiBmdEri2RgddKFhuJoqb/af/yw/d3zTNM6UkaOfis62aUgddAbnz+rXuPY+Vnzjt9/CzAAbmLjCrfBiRgAAAAASUVORK5CYII=); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-aac { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnhJREFUeNp0Uk1PE0EYftruVlvAUkhVEPoBcsEoLRJBY01MPHjCs3cvogcT/4qJJN5NvHhoohcOnPw4YEGIkCh+oLGBKm3Z7nZ3dme2vjOhTcjiJJvZzPvOM8/HG2q325Dr3kLp7Y1ibpIxjs4KhQBZfvV6s7K5Vb0bjeof5ZlcGysP1a51mifODybvzE8mzCbrAoTDIThMoGXZiZ4YSiurf+Z1XeuCqJ7Oj+sK3jQcNAmg8xkGQ71mYejcAB49vpmeuzJccl0+dUj6KIAvfHCPg3N+uAv4vg9BOxcCmfEzuP/genpmeqhEMgude10Jwm+DuUIyUdTlqu2byoMfX/dRermBeExHsTiWNi3+lMpzRwDki8zxCIATmzbevfmClukiP5NFhJgwkjeRTeLShdOoVJqnAgwkgCAZ6+UdLC9twjQZ8pdzioFkZBHY3q6B3l4dJEEEPOCeD4cYVH7Xsf15F+FImC775INAJBJSkVoWo0QY9YqgiR4ZZzRaGBkdwK3bFxGLRZUfB3Rm2x4x9CGtsUxH9QYkKICDFuLxKAozGZwdTqBRs2FbLlXbiPdECMCHadj/AaDXZNFqedCIvnRcS4UpRo7+hC5zUmw8Ope9wUFinvpmZ7NKt2RTmB4hKZo6n8qP4Oq1HBkKlVYAQBrUlziB0XQSif4YmQhksgNIJk9iaLhPaV9b/Um+uJSCdzyDbGZQRSkvjo+n4JNxubGUSsCj+ZCpODYjkGMAND2k7exUsfhkCd+29yguB88Wl7FW/o6tT7/gcXqAgGv7hhx1LWBireHVn79YP6ChQ3njb/eFlfWqGqT3H3ZlGIhGI2i2UO/U/wkwAAmoalcxlNA1AAAAAElFTkSuQmCC); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-ai { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAk5JREFUeNpsU01vElEUPTPzZqBAQaSFQiJYUmlKYhoTF41L3Tbu/Q/+AvsX3Bp/gPsuWLrqyqQ7TUxMtAvF1tYGoXwNw7wv7zwYgtKX3Lw379575p5z77O01ohW+/DVh8zj7aYKhflGdG9ZsGwLNydffgVfr19YHvsEa+Zu/nxndob5StQK+dyzvZzyw/gKlmMj7IygFM+xvNcanp4/t5dAomXHBy2UUBOO2MAl/B9/cPb6PULuoHx0WM0e3GvpUOxD3wZAJWutZqYUYmqpSg5OMgH3YQObL59W0/ullpryR3HegkKEqiWBSGV4R3vQ7sIhScTZFTpHx3A215B5sluVY/WWMg7+ATB/lcLsKpTonHzD+OMFEuTz8ikkt9Kwt9YJZB38cpBdoQAZJdLvCGByfoPB6Xdk90pYy6Xg3c/DaWwArg09DaG5lCsUFN0pckZAojdC8m4auBqaALuSgez7VB1RtDSUWOQvUaBLFUzJBMJ2DwmPgd1Jwm0WoSgJfjDvrTKxtwAIyEkAOQ5hU//Zdg5uowDlUNMnwZLW0sSuUuACYhwQRwFvJxupCjEYUUccOkoaKmdOlZnY1TkgAcXAhxhOwLsDsHoN3u4O5JTDfVCH6I9nfjId3gIgSUATFJk/hVevGtOMwS0XwQ3AzB/FrlKg8Q27I2javVoZrFgwD4qVipAEyMlnaFArzaj/D0DiMXlJAFQyK2r8fnMMRZp4lQ1MaSL5tU/1kqAkMCh2tYI+7+kh70cjPbr4bEZ51jZr8TJnB9PJXpz3V4ABAPOQVJn2Q60GAAAAAElFTkSuQmCC); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-aiff { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAohJREFUeNpkU9tqE1EUXZmZpE3aTBLbJFPTtFURtSCthr7UCyKKFJ/9An3og6Ag/oXfoUj7og9asCBYKT6UIPHaWtpq7NU2aZK5z5wZ9xxMpMwZDuewz9prr32ZiO/7CNaDx3OLt6fOjBqGg/aKRCIInp8+KzfKH7fudnVF58nE16el+/yU2mBFSWZKpWJKVc0OgUBo02K4NDmU6o75Mx+Wdu9IUXFeiOA/pn1xHeYaugVDdzpbp91qGlAKGTx8dC19/Wpxhjnsxj/RRwk85hGJC9d1O6fneWAuoztDYSSLe9OT6SuXB2ccx73Z9uukwDwfls1g0xZIY/Ad/Gnyt/XVfbyYrSDRE8PExHB6/8B6QuaxIwRBFMt0iIAiMx+LCys8jfGJEUik2WpZOD2SQf9oDtVqQwopCAiY66FS/om3b75CVS2MlU7AJ2WiJBCZjZ2dJuRkDJZFwFAR7UCBja3fNfxY2YEoCtRCj9em3Tpds6FpJseGCBxS0GgYGBzqw62p84gnYnAI2CSbSbPhEpFAaE2zODaUAlWWwDoS5DheGqbWpVE/0CmqCY9qkEyINBceb2uADRNQ8bSWAVVzIFKomCQim+0luS4yKYlsHlRyZo7EsSEC23K5vAsXh/H92zZkuRvxeBS5nEx2yp2KqhxPoV5TYS/8CtdApylM9sZQKKSQzyeRTseRV2QoAzIYY8jme5DN9fI0dQoUIjANGydP9VM7PZw9p/AiBpNYrdbw/t0yTJqRtdU9UrfJCUMpSJIgbWzsYe51BcViHzLHeqCRqhZ1YX1tFwNfZBxS9O3NWkAcHqR606k/n/3coKAoV/Y7vQ/OYCZevlrmv3c0GsFh06u3/f4KMABvSWfDHmbK2gAAAABJRU5ErkJggg==); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-avi { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAm1JREFUeNpsU8tu00AUPXZcN0nzTpq2KQ3pAwkIAnWHqCoeexBb+AQ+ABZ8A2s+AIkdm266QUJIFWKBkHg1KpRHi5omJGkbJ3bGHj+4M1EQrTvSyGPPueeec++1EgQBxHp+/9mbyuriRZdxjJaiKBD3W+u1+p9a856max+gDO8ebT+WT20Ezi9NZi/crqadvn2MQBAGfpCOpqNru2937vxPIpY6Onjccx3Twck9MBiSU0ncfHirXFmZX3Md9wqCUwiEVN/zaQfHt0vfbBe5uQyuPVgpl5Zn11ybL4/i/lkICOw5niQRGQShoiqI6Bo43W2ub8n3hRtLZT7gTynk6gkCX9gAOxpAnxhHZDwC1/aI1EViJolu/QhKRMHZ1UX0Gr1USIEn5FPWHy+/wTokkrQOq2vBaHZBN4hmY9Jwfr4An/teiEB45ZZDwDiMhoExT0N+sYDCuUkkplLIlXP4/XEXdo+RUhdhBSSfUwtVTUG8MIHK9QVqI7D/uY6vr2pwmCPrkz+Tk9gwARWQ9WxppbXZhNnpw+ya4A5HZi6L4lIR8WyCcL6sTZiAWjWgAmpxkn5+kqTamK6WkCwmERmLDLvjB0ML9ikWXPLFuozYOap3L8HYN6DHdbS/d5CeTVBndBz87FCBLYkNTyIjBQemnIEsSY5lYrK1+UoWcToLMjEHAyIQ2BCBSx/NVh+ZUhrqmEqBebS3WyhdLg0zt/ugAaIklsSGLHCLa6zDMGhZ2HjyGsnpFPqNHnY2fmHv3R5SMymYbROszSQ2ROAY9qHiofvlxSc5xsKKqqnY3diRE9h4X5d/pzg7lnM4ivsrwADe9Wg/CQJgFAAAAABJRU5ErkJggg==); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-bmp { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmZJREFUeNp0U+1rUlEY/13v9YV0vq2wttI5CdpL9aEGBZUDv0df668I6n+or0UQ/RuuD0EgVDAZrsKF4AR1a6COKW5qXvXec27PuVeda3bgcF6e8/ye5/d7niMZhgExnK9fbTrm5pbBGMZDkgCyq+VyhTUaT6Eo2ZHJePPWXJXRhez3B1yxmM/QdctXUSCgtV4Py4CvY3cky4e1x5DlLCaGbbzjXDcousG5OQe5HPRSCQPK4PpsEM/XH4WvhS4noeu3JwHGGRiULhsMoKZS4I0GtEIB9mgULJGA0+9DPBpBT7sffvf1W/Lg6OgJufw8C0CRGEXWazUwiiyFQjA8bsjVKjaJzovMD/Q5gxyJhG2cvyeXe2cAuADQNGBmBvLaGuTFRaDfh31lBTWi9pumjbK0B4JQul3vOQpM8JdskOLrdCvDcDjAsjtg5TIkoiKLaokMNR2cnZbqNAMycqG7XbHKR2fMzwO/dsxSwu0BiBJsNsv2LwAJAJCI5ux2gXYbqNetcz5PoORI1cDS0n8AxGW7A+zvEYBKZ2ZlcsEtJLbedMjePBaCTQMghx45ulyWkzxMVUQ2RMQhLfFO16YAqCrixPnm6iqKrRb2W23EfF4cUNSrHg90cr7hDyB33MTnSmUKALVs4uIlROjxg+AsPhGVl3fuIl2tIOB0Ya91gkOi9mxhAal0ekork1ic/kGLBORMxy2K1qS9V1ZQbNThIj2EGh+2tsyOnSai8r1UxMNIBB+LRTTULr4Uds0K1tU/uOLxIrmbNz8XXSrnASSpubG9fbKRyVh1n/zSw29t9oC1b47MfwUYAAUsLiWr4QUJAAAAAElFTkSuQmCC); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-c { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAcxJREFUeNqEUk1rE0EYfmZnkgoJCaGNCehuJTalhJZSUZB66a0HwXsP/Qn+FM+9+hty0LNYCr2I7UVLIW0Fc0hpQpSS7O7MrO9MspuvVV8YMnk/nn2e5x0WRRFMvP/w6WSz5jbi/9NxfP693Wp3DrJCnMW5d28P7a+IE15lufR8o1ZEStwPhkWHsWbrZ+eNEPxsuubEF6m0TBv2Q4liPofXuzveulttSqW2UwH+GjqC0horpSL2njU89+FyMwjlTlxOJMTa9ZQHzDQIjgwdom9zLzfXPc75kbnOAswBJTlC2XrqQRMLxhi442DgB4UFBhgPpm3B5pgBHNUUxQKAHs8pHf3TEuFMetM9IKr/i2mWMwC0SnuSFTG2YKyppwKYVdGO7TFhzBqGIenVeLCUtfURgErucx5ECKREKBU4d3B718PHz6cICGT/1Qs8qpQtGOdyhtGEARWDQFqQJSeDL98u4VbLaKw9IRAJPwjtoJGlVAoDQ800+fRFTTYXcjlcXN2g++s36p5Lzzlve1iEROa8BGH1EbrSAeqrjxEqicHQt8/YSDHMpaNs7wJAp9vvfb287idboAVkRAa5fBYXP9rxO4Mgf0xvPPdHgAEA8OoGd40i1j0AAAAASUVORK5CYII=); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-cpp { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAfJJREFUeNqEUs9PE0EU/mZ2WgqpXX+QIDFdalVslh8NlAOQaOKFAwfvHvwT/FM8e/U/MOnBmwcj8WD0ACEGghIkbU0baaEthe3OTJ0ZWV26q37JZt68ee/b9733yGAwgMbL12/fz+azbnAPY2Nrt7Zfqz9JMrYZ+J4/e2pOFjiciRvXlgp5GzHonXk2o6S8V6k/TjBrM/xGA4MLyeOSPZ8jkx7D+uqCU3Amy1yIYizB36AlCSkwfjWDR4uu40yMl/s+XwjeWThQQ4Z6QNSnSkYykcDXasP4lmfvOZTSF9q8TDBEFPbN5bOqCglCCCxK0TvvZyIV4CIxbgpC+4gm/PUmFCIE8iJPyME/e8Lon9j4HvyHYLjKSwRCSEUgf9+15mFbx8QS6CZJMzJ9SlBCwX3fJDLG4PX7ykcwkmQmJtpEhWa7g1dvNlSwjwelebz7tAXLolh0p/Fxe9fErK2WDFGEgKjxfNjegX0lDTc/heNuF99/HGEslcKXwyoazWNDdlCr6+DoJgrBzdI0T9rYO6yg2zszMlaKM3Dv5OBzbuyZuzm1B16U4Nzz2f3cFOx0Gq12F9cztpExncsqYoaHpSIKtx0zJdVIFpHQ6py29muNk1uTN829o/6SHEnh80HFaE6NjmLnWxUJy1LyTltB3k8BBgBeEeQTiWRskAAAAABJRU5ErkJggg==); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-css { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAk1JREFUeNpsUktvUlEQ/u5DoCLl/RAKKKUvWmIxjYntQtcu3LvwJ/hTXLt16coFC2PsojEaMKZtCqFaTdGmjbS0CG3By+vei3OOBSGXSU7uzNyZ78z3zRF6vR6YvXzzPrMUCyf68bB9zO+VfpROn5hkOdfPPX/2lH/lfiLidztX5mN2jLGG0rKLENIE8liWpdzwP7HvqJqujmvudFU4bFY8Wk1FZsOBtKppd8YCDNu77CZevd3gflfTUFcUhP0ePLibiIR9rjSBpgwAfe4dVcV6dhtep4PH5msylGYLrzeybErcT85FYiH/CyPAf74gObC2vMhzsiRhPhpC6eQUM+EA1pJzILEnjRSuJsju7MJqsUCSRei6Dp3yXqcdGlHZ/rLPazQWGCn8+6YW4pAkEW0SjzUzanWlCa/LgcR0lNfovTEi6lcIkzesnM/R8RlN0INGp3h4DHoDsE5YRvQyiKiRSMzikRAOS2WoqoZWu41K7RwzlOOAVDMMMHhIGvFlRxJFrKYW0ep0IYgC3SDh4b1lTJjNfENsrazOAMAw680mPuW+8lFno1P4XDigRhOiwQAyJK7TbsNS/PaA7giAIAhYz2yRgBIfsVA8wIetPG6FAqhdNrC5u0f+TUyHgyMTDDToEt/ftQsEvW4EPG5OZcrvw0mlimarTXkPfpXPcNlQoGtjACgpryQXsPNtH/nvRXqBJpoKHMzGNkNB0Odls7LNyAYKpUq1dt1iuvB7fRDp9kr9D1xOFwkpoksXusmXaZWFn0coV89r/b6/AgwAkUENaQaRxswAAAAASUVORK5CYII=); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-dat { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAfVJREFUeNqMU01PE1EUPe/Na0uptmlASg3MoiZgCA3hQ8PHAjbqwsS9C3+CP8W1W/+BSReyYUPwI4QAVkAgUEgIbVIg1FZb2pl5b3zv2cHBjsaTTOa+e989OffcGeK6LhTevFv+OJoZHPHOfrz/sl86KpWfhxnLe7lXL1/oN/MSZqonOXU/k0AA6lfNhEFIrlAsP2PMyPtr1AscLpyg5pbtIHErhqez4+awmc45nI8FEvwNaiQuBHqTcSxMjJhmX0/Osp1xr878FxWEzwMinxAzEA4xFIpnOjedHTKpYbxW4U2CP4j8uWxmUKsghMCgFI2mFe9QgHZj0Ba4yhFF+KvGJToIRLuPC/efnjD6+26wB1Lq/xgbSCBXKeWJG/OTdky8cWTdT3C9RmWSGk2XCLlWo4xTNbfN5qh7PpXM72GjZeHt0gpq9QbmH4whGb+NpU/reDQ7hcWVVXxvXOHxzCQopQEKXKEbL6o1ZIcy+LC5g62DY2zsHeC0fA4zndIrHOjvg2XbAQRSfsuy9XxC2qzi/H5B6/68W0AsGkW0KyJPBLbDO0fg3JX/CUM81i0bD6WKe6j9qOPJ3EMcF0tSNsFA6g6alqW+VtZBUL78Vtk+Oqne7U9rs5qOQCjSheJFBeFIFOfVujSUYu3rIc4uqxWv76cAAwCwbvRb3SgYxQAAAABJRU5ErkJggg==); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-dmg { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAn9JREFUeNpsU01rE1EUPe9lkk47yWTStCmtNhFSWxos2EXVhSsRcasuxYV05V8Qf4DgD/AvCK5EV1oFI7iUBqmCNdDvppq2mWSSzEzy3vPOpFFq+uDNfR/3nnvueXeYUgrBWH1/9/NE7k5BKRnuRcfF2qdnmJq9DeF9tQ+2isuMsxXGWHh/a1mEVsPJSI5fSU3OPEj291IIlN49RXz0KqzEQjIeZS/L5Y/3wPGhDxIM/i/A7fZWgVG0t5EaG0ZUa0JGM8gvPrZmLt58QYwv91mfAqCIE0sAqgumBFITGQzpUYhuF0KfRa7waDyXXXolpVrsh/0tgSLDr5I+wUZo1UHCSkAficPzY6juFSmbRPrC/azjq+fkcO00gAqoU7B0ETKkfWbuCTjTYeq5oESAauexcTScX+ZACWFm0YQSLZKhHdr67+/wW0e0dgjYo3sCEXXybYtBDVSHLp2es3IpsILS24c42lkBg6DzRjgRzCDZ/xr0GNRJwwYiWgzt+hYMawleu0V3wbkT+kUirOc7IGJAz68R/Qak1BAlx3hqASPGBJRXpXOv58dkz3eAgQoOm4hyj57NgZm0MHvpBmK6QdUdg/DAg9cRkhicBSDaKJdeo1bdxmR2DtWDDUxl51HZ+QHTysD3XdQO95Gfv06aeGcAdBrY3Chi8lwO3768QWX7J5q1XWyVSxgajiOXLyBG2hzurRKV9lmt7ISNkkjo6HhNyjoK+2gXRsKE57ZIE2ot10Z1fz0Ue4ABVw3NMjnW14rInh8jTYywoTg3EOFpOM4mXNfH9PQUfGlrAwBOs3I8ljbtuMWhRWzIIPrkn+GcYcgIWEowbZ+0qB334/4IMADESjqbnHbH0gAAAABJRU5ErkJggg==); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-doc { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAppJREFUeNpsU79PFEEU/mZ39vZu77g7DokcP04BBSUmiEKCSCxs7Ei00JAYO2NlTKyMrX+CJhaGwopSQ0dMtFEsbDRBgiZEQIF4IHcg+2t2Z8eZ5QDlnM1mZ9+8973vfe8NEUJArfSNhzPG0VIfeIiDRSDkw1cWVt3N8rhG6SdSO2Gvn8dfuueqZwuNZqk3Jxg7iNcIfBbgXD6ZC8u5qffzX8eoYeyDxC77uygKhcouovgVUQj1H4YB2ovNuD9+tTTU0zMVBmG/+C8AIYh8F361DL/yE5HnADKYlVdg6MDAmW7cuz5WGuw+PsWDYGAvbL8ECFUt4K7/AHd/I9c7BLaxinD2Ld5Zo7g78RLuRhlBS2cpWbGfStfhfwCEpK0nUjCbWuGsLciSOELPhkq/YgdY3l6HsLfRcLYf+pHNbH0JigEPkLAyMsiEJ7NrqQzM1i7wyhoMZqOhvQs6Z0ovXgdAJACRoulEg5HOwrOroKk0zOY2BDtVpTF0CU6kLkQJXa+BNEoG0lMSsBBKQXWNQktmoGcaYeSaQCIVWOvUYQAiWZFQtk5mSMoSzEILtBrTfEcviC5bwVwQmoh96wA0ic5dB57ngeoaTIPCdb34zDITYNLOOIeVSsW+dQC+7+NSWx6jJ4tY/rWNV7PfcGv0tBoPTM7M4eKJVgx2FTE9u4QPS6x+kHzfw/mOAjarW2hJG3hy8zIceweuY+PRtREMdzbjzcd5WBqPB6xeRGUMGRzHjWvMmxQ7tiOF1JBN6FiTd6Sy9RuFbHpX7MMMqOD088Ii+op5OUAO7jyeRGfBwrF8Cg8mXuDL4neMXzgFwhwZz+hf7a9d5yu3Z6DTPjVQIY9k7erO7Y63Lvc8ErEeyq6JaM6efjai4v4IMABI0DEPqPKkigAAAABJRU5ErkJggg==); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-dotx { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAndJREFUeNpsU01rE1EUPTPzJk0y+WhMStW2qdVWxUVEQUF0I+4ELQiC7lz4N9z0T+hG9wrdZKUgLqulhrbSag1CKpT0g7RpYjqZmffle5NEKdMHlzfvvXvPPffcO4aUEno9f3Vt4dTp+BXOe+fB0u/NbVpv7h89NU1j1TCM8H7+xY9wJwPHZMbOjRadLAvE/2gToJTiTPx89k+OlVd/LT+0TPIPpO/SzyQk40xCMxBSZ9Z3CoAx5DOjeHT7SbE0XSpzwa8OWB9jINELolQg8AR0EgUKn1PIlIWpkUt4cPNxkTOU12trs8p95RiAXpqaztqou8q6SKQJJmZSqGwsodFsIJk1kcyLYv7IeafcLx4HUNkFF4jFTExMZ0B9DrfD4HUEusYhWs4GPEJg5wly/tBYRIOeDhpEwlS34xcyajdQr3UwOT2MlJOEBRuGNHWp9AQRVXDfQiFV/U5GBSiQ5p6ngBEa5z3fiIhC6g6IMDBwOdoHPkYnHPVyhN0tF7E4QSpr94CEOKELffq+y9Bq+DCJ7rWBoQQBVbPR2O6G4OlsLASJMtCZfQqm0NP5IVWnamdAkUxbyuIYtD7wWegb0YAzAVMkkI6NwPM9xEwHloyDGAmk7AKS9rAS0FKOdugbYeAHPu7OPEM+MY7q3hIKqTFQHmC3XcONc/fxdfMDrk/ew/edzyhvvTmBAddocVRqH3Frahau56qpZDho7+PnTgXffi/gbHYmLEvPSIQBp5JU62sYz13G609zKBXvoOMdYn2zgm7Xg2MVML/4Eu3uPgxhk2gXmNl8v/i2pcXTP8tKdTEcbWLZqDQXwu/l6pfwbEnSGsT9FWAA4mdHv2/9YJ4AAAAASUVORK5CYII=); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-dwg { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAoFJREFUeNpsU0tPE2EUPfOg006hD4rQh8WgbCSwkKgbF2owujaCiQsXxpX+D6MmbtXEsHCLmIAbE6NLo8YlGIxREIshIqVl+mQ6j8/zFVCb4UtuZua795577rl3FCEE5Bl79vPd5LHYiOP7cH1AUWi85ytmvlas1bJ9E5ryBntH3BpuP/X9i7ovkluuiE8N9SDepaLpCcRCCqa/VDCaMuIjSWP25Upl6n+QDoCz6Yh7KKzh3sI2LuUimPtRRyaqodj0MDloYiITSTi+mH29Wu0AUf9CsZPJoW5czJl48LmCc5kIKo5Al67B9gUGYxrun+5NnMlFZ+GKiQADj2a7AquseLIvjMv5KMaSBu4sWVir+3i8VIVKYSby0UTdFU8Znu8AYBHQgVOJEN5uOXi4UsdawwU0FSf6TaSoyw6DRvukPkgGWpDKy4F8a3jImCrqFDFn6rhKPR4VGnhvOTAY3WLcjifcQAsqRfhUc/Gq1MKNbBh9nIAMDjEppocxs9HCMktfGTCwP/oOBkUKNk/qF3pDYC6Ktk8RfWzyaaoKrqdDaBDwya8W1m0/CPCR3kFy7CcnmWQRUJqcRJFUKtTnPCeR71LwoeYF92CYyVnCFZpCTrRtCv5to2St8SOrKxiPqEEA4fkYT+mI0rdoeUiH1XZVuQPpsIKqw2QmfifTsnOABiWySlH9uU0Hh2MqjsZV5LtpPSoGeN9rKnhBX7ehoOSLIIPfnGONXGMMWN7xUfVldYDbjM3mrh5HCDgS17DhHgDQcIU+XbBxnDTn1x1UuQcJ9iv7l5Q5e1zLGri92EDJFnoAgHtcfr6wbbVXUqq193+0z97n3UJt1+d51n7aHwEGAAHXJoAuZNlzAAAAAElFTkSuQmCC); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-dxf { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAo5JREFUeNpsU0trE1EYPfNMmtdoH2kDNmJbaVFcaBVFpAsREQpFwY0bu3HjQnTj1mVd+ANcuC3qQixmry6E0kWFVIQ+bKy2tbFJm3emyXTujGca+4DkwsedfLnn3POd77uS67rw1vC79ek7fZEzpu3AYUqS9tKQGZPLpa3VXP0uFCmJ/8t9OLC3q/uJbcs5bkIybvdHoMsSbLKENRmvU2WcNnTjRFD7ML1WGSPJHI6sA4KRWMAWVDPxLYex3iCmfpuIh1QsFSyMxQO4GvXHHwOJ6XWSyIck8v6HQsnjAxFc7vTj2VwBg4aG78VdBHQFCk+dbVcxMdwev9gTSEC455sIBOu2KLsoJFzqasP9vjCeDBlYqzn4VXXwarGKZN7Crd5QfLDT/7KpBM84c9fFUFjFp2wdk6smflRsKKqMa7EgfJJ3Ac2OKlit2pEmBTQfngdpnupoU7BUtRGiiTe7fXiRqmK+KuDn6TpvYogmBRJcrOwIJLIWxmM+dOsyLKryQAaJpjJ1/AxrGO3SqdZt7kKZJrzJWBg5piHENuY8vV6e0UOye1TyftvC5l+gZB8SHJTwpSx4q4JeTUKaxhXoR57h7Rn+3iFolJ3xvPhab6HgJG/pJ7jsNP4sUX+jZiCgEsWd/DjH5IrSYpBUAr0yHpzSoXKOP25a6OBhndh0zcX1qIYM2RIbu6i0KiHD5B/GTMHG03kTGpEL7H80wHFOWwhqDZ+SpkBOtCDYJDhZE4gRcKNbYynAqbCMbXpwpVPFbEng0aKJGbYzK1p4wIegLlcEPmdt+DjXbzcsxFlCynRwwVAwW6hjqeg0Zt521SYCWCJvbe0Un29UDx7Hgrs3IEitHXkw3jOv2fl92D8BBgAJeyqBh90ENQAAAABJRU5ErkJggg==); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-eps { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmlJREFUeNp0U01vElEUPfMFCEVArdoSqEA0KV246UJdUJM2Lo2JK/9FjXu3utJqTNz4D9worrsQExbFpAFT0TYp0CZ8pIAiyMfMvBnvm2Foa9uX3Lw7c98979x77hNM0wRf7ufPsq7Z2SQYw2QJAkDxQalUZa3WI8hy3gmZr15bu+z8kILBkCeRCJi6bufKMji0NhwiCQR6iitdatTvQ5LyOLLEiWcYukm3m4Zhmbq1BX13FyoxuH7xAlbvpqKRK1fT0PWbRwEmDEyiy1QVg/V1GO02tO1tKLEY2PIy3KEAlmJRDLXb0TeZL+n9g4MHlLJ5HIBuYnSzXq+DlcsQLk/D9Hoh1WrIUjlPcpsYGQzS3LWoaBhvKeXWMQCDA1D9pt8PaXERUjwOjEZQFhZQp9L2yERiqYRCkPt/z58ogTGqHQLE1BLgUmC6XGD5AlipBIFKkbhanKHGYLBDqQ4ZED0OAbfLlo8OIxwGvhVgyTHlA3xkomjH/gegBgDURMv6faDbBZpN+/tHkUApkdTA/PwZAPxntwdUyjYA/+ZMqJHjLgM9iv/6zRt2GgMaIE21aVIjnSm0DGPfmhzyde0UAE2Dj+p7urKCPvkZku9eJILOSMUnkvVhIo7GYIB3xSKYdhoA1erXGVKXpvFxZwdBonnD68PQ7YEwM4O4xwMPxc8RYE87g4FIcz+kvfmnA0YzIJIy77/m0OCqsTkkCTysKPjJG3viLei63Gm3kCO6UWqcMejjxecMPmxsoFKtYop6UNirYL9Wtc5OHqzznIXHq1na7OfMJROcK8a6O7MjW7nfzZdrd7jzT4ABACh3NGsh3GcdAAAAAElFTkSuQmCC); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-exe { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAo1JREFUeNp0k8tPE1EUxr+ZzvRJO62lUAQaKIQ0FVJFjBBdoIkrDDHuXJi4NnHtX+HCjW408Q/QmHTRaCRRohIJifgiiBICTQu29mHfnc7MHc+MlECKdxZz595zf+c737nD6boOYzxJLC6Nhwej7e/24HkO779s7G6mMjcEwfKZ21+/d+em+RbagaFev28qEpZwzKg3ZckqCPH1nfS8hScIdyhBe6JqTG3PfyTTeLrwFhvbKdy9/xi5QglXL0yGJsKDccZY7LDIAwWHpSferWBh+RN8ni4UylVER8MY6PHj0uSpUK0hxzfTmWsUtnoEwO3rer64jEyxim6/Hy67DXaHExvJX3jw7CX8XjfORUdDlOohhU4fAVjILCPbm9V1yIqK2FgYt+ZmsZcv4lH8Nb5upXD7+hVMjIRQa8qeDg8UTYPU5cTcxSk4nS709XTD53ZhpD+IYMAPj+TBz93fZiz5oHV4AP1fGdlyHZIkIZkrI7GyhnK9CZXy+Aig6p1+HQAY003AcF8AVtGGfLWG9XTO4MLZ5cL0WAixoT4zVmPHADSiMo3hzHA/xgeDWFjbNg8H3A7kKnX0koEcPdTu/ylgRGZgOjNv38zoSXC8BZJDRKOlwGEV0VJVGM0y4joAPO1spXbx6sNHeD1uRIYGUCxVSRlDt1fC8rfvcDnsmJ+dOaLgoAs6AVLZPJJ7WdhEkUyT8GJpBflSBcVKDTvpDBw2GzQqQT1OgaZqUOhtFQUTUKnVTVWNpgy51YLVKph7sqKYkA4A1ScEfT66vm5kC3+ofh6Xz59FQ5bpkvE4QW3M5Apoyorhl9ABIKnFgNdTOh2NkJG6WSf9eRBJtmFwLDJmriUzeaOkYvvcXwEGAIVNH6cDA1DkAAAAAElFTkSuQmCC); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-flv { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmtJREFUeNpsUl1PE0EUPbssLYUCXdpaC9gWoSTgAyFigiRGY+KjvuuTr/4A44MP/gx/gMYfwIsan0RjIjGiJIZgSIGFIoXSD0t3Z3dnd70zpITazuZmJzP3nnvumaMEQQCx3jx69SV3a3KWMxetpSgKxP3m242Do43SQy2k/YRydvds67n8a63k+FRSn7l/bdg5tdsAuM3he/5weDC8vLdqPLgIIpba2niux52mg//DqlsYSg3iztO7mczN3DJ3+ByCLgCBH4hOFEF7cDpzPCRyOpaeLGXSc2PL3HbnW3XaRQCPEgWI2MsRVAVqrwbX9bHxbhOKpiJ/bzpDOr2k68V2BtRNzMtqDEqPejY/4zSGjb54BM0mQ8k4xsDoIMauXxnqYOD7PmwScP31d0SS/eAuh1lrolFpIBQNQw2pqJdqsAlIceB1AJCIkkE/FZskXDQVRXw6IYHiE0nBEcaPXSSvJnGwWkQXAE4acAhbxPMJpOdHweoMhc9b2F8zwKizbdlyPLVH7QLg+JKBYzoorxzjz3oRzUoToaEw9KyO8XQW5AE5jrFT6AbAYVVNxCZ0Ka3So+DSTAoDiej5ywTySbls1OEDobhFlMcXxrHw+AbINEjNXgb7y6BndLhk8cRkHHbD7g4gEhiJFxsdhrDqaamBaDKKerGGSKwPI9kR9EZCaNA5ubE7A5s8IFhsrxQkgJhZoa/06xC5xRz2v+3BOjFlbqcGlquxsondT9vY+2pAJdeZR6fI355CgQCN2A4O1w7gkQ7cdLUOAKdhV6uFSv3kd/n8mT68eC8dKWLnY4FsfeZQh7nVVt0/AQYAsf5g+SvepeQAAAAASUVORK5CYII=); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-gif { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmVJREFUeNp0U0tPE1EU/trplAqlL0laiw40xASByEJIZFGVnSvj1j+gWxNXJq7VrbrwF7h10cSNhMRHojEuACVBKmH6SJQyJeXRxzzv9dyZPiCtN5lMe8853znf953xcc4hztDzZ1+C6fQMHAfd4/MBFG+p6h/n4OAeAoGNToi/eOm+A50LKRaLh6amoty2vVpZdotNXccMEK3LwZxa2bsDSdrAqePv/mLM5tSdMwYBYqyvw9zdhUn/L59P4OGtG8qlZCoH254/DdCdQBCxqZu+ugqnWoW9swN5ehp2NotgIo6bGQWGtaS8+vQ5V9a0u5S+1gfABEilAqdUgm98HDwUQkDT8JXoPPq+BoM5kCYmFT9jryn1+hkAt7heBx8dhbSwACmTAUwTgdlZ/CVKJaLnI1GD8TikZiPSR8Gxib8chH95mZTxgwWHwH7+gFMswqcokIRbjMO2HDCnZ1VvArpjEmnKZc8+cZJJYGsLsMiZ8AgwEqaY6Mb6RQR33JFhGECzCRyfAFXNu9v+RVNRZWIMuDJNuYMAaDycUFGhCOgtuAtFVDA83G5A8TrFDw+F5QMAxAKJJxz2xnW3RPJGbm+rCyjotZetH4DGzaSSeDA3h4Zl4R0JOEZWTpIzF4n/m995bNdqZwB6m0gFft3Ak6vz+KYWwFsGlqIxXItEcDt1ARMEtKdVgZb+fwA0G2C2hXM0ZTZNRcSf0b1pmXi7uYnjI+Lfanm5fRQsK8BIxKcrK7i/uIgP+Tw+FlREqHN5fx/vyU4uHBE6UO4gDWqk/JFaLuMxcXeFk6TuJ90V0HOk1in7J8AAjmgkPfjU+isAAAAASUVORK5CYII=); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-h { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAbRJREFUeNqMUk1Lw0AQnf0woK0ttVqp0hwqVCl+UBERT94F7x78Cf4Uz179DT14F8WbYHtRkBYRLNqDtdaPZLObuLs1NGlXcWDJZGbey+x7QUEQgIqT07PL5WKhHL5H46J+22q22vsWpbWwdnR4oJ80LNiz2czGUjENhvj4ctIE4Wrj8XmPUlKL9nCYcOFzE9j1OKSTCdjdrtiLdr7KhVgzEvwW6krC92E6k4Kd9bJt57JV5vFK2KfRQRV+RAMkzxglYI1RaDy2dW1rpWRjQo5VGicYIorWVooFvQVCCAjG8Omw1MgG8AM0uSBUDSnCfk/IGCHwf3DCD/7UhOLBrFkDuep/hDUSSCv1iYo4rIfqGwmUSNJjfYbBcQKhZw0aBMA4B48LwBhBt/cON80HmM9NQ6fXg/Wlku4TwmNWDzaQqzHG+0PSKod5cH5Vh2RiAhYKc8DlV1UPSyuFMGygVlMg1/P6BC6DqXQK8jNZDXAYA1f21V34wMXYFaiyVw0rJyzLgs3VMkxOjGtix/V0XWChZ0cI2i/dzvXdfTd0Qf91BMPrhyNzgKfOmxaWypqaDXHfAgwAtCL8XOfF47gAAAAASUVORK5CYII=); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-hpp { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAehJREFUeNqEUk1v00AUHK/XKf1yZdESVRBXjRSRFqMQVBA5Ic5I3DnwE/gpnLnyG3LgXglx4UDDLZS0RWkDLiRxSusk9u6GXSembmLgWZbX7+2bnZl92mg0goo3b3ffO/ncdvyfjHef6q2Dlvs8Q2ktzr16+SL60jhhZ69bO8X8ClLC7w9XdKJVG8fuM0r1WrJG4gXjgqU1D0MGc2kBTytl+7a9XmWcl1IB/hZKEhccq5aJJ/e3bTu7Wg1CVo7rNLlRhUh4oMnXoDoyhoHGyWmUe+QUbELIa7W8CjAFlMzdzeckCwFN06ATAn8QmDMMMGlMuwWucpoCHNe4jBkAMenjYvRPTyi53JvuwX8AplleAeBcRFrH6rXIxLim9I/pi3QA1RhKaYxdjkN8IwalCMIwWs9ljMkh0wzk+9M7w179C3LZNXxve2h+c3Hu91HeKmD/6zHOLnw83ilB1/V0CeqU3Q81LC/O41b2Btx2N2JVP2riR8eTUxmi0TzBwrKZMsqMoz8MsDh/DWuWhUBKURLKxQIeOMWoptYPnS1c+INZBkwISomOSsmBZS7B+3WOzZvrKGzkMAiGqNy7g+LmRkRfekBnANy2163PZXrSbrQ6vch19Xz8fPDHyL39QzkHBKedXjfu+y3AAGU37INBJto1AAAAAElFTkSuQmCC); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-html { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmBJREFUeNqEUktPE1EU/mY605a+hhZTBNKRDApNrWIRA4nEBUZdmCgLNi4MK5f+FNdu3bFv1J1EXODCR1JJSMTwpqUP6NiCpe10Zjz3hj5Mm3iSybl37jnf+c53jmDbNpi9eb+6Ftcisea909bWNzNb6dwzSXKkhIt/r14+515qBqmDA8HpqKagh53XaopblpIbe+knDpFAhPab2Dw0TKvRK7lmNODzePBgZlK9oUWSpmVNdpIU8T+jaMsyMaD4MDcZVa+NhJMN00w0n6V2nN3yQgdHWZag+LzYPTomIAtT0THVtPGanmb/BbjwLFkvn2IttYGYplKyDzsHh7gdmyAWfh5zVq0Guhg4RAHFUhmfvq3j134aXo8bd+ITnMFOOovU5jbGRoZwNxFn1cxuAIcDW/sZDjA/c4u+BNxOJyxqaenpI3z88gMfPn9Hv98HQZS6RazW6kjExvFi8TGdDSy/W0Emf4LS6R8sv11BmfzSwkPcm74Jo9Ei0GZgmkw8QCOao8OXcaz/5vSZnPdnp3ApqBBLkWJE0Ci7ASzbIhCLLQ1E0iOkBDh9NpUgiUejo8oNuJwyn0YPABtn51UYFFivG3yBGCNZkuDtc/MW+ZQI3OrYpBaARCKufk3B5XIiWyhiL5ODp8+FfFHH+KiKSqWKUL8fC/NznGlPBmz+24dZjKnD0CJDcMoyW0SqXuMtHBFw7rhIAD1ErNUNafxKBNevapwu65NpEQ4FqXIA+RMd6VwBP3cPSERb6gLIFIq61+UqGWaFdcrVt/lmAuWjAi2aiMFwmOYuIJ/N6M28vwIMAMoNDyg4rcU9AAAAAElFTkSuQmCC); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-ics { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAhRJREFUeNqEUkFPE0EU/mZ2dra7bLNpi2AxQFKalkJrohICiYkXPagXrx78Df4K48GDBzmQePLMhUODNxQ5ciEkJVqDtJGmMWrCATRbd2ecoS5u3aovmezsvu9973vfPiKlhI4XL7c2r5YL81LIELEghLA3u/udxmHnPmfGW/Wuv+LpwwdneRYBx7PeWK0wOYYhcXxyckGV1fdbnbuMsXcklqPRJQxFMKz4RxDCtVO4s3xlRjWoB0FYjlQPEEBieChwKCRGMx5uLtaKs1P5ei8IKlGa/YkXMXYtlTEDlsnw/mMXhBJcqxSK6vlcpa4PEpCooUyIqs5M6hG1o2CUwqA091cFcYLf/sjzcX75EiQIojI9779CTYR4jwTBf+r7GAwh0AxCiL6JMT/04vQ79u8aI2O/7Jzg69o6Go8ewycUahtBpADhHKLnK/eVbkMdtROWIv80NQ2sPhncA9Htwn+9hZG0rY6DzFwJl+7dhs0ZstUy8rduwPS/wd/ehmi3kwq4zTHiWUgXp+EuL8FvNvFl5Rn4xAS86iyI2kY3n0Mv48ByrOQmancdi8I0Kcj3U5iuA29xAelKCUHrEIayzltagG2E4IwkFaQgSC6lYI09iN0d8It5uNV5nG5sgJdKYC0G8WoTOZvBISFNEBxnsuzD3GX4vfDsszzqAu0jkJQDedCGbB6AWg54pYbPo+NGVPdTgAEAqQq70PytIL0AAAAASUVORK5CYII=); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-iso { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAjlJREFUeNp0kstrU0EUxr/k5qbJzdPYpGkpsUJoA2q1oLjTdiGiIC5cuXHlxv9BEOrStTvBnQvRrSAIsejCrlqpsURq2hCJNQ+TNLm5uc/x3MmzJh34mDNnvvnNzOE4GGOwx8+t9XQkfn0VE0Y5/7Z+kHm+dvOhtd3P9c/xwNZh7nWaMYtNUmX/Fct/vlN7/8J5aRRgyzm8xzpRDjGE2aVH4VTqdnoUYg/XkEhmy+Cx3DhA5tMzdFolvg5Mx3Fx9SmH0JIg79Zo3j4GADMIokJTKtjbfAKXU4Y/2NvSfyH75TFOxa9Cmr0XnlPFl5ReOQ6wNMDsoFX6AElqQlNV1KsOuNwS/AGFjEUIDhmn5+/DMM16/9igBowAzFKIswPJr6MjlxFP3sV04gaP7RzMPe6xvWM1gNUBM2UKYlBau3QghGphg29J3gDlLLilWNdD3gkvIIDRhD9yGe2mCV0V4HFXuCxT5Dlv8Dz3sIkAs03FalDxBMQSt9BRBMhNncuO7dyU28c9tnf8C/Q0ZtR4GImeQSj8APLRH772BWcgiFODffCv/t8H9tO0v3RjV7VqkeeXLlzDfvYjj88uXhl4JwIsrYxmLY/M1gYclIvGE9jZfNPrSCD3/QgLyeWTADV6wW9AryIcCkB0u1Aq/oCPumlufoF72vIheaLDr4wCLIOqrYnULA14PSoqpSJEAUilZrD77Sv3LK+cI0+Be8cAbbmAOrob0agtD491LYfkoqvnyZLsWRkA/gkwABL4S3L78XYyAAAAAElFTkSuQmCC); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-java { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAjxJREFUeNp8U01v00AUnNiOEyepQyhQobRBSlVIoRCBEPTAjQsSEneE+An8FM5cuXLNoQduIAE3qopKNJAIIppA2jrOR93aa6/N8yZuUxyxkrXr3ffmzczbTQRBgHC83nj3ca28dD36nx6fvnzrNNrdp4oibyUmey9fPBezEgWVFuYLdyvlPGaMY4fl1aRS+9pqP5ElAkmcnknRwuO+Nyt5u/ETYfyj9WrpZnmpxn2/Ok1Swn/GvtnH5k4TLue4kNfxoFoprRQv1TzOb8cAIu3+ZD7oD/Hm7XuxzqRUNDtdkuLiTmW5tFxceBXlnXgQTAORSMt2oGezUJJJrK9dFWdEH7Ik4dB29LiESeUEJXd7/dAT3L+1ivlCHr8NEzutXTBvbJPPSdO/AH5wysChwM/1HzCGlmAzOrKxu2eCud6Z2Jke2MwThpUXL6Nn2ZAVFTlNw70bK0iRnGAq9qwHtOmTRpsx1NsHyKRVnNPnoMoK9kc2BjbD4vk5JGV5NkBoEPM4FFnCteJFWOS4ntHEfphQyKaFTWFLw704AJ26ZFx/ZEEi3YyY0O1Dmr4EKTUHA8hUnS6siI0DEHLYog+b28RCRuNXR/iQUpPUEQ+NVht6Lodnjx+GXYgDSFRnq97Ed2pXSlXhUSeGhxYc5sKlNXM5DGLR2TMwfZVPAIi+otGNWy1fEZUKeo4qc4ysI+F8VksLIJfYcD9QYgB/DNPMptWBlsnBIS86xmDMTBo/PWd0LB6VZfdEbJT3V4ABAA5HIzlv9dtdAAAAAElFTkSuQmCC); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-jpeg, -.ipfs-jpg { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmlJREFUeNpsU8luUlEY/s4dmMpkWxRopGJNNbiwhk1tItbGtXHr0hcwmvgOdWld6Bu4coXumtREE3ZKu8FgOlC1kIoXtC3jPfdc/8PUIpzkBM7wf+f/hsts24YczuerGUc0moBlYTAYA+i8sbdXtAzjITRtq39kr73s/Gr9DTUYPOeamwvYnHdrdR0SnDebuCbswJGqpX+Uf92Hqm7hzFAG/4TgNr1uCwEJ0trcBC8U0Kb1/PQkHt9JxSLnL6TB+Y2zAIMOJBGLXmtsbEAYBsx8HnqCGKVScAX8uHf5EpqmGXv18VO6VDEe0PXsKABN8+AAgiabmYFNNJTDQ2RUFc8+Z9G0OPR4PKYwvKari0MAgiY/OQGCAajhMNR4nDZMaInrKBGl70SPMScck1NQG3X/CAWLE3/dAWV5hRRVIJxOWNksrP19sFgMqqAebUGYHMI6teq0A9oTVAhqu2sfbYYjsL7lCZ3683gA70T3TK7/B4BNoO020GwB9TpwfAz8LgMtWn/NkV8EHgoB81c7nYwCyBZlEVkHcqMTKFnkmehJTOPvEfCnKi0fAyADJKfXC/h83TaZTJjaa5lANLpOFqAXtlEAorAwO9u5syT5UxLfU0e3o1FMu1x4u7ODYq02BKAMAVSrSNLrK1MhLPj8mNF0vFm+C1ZvwKBwXXE4AGn1WAASazESwUW3BzUSMeJ2o1Aq4sPurvQYSRLwlhRR6mSaYyi0WlpAJrFRx3ouh5/lMt5lv8BLwXp0M4lSpYL17e2uK5wP6lj/c2ZPn2RI+YT8fDvqoyegVLyfG5kBKaQQOfvF2pLc+ifAABiQH3PEc1i/AAAAAElFTkSuQmCC); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-js { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RUQ5ODY5Q0NGMTE4MTFFMTlDRjlDN0VBQTY3QTk0MTEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RUQ5ODY5Q0RGMTE4MTFFMTlDRjlDN0VBQTY3QTk0MTEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpFRDk4NjlDQUYxMTgxMUUxOUNGOUM3RUFBNjdBOTQxMSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpFRDk4NjlDQkYxMTgxMUUxOUNGOUM3RUFBNjdBOTQxMSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PoT8zQ8AAAJdSURBVHjadFNbTxNREP52t7S0bktbKFAvTUVaw60YqkExUTD6oD74qC/yD/wp/gh885XEEI0RAyYQUiMpIBGMkYR6o23abi+73e2uc04v1LROMtnZPTPffvPNHMGyLDB7sbJ2ciUSli3U35smkK9t7x9v7n2dD/g8KUkUwWqeP3vKz23NxJGzgwOx0RC6mSgIo+WKuvP56MeUzy2nJEk8PWsGJVVTuhWbpgmHw47FB7d98Wg4mVWK52o1sxOg3Va3PmFp+Q2PdUquaFUM9/vw+O6cP3bxwm46Xwh1ALR3/vL1e+hGjcc9koScUsTSq3coVDQsXJ3wzo5HEs3clgZNMTVdx1T0Ep7cn6//QRQwMhzA6uZHLD5cIFEFSKIU+G8LK+tb0KsGZKcTJoEyP08AbpcLy6sbPKdQrigdAGaDwWxsDH1uGbliCYIgcM8WFPg8Mq5Pjzdyu4jYbCE44EepXMHuwXe+A8x3KKYxYsjvbUzmlPGpBmYdgI1oYjSMbL4Ao1YXMkcM2Dd2xnbAamPQAqg1GORLZdycmYTdJqFKk2DPR3fmwI4zBDrg9RADqxPAbPBif2WTSB584/3/TGegEOit+DRcvQ4OZJi1LgwIQKVCg2i6nb1I7H3Br3QWqT9pBAP9uDY5xjdSM3RqxeoUkfVnEOW8UkLykERTNXjkM7h3Iw6NNvHw6JjuhAhVrba0+QeALozcI9nQR0VvNxJc/ZmxCNGvIBQcpDG6udA22kyW29HC72wu8yG579ZoiSYuR/ly2+y9CA4NceWLmo717T1i5ULqJNtapL8CDACskxPFZRxLwQAAAABJRU5ErkJggg==); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-key { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAlZJREFUeNpsU11PE0EUPbM7u/2AtJUWU6qiiSYYo5EmmPDCD9AH46sx8cEnja/+CB989z+Y+MKPgMiDsYQACcbaWBBogYD92t2Zud7ZlQZsbzKZ3bl3zj3n3IwgItjYeDO3MlWme0bjUth8e8/fO2tHzx3XqUEk50uft+Ndnhdmc3SlfNPkVZT8Cy600DoIISvVfKYtlvfX1p66XmoIYsMZdjJQWvEFbbsC/S5g2QhSkKUK7rx6OzvzqLpsovAhaAxA3DUBQn2TUFsl7KwTfm4Z9DoO5LW7uPXi9Wxpfn7ZKF09vyPxX2iWcNRkKGZz0mQWKoNs8AVB6x1yRY2pYnc2LLofuXTxMgAlmlXIfngCxNxEzM+DPv6NQa2BygLgZyX6JT83ngHTN5GAL0WSoUQkSQnXkyBh/k0GegTAaldM20sTKvet+yyhIZApECamL0jUSe3oFChx3TopM4TeEQP2gc6BgGIwb4KGNXRhCkMGxgg2kJeybRiZM45D8W61qEAknSmpHStBhywu0nFVupSCTAcM4ECwqapv+NQ6LS9JGALoMIIoPYDjZiEL1xHtbyO39AQUDaA7R1AH23DSeSA4hv5RG/VAhxomPYP8sw9A4TaC9iHkjUWmrtGvbyC18BLe3GP0m3WW4I5hEBEnPIStXzyuFIxb4EkMEJ79Qa/xHbKxCdM7xeCwzUZOjgEwnuzt7qLz6T3cySmQP43uzjeIiTJM6io6W19B/NLCKMVGCzkCoLR/0lrfOI2fNy/huKC1FTsK/rbGNeMRC8dHpHByfu+vAAMAL/0jvAVZQl0AAAAASUVORK5CYII=); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-less { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RjZERjZENTJGMTE4MTFFMUIwOEVERjQ5MTZEMkVBREUiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RjZERjZENTNGMTE4MTFFMUIwOEVERjQ5MTZEMkVBREUiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpGNkRGNkQ1MEYxMTgxMUUxQjA4RURGNDkxNkQyRUFERSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpGNkRGNkQ1MUYxMTgxMUUxQjA4RURGNDkxNkQyRUFERSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pl1w97IAAAJhSURBVHjahJNLbxJRFMf/wPAIMIxMkUI7tS0VYqlGDLGhjdKkqyZ24cJFN925de+XcONHaHRj4k7TND6SGo1VWwmp2kSLhlqMDbQ87gzPYcY7k4GgoJ6bmdw598zvnvM/95pUVYVma+svcovx8yMnFZHAMJPJBJfDzq5vpX6+/vD5qo/z7DOMBdo/d26t6jFMJ3iY51jBz4M+LP6wxEw40Gy23qYzB3HO7fpmpZCOmfEfa7Xb4NxOrC4lvbPToe2yKE3K1PdPwNOtHdx79ESfq4qKkijB5/XgevIyHxEC24USmewDqD2ABxubaLRkfW6zMqjWGlh7/ByyAtxYnOPnL0Q2+gGGmKRaw8zUBJaTiS5QOO1FJnuIAM8hciaIWHgi8NcSNt+loVDY8JBXh2ojJAR1HbTSNFMUpV8Dxcjg0nSYBrtBxdLbqI1iheCUh9XXNGurAwCdEkb9QyBSFam9TDfoPZ1LUg1BH28IiwEARTVAQOzcFKRaHZpLoa9avY6L1Gfs0c32t4PU6W2lWsV8LAorw0Cs1nXftYWE3qZGqwWHzYp2zzlgetuolVFvtiDLbRRKFTAWCxx2G/KlMtXFhWPqOzsWHJwBx7rxKv2R7mwFz3lw9/5DLC/M4Us2RwV0g3U58XJnF7dvrsBOoX0Abbej/DFKRMKI30fTVGC32WA2m5H9cQQvhYi0vE/7Wdgczn6ARA9QPBrBszcp/XvpyqxebzQ0Tlsq6llxLhe9bD4cFMr9XdjLHpLv+SLGBYHAYiVu1kNOpAaRTWbCejgiw0zGhFGSK1aw+zXbvfK/BBgAPwADAs5GpGsAAAAASUVORK5CYII=); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-logo { - background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 553 235.3'%3E%3Cdefs%3E%3C/defs%3E%3Cpath fill='%23ffffff' d='M239 63h17.8v105H239V63zm35.6 0h36.3c7.9 0 14.5.9 19.6 2.6s9.2 4.1 12.1 7.1a24.45 24.45 0 0 1 6.2 10.2 40.75 40.75 0 0 1 1.8 12.1 45.69 45.69 0 0 1-1.8 12.9 26.58 26.58 0 0 1-6.2 10.8 30.59 30.59 0 0 1-12.1 7.3c-5.1 1.8-11.5 2.7-19.3 2.7h-19.1V168h-17.5V63zm36.2 51a38.37 38.37 0 0 0 11.1-1.3 16.3 16.3 0 0 0 6.8-3.7 13.34 13.34 0 0 0 3.5-5.8 29.75 29.75 0 0 0 1-7.6 25.68 25.68 0 0 0-1-7.7 12 12 0 0 0-3.6-5.5 17.15 17.15 0 0 0-6.9-3.4 41.58 41.58 0 0 0-10.9-1.2h-18.5V114h18.5zm119.9-51v15.3h-49.2V108h46.3v15.4h-46.3V168h-17.8V63h67zm26.2 72.9c.8 6.9 3.3 11.9 7.4 15s10.4 4.7 18.6 4.7a32.61 32.61 0 0 0 10.1-1.3 20.52 20.52 0 0 0 6.6-3.5 12 12 0 0 0 3.5-5.2 19.08 19.08 0 0 0 1-6.4 16.14 16.14 0 0 0-.7-4.9 12.87 12.87 0 0 0-2.6-4.5 16.59 16.59 0 0 0-5.1-3.6 35 35 0 0 0-8.2-2.4l-13.4-2.5a89.76 89.76 0 0 1-14.1-3.7 33.51 33.51 0 0 1-10.4-5.8 22.28 22.28 0 0 1-6.3-8.8 34.1 34.1 0 0 1-2.1-12.7 26 26 0 0 1 11.3-22.4 36.35 36.35 0 0 1 12.6-5.6 65.89 65.89 0 0 1 15.8-1.8c7.2 0 13.3.8 18.2 2.5a34.46 34.46 0 0 1 11.9 6.5 28.21 28.21 0 0 1 6.9 9.3 42.1 42.1 0 0 1 3.2 11l-16.8 2.6c-1.4-5.9-3.7-10.2-7.1-13.1s-8.7-4.3-16.1-4.3a43.9 43.9 0 0 0-10.5 1.1 19.47 19.47 0 0 0-6.8 3.1 11.63 11.63 0 0 0-3.7 4.6 14.08 14.08 0 0 0-1.1 5.4c0 4.6 1.2 8 3.7 10.3s6.9 4 13.2 5.3l14.5 2.8c11.1 2.1 19.2 5.6 24.4 10.5s7.8 12.1 7.8 21.4a31.37 31.37 0 0 1-2.4 12.3 25.27 25.27 0 0 1-7.4 9.8 36.58 36.58 0 0 1-12.4 6.6 56 56 0 0 1-17.3 2.4c-13.4 0-24-2.8-31.6-8.5s-11.9-14.4-12.6-26.2h18z'/%3E%3Cpath fill='%23469ea2' d='M30.3 164l84 48.5 84-48.5V67l-84-48.5-84 48.5v97z'/%3E%3Cpath fill='%236acad1' d='M105.7 30.1l-61 35.2a18.19 18.19 0 0 1 0 3.3l60.9 35.2a14.55 14.55 0 0 1 17.3 0l60.9-35.2a18.19 18.19 0 0 1 0-3.3L123 30.1a14.55 14.55 0 0 1-17.3 0zm84 48.2l-61 35.6a14.73 14.73 0 0 1-8.6 15l.1 70a15.57 15.57 0 0 1 2.8 1.6l60.9-35.2a14.73 14.73 0 0 1 8.6-15V79.9a20 20 0 0 1-2.8-1.6zm-150.8.4a15.57 15.57 0 0 1-2.8 1.6v70.4a14.38 14.38 0 0 1 8.6 15l60.9 35.2a15.57 15.57 0 0 1 2.8-1.6v-70.4a14.38 14.38 0 0 1-8.6-15L38.9 78.7z'/%3E%3Cpath fill='%23469ea2' d='M114.3 29l75.1 43.4v86.7l-75.1 43.4-75.1-43.4V72.3L114.3 29m0-10.3l-84 48.5v97l84 48.5 84-48.5v-97l-84-48.5z'/%3E%3Cpath fill='%23469ea2' d='M114.9 132h-1.2A15.66 15.66 0 0 1 98 116.3v-1.2a15.66 15.66 0 0 1 15.7-15.7h1.2a15.66 15.66 0 0 1 15.7 15.7v1.2a15.66 15.66 0 0 1-15.7 15.7zm0 64.5h-1.2a15.65 15.65 0 0 0-13.7 8l14.3 8.2 14.3-8.2a15.65 15.65 0 0 0-13.7-8zm83.5-48.5h-.6a15.66 15.66 0 0 0-15.7 15.7v1.2a15.13 15.13 0 0 0 2 7.6l14.3-8.3V148zm-14.3-89a15.4 15.4 0 0 0-2 7.6v1.2a15.66 15.66 0 0 0 15.7 15.7h.6V67.2L184.1 59zm-69.8-40.3L100 26.9a15.73 15.73 0 0 0 13.7 8.1h1.2a15.65 15.65 0 0 0 13.7-8l-14.3-8.3zM44.6 58.9l-14.3 8.3v16.3h.6a15.66 15.66 0 0 0 15.7-15.7v-1.2a16.63 16.63 0 0 0-2-7.7zM30.9 148h-.6v16.2l14.3 8.3a15.4 15.4 0 0 0 2-7.6v-1.2A15.66 15.66 0 0 0 30.9 148z'/%3E%3Cpath fill='%23083b54' fill-opacity='0.15' d='M114.3 213.2v-97.1l-84-48.5v97.1z'/%3E%3Cpath fill='%23083b54' fill-opacity='0.05' d='M198.4 163.8v-97l-84 48.5v97.1z'/%3E%3C/svg%3E%0A"); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-mid { - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-mkv { - background-image:url("data:image/svg+xml;charset=utf8,%3Csvg id='Layer_2' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 72 100'%3E%3Cstyle/%3E%3ClinearGradient id='SVGID_1_' gradientUnits='userSpaceOnUse' x1='36.2' y1='101' x2='36.2' y2='3.005' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='0' stop-color='%23e2cde4'/%3E%3Cstop offset='.17' stop-color='%23e0cae2'/%3E%3Cstop offset='.313' stop-color='%23dbc0dd'/%3E%3Cstop offset='.447' stop-color='%23d2b1d4'/%3E%3Cstop offset='.575' stop-color='%23c79dc7'/%3E%3Cstop offset='.698' stop-color='%23ba84b9'/%3E%3Cstop offset='.819' stop-color='%23ab68a9'/%3E%3Cstop offset='.934' stop-color='%239c4598'/%3E%3Cstop offset='1' stop-color='%23932a8e'/%3E%3C/linearGradient%3E%3Cpath d='M45.2 1l27 26.7V99H.2V1h45z' fill='url(%23SVGID_1_)'/%3E%3Cpath d='M45.2 1l27 26.7V99H.2V1h45z' fill-opacity='0' stroke='%23882383' stroke-width='2'/%3E%3Cpath d='M7.5 91.1V71.2h6.1l3.6 13.5 3.6-13.5h6.1V91h-3.8V75.4l-4 15.6h-3.9l-4-15.6V91H7.5zm23.5 0V71.2h4V80l8.2-8.8h5.4L41.1 79l8 12.1h-5.2l-5.5-9.3-3.4 3.3v6h-4zm25.2 0L49 71.3h4.4L58.5 86l4.9-14.7h4.3l-7.2 19.8h-4.3z' fill='%23fff'/%3E%3ClinearGradient id='SVGID_2_' gradientUnits='userSpaceOnUse' x1='18.2' y1='50.023' x2='18.2' y2='50.023' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='.005' stop-color='%23963491'/%3E%3Cstop offset='1' stop-color='%2370136b'/%3E%3C/linearGradient%3E%3ClinearGradient id='SVGID_3_' gradientUnits='userSpaceOnUse' x1='11.511' y1='51.716' x2='65.211' y2='51.716' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='.005' stop-color='%23963491'/%3E%3Cstop offset='1' stop-color='%2370136b'/%3E%3C/linearGradient%3E%3Cpath d='M64.3 55.5c-1.7-.2-3.4-.3-5.1-.3-7.3-.1-13.3 1.6-18.8 3.7S29.6 63.6 23.3 64c-3.4.2-7.3-.6-8.5-2.4-.8-1.3-.8-3.5-1-5.7-.6-5.7-1.6-11.7-2.4-17.3.8-.9 2.1-1.3 3.4-1.7.4 1.1.2 2.7.6 3.8 7.1.7 13.6-.4 20-1.5 6.3-1.1 12.4-2.2 19.4-2.6 3.4-.2 6.9-.2 10.3 0m-9.9 15.3c.5-.2 1.1-.3 1.9-.2.2-3.7.3-7.3.3-11.2-6.2.2-11.9.9-17 2.2.2 4 .4 7.8.3 12 4-1.1 7.7-2.5 12.6-2.7m2-12.1h1.1c.4-.4.2-1.2.2-1.9-1.5-.6-1.8 1-1.3 1.9zm3.9-.2h1.5V38h-1.3c0 .7-.4.9-.2 1.7zm4 0c.5-.1.8 0 1.1.2.4-.3.2-1.2.2-1.9h-1.3v1.7zm-11.5.3h.9c.4-.3.2-1.2.2-1.9-1.4-.4-1.6 1.2-1.1 1.9zm-4 .4c.7.2.8-.3 1.5-.2v-1.7c-1.5-.4-1.7.6-1.5 1.9zm-3.6-1.1c0 .6-.1 1.4.2 1.7.5.1.5-.4 1.1-.2-.2-.6.5-2-.4-1.9-.1.4-.8.1-.9.4zm-31.5.8c.4-.1 1.1.6 1.3 0-.5 0-.1-.8-.2-1.1-.7.2-1.3.3-1.1 1.1zm28.3-.4c-.3.3.2 1.1 0 1.9.6.2.6-.3 1.1-.2-.2-.6.5-2-.4-1.9-.1.3-.4.2-.7.2zm-3.5 2.8c.5-.1.9-.2 1.3-.4.2-.8-.4-.9-.2-1.7h-.9c-.3.3-.1 1.3-.2 2.1zm26.9-1.8c-2.1-.1-3.3-.2-5.5-.2-.5 3.4 0 7.8-.5 11.2 2.4 0 3.6.1 5.8.3M33.4 41.6c.5.2.1 1.2.2 1.7.5-.1 1.1-.2 1.5-.4.6-1.9-.9-2.4-1.7-1.3zm-4.7.6v1.9c.9.2 1.2-.2 1.9-.2-.1-.7.2-1.7-.2-2.1-.5.2-1.3.1-1.7.4zm-5.3.6c.3.5 0 1.6.4 2.1.7.1.8-.4 1.5-.2-.1-.7-.3-1.2-.2-2.1-.8-.2-.9.3-1.7.2zm-7.5 2H17c.2-.9-.4-1.2-.2-2.1-.4.1-1.2-.3-1.3.2.6.2-.1 1.7.4 1.9zm3.4 1c.1 4.1.9 9.3 1.4 13.7 8 .1 13.1-2.7 19.2-4.5-.5-3.9.1-8.7-.7-12.2-6.2 1.6-12.1 3.2-19.9 3zm.5-.8h1.1c.4-.5-.2-1.2 0-2.1h-1.5c.1.7.1 1.6.4 2.1zm-5.4 7.8c.2 0 .3.2.4.4-.4-.7-.7.5-.2.6.1-.2 0-.4.2-.4.3.5-.8.7-.2.8.7-.5 1.3-1.2 2.4-1.5-.1 1.5.4 2.4.4 3.8-.7.5-1.7.7-1.9 1.7 1.2.7 2.5 1.2 4.2 1.3-.7-4.9-1.1-8.8-1.6-13.7-2.2.3-4-.8-5.1-.9.9.8.6 2.5.8 3.6 0-.2 0-.4.2-.4-.1.7.1 1.7-.2 2.1.7.3.5-.2.4.9m44.6 3.2h1.1c.3-.3.2-1.1.2-1.7h-1.3v1.7zm-4-1.4v1.3c.4.4.7-.2 1.5 0v-1.5c-.6 0-1.2 0-1.5.2zm7.6 1.4h1.3v-1.5h-1.3c.1.5 0 1 0 1.5zm-11-1v1.3h1.1c.3-.3.4-1.7-.2-1.7-.1.4-.8.1-.9.4zm-3.6.4c.1.6-.3 1.7.4 1.7 0-.3.5-.2.9-.2-.2-.5.4-1.8-.4-1.7-.1.3-.6.2-.9.2zm-3.4 1v1.5c.7.2.6-.4 1.3-.2-.2-.5.4-1.8-.4-1.7-.1.3-.8.2-.9.4zM15 57c.7-.5 1.3-1.7.2-2.3-.7.4-.8 1.6-.2 2.3zm26.1-1.3c-.1.7.4.8.2 1.5.9 0 1.2-.6 1.1-1.7-.4-.5-.8.1-1.3.2zm-3 2.7c1 0 1.2-.8 1.1-1.9h-.9c-.3.4-.1 1.3-.2 1.9zm-3.6-.4v1.7c.6-.1 1.3-.2 1.5-.8-.6 0 .3-1.6-.6-1.3 0 .4-.7.1-.9.4zM16 60.8c-.4-.7-.2-2-1.3-1.9.2.7.2 2.7 1.3 1.9zm13.8-.9c.5 0 .1.9.2 1.3.8.1 1.2-.2 1.7-.4v-1.7c-.9-.1-1.6.1-1.9.8zm-4.7.6c0 .8-.1 1.7.4 1.9 0-.5.8-.1 1.1-.2.3-.3-.2-1.1 0-1.9-.7-.2-1 .1-1.5.2zM19 62.3v-1.7c-.5 0-.6-.4-1.3-.2-.1 1.1 0 2.1 1.3 1.9zm2.5.2h1.3c.2-.9-.3-1.1-.2-1.9h-1.3c-.1.9.2 1.2.2 1.9z' fill='url(%23SVGID_3_)'/%3E%3ClinearGradient id='SVGID_4_' gradientUnits='userSpaceOnUse' x1='45.269' y1='74.206' x2='58.769' y2='87.706' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='0' stop-color='%23f9eff6'/%3E%3Cstop offset='.378' stop-color='%23f8edf5'/%3E%3Cstop offset='.515' stop-color='%23f3e6f1'/%3E%3Cstop offset='.612' stop-color='%23ecdbeb'/%3E%3Cstop offset='.69' stop-color='%23e3cce2'/%3E%3Cstop offset='.757' stop-color='%23d7b8d7'/%3E%3Cstop offset='.817' stop-color='%23caa1c9'/%3E%3Cstop offset='.871' stop-color='%23bc88bb'/%3E%3Cstop offset='.921' stop-color='%23ae6cab'/%3E%3Cstop offset='.965' stop-color='%239f4d9b'/%3E%3Cstop offset='1' stop-color='%23932a8e'/%3E%3C/linearGradient%3E%3Cpath d='M45.2 1l27 26.7h-27V1z' fill='url(%23SVGID_4_)'/%3E%3Cpath d='M45.2 1l27 26.7h-27V1z' fill-opacity='0' stroke='%23882383' stroke-width='2' stroke-linejoin='bevel'/%3E%3C/svg%3E"); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-mov { - background-image:url("data:image/svg+xml;charset=utf8,%3Csvg id='Layer_2' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 72 100'%3E%3Cstyle/%3E%3ClinearGradient id='SVGID_1_' gradientUnits='userSpaceOnUse' x1='36.2' y1='101' x2='36.2' y2='3.005' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='0' stop-color='%23e2cde4'/%3E%3Cstop offset='.17' stop-color='%23e0cae2'/%3E%3Cstop offset='.313' stop-color='%23dbc0dd'/%3E%3Cstop offset='.447' stop-color='%23d2b1d4'/%3E%3Cstop offset='.575' stop-color='%23c79dc7'/%3E%3Cstop offset='.698' stop-color='%23ba84b9'/%3E%3Cstop offset='.819' stop-color='%23ab68a9'/%3E%3Cstop offset='.934' stop-color='%239c4598'/%3E%3Cstop offset='1' stop-color='%23932a8e'/%3E%3C/linearGradient%3E%3Cpath d='M45.2 1l27 26.7V99H.2V1h45z' fill='url(%23SVGID_1_)'/%3E%3Cpath d='M45.2 1l27 26.7V99H.2V1h45z' fill-opacity='0' stroke='%23882383' stroke-width='2'/%3E%3Cpath d='M6.1 91.1V71.2h6.1l3.6 13.5 3.6-13.5h6.1V91h-3.8V75.4l-4 15.6h-3.9l-4-15.6V91H6.1zm22.6-9.8c0-2 .3-3.7.9-5.1.5-1 1.1-1.9 1.9-2.7.8-.8 1.7-1.4 2.6-1.8 1.2-.5 2.7-.8 4.3-.8 3 0 5.3.9 7.1 2.7 1.8 1.8 2.7 4.3 2.7 7.6 0 3.2-.9 5.7-2.6 7.5-1.8 1.8-4.1 2.7-7.1 2.7s-5.4-.9-7.1-2.7c-1.8-1.8-2.7-4.3-2.7-7.4zm4.1-.2c0 2.2.5 4 1.6 5.1 1 1.2 2.4 1.7 4 1.7s2.9-.6 4-1.7c1-1.2 1.6-2.9 1.6-5.2 0-2.3-.5-4-1.5-5.1-1-1.1-2.3-1.7-4-1.7s-3 .6-4 1.7c-1.1 1.2-1.7 3-1.7 5.2zm23.6 10l-7.2-19.8h4.4L58.7 86l4.9-14.7h4.3l-7.2 19.8h-4.3z' fill='%23fff'/%3E%3ClinearGradient id='SVGID_2_' gradientUnits='userSpaceOnUse' x1='18.2' y1='50.023' x2='18.2' y2='50.023' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='.005' stop-color='%23963491'/%3E%3Cstop offset='1' stop-color='%2370136b'/%3E%3C/linearGradient%3E%3ClinearGradient id='SVGID_3_' gradientUnits='userSpaceOnUse' x1='11.511' y1='51.716' x2='65.211' y2='51.716' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='.005' stop-color='%23963491'/%3E%3Cstop offset='1' stop-color='%2370136b'/%3E%3C/linearGradient%3E%3Cpath d='M64.3 55.5c-1.7-.2-3.4-.3-5.1-.3-7.3-.1-13.3 1.6-18.8 3.7S29.6 63.6 23.3 64c-3.4.2-7.3-.6-8.5-2.4-.8-1.3-.8-3.5-1-5.7-.6-5.7-1.6-11.7-2.4-17.3.8-.9 2.1-1.3 3.4-1.7.4 1.1.2 2.7.6 3.8 7.1.7 13.6-.4 20-1.5 6.3-1.1 12.4-2.2 19.4-2.6 3.4-.2 6.9-.2 10.3 0m-9.9 15.3c.5-.2 1.1-.3 1.9-.2.2-3.7.3-7.3.3-11.2-6.2.2-11.9.9-17 2.2.2 4 .4 7.8.3 12 4-1.1 7.7-2.5 12.6-2.7m2-12.1h1.1c.4-.4.2-1.2.2-1.9-1.5-.6-1.8 1-1.3 1.9zm3.9-.2h1.5V38h-1.3c0 .7-.4.9-.2 1.7zm4 0c.5-.1.8 0 1.1.2.4-.3.2-1.2.2-1.9h-1.3v1.7zm-11.5.3h.9c.4-.3.2-1.2.2-1.9-1.4-.4-1.6 1.2-1.1 1.9zm-4 .4c.7.2.8-.3 1.5-.2v-1.7c-1.5-.4-1.7.6-1.5 1.9zm-3.6-1.1c0 .6-.1 1.4.2 1.7.5.1.5-.4 1.1-.2-.2-.6.5-2-.4-1.9-.1.4-.8.1-.9.4zm-31.5.8c.4-.1 1.1.6 1.3 0-.5 0-.1-.8-.2-1.1-.7.2-1.3.3-1.1 1.1zm28.3-.4c-.3.3.2 1.1 0 1.9.6.2.6-.3 1.1-.2-.2-.6.5-2-.4-1.9-.1.3-.4.2-.7.2zm-3.5 2.8c.5-.1.9-.2 1.3-.4.2-.8-.4-.9-.2-1.7h-.9c-.3.3-.1 1.3-.2 2.1zm26.9-1.8c-2.1-.1-3.3-.2-5.5-.2-.5 3.4 0 7.8-.5 11.2 2.4 0 3.6.1 5.8.3M33.4 41.6c.5.2.1 1.2.2 1.7.5-.1 1.1-.2 1.5-.4.6-1.9-.9-2.4-1.7-1.3zm-4.7.6v1.9c.9.2 1.2-.2 1.9-.2-.1-.7.2-1.7-.2-2.1-.5.2-1.3.1-1.7.4zm-5.3.6c.3.5 0 1.6.4 2.1.7.1.8-.4 1.5-.2-.1-.7-.3-1.2-.2-2.1-.8-.2-.9.3-1.7.2zm-7.5 2H17c.2-.9-.4-1.2-.2-2.1-.4.1-1.2-.3-1.3.2.6.2-.1 1.7.4 1.9zm3.4 1c.1 4.1.9 9.3 1.4 13.7 8 .1 13.1-2.7 19.2-4.5-.5-3.9.1-8.7-.7-12.2-6.2 1.6-12.1 3.2-19.9 3zm.5-.8h1.1c.4-.5-.2-1.2 0-2.1h-1.5c.1.7.1 1.6.4 2.1zm-5.4 7.8c.2 0 .3.2.4.4-.4-.7-.7.5-.2.6.1-.2 0-.4.2-.4.3.5-.8.7-.2.8.7-.5 1.3-1.2 2.4-1.5-.1 1.5.4 2.4.4 3.8-.7.5-1.7.7-1.9 1.7 1.2.7 2.5 1.2 4.2 1.3-.7-4.9-1.1-8.8-1.6-13.7-2.2.3-4-.8-5.1-.9.9.8.6 2.5.8 3.6 0-.2 0-.4.2-.4-.1.7.1 1.7-.2 2.1.7.3.5-.2.4.9m44.6 3.2h1.1c.3-.3.2-1.1.2-1.7h-1.3v1.7zm-4-1.4v1.3c.4.4.7-.2 1.5 0v-1.5c-.6 0-1.2 0-1.5.2zm7.6 1.4h1.3v-1.5h-1.3c.1.5 0 1 0 1.5zm-11-1v1.3h1.1c.3-.3.4-1.7-.2-1.7-.1.4-.8.1-.9.4zm-3.6.4c.1.6-.3 1.7.4 1.7 0-.3.5-.2.9-.2-.2-.5.4-1.8-.4-1.7-.1.3-.6.2-.9.2zm-3.4 1v1.5c.7.2.6-.4 1.3-.2-.2-.5.4-1.8-.4-1.7-.1.3-.8.2-.9.4zM15 57c.7-.5 1.3-1.7.2-2.3-.7.4-.8 1.6-.2 2.3zm26.1-1.3c-.1.7.4.8.2 1.5.9 0 1.2-.6 1.1-1.7-.4-.5-.8.1-1.3.2zm-3 2.7c1 0 1.2-.8 1.1-1.9h-.9c-.3.4-.1 1.3-.2 1.9zm-3.6-.4v1.7c.6-.1 1.3-.2 1.5-.8-.6 0 .3-1.6-.6-1.3 0 .4-.7.1-.9.4zM16 60.8c-.4-.7-.2-2-1.3-1.9.2.7.2 2.7 1.3 1.9zm13.8-.9c.5 0 .1.9.2 1.3.8.1 1.2-.2 1.7-.4v-1.7c-.9-.1-1.6.1-1.9.8zm-4.7.6c0 .8-.1 1.7.4 1.9 0-.5.8-.1 1.1-.2.3-.3-.2-1.1 0-1.9-.7-.2-1 .1-1.5.2zM19 62.3v-1.7c-.5 0-.6-.4-1.3-.2-.1 1.1 0 2.1 1.3 1.9zm2.5.2h1.3c.2-.9-.3-1.1-.2-1.9h-1.3c-.1.9.2 1.2.2 1.9z' fill='url(%23SVGID_3_)'/%3E%3ClinearGradient id='SVGID_4_' gradientUnits='userSpaceOnUse' x1='45.269' y1='74.206' x2='58.769' y2='87.706' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='0' stop-color='%23f9eff6'/%3E%3Cstop offset='.378' stop-color='%23f8edf5'/%3E%3Cstop offset='.515' stop-color='%23f3e6f1'/%3E%3Cstop offset='.612' stop-color='%23ecdbeb'/%3E%3Cstop offset='.69' stop-color='%23e3cce2'/%3E%3Cstop offset='.757' stop-color='%23d7b8d7'/%3E%3Cstop offset='.817' stop-color='%23caa1c9'/%3E%3Cstop offset='.871' stop-color='%23bc88bb'/%3E%3Cstop offset='.921' stop-color='%23ae6cab'/%3E%3Cstop offset='.965' stop-color='%239f4d9b'/%3E%3Cstop offset='1' stop-color='%23932a8e'/%3E%3C/linearGradient%3E%3Cpath d='M45.2 1l27 26.7h-27V1z' fill='url(%23SVGID_4_)'/%3E%3Cpath d='M45.2 1l27 26.7h-27V1z' fill-opacity='0' stroke='%23882383' stroke-width='2' stroke-linejoin='bevel'/%3E%3C/svg%3E"); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-mp3 { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnxJREFUeNp0U89PE0EU/ra7XWxpSsFYIbVQf9REFBHkYBRIPJh4wrN3DsZ4MPGP8b/wUCIHEw5EY0w04o9ILcREGmwVgaXbbXdnd2bXNxPahGyczebtzrz3ve99740WRRHkWn5cebu4cH6SMY7e0jRAHr9c3WxsVvcemmbys9yT6+uHJ8oaPefypdPDD5Ymh5w26wMkEho8JtDtuEOZFCrvN/4uJZNGH0T59D58X/C27aFNAL3Xthmsww5GCyN4+uzu+OLtQsUPxPQx6ZMAoQjBAw7O+bEVCMMQgqygs+LFs1h+dGd8bna0QmXO9OL6JYgwAvOFZKKoy3V44CgNfv7Yx8oLH+lUEgvzF8Ydhz+n41snAGRG5gUEwClzhHdvttFxfNyYK0EnJozKK5eGcf1qHo1GOxtjwI+pfvm4g/W1qtJgerYE2SXJSIL9+W0jk0mCShAxDXgQKgbNXxZq35vQKCiKQkSUXdc1+gcch1FHGPmKuIgBCdc66qJQHMG9+1NIpUylxxHtuW6gEiTIu+N4yjdWgty0yTmdNjFzcwKjY0MU7MLt+IjoSad16FoIx3b/A0DZ7FYXnsdpAjUMDOjI5zPgfoBsRodhhGhZHfBBU/nGAGRtxWIOg5lT2NtrI5dL0SB5KJzLodloqXaOEatPGztKq5gG3S5DNjuAK5NjKJfPYKI0okBkSdemCiSgS/rkQNLSePtxBj4LSCwfFtE0krqqX7ZVMnu9XlMXy2l7ME0dzA3iANQyY6vWxC61UY41zTyNcYh6/QCNXQvzi5dR39nHVq1BUyuMGAARsF6tbbe4iKD1r7Om5iFBdmW1SsDflLiuB6sX90+AAQDHAW7dW0YnzgAAAABJRU5ErkJggg==); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-mp4 { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnBJREFUeNpsk99r01AUx79psrTrujVtbceabnZs4DYRHSoMh6Dgq77rn+AfoA/+If4Bok+C0CfxVRDBh+I2NqZzrpS1DVvbtU3SJPcm8SSlsJlecsn9dT73nO85V/B9H0H78OLdt/LDlQ1uMYybIAgI9n99OWxoe83nkiz9hDDae330JvxL48O51Xxm/enNtKPbVwAh0Ec6kYpXat9Pnl2GBC02HrjM5Y7h4P8+7FtIFVJ49OrxUnl7ucIdfhv+BIDv+fBcj7p/tXMPrs2RXVTw4OX2UnFTrXCbbY7tpMsA13FDSDAOQ4gJEGUJLs0PPh9CkESsPrmxxEz2lra3rnpAt3G6adgdQhBpmeLkFodNmsjpOPoXBrQTDcmFFNS7i3MRDzzPCw/vva8ikU+COQxm14BBhvJcHLGpGPTOAJxxeLbrRgAkYujBdH4G5oWJWXUW19YL4XqunAMFhnq1BqWYgaY1MAHASQOiU96zKzkU76mwehaOvx6h9uMv7KFN3RopL4oTAI4HRh4wSl399xla+00YbR3yrIzM9SzSqgJJnoKcklGrH08CcJjnBtLLCsSEGGpSWJvHtDKNoFippsJ0ulIsDDUCCATMlBQkNuahEyiZTcLsmFBKaQxaOk53TlHeKkM70AjAooCghBOk9sKtIvqtPqS4FBaRnJSRX8tj2DOh3lFB5Qw2ZNFK5LRo6w4sKt2ggAzywidAMN/9uIPSZglBLDO5FF3mRD3wHE9qVRvoHrUpfn+UEQK0/7ShtwboHJ6jdH8RZxSC57hSVETb7e5/2u0FxqPHJow+8iZ4lYY2QGu3idhIxO7Y7p8AAwALCGZKEPBGCgAAAABJRU5ErkJggg==); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-mpg { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnxJREFUeNpsU0tPE1EU/ubRdlqmnUBboa0UeUQDiUGCC1+JmrhxoXt/gBvXJi74If4AV0Y3sNKF0YUaICqoIfjgVShEiGF4tDOdO/fOeOaSKtie5GZu7pzz3e/c7ztKGIaI4vn9p+/P3h4e4a6Pv6EoQBDiy7P5rc1P1Xt6XP8M5ejXo6UJ+dWbuemeTGdpvNdiNe9YvQLe4Bi4PmTpRmyq8m71rp74BxKF2twIHvAo+f/l1T2Yp0zceHizfOZa/xRnfBRhG4CQqAYioBWeXDyA8Di6ei1ceXC1XBwrTXHPH2vW6ccBBBMI6BsSUEQzakGL6xB0tvjyBxRNxdCtc2Xf8R9TyaWTDOg2TjfVdw6hqIoE9B2GxkEDWlLH7s4ette2kSp0oDRezrQwCIIA3oGHr0/mKMmE53qo23W4+w5S+Q5ohob9X3tgHgO8ULQACC7gMx9mKQP30EW6mEHpYi8xcJEdzMucjfkKcrTfmqmiFYBxCF/Id+gayKJwoQjHdrA5v4HK7Cq44KjZNWpagaqp7QACks0H9znW365ia24DzoEDozOJbH8eVtGShXHTwNracnsG7q6LzsEuaAlNPm9h7DSSVjLyCMkppDI+GS2StQWA1RlKo0X56n2X+6QHkmkDakxF9WMVqWyK+s/BrthYfvWz1Ug+zUDcjMPMm0h3pxEjFma3CbIuCud7oMc0LL1ZgmElpGJtW3B+15HIGNITrMYIlOH7i0U41NrInREylYbu4R5qQbQBaAh95fVKZCnpQCnb9DrWZyrRERS6NDeUw+yHaXh7rt4C4B8y+9vkwn7kwKNRpDoa9aiFKBYnF+RcREqQ2e1m3R8BBgAy9kz9ysCE6QAAAABJRU5ErkJggg==); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-odf { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAi5JREFUeNp0UktrU0EU/mbu3FfE1KRRUpWYheALNBURUVy7cy9UkO6KW/+Lbt0IPsFui4gLBbUqFaUuXETUKCYa0jS5yZ2ZO557b5MmTXpgmDPnfOc7jznMGINYPi0de5UvmpORxpjE/kbNqW005DVu8TWw1H758ZfkFgNgJmtyxSPRjJIj0QTW/RDiYGXGb7Dl32/eXrVsd0gSCx9miqC0ooCdp69g5Q/h6OLN0ty5ynIkwzMwUwh2FwMdcbDiCZQXlkqFCpEoPT/wih1YjLInANcD+/Ua9bu3wJlGvrBZCmet2+S6ME5g4oGlZ9A/I70XCDhhDexPNTFmswJBwcnuXkF86VSNZxVu0ukLSGnBcqlnN4HoCQIaIuIv7LUooMOgQ7q75LAAb59B9gCBHSKgqemRr94mMKmD24CfM8nb7THYGQNLpAkUkcb66JyGBFFEWRVL57gFEH5qj8Lxwca2qS3EZaugmzAw24dR/XQgwtsCSBjPIdWbUoE2UJLBnV8Ac/ciWHsK9/glWLnD6K2vgPszsOdOQdfeQ1c/ThKoTgDn9A3KUED/52d45xchZsvorD6Bf/Z60riV3Q9Z/0bbGU1uopYGkfERSQ3VbsMwl0qlqoIARmSoPYXWy0dor79LfBMEEd8jGs/uQ3Yl7PJFNFbuEXiV2riCf88fovXhBbo/vqP3t02/ZYmJFqTkzY160Go9uEMbFK8hR/NrdXtFuUVmnmySVGgO4v4LMAAjRgmO+SJJiQAAAABJRU5ErkJggg==); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-ods { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAetJREFUeNqMUj1IHEEU/i7u7Z23e8tGgneGQPw3hZDkkhQiSuwMQREba4uUgpVlCrvEQhurkCoWqcQQ0oTAaYKNqJygGEwgHCSB6Knn7eXcdX/GmdHVPWYFP3gw78173/vmvYkQQsAwNvckq96UnyIEh7/d4t7uUd/8y+85P+bXSX4grkhI6nJYPW7LrXpBK2YxiSoShhu4Buq1NPofDeqdrZ3Z4cl7D4J3UtA5VyVAlmJoru9Af2ZAp1lcCQ3nqgiuKmbY3l/BH+MnHM9GVLP0Ww3KNA33CQoQQnL834Fj74PUGkANEIkCSSsa8gQqgYTIcB0PVsXB318GInRiCVWCkpRFAs+j5gKlA4t29Ggh4d0t04FKt9PQqF4UFgumSEA8ApeaElilWbYRVy/lsns/N1QBkxtENF4jxPxcgcB1CZVOrvMteK5IQDtJJIGh++PcX9iYwWjXK37+vP0WdYk0Ht99jtX8JywWFkQChw4tc+cZcvlF7rMze+ubbxN40fMalRMDP/6twaiUeK7wlZ0TD0a5hLTWxo2d45KKprqHKJslTsy209s2wnMFBTYNZjc/oLt9gPvLOx+hxVJIKS2YW5pCbSyJTGMK775O8VyBwDJd2LTDl/X5i8v3S7NVw9vJb51tITDEUwEGANCx2/rXEEFFAAAAAElFTkSuQmCC); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-odt { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAepJREFUeNqMkz1II1EQx/+7Ca6JkqyYiJ8cKEpAQbBQFDm0sVOsFBS9wt5KOTgEG5twxVlZ+XEnKNiIghYKxx5nwEpIIXaiSAgKGmMi0d23u8+3T7OaZJEMLG9mmPnN/w1vBUopLPNNhRWXHOyDg0nx82TiJtZPlPVoNpftc2cTotcHtxx06kdXpSQ/BvzKESZzIDmAz6y+NojOjpDMZiqRPIgNoFyWM8DrKUV7axO+gcp4g7AzmquAdVNqOgL2z2I4id1B0wgeygOyt/rLL5buLwAIDgA9dY+L+DkuDQOCrkMgBsRglcMOqAGwIstMg8AkGsuZMNUMRMkLqE+QGloglvlA7uIOAKvZajR0qJkUj/XHe0BTIclVKKlrfKsj9qA8gA6wqSJzPaXlr7ky//tdLEUfawsBjExUFGVWbT7AxSa42H2LMfODmvd3wKb7RAMLYwM8nts8xJ/pEe7/3PmP2eGv3D+9usb35W0bINoA7RmjXSHsH0f5Z/mUSZ0Ir2JmsBtD80s8/rGyzWsLFTD5yUQCbfUBHl9d38LvkdDTXIuHVBo0k+bbt06qO+yAPGXwe/cA4wO9PN44jKDG70GougIzi2tQ00ms7/3lpwnBBgjZ37Kkd1Shht5XzBIFl/ufFtniT/lFgAEAU//g6kvdGBMAAAAASUVORK5CYII=); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-otp { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAcJJREFUeNqMkssvA1EUxr+ZjkdbrfFKVD12ErYSRELY2fkH+BMsLcQaSwsrSzZi47EjJEQkEhYkFlhYSVtFpdqOqpk717l3jKZmiC+5mZlzv/s795wzCuccQncz3YeRBj4KHz0/RrOZe2NsZPP20o255zQ3EAxzEAC+6uzTw13G4TFQAakA/CWtIYbY0KBOrx7IvwDQqlHV1o3YxKTOvyAUvfQCfqmA3e4ikyS/zRAKvOot7eoSHEgZIHrCfQAfBqBaKQQDKScQAExd8emBANg+2U2CvNMkkgSqBmrCxFB8mujeoJBWwEqARcssKTAJEGrmaGrjqK1zvNknH4BtyxKl2VUpRxmj5W+x73q9AEaZrR/ND1EJluIpS3i9JQiA+a+hSq8HwJjTsLrRaWitPTCOlhEZn5N75sM1qigmlN+dB3u++Qao5W4TtbEXXIsiszGL4PA00itTsu6XnQWo0TjMTAJqfMDx/ryBJcaVzSNSH4fW0Q+rkIf5rsjRiid7yyN7uoXS3Zn0egE0NiORAN9bQ017D1Lri7CLlP2EDr3Rf7C/itzV2bfXA/igLDaRixfngFhSCooH2xVPCWBlwKcAAwBX1suA6te+hAAAAABJRU5ErkJggg==); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-ots { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAfZJREFUeNqMUk1rE1EUPS8zmabJdDKB2glEwY9ExJYiBUEQpV25qgtBXfgbpEtXuujKf+AfEKRddOdOGHClbYVCvyKWaijT2mhjphk7Sd7Me76ZONp0EsiBYWbOvfe88+69hHOOAE9f3zTVnDKNHvhlsfqPw/rM0ovyWsRFdXJEpDIyRnSlVz0KSkmvabaJeXSJBEhgAJzTDNybmtUnS5Pmg/lrN07H5NM/f13FoMgpXDSuhiIiK3Qi6LUugX7FAbaPPsJqfIHHKCStqRsXVFPQuZgD9BBxjikSiRq41AAkgCQBzVf0+BWEBX7GBm0xgHHUqk1UbBuEcIydzyCZlOI9YEGuDxwduCCitS3Xh3viCZ4jrcq4PJ6DLHd67tjtuAAXib54dCPVEfQ5XIcik/0/2iDeOYz3ceCxrisMi904y0XiMQFfkB7lg6xFHwFxEqUMV0anUNBLWKm8xd3i4zBWOzmASx0UsiW831mA59Xjm+h7HCOygduXHqJatzA7Poey9QnXjTuoVD/j/sRcmDOWLgqnLC5A2wwST+Pn8T629lahSCo291bwu9XA7vcy3m2+gTaUR14thrk9BXasbdiOjSe3nmPpwys0xSi/HpbDd3bIQC6dx/q3ZbRb/j8BEi3Po5cTJpHI9CBNDEa++GyDBN9/BBgAwfDlCVUQaNAAAAAASUVORK5CYII=); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-ott { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAdFJREFUeNqMU89r02AYfpJ0iVm7EqhVOxw7dDBEdpiCE1RoEZRddvUgbIex/Rs7eehppyF4LOzQu4MxwYp0HgShIuwwUVSCVtl0s13afl+SzzcpyZYmyF74eN583/s+PO+PSEIIeJZdrtQVI19Cgmk/Ph39bpllXq82g7sgLxVcyKNZpIx8Uj5u5zSjc9Gov8ZihCRC8D+7On4JczevGeTGSEIC4ctKJtB1DTPXi1iCCEkIm1EFlC2Em0iwtWfinXkIzjiO0jljtDC5TtflGIGUQMB+mfja/oPv2Rx9MMjpMdJxOXyXTwkcwIkewfqQ1QtQNB385zcI14FrtQexsSb6SRysZ4Fbf+F6eHwATc9gJGNAm5iCTL5n/LCVRGADNoeaGoHqyaXj5gqQlTODovcwNk5Aj6wXqV8eCo7EDhMonEHpW+dZC7gUG98D3geo7vkb01h9cAvPdt76OGy1xntUd3bjUxAk3+l2sHJ/FgtrT0MUJNfDSm0bjQ/72Hzxxo+NK+h3B7XRNO4UrwymQtMIkdTBU0m+sBOayLsn8Ka78mQDjx/e87HXPkb1+UsfP37+AmZ1fP/suknBb6nefVQXjl06TxMlJfWKNWr+Kv8TYAAkUueexJF47QAAAABJRU5ErkJggg==); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-pdf { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmhJREFUeNp0U0trU0EYPTP35qYxaW6TlDapNKWGbgo2FkF8rARB6rboXusf0F/hyq2U4krFqugqSBeuAyL4SERBstHa0iR9JKZJ7mvu+M0tqZGkH3x8987jzDnnm2FSSqh4ns0VU1ybFzj674Wa3uWiWbfsFQb+jrGj8Xvbm0HlvYVRxhJprpmTlGmum+OMm5uNPZNbtjk3l82ey8++8oW4Jv/H/wdA456g2kvH99FyHNiuAz2dwflbN8YW8zMK5Go/CMfQkAhpGsyQgRCtlpE4jIULyC9fHzu7MPPEl/5ib6WOE0JJNRiHHg6j86mMjw/2gG4bkbY4PW4Yj2j64skA5FTHdaEMPiAJszt1sK0d4suJmY4k0+IDDGRfqmh0u5gejQc+fG8eYCIahRQCEfgQnIuhEkgtONE+dGxYxEDj1DhiEycZ+1YXdUpHCqTMJIYyEES5aXXQsi2kYlGEia5GtHVKn+amPBeCutPgfLALPuVu+xDVPw2EQyFEjHDghbpYNm1yKVVnYjTOerepn4E6XQmLGSPkPkOXWATMSDcjQEkAaqOu6+i/rccALtFL53LI3r0Nq1ZD4/MXZJaWYFer+PXiJc6s3IEgY3+uPYZHTAcAHM+DTE8gnM1CSyaCulv+GrRy8uYyElcu4XfhLVpkpNtn/DGA5Uu0abFH36WnzzCayWAkmYJvWeCkfb9SwY+NDbSoOx4bYqJF8rZqVRRXV/HhzWtUSmWwmWl0RmN4v76OUqGASrmMOkntSHF8MOs954dT08W248wzYsJDOujRBAaqqikTpRo/qqd0/dv97c3Lat9fAQYA4z8bX9nTsb8AAAAASUVORK5CYII=); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-php { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAhNJREFUeNqMkltrE0EUx//ZbDaXNrvZzdIkbYOXGgxYQlCK2IIY6EufxGdB8Av44AdR8AP44JOPBR+Ego0PClUKTTXQSmkTYtOkmubSJrQ1e3H2yJSEJNIDs3PmP+f89pyZcdm2DcdWvn7LzkxFHmCIra7nm9ulg8yLZ09yXON55Dgjt1PM2iPs0+aW/frdh8bzV2/SvQBnCLiEqcFxLKSSodlrU9leiGPihWePBkgeEZO6ShC2dCAZNuf6ADb+ldQ5PUPx4BCFcgXfdwq4Ph1Dtd5CZi4Nw7SQiMdCXkl6yVIy/QBWgcU+yx/XsLK2cdHndqlK/lZxH/OpJO7fnsWY3z/YAq+g0TmHpoUH2vB5PXi8RD9Fo10aAmDJTgWyIuOupmK38rsPcOvqJO33XWEvwLJsmKxHRVEwf/MKWl/yUMf8mIloWN8rw+sP0D6PHQmYuzGNgCRiMZVA17IQV4OIaTI8buH/AJMFd02Tkp05PO4jnWvc57EDAINt7u1X8Pb9KgI+Lxbv3cFR8xjx6AQ+b+Txs/qL9KePlih2CMBCq92hg2qzt1AoV7H5YxdhdqhHzRbgcpFeqdUplpvQW4FhmAixZ/sws4BoWCM/qmsE5XqE3dDQCrqGAYWdejqZgK6GUD8+IV9VghBFN1RZJv3sT5diBwC15gncggCPJKF0WCPN8dun55jQdVpz3Ynl9leAAQAJhiGatD9AOgAAAABJRU5ErkJggg==); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-png { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmtJREFUeNpsU9tOE1EUXXPp0CAUWmJbC04xBANNTF+kKhG8fID6aqL/gPEj9E0lIf6Dj30HL03wxQtVIC0QKrWxNG1Dk9Z2Oj1zxn1m0oIZTnIyZ8/ee+211z5Hsm0bYg29fLGpxWIJWBYGS5IA8ncKhT9Wvf4Yqprtu+w3q85X7f9QxseD/pmZMZsxN9fnc5JNw0ACGGv6tPSvyvEDKEoWZ5Y8OHHObKpucw4B0t3agnl4CJPs2YkQVu4s61ORaBqMJc8CDBiIRhhVM9bXYdVqYAcH8M3NgS0tQQsFcfdKHEbvlr6WyaR/V6uPKPy7B4DT7lUq4MUipMlJ2MPDUKtVfKZ2nn/5BoNbkONxXeb8LYXe/A9AJLNWCxgdhZJagDI9DZg9qIkEytRSkdqTSFQtGILSbgc8LViM+tc0yPfukzIyOJ359k9YR0eQdB2KmBbpwXoM3Dod1SkD+scpEapCI5DdpsJhIJcjajQZagcjI+5oLe4VkeQnyiZgdIH2X6BJ7dSqQLfrggjw0AQwP+/GegCIHppNoFAgEMO1RZKo7BQgRi3yN05cnwdA0BQMAgF3C6pnbuNg92M9AFT1diSCh6kb+FGvo2MxnBB9ocZxp4Mns1cde213B81e7xwAcl4jkaa0IUSjUdLJwkL0Ej6VSvArCt7l81iku6GrKnYEU89VJlSJRmR0Dax+fI9suYxSo4HlWIw6M3FBlnD9YhiXabyOsOeIqG7TzDeIYo6EDGp+ZPb2kKKqH8h+mkxiI5/D1/19J3bwYPvPWXq2skkiJVxesqt0XzghpKM8nRVV2Lv2q9eLIvSfAAMAaacnllcFBmYAAAAASUVORK5CYII=); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-ppt { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAkhJREFUeNpsU11rE0EUPTM7ySZpmzT9DNamWAtFfSiCigr+AxF9zKtv/hvf/Aki+FEi6ov4ItWHPGiwiBUKoUqqTUJImmR3M7Mz3t0kNe1m4LIwc+65595zlxljEJzdR5uf5nLmsvZx6gSvtd9W9bjhF7jg5dH9nRc/wq8YXaTSJptb0xklx7IZoKUEz1zJ2DUU69/37vFYrDxegJ9U0lC+AoIIVGg9CL+vIObP48KDQn7x0sWiVnJrnEDg7KGk+i/Ac4iUM/R7BsmrSSxtXMfa3X7el8+Kjf3KfUJ+iRJQw4w0Tc8BRyWGRAZY3rBR/VlC+XED2ayDhZyXl03+hNA3TxNQshlGLAnE44zCIL1goXZwiMNvB1i6zbC0KuAsxNITWwgNMYPeLVJiFEO9ArjHAivrAjNzBr4f4vwIgdGD4YUACsZCE8AtYGWT5jCsGQw5wEYJzP/pj5RwYTA1b07eQmfZ8P0sgdaM2FlYwWkMgMpl6NQAO33GKM0wsQWflkh1uqGVmVWblsiDkQyqxwfag35SqcktaEWTUTHYNx4iGU/C29+BvX4Lpu/C7zYgFjegSY63WySsHyXwpYHU00ieu0bAOuJbBTArBkiXKiaAmTzcvRJUV9E8rOgqBwqlY8ASs/AadbRLb8CzeTjVClqft6FdB17tL7yeCbFRBYoLr6vR/PiSEl5BZJaBD0/R2nkOZqfQ2fsKt+0SEQ+GLSIEUvJm+6jbah2+pS2aon+4g/afd4SYJVuA7vvXdC/IHQtSoTnK+yfAAIEaId1m+vudAAAAAElFTkSuQmCC); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-psd { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAqxJREFUeNpsU01ME0EYfbtdKKWGtoItRWgJHApCBE2I0YuoiSaaeDJeOJh41YN3TfTixcRwMfEk8eDJGA+Eg0YTTRRMg02KKFooCBbTlkJLS7f7P+u3K9Xo8iWT3Zn55s173/uGM00TVlwZfzJztD92iKO5ouvQGQPHcQDN380vlDPr65fdLj4Oa41i9sFt+ytgN7o7woGOrqgvvpLBaF8vWj1NUAwGTVNRM3mf5vU/zaU+XySQuTqIFXz9hxmGLkoS7r+YxvVnrzGzlgXPDOzUZPT4m3Dt/KlIuH9oUjXYEHZZ/wOgGQZi4TZcGI5hLb+FO++TSOSKcLtcMA0dI0EPrp4+HtnfG5skiUecDGwQE2MjAwiGWlFVNDz+tIyCokJhPKYSX7Gdz2I01hOJdnY9rJ/7UwPGTEiqjtbmJtw4MYx78S/4Wa3h5UoOYwPdIOp2Xi/t18rlFgcDw6o+ydiWVRwOBnCpL0oOAMmNEhLZIgSeoxwGSWcERon/M9DoBknTIdNQNAMnO4PIVGpIFXcwndlA2OtGc4MAxml27p4AIulWSIa9QVadiYSoJxhqBJivKgh5ad3k9gaw6JdlDaqq7q5wINY4F22HaLHSDZQkBW72O9cBYFEviBIURQH7a7MN0uDisUW12ZZcaGlmdq4DwCqeTo1zNtZuW7hUqGIw7MNqSUS2ImNsKEpSdEwt5lGhfQdAkQBEoub3NNrDJfAIeBuRrcrY5xGQ2RFJAjl00I8PCckJUCB9q1URBnk38XEJEuk41tmGwZAf66s1VOh2keqwoUnYpFxHH4iKIixkN3HzVQKP3iQR/5GDKMuYmE3h+fx3MHqh1sMafztHLuiCg0FAk0uFdLqcpGY5QEXbTC/j7mIaVjc18DxufUtBJ/vcggs+3ijVz/0SYABsJHPUtu/OYwAAAABJRU5ErkJggg==); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-py { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAlVJREFUeNpsUktvEmEUPTPzTUFmgJK2UqXQFG3pA6OBLrQxamJcaYwuu3Dp0l9iXLvVtRuDpgt3JIYaTVSaxtRHsJq2xEJBHgXmifebMhECXzKZme+ee+65516h2+2Cn2cb2VwyHl12//vP2/zOQaF4uD7GWN69e/LogfNm7kUsPBFaXYwHMeK0OlpQEJApHJTuykzK98dE98O0bLM/UNgr4v32Dj1fwSQRt9dSsfmZcMa0rIv9ODaqYrPVxuPnL1Cu1aEbJu7fvIZUIo4bqeVYRzcyv/8c3SPYpwECt/dmu4ON3Ed4TymI+hQc1ZqoE+F+uQLDsnHlwkKMscJTgl4eJOi9fxZLePNhGx6ZQRRFqH4VjZaGSv0Y6cQcJLpra0ZguIWegqDiw7lYBBZV6xiGk9DQDLzK5bEyF4Hi9VLMsoYI7J6Es5PjeHjnOl5ubqHaaJGBEkzbxplQAKIgDmBHekDTgI+qKKqKLvNApgmEgyquLs1CoFn2Y4cIeLJpkjoCLkWnUSIF3JxISIUsCjAoxhWNJLBIJs3YeXj/08oYZkOKY65HllE/bkMmY504YUd40HUq2JSSyW6iVPmLiXE/ZMYQCU+hXK3h1toqdNN0sEObyKtqtDQ6kXDwcadDS2TBryp4nX2HxXjsJK6bDnZIAZem6Tp5YMMmicn5OC4lztNWtvB9cg+hQABtWjKL2jH/T3GgBcYDXEE6mcDM6SlaJAGMWkivLBC54ZgniZaDHSI4rNSqn7/t1vgkGJPwZXffSeCjk2iUWz9+nSTQN8e6ef8EGAClUi/qoiOc3wAAAABJRU5ErkJggg==); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-qt { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnVJREFUeNpsU8tu00AUPU5sp41NkzRxpfSZqi0VIIQqEEJUZYXECvbwCWxYsuBD+ABUFrDrCnWBQEJdIWigBSr6pqRJ1ebhxrE9M7aZmSrQ4o505fHMnXPPPWdGiaIIYrx89GKpNDdxmXkU3aEoCsT+z8W1Sm21+jCpJctQTvaerj+TX7WbnJ+0cpfuX8mQtn8GgJ4AZtIFY2Hz3foDVRcgyt+cRHcS0IARh+D/8G0PpmVi7smd0dLs+AIjwTVEiANEYYQwCHlEZyJgIQKfoX84g9uPZ0cHZ4YWmE9nuufU0wABCSSImMsWEgqSuoqA/39/swZFTWLy7vQo7dDnfPvWWQa8GuOV3IYLJXmyzDzG2/ChZ3pwbHdQ267BKJoYuj7SF2MQhiF8LuDK/Gf0DKTBKINz1IbTbEMzU1ANDW7LAfEIQKIgBsBFlAx6LYOz6MAcvoDCtAVGGPKlAiIu/F55F33FDA6W93EOAOMaMOl7biKPwRtD8Foetj5sYPfTDtxjl1f3Ubo5jkQieQ4ACSUD2iE4XDpAdbUiW9D7UsiN9WNkZgxajwbd0LGzt3keAJPUc1N5SVeENT0Ao2BKV6QzwlZeRBSKAYhe3aYHcZWn7l1EfjyPypcK9LQGa8qCvW9j9+MvaasQOHaRhGWdhsNLR8hwodYWf6B4tYjDjSOovRqq32rSYq/lytw4A77o1V2ERiAtzY5kkUrrsH+3QF2KY87ArTtQuQ6nAf4x6FCV1D001+vYersBM2vA4y1Rm2D7/Rac/TZIw4d/6MrcGAPf9htN0miJh7Lyuoyvr8rQeP9iVJcrSKgJ+TrFcyYebXTP/RFgAFQobmIOBxbsAAAAAElFTkSuQmCC); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-rar { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnpJREFUeNpsUktPE1EU/u68OgylZXi0hZACQU1LEKKCMcat7jTRnQsXxsQtv4E/4M74P1iriUaNCw1FgxpjCJQKKAU60+m8mJnrmSll4XCTc8+959zz3e88GOcc8aq9evChOHl/lvMoubvWX/z4+BwTlbvw7bXdg8b7h6LE1gGW+O88CRMt4XTlR6/rYxce5Xv3jlHH19fPkBu+gWy5mlcFb3Wn/umeKOEMJF5C7xCFbtA9dRXjFoYKGiTRAlPGUV1aKU9O3VwNQ74A8DQAIZxqAuAhBPIMFYpQVAVB4CPSZjEzv1weH5tbDQN+JQ2Abu488mnzIbAAA3o/VK2PwDJo7r5Fy7ZRuvi4PFS6+qIXdVYD8Jg6BUcuOD8BozSLlRWyicgVKkTMQWwUlFF0Ooe5FIPk57BD7G0SiywyjD8bCDyHsOkeeeR3SUxEkROmU6BfQYFJMHfhWXV8efkUrb13VPMTsrcTQSzxZ/+n0GVA6EGbSGdgG9vo15fg2nFgbO8k70SRdd+mahDT81vUxTZRlJBRMsjq89C0EXCvSf7TIBZ136YZUJEiE7LgJ2dN01BZuE0dkIhxE7KcQTK1QUj+cwAEyrPZ+IydzRoyah+mLy2isbWBweESJEnB9q+1RM9Ub9GQOWkABg8HjRr2d9Yh0hTlBlRsfn+D4vg0BvUC9rZqECUJuk7Tzr1zahCYlB6HJAREPwfbbMBzLBzsbUKVI0qBgQkc+SxgWUYaIAqOpKwKXJ6bgGlaaDV/YvHaFNrtDsKTfVSrJeqIg/bRNwjclFIALeP3saybhu8SC4VBHwnhBXXIKocYRXD9QzBi4Xgchmkd9+L+CTAAMqwy+ZzluBgAAAAASUVORK5CYII=); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-rb { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAixJREFUeNqEUktvElEU/mag5f2yJhXLwxIt0kiqsVEXujP+A925cu1Pce3WtXVtYuJCF7KtTY0NrVQIpRVKeXTkMcO9F8+9ZVooJJ5kcmbmfOe733fO1YbDIWS8+/g1dycVX7W/xyO3vdsuVKqvnE7HZ230783rlyo7bVBicSGyfjsVwozomVbIPe/c+FmsPHfoRKJd1HT7hXHBZjVbA4aA14NnD9bC2VR8gwuxPi5Sx39Cp+M0XUP0ahhP1jLhW7HFD4zze3b93ILtXYyyVKlR8/5hFbnvO9gtlrGSjOF+OpXkYviWyo8mCS4R6bqO4p86vm3v4fC4DrPfw4unj1XN6JvBaQtjChzUXK43sVU4wNFJA43Tv/B73edQwTmfIhAjCVL6UdPAj1IVFSKhCdAcAI9rnjBiAjtBYEu3GEeh1sKJ0YXR68sVIujzIhzwY8DEBHZqiLRKkicQDfvABxaiQTc4Y/C65pCOXwcjcmlvJgHtlwi4epYifiQWgmoLZwPW6HQG07LgcOgKO0UglAKOTt/E+09fwAiUWU7QAE9xUK3jbvomsispZVHMVEDSZdHo9rCZ/4VIMKAu0XGjpU7d2S8hk0pCELHEzrjKnCQOYJoD+Dxu1RyiwUm5LaMDo9NFt2cqDLvY4oQFp/QpfT/MrmI5FkWebt+NpWto0j2QmQkOjZ9hpwhqjXZzM/+7LU+cc7lRrjXh8/lVLRK5ovLWXglOsiOxdt8/AQYAzv8qbmu6vgEAAAAASUVORK5CYII=); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-rtf { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAe5JREFUeNqEU01PE0EYfnZmd5FSvgLYFuwWt9EgHyEaox68eDJevHvwJ/hTPHv1N/QgZ2NC4g3kUAQKFKGhjVKqRrvbnRlnht262FHfy+y8877PPM8z71pCCKh4/ebt+rJfXEz26Vjf2mnsN5rPKKWbVpx7+eK5Xu2kyMtNTd5d8MdhiJ9BOO7atFI9ajy1UyAqSPIRMR6ZmoNehNHMMB7fX/UWvEKFMbYKE8DfQnAhwRmmJkbx6M6S5+WmK2Evup2c9yUk2nnKA0XVcSiGXAe1k5beP1i+4RFCXqnPywB/AKVzK34RjHNYlgVKCH50w7EBBogbTa/AVM5SgBdn0gc2AMDjPsbFPz2xye9asweS6n+NTbG8BCCfUtLjff2WoVnVpAH6z6hMUtJE3EykYfpF4vUiL3QNS7FMeSAQRBHW3r1Hq91B+VoBQRji4+ExFsvz6Hz7jm7Yw5OH92AcJKW9G4SoHhzhy/lXbB98Qmm2oCXN5WawsV2TACEoJXqwTKOsb3BtR2ucmZxANpPB8JUhyPnHWDaDpfJ1eZFALzJJ4MKO5MEtv4TSXB7V/br8iQLMz+almRZWbvoo5q9qRlxwewCgeXbe3qrVO5ZkUD/9jJGRLPaOm6COi92TU1DbxYe9umRD0DrrtJO+XwIMABWp9nS+FgaoAAAAAElFTkSuQmCC); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-sass { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MDNDMTBBM0JGMTE5MTFFMTg3N0NFOTIyMTQ2QzhBNkQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MDNDMTBBM0NGMTE5MTFFMTg3N0NFOTIyMTQ2QzhBNkQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDowM0MxMEEzOUYxMTkxMUUxODc3Q0U5MjIxNDZDOEE2RCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDowM0MxMEEzQUYxMTkxMUUxODc3Q0U5MjIxNDZDOEE2RCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Po72XUcAAAJcSURBVHjahFJdTxNBFD1bykc/ttvdtttWGgI0bYrUgDZoNYqRJ014kMRXHvwB/hQTH/wFhMREJfFBQxBjhMRIFEQSCAlQxKYGggiU3e3HbnfX2bFt1EU9k9m9mblz5p4zlzFNExYmpue/jmTSZw5PZAl1MAwDT0c7O72wvPdudeNakPNtOZ0tsM7cvzdOc5yN5LDAsTFRAJks/kC2PxFRVe39Si6f4byez62EpAEH/gNN18F53Ri/Ocxf7OtdLMpKT42s/ZPg1cISJp/P0tg0TBzLCoK8D7eHh4RkLLJ4cCz12AjMXwgez8yhqtVo3NbqRKlcxcSL16gZwJ2Ry8KVc8kZO0HdTKlURn+8G6PD2SZhLMQj96WAiMAh2RXFYKI78lcJcx9WYBCycICnpNbojUWpD5Y0C4Zh2D0w6hWc70uQZC+IWfQZrXF0IsHvY+meBd08haAhoVMMQFJKWF7PNZM+klhRyogGhbqxOIXAMOtEwGAqDqVcgbVkkE+5UsEAWavf0az2t0ZqvK2qabh6IU3joizDwTgwej1LdVfJXkdbK8mt2QkayO99A0/0trQ46I1lVcX+UREhnsP34yLp1AD1xibBMuntpzU8mJyi3Tc1O4+l9U06n7x8Q/8PHz1DrrALt8tlr0CrkbJMHTop9Sk5sLa1g8L+ARJdnShKClY3tunN69t5iGLYTlCtakjFY7gxNABdN3B37BaqqoYT8pyX0in4ORbRkIA46YlDRbUTbBZ2Jb/Pw4qiKFnapcpPo9pdbrg8DjAOBsFgELJmsGs7eWkkc5bu/xBgAHkWC6UPADTOAAAAAElFTkSuQmCC); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-scss { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RkM4QjYyNDVGMTE4MTFFMTlBREZCNDNEM0ExMTk0MUIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RkM4QjYyNDZGMTE4MTFFMTlBREZCNDNEM0ExMTk0MUIiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpGQzhCNjI0M0YxMTgxMUUxOUFERkI0M0QzQTExOTQxQiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpGQzhCNjI0NEYxMTgxMUUxOUFERkI0M0QzQTExOTQxQiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pkf1yeMAAAJbSURBVHjahFNdTxNBFD0tLULpB91uodVWPmorUIxo0VSiNSExMYYHE33l0Ud/in+C+OSjYgjRGDBRCKJIUkIEWi0WKlja0ul22+5219lJ26gLeiezuXvn7rnnnrlrUFUVms3Mvd2bjIyezRVLBA0zGAzo6jhjm1te+7EU37rFO+w7JlMbtG+ePJ5mOaZmci/nsPl6ONBtw18WDQc9tZq0sp7YjTisXV/NFKRpRvzHpHodDqsF03djzuvDg6vHJWFAprF/Arxe/oins6+YryoqCiUBvNOO+7FrXMjnWc0WyIAOQP0N4Nn8IqqSzPx2swllsYqZl28gK8DDyRvcxKXQvB6gISYpiwgH+jEVi7YAfW4nEqk0PJwDofNejAX7Pae2sPhhHQoF63U5Gai2Bn1epoPWmmaKoug1UBoMrgwHabIVVCx2jdrKFwm67TZ2plldPQGg2cK5HheIUMbaZqKV9In6giDCy3MNYXECgKI2gICxoQAEsQItpNCHWKngMo01arTY/jFIzbutShJuXh1Fm9FImYiM7tTtKOtbO+toN9Nc+fQ5SGUOIVYl7HzPIH2YRZ0y2KZ+sVzBHn2v1mpMGx0DTaR3nzfwfGEJdybGkdo/wEigDyvxLzg4yiESvojZhfd49OAeLJ2degaSLIPOO6vwgiYaaRErTRREEdn8MeJbSVZ5M7nLdNExqFLaQwEfFfACQn1+HBWKSKb3MT4Sgstuh9vVDa+bQ4DORE6o6RlspzMk9TOPfr+fiLJCLFYr3TZSKNcI7+aJwWQmPM+TkqRg49tu65f/JcAAMwMas6WUKd8AAAAASUVORK5CYII=); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-sql { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAh5JREFUeNp8kctrE1EUxr+ZyXMkoa1NBROaSkpTBE23PhZ25cql2y5duvAPUdGFS1FxIRRBXZlFQ9GVdDENIhGJxkDsw2mneZnM83ruNZlOmNoDhzlzz3d/9zv3Sowx8Ch/qlYK2XM3cEJsbH0+qjV/rd6/u6aN18b7RMFT+9aosP/Ex+0ae/puw7j36PlKEMAzctKJ3aGFamMHjV0d+wcGitkMrpWWp6hVIciEk2MAOwbUWjosx0UiFoWqJpGMx5DNzODq5aIPoa82AWBg/lyKLMH1PMp/a9XvLXLzG1cuFlBaWpiKxaIPSLY6CaC93ggQjyiQZRkeQSzLRovGaPciWLt5faSWEBoh6KBvOhiaNga0+Y9pwaFxvu7rfp8F5pWDt+qNMp2IijHGwddWCvN+33/CoAOP5nVdT9SdoQ1JkggiQ6Yvr7V60+9z7akA2gfH9cRF8hO5F5Ve4lQAF9uuK+qFsylkzsQxrcaQm04hdWkR83Mzfp9rQ3fAFzu9Ph6+WMfjl6/pGBdb2jbKmx8QlRjWy5vkyhUZBPgOeGNHN9AbDLGUz6He2hVj3Ll9C8/evsdgaMK0HV8bcmDTU0UUBYXcedR+NLGnH0I3jvDk1Rsy46FP4C/1BtrdntCGHNiOAzWZgEKQ5Qt5lIqLojbaXSQTcRy2OwT4SZqk0IYAOgkVWUE+lxX/zb0DpFNpkTzmZmfFtzewhHYcfwUYAMZmVaZQlLFHAAAAAElFTkSuQmCC); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-tga { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnxJREFUeNp0U89PE0EU/ra725K22ILRGipb22pMG6JcSEQTbUIwnozxpBcvepeEP0KPogcT/wlNT17kIKbEmChFUYKGVtL0R2gLtNCl3Z1Z3+zSAlonmezOe/O+973vvZEsy4JYnqdPMu6RkSQYQ29JEkB+PZcrslrtPhQl23VZc8/tr9I1yMHg0EA8HrBM04lVFAhoY38fSSDQVN3pfKV8G7KcxZHl6v1xblqU3eLc3p2VFZjr6+gQgwsnhzGTuq6Nhs6kYZqXjwL0GFhEl3U60OfnwWs1GGtrUKNRsKkpeIIBpKIRtI1J7cX7hXRhc/MOhXw5DkCZGG2zXAajzFIoBMvng1ypIKOqmP30GW3OIEcimovzlxRy5RgAFwDEAIODkCcmIMdiQLsNdWwMZdJlg8pzEUt1aBhKq3XinxKYqF9yQbqRIqsMy+0Gyy47bKgUWXSLtDENE5wdtuqQATm50F1VnPbRGeEw8HXZbiV8fsDvI9ldju9vADAyihLEbrWAZhOoVp3z6iqBUiB1A4nEfwCEsbkL/M4TgE5n5jDx+oTEzp1d8m9tC8H6MaAB0imzx0NU/WKUYE+loEyawDBo2ui6TGfT6ANAxrvx87gYCGCxXEKVJvCWFsG3eh1vN/J4OD6Od4UC8o0G3TX7TGLHwI9iEQmvF9X6Fh7F4/iYy+GcLOMSlfEgGsP0qdNOmX0BiGKpVkV1bw/1nW2b/gCpf1PTcI+Y7eg6ps+G4bG4PR99SjAVo9HE4q+fKNE0vl5awuSohjeijbRefVjAtUgEQRK7Yhi9OKn7nKWZxxlSPWl3QwgnaIrW8QMhD542vUbx/W49m7sq4v4IMABOqi3Ej7bAEAAAAABJRU5ErkJggg==); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-tgz { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAnhJREFUeNpsU1trE0EYPbMzSTfdtInFtkkpiaXVWou2FRUEn/so6JugL/oH/Af+B1988if40jcFERQURNBSQdDWlLQN2lsue8neZsZvc7FoOrDszM75znfOmVmmtUYyvry++36yfOeS1qqzDtvH2P76ApPlW3Drb2sHex/uccHWAdbZX30kO2+B3siN3zhTnHuQ66+95i423jzFzOVljBdKOZNHazvVT7e5wF+SZBj9iZJ+3J11mbW2kR8T4LwFli5i4fqTUvnczTUp9RLtDhKgJx0q4dEwWAxrREKICHEsoYYXMXvlcWmquLgmY71yCkG/c0AkARgLMZpnMDMpGNzEYe0dGp6HwvmHpbHC1Wf9MnFCkHQOyYEPzSJwQ2B65Tm5NZG3Fshim6wbMNJn4bpHowMKtIqo2COgR2IcAptwjvcgo6i77igjEmVDqbY8xQJ1VwRULhiBI6+G9Zf3cbTziuzIDkmHSNqECTFgQScEcYuc2NA8TcdYwXD+GkK/TYVN+u72WrIudiAD8o6oAR2RRCmQMjis3CIy1iSpPySCXhFTXeyAgh4BR+JVw8pauLi0Cp4yCX9A90FQhnSBYtnF/k+Q+HYam9itfIZB3QvT8zj8XSW5EhNTs9ivbSLwPUzPLNPJBIMEKnaQYg6aB9+RGR5F5VsNgnNKXMI1NdJGG5WfHzFVLJ7k8c8xUngpVodlDSGbFYj8Y4yMpOG09lHf3yIFPzA3fwHZTAQVtU4JUTeFDrdgDdlI8wAz5Qy2KxswReI7QODZcOr0ZH3q2hIDBI7zq16tuk3FNPxAI4wN+pkoccYoE4YJU5EdUtM4Qst26v26PwIMAKj3P/2YUKgYAAAAAElFTkSuQmCC); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-tiff { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmRJREFUeNp0UktPE1EU/qYzHWstlrYJNcWUElyUJsaNGh9B0g1Lo0v9Ey78EbrVxBhXuHShm25YGBJRQpAYBDEWpaEPEhksdVpbyjzveO4MfZDCTWbauefc736PIziOA77OPH2yJCcSGdg2uksQAKofFou/7VrtASRpvVNynj13f6XOhjg8HAlMTIQdy/LO+v3uYUPTkAHCTb+cK+0pdyGK6+hbvu4/xiyHbncYAwfR19ZgbG/DoO9LsSgeTd9JXoxfyMG2rvQDdBlwIZauQ5ufh12twioU4E+nYU1NIRCNIDs+Bt28mXzx8VNuZ796j9q/DgAwomwqClilAmF0FE4wCInAlkjO4y+r0JgNX2os6XPYS2q/cQyAcQatFjA0BPH6NYipccAwIGUy2CVJFZInkKlyJAqx3T4/IMGmJkeWIWSz5KgI5pdhb3yDXS5DSCYh8rTID8s0wexeVD0GtMd85KkkefFxUfE47M1NokbJkByEQl6tL+ouAI+MUwbFhnYbaJKc/Sqg0x4H4eDRGDA56fUOABA9/GsCpaIHwr8FOhQ823O5RfW66tUGADhNy3RNRDjcN41HLxdQ8J6jYTsOQLfOJBK4f+s2/uoathoNGKT1MtFeVHZxdWTEZfEq/wMKl3rCJOIzTV6ADs2R5ulYDDNkYjp0DhrF+zCVgkw31+v1UxjQZkNV0SADd2o1MIuc9gmY+/kLxb0/UFoHePd9A1qzeUoKpilx9xcLWzgg+u/zeVfuQqkM9bCN1ysrWKXxdtPgvScwUAm58XZ52W16QyPtifRUzi588GbEi1ztHPsvwAC4uC9qhnsZvwAAAABJRU5ErkJggg==); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-txt { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAeJJREFUeNp8UrtOG1EQPfsyXiyzBguIJSyChZBBEFCKpKHLo6egpErNn8CHgH8gkZIiTSIXLhJAWCgkoMgRMSiRBSK29z4y9+I1d/HCrFb3MTPnnjkzlpQSynY+fP70fGF2gQuByCz6lfdd9Uurfvrrjes6762eb3tzQ69uFJwPsqOPC+MBEmxxphi4tlU5OGmsOzaBWLc+O9oIIVhScidkyGZ8vH62nHtSKlaI4cse6TjAfSaFBBcco0EWqyvzubmpyQrj/FXk75cQaSEMeMXU8xykPA/Hjd/6/LRcyjEpt2i7HAe4A2TeLZWKUOJaVLxj27j813EHGKCXaAJExu/4BOdiAED08riQD2riOrexyRoYc3CvsAbLGAAjZga7vgZG23WMCdBvoxKJc36TRBlMiaa2JByjNqqD8qkYc1pjDK7abey+/YhrWlfKswhpiCR96aEU9o5+QE3g2ovVWDm2Sc22bBQm8vrVpbkS9r+doPr1EOWZaQ0yFoxg2PcREosEAI4uvZhJpzFMP+cSXRbq+043RManez+tNWKMI6GN0g0Z04HFR+NoNC/0yx717efZOSbzY3AcR4Op2AGA5p/W31r9e0vNgSrh9OwCrpeCkqvZuqTybnpRqx/r2CjvvwADAJC/7lzAzQmwAAAAAElFTkSuQmCC); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-wav { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAApFJREFUeNpsU1tPE0EYPXtpKbX0wqUQKVQMFdIXQBNCQBs06KP+B8ODGh+Mf4b/4IsGE54kxhcMBrkp7YOQgBRvSKG73fvsrt8Otoask0xmd+b7zpxzvm8E3/cRjPkniyulW0NFy2JoDkEAguOlpXJ9p3L8MBqVl4O9YHxae8pXuRlcGO7KPLhfTDVUqwUgigJMy4Whm6lEXHjxYf3XnByRN0QB/2KaH7btMlUxoRJAcyqKhdOaht7+DJ49n+2cvTnwynXcsb+kLwJ4rgfmMDDGWqvneXCZS9ND7mov5h9ND85M9y86Dpto5rUkuJ4Py3YDJpy6QGJPayqB+Njf+43XL220t0cwOZkfrNXsBUqZugDA6CbLdAiAwaek1ZU9LmP8Rh6S78GsGxjOp9FdzKJaVZIhBgGASzK21w/wbrnCk8euX+EMAjaaZuPHdwUdHVFYluuGPGCORwwYjg5rqOwccRk+3Ux0IEvntmsNG4ZmUayL/wAwKHUNfZfTKN0ZRaw9Cof8qJ/pMAyHy5KkAMTksSEJtnMenM7EMVMawbejMzJRh67bXEYiIXEAVTW50SEAhzqwfqrBcXx4VOhYm4RsNgHbsJFOyZTsQ1MN+hcohoUlkFiMT+TQFpMwXOjGpXgE+XwGk1N5pFJtKNCequgYGupCRBbCDOp0KBJc4VoP3dyBONW8uydBgBHUThqQKCk3mEZ/LoUG+RBioJO7VarAwEAntjYPiUUW9Hh4b2R7k9j98hN37xWx8fGAt3eIAdVMLn+uUv+b2KReSCZjZJiB9bV9jIz2ofr1BKvvd7G9dRC80lae0HzOt+cWVnrSKDrMJykifwNBpCgE/UAllEXufmDu8Zlffvvm8XSQ90eAAQA0pF7c08o4PAAAAABJRU5ErkJggg==); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-wmv { - background-image:url("data:image/svg+xml;charset=utf8,%3Csvg id='Layer_2' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 72 100'%3E%3Cstyle/%3E%3ClinearGradient id='SVGID_1_' gradientUnits='userSpaceOnUse' x1='36.2' y1='101' x2='36.2' y2='3.005' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='0' stop-color='%23e2cde4'/%3E%3Cstop offset='.17' stop-color='%23e0cae2'/%3E%3Cstop offset='.313' stop-color='%23dbc0dd'/%3E%3Cstop offset='.447' stop-color='%23d2b1d4'/%3E%3Cstop offset='.575' stop-color='%23c79dc7'/%3E%3Cstop offset='.698' stop-color='%23ba84b9'/%3E%3Cstop offset='.819' stop-color='%23ab68a9'/%3E%3Cstop offset='.934' stop-color='%239c4598'/%3E%3Cstop offset='1' stop-color='%23932a8e'/%3E%3C/linearGradient%3E%3Cpath d='M45.2 1l27 26.7V99H.2V1h45z' fill='url(%23SVGID_1_)'/%3E%3Cpath d='M45.2 1l27 26.7V99H.2V1h45z' fill-opacity='0' stroke='%23882383' stroke-width='2'/%3E%3Cpath d='M9.1 91.1L4.7 72.5h3.9l2.8 12.8 3.4-12.8h4.5l3.3 13 2.9-13h3.8l-4.6 18.6h-4L17 77.2l-3.7 13.9H9.1zm22.1 0V72.5h5.7l3.4 12.7 3.4-12.7h5.7v18.6h-3.5V76.4l-3.7 14.7h-3.7l-3.7-14.7v14.7h-3.6zm26.7 0l-6.7-18.6h4.1l4.8 13.8 4.6-13.8h4L62 91.1h-4.1z' fill='%23fff'/%3E%3ClinearGradient id='SVGID_2_' gradientUnits='userSpaceOnUse' x1='18.2' y1='50.023' x2='18.2' y2='50.023' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='.005' stop-color='%23963491'/%3E%3Cstop offset='1' stop-color='%2370136b'/%3E%3C/linearGradient%3E%3ClinearGradient id='SVGID_3_' gradientUnits='userSpaceOnUse' x1='11.511' y1='51.716' x2='65.211' y2='51.716' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='.005' stop-color='%23963491'/%3E%3Cstop offset='1' stop-color='%2370136b'/%3E%3C/linearGradient%3E%3Cpath d='M64.3 55.5c-1.7-.2-3.4-.3-5.1-.3-7.3-.1-13.3 1.6-18.8 3.7S29.6 63.6 23.3 64c-3.4.2-7.3-.6-8.5-2.4-.8-1.3-.8-3.5-1-5.7-.6-5.7-1.6-11.7-2.4-17.3.8-.9 2.1-1.3 3.4-1.7.4 1.1.2 2.7.6 3.8 7.1.7 13.6-.4 20-1.5 6.3-1.1 12.4-2.2 19.4-2.6 3.4-.2 6.9-.2 10.3 0m-9.9 15.3c.5-.2 1.1-.3 1.9-.2.2-3.7.3-7.3.3-11.2-6.2.2-11.9.9-17 2.2.2 4 .4 7.8.3 12 4-1.1 7.7-2.5 12.6-2.7m2-12.1h1.1c.4-.4.2-1.2.2-1.9-1.5-.6-1.8 1-1.3 1.9zm3.9-.2h1.5V38h-1.3c0 .7-.4.9-.2 1.7zm4 0c.5-.1.8 0 1.1.2.4-.3.2-1.2.2-1.9h-1.3v1.7zm-11.5.3h.9c.4-.3.2-1.2.2-1.9-1.4-.4-1.6 1.2-1.1 1.9zm-4 .4c.7.2.8-.3 1.5-.2v-1.7c-1.5-.4-1.7.6-1.5 1.9zm-3.6-1.1c0 .6-.1 1.4.2 1.7.5.1.5-.4 1.1-.2-.2-.6.5-2-.4-1.9-.1.4-.8.1-.9.4zm-31.5.8c.4-.1 1.1.6 1.3 0-.5 0-.1-.8-.2-1.1-.7.2-1.3.3-1.1 1.1zm28.3-.4c-.3.3.2 1.1 0 1.9.6.2.6-.3 1.1-.2-.2-.6.5-2-.4-1.9-.1.3-.4.2-.7.2zm-3.5 2.8c.5-.1.9-.2 1.3-.4.2-.8-.4-.9-.2-1.7h-.9c-.3.3-.1 1.3-.2 2.1zm26.9-1.8c-2.1-.1-3.3-.2-5.5-.2-.5 3.4 0 7.8-.5 11.2 2.4 0 3.6.1 5.8.3M33.4 41.6c.5.2.1 1.2.2 1.7.5-.1 1.1-.2 1.5-.4.6-1.9-.9-2.4-1.7-1.3zm-4.7.6v1.9c.9.2 1.2-.2 1.9-.2-.1-.7.2-1.7-.2-2.1-.5.2-1.3.1-1.7.4zm-5.3.6c.3.5 0 1.6.4 2.1.7.1.8-.4 1.5-.2-.1-.7-.3-1.2-.2-2.1-.8-.2-.9.3-1.7.2zm-7.5 2H17c.2-.9-.4-1.2-.2-2.1-.4.1-1.2-.3-1.3.2.6.2-.1 1.7.4 1.9zm3.4 1c.1 4.1.9 9.3 1.4 13.7 8 .1 13.1-2.7 19.2-4.5-.5-3.9.1-8.7-.7-12.2-6.2 1.6-12.1 3.2-19.9 3zm.5-.8h1.1c.4-.5-.2-1.2 0-2.1h-1.5c.1.7.1 1.6.4 2.1zm-5.4 7.8c.2 0 .3.2.4.4-.4-.7-.7.5-.2.6.1-.2 0-.4.2-.4.3.5-.8.7-.2.8.7-.5 1.3-1.2 2.4-1.5-.1 1.5.4 2.4.4 3.8-.7.5-1.7.7-1.9 1.7 1.2.7 2.5 1.2 4.2 1.3-.7-4.9-1.1-8.8-1.6-13.7-2.2.3-4-.8-5.1-.9.9.8.6 2.5.8 3.6 0-.2 0-.4.2-.4-.1.7.1 1.7-.2 2.1.7.3.5-.2.4.9m44.6 3.2h1.1c.3-.3.2-1.1.2-1.7h-1.3v1.7zm-4-1.4v1.3c.4.4.7-.2 1.5 0v-1.5c-.6 0-1.2 0-1.5.2zm7.6 1.4h1.3v-1.5h-1.3c.1.5 0 1 0 1.5zm-11-1v1.3h1.1c.3-.3.4-1.7-.2-1.7-.1.4-.8.1-.9.4zm-3.6.4c.1.6-.3 1.7.4 1.7 0-.3.5-.2.9-.2-.2-.5.4-1.8-.4-1.7-.1.3-.6.2-.9.2zm-3.4 1v1.5c.7.2.6-.4 1.3-.2-.2-.5.4-1.8-.4-1.7-.1.3-.8.2-.9.4zM15 57c.7-.5 1.3-1.7.2-2.3-.7.4-.8 1.6-.2 2.3zm26.1-1.3c-.1.7.4.8.2 1.5.9 0 1.2-.6 1.1-1.7-.4-.5-.8.1-1.3.2zm-3 2.7c1 0 1.2-.8 1.1-1.9h-.9c-.3.4-.1 1.3-.2 1.9zm-3.6-.4v1.7c.6-.1 1.3-.2 1.5-.8-.6 0 .3-1.6-.6-1.3 0 .4-.7.1-.9.4zM16 60.8c-.4-.7-.2-2-1.3-1.9.2.7.2 2.7 1.3 1.9zm13.8-.9c.5 0 .1.9.2 1.3.8.1 1.2-.2 1.7-.4v-1.7c-.9-.1-1.6.1-1.9.8zm-4.7.6c0 .8-.1 1.7.4 1.9 0-.5.8-.1 1.1-.2.3-.3-.2-1.1 0-1.9-.7-.2-1 .1-1.5.2zM19 62.3v-1.7c-.5 0-.6-.4-1.3-.2-.1 1.1 0 2.1 1.3 1.9zm2.5.2h1.3c.2-.9-.3-1.1-.2-1.9h-1.3c-.1.9.2 1.2.2 1.9z' fill='url(%23SVGID_3_)'/%3E%3ClinearGradient id='SVGID_4_' gradientUnits='userSpaceOnUse' x1='45.269' y1='74.206' x2='58.769' y2='87.706' gradientTransform='matrix(1 0 0 -1 0 102)'%3E%3Cstop offset='0' stop-color='%23f9eff6'/%3E%3Cstop offset='.378' stop-color='%23f8edf5'/%3E%3Cstop offset='.515' stop-color='%23f3e6f1'/%3E%3Cstop offset='.612' stop-color='%23ecdbeb'/%3E%3Cstop offset='.69' stop-color='%23e3cce2'/%3E%3Cstop offset='.757' stop-color='%23d7b8d7'/%3E%3Cstop offset='.817' stop-color='%23caa1c9'/%3E%3Cstop offset='.871' stop-color='%23bc88bb'/%3E%3Cstop offset='.921' stop-color='%23ae6cab'/%3E%3Cstop offset='.965' stop-color='%239f4d9b'/%3E%3Cstop offset='1' stop-color='%23932a8e'/%3E%3C/linearGradient%3E%3Cpath d='M45.2 1l27 26.7h-27V1z' fill='url(%23SVGID_4_)'/%3E%3Cpath d='M45.2 1l27 26.7h-27V1z' fill-opacity='0' stroke='%23882383' stroke-width='2' stroke-linejoin='bevel'/%3E%3C/svg%3E"); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-xls { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmxJREFUeNpsU0trFEEQ/mamZ3Y2+0zIC2MmITEkUYgERFQErx5E8KTi1b/h79A/4SW3nCNeYggBYZVEMU/y3N3Z7M7OTD/G6lk2ruw20zRdU/XV91VVG0mSQK/3n1a/jky6d6Xs3G8WXS+Pw5N6LXjLLGuna/78oZKerGsYKtrDE16uJGL1L9gEOOcYd2dL1fNwrbL//aXN7J1efPMmkUqEFAk0A0VZNbFEaQCBscIkXj975y3NLq9xye8PBkAniHOFph+j2eC4rsdoB4LsFubGl/Hq8RtvYWpxTQi52o1jvWiGYaRZL0/auDgOkC/Z8BYL2Pqxidp1FZkhoDxpeaXA/Ujuj/4HoOxKKjiOiek7RUShRNQWaNYFQuMafrYCxiw4ozZKfqbYJ0EvRdl1DQyyTs8XCNTA6UELMwvDyLpZWIZNNlNLlQOK2LMJRJ+5AkuZ1S7CFFzJzk56GnUjQWlYkqCoBWFbonEVYcLLA4dNnB624GQsDBWIgfZJEgxkoChzSFWvn4VpQemDm2VwXQsXJwF1h6c+gxlQ5jgSiEUEt0wdIe7tMES+nEG2aCLiJMOIIWIr9e0DEELAMUrwRuchVAyTKimUwO75Jm6VF3Bv7imOaj+xd7UFKVS/BPJF1b/E4tgTrE49J60O5kceoNqowiuuYKa8ghHXA48U9MT2AQgyRvTThE30bQiaSGa4yLMJNFo+Dq/2cHt4CYlwyFf2S6BHwwrMw/avDbR5C1k7h1YQ4KH3Amf+AcZyEbZPv9CItzQD1l9EbtYOjv74v/d3O9RMPTDrsEwGIWN8q2yk7XNYRs9JrRv3V4ABADSGR6eQ0/NQAAAAAElFTkSuQmCC); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-xlsx { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmlJREFUeNpsU8tqFEEUPVXdPY/ueWZIoiYZiSYKYhJc6EbduHOhgijo3t/wH1z6B0JAhOyMILhxo4kJGk1ASTAxwWF0Mpp5dHc9vFUzYwidaoqmq+8959xzbzGtNcx69PTS26ETmQtS9r4Hy/xv7MW7jV+th5yzVcaYPX/++It9u4NAv+CVR6tBUUTqMJsDcRzjZOZM8W9ZLKx+/XDb4e5/kH5In0lpIYWGUaC0YTZnBCAEKoVR3L36oDo7NbsglZwbqD6iQKOXFMcKUVfBkBAoQhlD5xxMDp/HrSv3q1JgYW3z0x0KXzkCYJaRZljru23aHWTzLiamAyytv0O9UYdf5PArqlppBfMUfu4oALErqZBKcUxMFRCHEp0DgW5Lo4N9NIN1dF0XXsVFOUyPJTzo+WBANDidjp8tgHGG3c0DnJ4uIRf4cOCBaW5KjY8xkZL72xpJ9QcFz5bVqHUJGHZL2YtNmKi06YCyiVFb4s/vEKMTAf1p4edOG6mMi1zR6wEpdUwX+vLDtkCzHoK7ptcM6ayLmGajvtex4PliyoIkFRjmUEASelB2rXQRSfjUCT9PlWpmW21iTGzCAyEkUixPRqXhe2V4zKczbdmybgkpJ0cGOuA6Y2MTCsKoi5HsNK7N3MN+uwYaWbxYfoLLkzdxcew6lrYWaZhm8PHHG3zffp1UwJSHz9vvkU8PodbcQYYYS5lxYkxTkGdVDQdV1Js1qPgYD6JIuIE7gsXVefIhIuM05k7dwMbeMmh87a18ufIMaVYyprrJLgje2Nr+1tzYXANnDnr3zRhHj37Vvy2wpXHtNAd5/wQYAD6WMuT2CwoVAAAAAElFTkSuQmCC); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-xml { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAilJREFUeNqMks1PE0EYxh+g3W2t1G0sEqyISynUFJsSOShNwCamiYZED3LgIkcuxoN/iCZePZiYGD2aGD+i0F5KMChxlVaakAK2ykcAt+WzdLu7zkxo3WZL4pu8mXfmeeY3885ug67roPFh5nvc62m9hjoR+5LMp7MrkYf370qVtco+VtCUFpbj+jGR+JbWn76OyQ8ePwsZATQb8R/hanZgINgj9IqeuBFCw1Kt9OMBnNWCs24XwkG/QKYUEiGjVAPQof/rq0783pShET3ULQo8xz0iS5FaANmrHQH2DoqY+DSLSz6RzecWlnD9ymU47LYjd4O5BXqDTG4FM3NpTEkpdJ5rw0AowLRMbhUfp58gTOaD/UHmNQPI6YmvKWRX1zESHUJ/oBs2nmPa+Mgw0ZIM3tZyGoJwygzQNB2jNyJIZX7iB0lpPoM70UGmPX8zCU+rG8NDVxHwdiC5mKsPUFUN/gvtLLf39sFzVqaN3YrC6TjBauqhXhNA1TQoqloV7Da+pjZq1FsXUCamF29j6LvYhf3iISamZ3Fv9DZevouhRzzPfOG+3hpA9U9UyioOlTJ7pFeTCQS6RGzIebyf+oz5pSzWtmSW1EO9phvQ00slBRt/8qR3DoWdXbiczUiTzd52D+tdLmyTB14mx1rMAKVcRpEATjrsuElee/HXGmnFRyBOGD30C/nEDjNgs7CDpsYmnHG3YPegBCvHs9oYfm8nG9dJa5X4K8AAQzQX4KSN3wcAAAAASUVORK5CYII=); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-yml { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAdxJREFUeNqMUl1rE0EUPbM7m5Y0Zptu21AwWwhYpfSDFh+kvvRd8N0Hf4I/xWdf/Q158F0QoQ+CVsFKaLSQpt/dpmvztTOzzky6cetOpWcZZvbO3MO5514SxzEU3r57/3GpWllM/tP4sL3TarROXuSo/SWJvX71Uu80Cfhlr/T4UdWFAVfdnmsTUtvdP35OUyQKVnJgXDBTcj9icAsTeLax7j/052qM81UjwW1QJXEhMF0qYnN90fdnvdogYmvJPU0/VBApD4hcDrWRcyikfB17srzgW7b9Rh1vEvxDlI4tVytaBSEEtmWh0xsUMwpwnWjqAlcxogiHd1wiQyCu87iI/+sJtf6+NXsgpd7FWCMB50KvkYMGMbLdZgLlfj+K9K4+FnFQ2x7WntIs50AbmiGwLILt+k+EvzvSNIHzdigdJ/AmXQRhiHv5POSwYmG+cqPVo0HqDxj8uTK2vn1Hfa+JmdIkvtZ/4fOPXU3WPDpFeNWVyUKryCiIGMN4zsH98gym3CIcOTwT+XHdXrdQQHAZotE8kBPpSqPNHtBOr48HUmLOcXRJT9dWNMGYJFby91pHOAvaykSaITg+bwefdhrteDRTMSwyrFCgI88E056Hy+4Ah2cXQZL3R4ABALUe7fqXWFN6AAAAAElFTkSuQmCC); - background-repeat:no-repeat; - background-size:contain -} - -.ipfs-zip { - background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAm9JREFUeNpsk0tv00AUhc+MY6dOmgeFJg1FoVVpUWlFC0s2IFF1jxBbhKj4BSxYdscPYcEmQmIDq0gsERIViy4TpD7VFzF1Ho5je2a4thOqNhlp5Mz4zudzzp0wpRTC8fPrk0/TC6+fDtYicLH97T1Kc2vQDcs+rH3eUAxVznn0fn1DRM8E+iOdv5ct3XmZG6yVlNj6solUbgVTt0q5FGtX6vXqC6VklTE+KAO/OODHSIQPRQpsXC+kkEz2ELA0ystv84tLzyucsbWByisAGf+QAS2CCDRRLMJMmxC+i8C4jdLCm/zM7OOKFGptcO6/BTpJ0yeQB0Y+mfKQuZZG0jQgeRbW8Xdomobs9LN8scc+UPHNy4Dwq8IljotIIQEm59/RoSyM1CKkXKZNBm7kIVgyM6wgAnSgRK9vqQfHPiMFDHqyFVsLR9Cm0o4YzoAASrSjCelQfRPb1Vc4qn0EY5L2W9GEaBLcxQgFHpGbkMIDJ69e+wjJ8VXqRgKid0r7ftQdxkRs9SqA2kgAm14SSIQh9uhuLGPMnKJs/5KquL1x0N0RCsizigoDaLqBdHoMiyvrlBsHVx1wphD4BCewoqxGKKDwAgtOy8JufYuk+5golGGaGZwc1sIGoDz3AOPZSVLaHgVwydoJDM1H4DbQODughB3YpOD44HfoHgnu4e7So0uAi0stHLJ3Aud8B9bpHu6vPoSu9TtDl6tUuoFiIYOgu0+158MKmOxomtyD3Qi/3MTR7i8K0EDG1GHO5DE3X4DvNahZlJOwEkOATvdPc2//hx3mXJ5lFJaF8K8bStd0YGfnOJbMGex21x6c+yfAAOlIPDJzr7cLAAAAAElFTkSuQmCC); - background-repeat:no-repeat; - background-size:contain -} diff --git a/assets/dir-index-html/src/style.css b/assets/dir-index-html/src/style.css deleted file mode 100644 index 3e7b8a734..000000000 --- a/assets/dir-index-html/src/style.css +++ /dev/null @@ -1,212 +0,0 @@ -body { - color:#34373f; - font-family:"Helvetica Neue", Helvetica, Arial, sans-serif; - font-size:14px; - line-height:1.43; - margin:0; - word-break:break-all; - -webkit-text-size-adjust:100%; - -ms-text-size-adjust:100%; - -webkit-tap-highlight-color:transparent -} - -a { - color:#117eb3; - text-decoration:none -} - -a:hover { - color:#00b0e9; - text-decoration:underline -} - -a:active, -a:visited { - color:#00b0e9 -} - -strong { - font-weight:700 -} - -table { - border-collapse:collapse; - border-spacing:0; - max-width:100%; - width:100% -} - -table:last-child { - border-bottom-left-radius:3px; - border-bottom-right-radius:3px -} - -tr:first-child td { - border-top:0 -} - -tr:nth-of-type(even) { - background-color:#f7f8fa -} - -td { - border-top:1px solid #d9dbe2; - padding:.65em; - vertical-align:top -} - -#page-header { - align-items:center; - background:#0b3a53; - border-bottom:4px solid #69c4cd; - color:#fff; - display:flex; - font-size:1.12em; - font-weight:500; - justify-content:space-between; - padding:0 1em -} - -#page-header a { - color:#69c4cd -} - -#page-header a:active { - color:#9ad4db -} - -#page-header a:hover { - color:#fff -} - -#page-header-logo { - height:2.25em; - margin:.7em .7em .7em 0; - width:7.15em -} - -#page-header-menu { - align-items:center; - display:flex; - margin:.65em 0 -} - -#page-header-menu div { - margin:0 .6em -} - -#page-header-menu div:last-child { - margin:0 0 0 .6em -} - -#page-header-menu svg { - fill:#69c4cd; - height:1.8em; - margin-top:.125em -} - -#page-header-menu svg:hover { - fill:#fff -} - -.menu-item-narrow { - display:none -} - -#content { - border:1px solid #d9dbe2; - border-radius:4px; - margin:1em -} - -#content-header { - background-color:#edf0f4; - border-bottom:1px solid #d9dbe2; - border-top-left-radius:3px; - border-top-right-radius:3px; - padding:.7em 1em -} - -.type-icon, -.type-icon>* { - width:1.15em -} - -.no-linebreak { - white-space:nowrap -} - -.ipfs-hash { - color:#7f8491; - font-family:monospace -} - -@media only screen and (max-width:500px) { - .menu-item-narrow { - display:inline - } - .menu-item-wide { - display:none - } -} - -@media print { - #page-header { - display:none - } - #content-header, - .ipfs-hash, - body { - color:#000 - } - #content-header { - border-bottom:1px solid #000 - } - #content { - border:1px solid #000 - } - a, - a:visited { - color:#000; - text-decoration:underline - } - a[href]:after { - content:" (" attr(href) ")" - } - tr { - page-break-inside:avoid - } - tr:nth-of-type(even) { - background-color:transparent - } - td { - border-top:1px solid #000 - } -} - -@-ms-viewport { - width:device-width -} - -.d-flex { - display:flex -} - -.flex-wrap { - flex-flow:wrap -} - -.flex-shrink-1 { - flex-shrink:1 -} - -.ml-auto { - margin-left:auto -} - -.table-responsive { - display:block; - width:100%; - overflow-x:auto; - -webkit-overflow-scrolling:touch -} diff --git a/assets/dir-index-html/test/go.mod b/assets/dir-index-html/test/go.mod deleted file mode 100644 index c1cff1b74..000000000 --- a/assets/dir-index-html/test/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/ipfs/dir-index-html/test - -go 1.17 diff --git a/assets/dir-index-html/test/main.go b/assets/dir-index-html/test/main.go deleted file mode 100644 index c02523a9f..000000000 --- a/assets/dir-index-html/test/main.go +++ /dev/null @@ -1,116 +0,0 @@ -package main - -import ( - "fmt" - "net/http" - "net/url" - "os" - "text/template" -) - -const templateFile = "../dir-index.html" - -// Copied from go-ipfs/core/corehttp/gateway_indexPage.go -type listingTemplateData struct { - GatewayURL string - DNSLink bool - Listing []directoryItem - Size string - Path string - Breadcrumbs []breadcrumb - BackLink string - Hash string -} - -type directoryItem struct { - Size string - Name string - Path string - Hash string - ShortHash string -} - -type breadcrumb struct { - Name string - Path string -} - -var testPath = "/ipfs/QmFooBarQXB2mzChmMeKY47C43LxUdg1NDJ5MWcKMKxDu7/a/b/c" -var testData = listingTemplateData{ - GatewayURL: "//localhost:3000", - DNSLink: true, - Listing: []directoryItem{{ - Size: "25 MiB", - Name: "short-film.mov", - Path: testPath + "/short-film.mov", - Hash: "QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR", - ShortHash: "QmbW\u2026sMnR", - }, { - Size: "23 KiB", - Name: "250pxيوسف_الوزاني_صورة_ملتقطة_بواسطة_مرصد_هابل_الفضائي_توضح_سديم_السرطان،_وهو_بقايا_مستعر_أعظم._.jpg", - Path: testPath + "/250pxيوسف_الوزاني_صورة_ملتقطة_بواسطة_مرصد_هابل_الفضائي_توضح_سديم_السرطان،_وهو_بقايا_مستعر_أعظم._.jpg", - Hash: "QmUwrKrMTrNv8QjWGKMMH5QV9FMPUtRCoQ6zxTdgxATQW6", - ShortHash: "QmUw\u2026TQW6", - }, { - Size: "1 KiB", - Name: "this-piece-of-papers-got-47-words-37-sentences-58-words-we-wanna-know.txt", - Path: testPath + "/this-piece-of-papers-got-47-words-37-sentences-58-words-we-wanna-know.txt", - Hash: "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi", - ShortHash: "bafy\u2026bzdi", - }}, - Size: "25 MiB", - Path: testPath, - Breadcrumbs: []breadcrumb{{ - Name: "ipfs", - }, { - Name: "QmFooBarQXB2mzChmMeKY47C43LxUdg1NDJ5MWcKMKxDu7", - Path: testPath + "/../../..", - }, { - Name: "a", - Path: testPath + "/../..", - }, { - Name: "b", - Path: testPath + "/..", - }, { - Name: "c", - Path: testPath, - }}, - BackLink: testPath + "/..", - Hash: "QmFooBazBar2mzChmMeKY47C43LxUdg1NDJ5MWcKMKxDu7", -} - -func main() { - mux := http.NewServeMux() - mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - if r.URL.Path != "/" { - http.Error(w, "Ha-ha, tricked you! There are no files here!", http.StatusNotFound) - return - } - listingTemplate, err := template.New("dir-index.html").Funcs(template.FuncMap{ - "iconFromExt": func(name string) string { - return "ipfs-_blank" // place-holder - }, - "urlEscape": func(rawUrl string) string { - pathUrl := url.URL{Path: rawUrl} - return pathUrl.String() - }, - }).ParseFiles(templateFile) - if err != nil { - http.Error(w, fmt.Sprintf("failed to parse template file: %s", err), http.StatusInternalServerError) - return - } - err = listingTemplate.Execute(w, &testData) - if err != nil { - http.Error(w, fmt.Sprintf("failed to execute template: %s", err), http.StatusInternalServerError) - return - } - w.WriteHeader(http.StatusOK) - }) - if _, err := os.Stat(templateFile); err != nil { - wd, _ := os.Getwd() - fmt.Printf("could not open template file %q, relative to %q: %s\n", templateFile, wd, err) - os.Exit(1) - } - fmt.Printf("listening on localhost:3000\n") - http.ListenAndServe("localhost:3000", mux) -} diff --git a/bin/container_daemon b/bin/container_daemon index ae8725be5..9651ad55d 100755 --- a/bin/container_daemon +++ b/bin/container_daemon @@ -7,9 +7,9 @@ repo="$IPFS_PATH" if [ "$(id -u)" -eq 0 ]; then echo "Changing user to $user" # ensure folder is writable - su-exec "$user" test -w "$repo" || chown -R -- "$user" "$repo" + gosu "$user" test -w "$repo" || chown -R -- "$user" "$repo" # restart script with new privileges - exec su-exec "$user" "$0" "$@" + exec gosu "$user" "$0" "$@" fi # 2nd invocation with regular user diff --git a/bin/get-docker-tags.sh b/bin/get-docker-tags.sh index e50ec69fa..1c4e184f2 100755 --- a/bin/get-docker-tags.sh +++ b/bin/get-docker-tags.sh @@ -6,22 +6,19 @@ # ./get-docker-tags.sh [git tag name] # # Example: -# +# # # get tag for the master branch # ./get-docker-tags.sh $(date -u +%F) testingsha master # # # get tag for a release tag # ./get-docker-tags.sh $(date -u +%F) testingsha release v0.5.0 # -# # Serving suggestion in circle ci - https://circleci.com/docs/2.0/env-vars/#built-in-environment-variables -# ./get-docker-tags.sh $(date -u +%F) "$CIRCLE_SHA1" "$CIRCLE_BRANCH" "$CIRCLE_TAG" -# set -euo pipefail if [[ $# -lt 1 ]] ; then echo 'At least 1 arg required.' echo 'Usage:' - echo './push-docker-tags.sh [git commit sha1] [git branch name] [git tag name]' + echo './get-docker-tags.sh [git commit sha1] [git branch name] [git tag name]' exit 1 fi @@ -53,9 +50,9 @@ elif [[ $GIT_BRANCH =~ ^bifrost-.* ]]; then branch=$(echo "$GIT_BRANCH" | tr '/' '-' | tr --delete --complement '[:alnum:]-') echoImageName "${branch}-${BUILD_NUM}-${GIT_SHA1_SHORT}" -elif [ "$GIT_BRANCH" = "master" ]; then - echoImageName "master-${BUILD_NUM}-${GIT_SHA1_SHORT}" - echoImageName "master-latest" +elif [ "$GIT_BRANCH" = "master" ] || [ "$GIT_BRANCH" = "staging" ]; then + echoImageName "${GIT_BRANCH}-${BUILD_NUM}-${GIT_SHA1_SHORT}" + echoImageName "${GIT_BRANCH}-latest" else echo "Nothing to do. No docker tag defined for branch: $GIT_BRANCH, tag: $GIT_TAG" diff --git a/bin/ipns-republish b/bin/ipns-republish index f5535ee4c..5fc81cd57 100755 --- a/bin/ipns-republish +++ b/bin/ipns-republish @@ -19,7 +19,7 @@ if [ $? -ne 0 ]; then fi # check the object is there -ipfs object stat "$1" >/dev/null +ipfs dag stat "$1" >/dev/null if [ $? -ne 0 ]; then echo "error: ipfs cannot find $1" exit 1 diff --git a/bin/mkreleaselog b/bin/mkreleaselog index 1b31854a7..2ff6c0e89 100755 --- a/bin/mkreleaselog +++ b/bin/mkreleaselog @@ -41,7 +41,6 @@ IGNORE_FILES=( "go.mod" "go.sum" ".github" - ".circleci" "*.pb.go" "cbor_gen.go" "ipldsch_*.go" @@ -261,7 +260,7 @@ recursive_release_log() { printf -- "Generating Changelog for %s %s..%s\n" "$module" "$start" "$end" >&2 - echo "### Changelog" + echo "### 📝 Changelog" echo echo "
Full Changelog" echo @@ -293,7 +292,7 @@ recursive_release_log() { echo echo "
" echo - echo "### Contributors" + echo "### 👨‍👩‍👧‍👦 Contributors" echo echo "| Contributor | Commits | Lines ± | Files Changed |" diff --git a/bin/push-docker-tags.sh b/bin/push-docker-tags.sh index 00d314c07..a33948307 100755 --- a/bin/push-docker-tags.sh +++ b/bin/push-docker-tags.sh @@ -1,12 +1,13 @@ #!/usr/bin/env bash - +# +# TODO: this script is legacy, use get-docker-tags.sh instead. +# # push-docker-tags.sh # -# Run from ci to tag images based on the current branch or tag name. +# Run from ci to tag images based on the current branch or tag name. # A bit like dockerhub autobuild config, but somewhere we can version control it. -# -# The `docker-build` job in .circleci/config.yml builds the current commit -# in docker and tags it as ipfs/go-ipfs:wip +# +# The `docker-build` job builds the current commit in docker and tags it as ipfs/go-ipfs:wip # # Then the `docker-publish` job runs this script to decide what tag, if any, # to publish to dockerhub. @@ -17,16 +18,13 @@ # Example: # # dry run. pass a 5th arg to have it print what it would do rather than do it. # ./push-docker-tags.sh $(date -u +%F) testingsha master "" dryrun -# +# # # push tag for the master branch # ./push-docker-tags.sh $(date -u +%F) testingsha master # # # push tag for a release tag # ./push-docker-tags.sh $(date -u +%F) testingsha release v0.5.0 # -# # Serving suggestion in circle ci - https://circleci.com/docs/2.0/env-vars/#built-in-environment-variables -# ./push-docker-tags.sh $(date -u +%F) "$CIRCLE_SHA1" "$CIRCLE_BRANCH" "$CIRCLE_TAG" -# set -euo pipefail if [[ $# -lt 1 ]] ; then @@ -72,9 +70,9 @@ elif [[ $GIT_BRANCH =~ ^bifrost-.* ]]; then branch=$(echo "$GIT_BRANCH" | tr '/' '-' | tr --delete --complement '[:alnum:]-') pushTag "${branch}-${BUILD_NUM}-${GIT_SHA1_SHORT}" -elif [ "$GIT_BRANCH" = "master" ]; then - pushTag "master-${BUILD_NUM}-${GIT_SHA1_SHORT}" - pushTag "master-latest" +elif [ "$GIT_BRANCH" = "master" ] || [ "$GIT_BRANCH" = "staging" ]; then + pushTag "${GIT_BRANCH}-${BUILD_NUM}-${GIT_SHA1_SHORT}" + pushTag "${GIT_BRANCH}-latest" else echo "Nothing to do. No docker tag defined for branch: $GIT_BRANCH, tag: $GIT_TAG" diff --git a/blocks/blockstoreutil/remove.go b/blocks/blockstoreutil/remove.go index 4440c2a65..d4219d7b1 100644 --- a/blocks/blockstoreutil/remove.go +++ b/blocks/blockstoreutil/remove.go @@ -6,9 +6,9 @@ import ( "errors" "fmt" + bs "github.com/ipfs/boxo/blockstore" + pin "github.com/ipfs/boxo/pinning/pinner" cid "github.com/ipfs/go-cid" - bs "github.com/ipfs/go-ipfs-blockstore" - pin "github.com/ipfs/go-ipfs-pinner" format "github.com/ipfs/go-ipld-format" ) diff --git a/client/rpc/README.md b/client/rpc/README.md new file mode 100644 index 000000000..d9744e5b6 --- /dev/null +++ b/client/rpc/README.md @@ -0,0 +1,48 @@ +# `coreiface.CoreAPI` over http `rpc` + +> IPFS CoreAPI implementation using HTTP API + +This package implements [`coreiface.CoreAPI`](https://pkg.go.dev/github.com/ipfs/kubo/core/coreiface#CoreAPI) over the HTTP API. + +## Documentation + +https://pkg.go.dev/github.com/ipfs/kubo/client/rpc + +### Example + +Pin file on your local IPFS node based on its CID: + +```go +package main + +import ( + "context" + "fmt" + + "github.com/ipfs/boxo/path" + "github.com/ipfs/go-cid" + "github.com/ipfs/kubo/client/rpc" +) + +func main() { + // "Connect" to local node + node, err := rpc.NewLocalApi() + if err != nil { + fmt.Println(err) + return + } + // Pin a given file by its CID + ctx := context.Background() + c, err := cid.Decode("bafkreidtuosuw37f5xmn65b3ksdiikajy7pwjjslzj2lxxz2vc4wdy3zku") + if err != nil { + fmt.Println(err) + return + } + p := path.FromCid(c) + err = node.Pin().Add(ctx, p) + if err != nil { + fmt.Println(err) + return + } +} +``` diff --git a/client/rpc/api.go b/client/rpc/api.go new file mode 100644 index 000000000..c4b73d387 --- /dev/null +++ b/client/rpc/api.go @@ -0,0 +1,292 @@ +package rpc + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "net" + "net/http" + "os" + "path/filepath" + "strings" + "sync" + "time" + + "github.com/blang/semver/v4" + "github.com/ipfs/boxo/ipld/merkledag" + "github.com/ipfs/go-cid" + legacy "github.com/ipfs/go-ipld-legacy" + ipfs "github.com/ipfs/kubo" + iface "github.com/ipfs/kubo/core/coreiface" + caopts "github.com/ipfs/kubo/core/coreiface/options" + "github.com/ipfs/kubo/misc/fsutil" + dagpb "github.com/ipld/go-codec-dagpb" + _ "github.com/ipld/go-ipld-prime/codec/dagcbor" + "github.com/ipld/go-ipld-prime/node/basicnode" + ma "github.com/multiformats/go-multiaddr" + manet "github.com/multiformats/go-multiaddr/net" +) + +const ( + DefaultPathName = ".ipfs" + DefaultPathRoot = "~/" + DefaultPathName + DefaultApiFile = "api" + EnvDir = "IPFS_PATH" +) + +// ErrApiNotFound if we fail to find a running daemon. +var ErrApiNotFound = errors.New("ipfs api address could not be found") + +// HttpApi implements github.com/ipfs/interface-go-ipfs-core/CoreAPI using +// IPFS HTTP API. +// +// For interface docs see +// https://godoc.org/github.com/ipfs/interface-go-ipfs-core#CoreAPI +type HttpApi struct { + url string + httpcli http.Client + Headers http.Header + applyGlobal func(*requestBuilder) + ipldDecoder *legacy.Decoder + versionMu sync.Mutex + version *semver.Version +} + +// NewLocalApi tries to construct new HttpApi instance communicating with local +// IPFS daemon +// +// Daemon api address is pulled from the $IPFS_PATH/api file. +// If $IPFS_PATH env var is not present, it defaults to ~/.ipfs. +func NewLocalApi() (*HttpApi, error) { + baseDir := os.Getenv(EnvDir) + if baseDir == "" { + baseDir = DefaultPathRoot + } + + return NewPathApi(baseDir) +} + +// NewPathApi constructs new HttpApi by pulling api address from specified +// ipfspath. Api file should be located at $ipfspath/api. +func NewPathApi(ipfspath string) (*HttpApi, error) { + a, err := ApiAddr(ipfspath) + if err != nil { + if os.IsNotExist(err) { + err = ErrApiNotFound + } + return nil, err + } + return NewApi(a) +} + +// ApiAddr reads api file in specified ipfs path. +func ApiAddr(ipfspath string) (ma.Multiaddr, error) { + baseDir, err := fsutil.ExpandHome(ipfspath) + if err != nil { + return nil, err + } + + apiFile := filepath.Join(baseDir, DefaultApiFile) + + api, err := os.ReadFile(apiFile) + if err != nil { + return nil, err + } + + return ma.NewMultiaddr(strings.TrimSpace(string(api))) +} + +// NewApi constructs HttpApi with specified endpoint. +func NewApi(a ma.Multiaddr) (*HttpApi, error) { + transport := &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DisableKeepAlives: true, + } + + network, address, err := manet.DialArgs(a) + if err != nil { + return nil, err + } + if network == "unix" { + transport.DialContext = func(_ context.Context, _, _ string) (net.Conn, error) { + return net.Dial("unix", address) + } + c := &http.Client{ + Transport: transport, + } + // This will create an API client which + // makes requests to `http://unix`. + return NewURLApiWithClient(network, c) + } + + c := &http.Client{ + Transport: transport, + } + + return NewApiWithClient(a, c) +} + +// NewApiWithClient constructs HttpApi with specified endpoint and custom http client. +func NewApiWithClient(a ma.Multiaddr, c *http.Client) (*HttpApi, error) { + _, url, err := manet.DialArgs(a) + if err != nil { + return nil, err + } + + if a, err := ma.NewMultiaddr(url); err == nil { + _, host, err := manet.DialArgs(a) + if err == nil { + url = host + } + } + + proto := "http://" + + // By default, DialArgs is going to provide details suitable for connecting + // a socket to, but not really suitable for making an informed choice of http + // protocol. For multiaddresses specifying tls and/or https we want to make + // a https request instead of a http request. + protocols := a.Protocols() + for _, p := range protocols { + if p.Code == ma.P_HTTPS || p.Code == ma.P_TLS { + proto = "https://" + break + } + } + + return NewURLApiWithClient(proto+url, c) +} + +func NewURLApiWithClient(url string, c *http.Client) (*HttpApi, error) { + decoder := legacy.NewDecoder() + // Add support for these codecs to match what is done in the merkledag library + // Note: to match prior behavior the go-ipld-prime CBOR decoder is manually included + // TODO: allow the codec registry used to be configured by the caller not through a global variable + decoder.RegisterCodec(cid.DagProtobuf, dagpb.Type.PBNode, merkledag.ProtoNodeConverter) + decoder.RegisterCodec(cid.Raw, basicnode.Prototype.Bytes, merkledag.RawNodeConverter) + + api := &HttpApi{ + url: url, + httpcli: *c, + Headers: make(map[string][]string), + applyGlobal: func(*requestBuilder) {}, + ipldDecoder: decoder, + } + + // We don't support redirects. + api.httpcli.CheckRedirect = func(_ *http.Request, _ []*http.Request) error { + return fmt.Errorf("unexpected redirect") + } + + return api, nil +} + +func (api *HttpApi) WithOptions(opts ...caopts.ApiOption) (iface.CoreAPI, error) { + options, err := caopts.ApiOptions(opts...) + if err != nil { + return nil, err + } + + subApi := &HttpApi{ + url: api.url, + httpcli: api.httpcli, + Headers: api.Headers, + applyGlobal: func(req *requestBuilder) { + if options.Offline { + req.Option("offline", options.Offline) + } + }, + ipldDecoder: api.ipldDecoder, + } + + return subApi, nil +} + +func (api *HttpApi) Request(command string, args ...string) RequestBuilder { + headers := make(map[string]string) + if api.Headers != nil { + for k := range api.Headers { + headers[k] = api.Headers.Get(k) + } + } + return &requestBuilder{ + command: command, + args: args, + shell: api, + headers: headers, + } +} + +func (api *HttpApi) Unixfs() iface.UnixfsAPI { + return (*UnixfsAPI)(api) +} + +func (api *HttpApi) Block() iface.BlockAPI { + return (*BlockAPI)(api) +} + +func (api *HttpApi) Dag() iface.APIDagService { + return (*HttpDagServ)(api) +} + +func (api *HttpApi) Name() iface.NameAPI { + return (*NameAPI)(api) +} + +func (api *HttpApi) Key() iface.KeyAPI { + return (*KeyAPI)(api) +} + +func (api *HttpApi) Pin() iface.PinAPI { + return (*PinAPI)(api) +} + +func (api *HttpApi) Object() iface.ObjectAPI { + return (*ObjectAPI)(api) +} + +func (api *HttpApi) Swarm() iface.SwarmAPI { + return (*SwarmAPI)(api) +} + +func (api *HttpApi) PubSub() iface.PubSubAPI { + return (*PubsubAPI)(api) +} + +func (api *HttpApi) Routing() iface.RoutingAPI { + return (*RoutingAPI)(api) +} + +func (api *HttpApi) loadRemoteVersion() (*semver.Version, error) { + api.versionMu.Lock() + defer api.versionMu.Unlock() + + if api.version == nil { + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Second*30)) + defer cancel() + + resp, err := api.Request("version").Send(ctx) + if err != nil { + return nil, err + } + if resp.Error != nil { + return nil, resp.Error + } + defer resp.Close() + var out ipfs.VersionInfo + dec := json.NewDecoder(resp.Output) + if err := dec.Decode(&out); err != nil { + return nil, err + } + + remoteVersion, err := semver.New(out.Version) + if err != nil { + return nil, err + } + + api.version = remoteVersion + } + + return api.version, nil +} diff --git a/client/rpc/api_test.go b/client/rpc/api_test.go new file mode 100644 index 000000000..c0da3d7b0 --- /dev/null +++ b/client/rpc/api_test.go @@ -0,0 +1,169 @@ +package rpc + +import ( + "context" + "net/http" + "net/http/httptest" + "runtime" + "strconv" + "strings" + "sync" + "testing" + "time" + + "github.com/ipfs/boxo/path" + iface "github.com/ipfs/kubo/core/coreiface" + "github.com/ipfs/kubo/core/coreiface/tests" + "github.com/ipfs/kubo/test/cli/harness" + ma "github.com/multiformats/go-multiaddr" + "go.uber.org/multierr" +) + +type NodeProvider struct{} + +func (np NodeProvider) MakeAPISwarm(t *testing.T, ctx context.Context, fullIdentity, online bool, n int) ([]iface.CoreAPI, error) { + h := harness.NewT(t) + + apis := make([]iface.CoreAPI, n) + nodes := h.NewNodes(n) + + var wg, zero sync.WaitGroup + zeroNode := nodes[0] + wg.Add(len(apis)) + zero.Add(1) + + var errs []error + var errsLk sync.Mutex + + for i, n := range nodes { + go func(i int, n *harness.Node) { + if err := func() error { + defer wg.Done() + var err error + + n.Init("--empty-repo") + + c := n.ReadConfig() + c.Experimental.FilestoreEnabled = true + n.WriteConfig(c) + n.StartDaemon("--enable-pubsub-experiment", "--offline="+strconv.FormatBool(!online)) + + if online { + if i > 0 { + zero.Wait() + n.Connect(zeroNode) + } else { + zero.Done() + } + } + + apiMaddr, err := n.TryAPIAddr() + if err != nil { + return err + } + + api, err := NewApi(apiMaddr) + if err != nil { + return err + } + apis[i] = api + + // empty node is pinned even with --empty-repo, we don't want that + emptyNode, err := path.NewPath("/ipfs/QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn") + if err != nil { + return err + } + + if err := api.Pin().Rm(ctx, emptyNode); err != nil { + return err + } + return nil + }(); err != nil { + errsLk.Lock() + errs = append(errs, err) + errsLk.Unlock() + } + }(i, n) + } + + wg.Wait() + + return apis, multierr.Combine(errs...) +} + +func TestHttpApi(t *testing.T) { + t.Parallel() + + if runtime.GOOS == "windows" { + t.Skip("skipping due to #9905") + } + + tests.TestApi(NodeProvider{})(t) +} + +func Test_NewURLApiWithClient_With_Headers(t *testing.T) { + t.Parallel() + + var ( + headerToTest = "Test-Header" + expectedHeaderValue = "thisisaheadertest" + ) + ts := httptest.NewServer( + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + val := r.Header.Get(headerToTest) + if val != expectedHeaderValue { + w.WriteHeader(400) + return + } + http.ServeContent(w, r, "", time.Now(), strings.NewReader("test")) + }), + ) + defer ts.Close() + api, err := NewURLApiWithClient(ts.URL, &http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DisableKeepAlives: true, + }, + }) + if err != nil { + t.Fatal(err) + } + api.Headers.Set(headerToTest, expectedHeaderValue) + p, err := path.NewPath("/ipfs/QmS4ustL54uo8FzR9455qaxZwuMiUhyvMcX9Ba8nUH4uVv") + if err != nil { + t.Fatal(err) + } + if err := api.Pin().Rm(context.Background(), p); err != nil { + t.Fatal(err) + } +} + +func Test_NewURLApiWithClient_HTTP_Variant(t *testing.T) { + t.Parallel() + + testcases := []struct { + address string + expected string + }{ + {address: "/ip4/127.0.0.1/tcp/80", expected: "http://127.0.0.1:80"}, + {address: "/ip4/127.0.0.1/tcp/443/tls", expected: "https://127.0.0.1:443"}, + {address: "/ip4/127.0.0.1/tcp/443/https", expected: "https://127.0.0.1:443"}, + {address: "/ip4/127.0.0.1/tcp/443/tls/http", expected: "https://127.0.0.1:443"}, + } + + for _, tc := range testcases { + address, err := ma.NewMultiaddr(tc.address) + if err != nil { + t.Fatal(err) + } + + api, err := NewApiWithClient(address, &http.Client{}) + if err != nil { + t.Fatal(err) + } + + if api.url != tc.expected { + t.Errorf("Expected = %s; got %s", tc.expected, api.url) + } + } +} diff --git a/client/rpc/apifile.go b/client/rpc/apifile.go new file mode 100644 index 000000000..57d82c5f7 --- /dev/null +++ b/client/rpc/apifile.go @@ -0,0 +1,356 @@ +package rpc + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "os" + "strconv" + "time" + + "github.com/ipfs/boxo/files" + unixfs "github.com/ipfs/boxo/ipld/unixfs" + "github.com/ipfs/boxo/path" + "github.com/ipfs/go-cid" +) + +const forwardSeekLimit = 1 << 14 // 16k + +func (api *UnixfsAPI) Get(ctx context.Context, p path.Path) (files.Node, error) { + if p.Mutable() { // use resolved path in case we are dealing with IPNS / MFS + var err error + p, _, err = api.core().ResolvePath(ctx, p) + if err != nil { + return nil, err + } + } + + var stat struct { + Hash string + Type string + Size int64 // unixfs size + Mode string + Mtime int64 + MtimeNsecs int + } + err := api.core().Request("files/stat", p.String()).Exec(ctx, &stat) + if err != nil { + return nil, err + } + + mode, err := stringToFileMode(stat.Mode) + if err != nil { + return nil, err + } + + var modTime time.Time + if stat.Mtime != 0 { + modTime = time.Unix(stat.Mtime, int64(stat.MtimeNsecs)).UTC() + } + + switch stat.Type { + case "file": + return api.getFile(ctx, p, stat.Size, mode, modTime) + case "directory": + return api.getDir(ctx, p, stat.Size, mode, modTime) + case "symlink": + return api.getSymlink(ctx, p, modTime) + default: + return nil, fmt.Errorf("unsupported file type '%s'", stat.Type) + } +} + +type apiFile struct { + ctx context.Context + core *HttpApi + size int64 + path path.Path + + mode os.FileMode + mtime time.Time + + r *Response + at int64 +} + +func (f *apiFile) reset() error { + if f.r != nil { + _ = f.r.Cancel() + f.r = nil + } + req := f.core.Request("cat", f.path.String()) + if f.at != 0 { + req.Option("offset", f.at) + } + resp, err := req.Send(f.ctx) + if err != nil { + return err + } + if resp.Error != nil { + return resp.Error + } + f.r = resp + return nil +} + +func (f *apiFile) Read(p []byte) (int, error) { + n, err := f.r.Output.Read(p) + if n > 0 { + f.at += int64(n) + } + return n, err +} + +func (f *apiFile) ReadAt(p []byte, off int64) (int, error) { + // Always make a new request. This method should be parallel-safe. + resp, err := f.core.Request("cat", f.path.String()). + Option("offset", off).Option("length", len(p)).Send(f.ctx) + if err != nil { + return 0, err + } + if resp.Error != nil { + return 0, resp.Error + } + defer resp.Output.Close() + + n, err := io.ReadFull(resp.Output, p) + if err == io.ErrUnexpectedEOF { + err = io.EOF + } + return n, err +} + +func (f *apiFile) Seek(offset int64, whence int) (int64, error) { + switch whence { + case io.SeekEnd: + offset = f.size + offset + case io.SeekCurrent: + offset = f.at + offset + } + if f.at == offset { // noop + return offset, nil + } + + if f.at < offset && offset-f.at < forwardSeekLimit { // forward skip + r, err := io.CopyN(io.Discard, f.r.Output, offset-f.at) + + f.at += r + return f.at, err + } + f.at = offset + return f.at, f.reset() +} + +func (f *apiFile) Close() error { + if f.r != nil { + return f.r.Cancel() + } + return nil +} + +func (f *apiFile) Mode() os.FileMode { + return f.mode +} + +func (f *apiFile) ModTime() time.Time { + return f.mtime +} + +func (f *apiFile) Size() (int64, error) { + return f.size, nil +} + +func stringToFileMode(mode string) (os.FileMode, error) { + if mode == "" { + return 0, nil + } + mode64, err := strconv.ParseUint(mode, 8, 32) + if err != nil { + return 0, fmt.Errorf("cannot parse mode %s: %s", mode, err) + } + return os.FileMode(uint32(mode64)), nil +} + +func (api *UnixfsAPI) getFile(ctx context.Context, p path.Path, size int64, mode os.FileMode, mtime time.Time) (files.Node, error) { + f := &apiFile{ + ctx: ctx, + core: api.core(), + size: size, + path: p, + mode: mode, + mtime: mtime, + } + + return f, f.reset() +} + +type apiIter struct { + ctx context.Context + core *UnixfsAPI + + err error + + dec *json.Decoder + curFile files.Node + cur lsLink +} + +func (it *apiIter) Err() error { + return it.err +} + +func (it *apiIter) Name() string { + return it.cur.Name +} + +func (it *apiIter) Next() bool { + if it.ctx.Err() != nil { + it.err = it.ctx.Err() + return false + } + + var out lsOutput + if err := it.dec.Decode(&out); err != nil { + if err != io.EOF { + it.err = err + } + return false + } + + if len(out.Objects) != 1 { + it.err = fmt.Errorf("ls returned more objects than expected (%d)", len(out.Objects)) + return false + } + + if len(out.Objects[0].Links) != 1 { + it.err = fmt.Errorf("ls returned more links than expected (%d)", len(out.Objects[0].Links)) + return false + } + + it.cur = out.Objects[0].Links[0] + c, err := cid.Parse(it.cur.Hash) + if err != nil { + it.err = err + return false + } + + switch it.cur.Type { + case unixfs.THAMTShard, unixfs.TMetadata, unixfs.TDirectory: + it.curFile, err = it.core.getDir(it.ctx, path.FromCid(c), int64(it.cur.Size), it.cur.Mode, it.cur.ModTime) + if err != nil { + it.err = err + return false + } + case unixfs.TFile: + it.curFile, err = it.core.getFile(it.ctx, path.FromCid(c), int64(it.cur.Size), it.cur.Mode, it.cur.ModTime) + if err != nil { + it.err = err + return false + } + case unixfs.TSymlink: + it.curFile, err = it.core.getSymlink(it.ctx, path.FromCid(c), it.cur.ModTime) + if err != nil { + it.err = err + return false + } + default: + it.err = fmt.Errorf("file type %d not supported", it.cur.Type) + return false + } + return true +} + +func (it *apiIter) Node() files.Node { + return it.curFile +} + +type apiDir struct { + ctx context.Context + core *UnixfsAPI + size int64 + path path.Path + + mode os.FileMode + mtime time.Time + + dec *json.Decoder +} + +func (d *apiDir) Close() error { + return nil +} + +func (d *apiDir) Mode() os.FileMode { + return d.mode +} + +func (d *apiDir) ModTime() time.Time { + return d.mtime +} + +func (d *apiDir) Size() (int64, error) { + return d.size, nil +} + +func (d *apiDir) Entries() files.DirIterator { + return &apiIter{ + ctx: d.ctx, + core: d.core, + dec: d.dec, + } +} + +func (api *UnixfsAPI) getDir(ctx context.Context, p path.Path, size int64, mode os.FileMode, modTime time.Time) (files.Node, error) { + resp, err := api.core().Request("ls", p.String()). + Option("resolve-size", true). + Option("stream", true).Send(ctx) + if err != nil { + return nil, err + } + if resp.Error != nil { + return nil, resp.Error + } + + data, _ := io.ReadAll(resp.Output) + rdr := bytes.NewReader(data) + + d := &apiDir{ + ctx: ctx, + core: api, + size: size, + path: p, + mode: mode, + mtime: modTime, + + //dec: json.NewDecoder(resp.Output), + dec: json.NewDecoder(rdr), + } + + return d, nil +} + +func (api *UnixfsAPI) getSymlink(ctx context.Context, p path.Path, modTime time.Time) (files.Node, error) { + resp, err := api.core().Request("cat", p.String()). + Option("resolve-size", true). + Option("stream", true).Send(ctx) + if err != nil { + return nil, err + } + if resp.Error != nil { + return nil, resp.Error + } + + target, err := io.ReadAll(resp.Output) + if err != nil { + return nil, err + } + + return files.NewSymlinkFile(string(target), modTime), nil +} + +var ( + _ files.File = &apiFile{} + _ files.Directory = &apiDir{} +) diff --git a/client/rpc/auth/auth.go b/client/rpc/auth/auth.go new file mode 100644 index 000000000..a6ed273cd --- /dev/null +++ b/client/rpc/auth/auth.go @@ -0,0 +1,29 @@ +package auth + +import "net/http" + +var _ http.RoundTripper = &AuthorizedRoundTripper{} + +type AuthorizedRoundTripper struct { + authorization string + roundTripper http.RoundTripper +} + +// NewAuthorizedRoundTripper creates a new [http.RoundTripper] that will set the +// Authorization HTTP header with the value of [authorization]. The given [roundTripper] is +// the base [http.RoundTripper]. If it is nil, [http.DefaultTransport] is used. +func NewAuthorizedRoundTripper(authorization string, roundTripper http.RoundTripper) http.RoundTripper { + if roundTripper == nil { + roundTripper = http.DefaultTransport + } + + return &AuthorizedRoundTripper{ + authorization: authorization, + roundTripper: roundTripper, + } +} + +func (tp *AuthorizedRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) { + r.Header.Set("Authorization", tp.authorization) + return tp.roundTripper.RoundTrip(r) +} diff --git a/client/rpc/block.go b/client/rpc/block.go new file mode 100644 index 000000000..9345a5f19 --- /dev/null +++ b/client/rpc/block.go @@ -0,0 +1,134 @@ +package rpc + +import ( + "bytes" + "context" + "fmt" + "io" + + "github.com/ipfs/boxo/path" + "github.com/ipfs/go-cid" + iface "github.com/ipfs/kubo/core/coreiface" + caopts "github.com/ipfs/kubo/core/coreiface/options" + mc "github.com/multiformats/go-multicodec" + mh "github.com/multiformats/go-multihash" +) + +type BlockAPI HttpApi + +type blockStat struct { + Key string + BSize int `json:"Size"` + + cid cid.Cid +} + +func (s *blockStat) Size() int { + return s.BSize +} + +func (s *blockStat) Path() path.ImmutablePath { + return path.FromCid(s.cid) +} + +func (api *BlockAPI) Put(ctx context.Context, r io.Reader, opts ...caopts.BlockPutOption) (iface.BlockStat, error) { + options, err := caopts.BlockPutOptions(opts...) + px := options.CidPrefix + if err != nil { + return nil, err + } + + mht, ok := mh.Codes[px.MhType] + if !ok { + return nil, fmt.Errorf("unknowm mhType %d", px.MhType) + } + + var cidOptKey, cidOptVal string + switch { + case px.Version == 0 && px.Codec == cid.DagProtobuf: + // ensure legacy --format=v0 passes as BlockPutOption still works + cidOptKey = "format" + cidOptVal = "v0" + default: + // pass codec as string + cidOptKey = "cid-codec" + cidOptVal = mc.Code(px.Codec).String() + } + + req := api.core().Request("block/put"). + Option("mhtype", mht). + Option("mhlen", px.MhLength). + Option(cidOptKey, cidOptVal). + Option("pin", options.Pin). + FileBody(r) + + var out blockStat + if err := req.Exec(ctx, &out); err != nil { + return nil, err + } + out.cid, err = cid.Parse(out.Key) + if err != nil { + return nil, err + } + + return &out, nil +} + +func (api *BlockAPI) Get(ctx context.Context, p path.Path) (io.Reader, error) { + resp, err := api.core().Request("block/get", p.String()).Send(ctx) + if err != nil { + return nil, err + } + if resp.Error != nil { + return nil, parseErrNotFoundWithFallbackToError(resp.Error) + } + + // TODO: make get return ReadCloser to avoid copying + defer resp.Close() + b := new(bytes.Buffer) + if _, err := io.Copy(b, resp.Output); err != nil { + return nil, err + } + + return b, nil +} + +func (api *BlockAPI) Rm(ctx context.Context, p path.Path, opts ...caopts.BlockRmOption) error { + options, err := caopts.BlockRmOptions(opts...) + if err != nil { + return err + } + + removedBlock := struct { + Hash string `json:",omitempty"` + Error string `json:",omitempty"` + }{} + + req := api.core().Request("block/rm"). + Option("force", options.Force). + Arguments(p.String()) + + if err := req.Exec(ctx, &removedBlock); err != nil { + return err + } + + return parseErrNotFoundWithFallbackToMSG(removedBlock.Error) +} + +func (api *BlockAPI) Stat(ctx context.Context, p path.Path) (iface.BlockStat, error) { + var out blockStat + err := api.core().Request("block/stat", p.String()).Exec(ctx, &out) + if err != nil { + return nil, parseErrNotFoundWithFallbackToError(err) + } + out.cid, err = cid.Parse(out.Key) + if err != nil { + return nil, err + } + + return &out, nil +} + +func (api *BlockAPI) core() *HttpApi { + return (*HttpApi)(api) +} diff --git a/client/rpc/dag.go b/client/rpc/dag.go new file mode 100644 index 000000000..63cac8f61 --- /dev/null +++ b/client/rpc/dag.go @@ -0,0 +1,138 @@ +package rpc + +import ( + "bytes" + "context" + "fmt" + "io" + + "github.com/ipfs/boxo/path" + blocks "github.com/ipfs/go-block-format" + "github.com/ipfs/go-cid" + format "github.com/ipfs/go-ipld-format" + "github.com/ipfs/kubo/core/coreiface/options" + multicodec "github.com/multiformats/go-multicodec" +) + +type ( + httpNodeAdder HttpApi + HttpDagServ httpNodeAdder + pinningHttpNodeAdder httpNodeAdder +) + +func (api *HttpDagServ) Get(ctx context.Context, c cid.Cid) (format.Node, error) { + r, err := api.core().Block().Get(ctx, path.FromCid(c)) + if err != nil { + return nil, err + } + + data, err := io.ReadAll(r) + if err != nil { + return nil, err + } + + blk, err := blocks.NewBlockWithCid(data, c) + if err != nil { + return nil, err + } + + return api.ipldDecoder.DecodeNode(ctx, blk) +} + +func (api *HttpDagServ) GetMany(ctx context.Context, cids []cid.Cid) <-chan *format.NodeOption { + out := make(chan *format.NodeOption) + + for _, c := range cids { + // TODO: Consider limiting concurrency of this somehow + go func(c cid.Cid) { + n, err := api.Get(ctx, c) + + select { + case out <- &format.NodeOption{Node: n, Err: err}: + case <-ctx.Done(): + } + }(c) + } + return out +} + +func (api *httpNodeAdder) add(ctx context.Context, nd format.Node, pin bool) error { + c := nd.Cid() + prefix := c.Prefix() + + // preserve 'cid-codec' when sent over HTTP + cidCodec := multicodec.Code(prefix.Codec).String() + + // 'format' got replaced by 'cid-codec' in https://github.com/ipfs/interface-go-ipfs-core/pull/80 + // but we still support it here for backward-compatibility with use of CIDv0 + format := "" + if prefix.Version == 0 { + cidCodec = "" + format = "v0" + } + + stat, err := api.core().Block().Put(ctx, bytes.NewReader(nd.RawData()), + options.Block.Hash(prefix.MhType, prefix.MhLength), + options.Block.CidCodec(cidCodec), + options.Block.Format(format), + options.Block.Pin(pin)) + if err != nil { + return err + } + if !stat.Path().RootCid().Equals(c) { + return fmt.Errorf("cids didn't match - local %s, remote %s", c.String(), stat.Path().RootCid().String()) + } + return nil +} + +func (api *httpNodeAdder) addMany(ctx context.Context, nds []format.Node, pin bool) error { + for _, nd := range nds { + // TODO: optimize + if err := api.add(ctx, nd, pin); err != nil { + return err + } + } + return nil +} + +func (api *HttpDagServ) AddMany(ctx context.Context, nds []format.Node) error { + return (*httpNodeAdder)(api).addMany(ctx, nds, false) +} + +func (api *HttpDagServ) Add(ctx context.Context, nd format.Node) error { + return (*httpNodeAdder)(api).add(ctx, nd, false) +} + +func (api *pinningHttpNodeAdder) Add(ctx context.Context, nd format.Node) error { + return (*httpNodeAdder)(api).add(ctx, nd, true) +} + +func (api *pinningHttpNodeAdder) AddMany(ctx context.Context, nds []format.Node) error { + return (*httpNodeAdder)(api).addMany(ctx, nds, true) +} + +func (api *HttpDagServ) Pinning() format.NodeAdder { + return (*pinningHttpNodeAdder)(api) +} + +func (api *HttpDagServ) Remove(ctx context.Context, c cid.Cid) error { + return api.core().Block().Rm(ctx, path.FromCid(c)) // TODO: should we force rm? +} + +func (api *HttpDagServ) RemoveMany(ctx context.Context, cids []cid.Cid) error { + for _, c := range cids { + // TODO: optimize + if err := api.Remove(ctx, c); err != nil { + return err + } + } + return nil +} + +func (api *httpNodeAdder) core() *HttpApi { + return (*HttpApi)(api) +} + +func (api *HttpDagServ) core() *HttpApi { + return (*HttpApi)(api) +} diff --git a/client/rpc/errors.go b/client/rpc/errors.go new file mode 100644 index 000000000..29f5487d4 --- /dev/null +++ b/client/rpc/errors.go @@ -0,0 +1,166 @@ +package rpc + +import ( + "errors" + "strings" + "unicode/utf8" + + "github.com/ipfs/go-cid" + ipld "github.com/ipfs/go-ipld-format" + mbase "github.com/multiformats/go-multibase" +) + +// This file handle parsing and returning the correct ABI based errors from error messages + +type prePostWrappedNotFoundError struct { + pre string + post string + + wrapped ipld.ErrNotFound +} + +func (e prePostWrappedNotFoundError) String() string { + return e.Error() +} + +func (e prePostWrappedNotFoundError) Error() string { + return e.pre + e.wrapped.Error() + e.post +} + +func (e prePostWrappedNotFoundError) Unwrap() error { + return e.wrapped +} + +func parseErrNotFoundWithFallbackToMSG(msg string) error { + err, handled := parseErrNotFound(msg) + if handled { + return err + } + + return errors.New(msg) +} + +func parseErrNotFoundWithFallbackToError(msg error) error { + err, handled := parseErrNotFound(msg.Error()) + if handled { + return err + } + + return msg +} + +func parseErrNotFound(msg string) (error, bool) { + if msg == "" { + return nil, true // Fast path + } + + if err, handled := parseIPLDErrNotFound(msg); handled { + return err, true + } + + if err, handled := parseBlockstoreNotFound(msg); handled { + return err, true + } + + return nil, false +} + +// Assume CIDs break on: +// - Whitespaces: " \t\n\r\v\f" +// - Semicolon: ";" this is to parse ipld.ErrNotFound wrapped in multierr +// - Double Quotes: "\"" this is for parsing %q and %#v formatting. +const cidBreakSet = " \t\n\r\v\f;\"" + +func parseIPLDErrNotFound(msg string) (error, bool) { + // The pattern we search for is: + const ipldErrNotFoundKey = "ipld: could not find " /*CID*/ + // We try to parse the CID, if it's invalid we give up and return a simple text error. + // We also accept "node" in place of the CID because that means it's an Undefined CID. + + keyIndex := strings.Index(msg, ipldErrNotFoundKey) + + if keyIndex < 0 { // Unknown error + return nil, false + } + + cidStart := keyIndex + len(ipldErrNotFoundKey) + + msgPostKey := msg[cidStart:] + var c cid.Cid + var postIndex int + if strings.HasPrefix(msgPostKey, "node") { + // Fallback case + c = cid.Undef + postIndex = len("node") + } else { + postIndex = strings.IndexFunc(msgPostKey, func(r rune) bool { + return strings.ContainsAny(string(r), cidBreakSet) + }) + if postIndex < 0 { + // no breakage meaning the string look like this something + "ipld: could not find bafy" + postIndex = len(msgPostKey) + } + + cidStr := msgPostKey[:postIndex] + + var err error + c, err = cid.Decode(cidStr) + if err != nil { + // failed to decode CID give up + return nil, false + } + + // check that the CID is either a CIDv0 or a base32 multibase + // because that what ipld.ErrNotFound.Error() -> cid.Cid.String() do currently + if c.Version() != 0 { + baseRune, _ := utf8.DecodeRuneInString(cidStr) + if baseRune == utf8.RuneError || baseRune != mbase.Base32 { + // not a multibase we expect, give up + return nil, false + } + } + } + + err := ipld.ErrNotFound{Cid: c} + pre := msg[:keyIndex] + post := msgPostKey[postIndex:] + + if len(pre) > 0 || len(post) > 0 { + return prePostWrappedNotFoundError{ + pre: pre, + post: post, + wrapped: err, + }, true + } + + return err, true +} + +// This is a simple error type that just return msg as Error(). +// But that also match ipld.ErrNotFound when called with Is(err). +// That is needed to keep compatibility with code that use string.Contains(err.Error(), "blockstore: block not found") +// and code using ipld.ErrNotFound. +type blockstoreNotFoundMatchingIPLDErrNotFound struct { + msg string +} + +func (e blockstoreNotFoundMatchingIPLDErrNotFound) String() string { + return e.Error() +} + +func (e blockstoreNotFoundMatchingIPLDErrNotFound) Error() string { + return e.msg +} + +func (e blockstoreNotFoundMatchingIPLDErrNotFound) Is(err error) bool { + _, ok := err.(ipld.ErrNotFound) + return ok +} + +func parseBlockstoreNotFound(msg string) (error, bool) { + if !strings.Contains(msg, "blockstore: block not found") { + return nil, false + } + + return blockstoreNotFoundMatchingIPLDErrNotFound{msg: msg}, true +} diff --git a/client/rpc/errors_test.go b/client/rpc/errors_test.go new file mode 100644 index 000000000..c14738740 --- /dev/null +++ b/client/rpc/errors_test.go @@ -0,0 +1,99 @@ +package rpc + +import ( + "errors" + "fmt" + "testing" + + "github.com/ipfs/go-cid" + ipld "github.com/ipfs/go-ipld-format" + mbase "github.com/multiformats/go-multibase" + mh "github.com/multiformats/go-multihash" +) + +var randomSha256MH = mh.Multihash{0x12, 0x20, 0x88, 0x82, 0x73, 0x37, 0x7c, 0xc1, 0xc9, 0x96, 0xad, 0xee, 0xd, 0x26, 0x84, 0x2, 0xc9, 0xc9, 0x5c, 0xf9, 0x5c, 0x4d, 0x9b, 0xc3, 0x3f, 0xfb, 0x4a, 0xd8, 0xaf, 0x28, 0x6b, 0xca, 0x1a, 0xf2} + +func doParseIpldNotFoundTest(t *testing.T, original error) { + originalMsg := original.Error() + + rebuilt := parseErrNotFoundWithFallbackToMSG(originalMsg) + + rebuiltMsg := rebuilt.Error() + + if originalMsg != rebuiltMsg { + t.Errorf("expected message to be %q; got %q", originalMsg, rebuiltMsg) + } + + originalNotFound := ipld.IsNotFound(original) + rebuiltNotFound := ipld.IsNotFound(rebuilt) + if originalNotFound != rebuiltNotFound { + t.Errorf("for %q expected Ipld.IsNotFound to be %t; got %t", originalMsg, originalNotFound, rebuiltNotFound) + } +} + +func TestParseIPLDNotFound(t *testing.T) { + t.Parallel() + + if err := parseErrNotFoundWithFallbackToMSG(""); err != nil { + t.Errorf("expected empty string to give no error; got %T %q", err, err.Error()) + } + + cidBreaks := make([]string, len(cidBreakSet)) + for i, v := range cidBreakSet { + cidBreaks[i] = "%w" + string(v) + } + + base58BTCEncoder, err := mbase.NewEncoder(mbase.Base58BTC) + if err != nil { + t.Fatalf("expected to find Base58BTC encoder; got error %q", err.Error()) + } + + for _, wrap := range append(cidBreaks, + "", + "merkledag: %w", + "testing: %w the test", + "%w is wrong", + ) { + for _, err := range [...]error{ + errors.New("ipld: could not find "), + errors.New("ipld: could not find Bad_CID"), + errors.New("ipld: could not find " + cid.NewCidV1(cid.Raw, randomSha256MH).Encode(base58BTCEncoder)), // Test that we only accept CIDv0 and base32 CIDs + errors.New("network connection timeout"), + ipld.ErrNotFound{Cid: cid.Undef}, + ipld.ErrNotFound{Cid: cid.NewCidV0(randomSha256MH)}, + ipld.ErrNotFound{Cid: cid.NewCidV1(cid.Raw, randomSha256MH)}, + } { + if wrap != "" { + err = fmt.Errorf(wrap, err) + } + + doParseIpldNotFoundTest(t, err) + } + } +} + +func TestBlockstoreNotFoundMatchingIPLDErrNotFound(t *testing.T) { + t.Parallel() + + if !ipld.IsNotFound(blockstoreNotFoundMatchingIPLDErrNotFound{}) { + t.Fatalf("expected blockstoreNotFoundMatchingIPLDErrNotFound to match ipld.IsNotFound; got false") + } + + for _, wrap := range [...]string{ + "", + "merkledag: %w", + "testing: %w the test", + "%w is wrong", + } { + for _, err := range [...]error{ + errors.New("network connection timeout"), + blockstoreNotFoundMatchingIPLDErrNotFound{"blockstore: block not found"}, + } { + if wrap != "" { + err = fmt.Errorf(wrap, err) + } + + doParseIpldNotFoundTest(t, err) + } + } +} diff --git a/client/rpc/key.go b/client/rpc/key.go new file mode 100644 index 000000000..710d9fb06 --- /dev/null +++ b/client/rpc/key.go @@ -0,0 +1,195 @@ +package rpc + +import ( + "bytes" + "context" + "errors" + + "github.com/ipfs/boxo/ipns" + "github.com/ipfs/boxo/path" + iface "github.com/ipfs/kubo/core/coreiface" + caopts "github.com/ipfs/kubo/core/coreiface/options" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/multiformats/go-multibase" +) + +type KeyAPI HttpApi + +type key struct { + name string + pid peer.ID + path path.Path +} + +func newKey(name, pidStr string) (*key, error) { + pid, err := peer.Decode(pidStr) + if err != nil { + return nil, err + } + + path, err := path.NewPath("/ipns/" + ipns.NameFromPeer(pid).String()) + if err != nil { + return nil, err + } + + return &key{name: name, pid: pid, path: path}, nil +} + +func (k *key) Name() string { + return k.name +} + +func (k *key) Path() path.Path { + return k.path +} + +func (k *key) ID() peer.ID { + return k.pid +} + +type keyOutput struct { + Name string + Id string +} + +func (api *KeyAPI) Generate(ctx context.Context, name string, opts ...caopts.KeyGenerateOption) (iface.Key, error) { + options, err := caopts.KeyGenerateOptions(opts...) + if err != nil { + return nil, err + } + + var out keyOutput + err = api.core().Request("key/gen", name). + Option("type", options.Algorithm). + Option("size", options.Size). + Exec(ctx, &out) + if err != nil { + return nil, err + } + + return newKey(out.Name, out.Id) +} + +func (api *KeyAPI) Rename(ctx context.Context, oldName string, newName string, opts ...caopts.KeyRenameOption) (iface.Key, bool, error) { + options, err := caopts.KeyRenameOptions(opts...) + if err != nil { + return nil, false, err + } + + var out struct { + Was string + Now string + Id string + Overwrite bool + } + err = api.core().Request("key/rename", oldName, newName). + Option("force", options.Force). + Exec(ctx, &out) + if err != nil { + return nil, false, err + } + + key, err := newKey(out.Now, out.Id) + if err != nil { + return nil, false, err + } + + return key, out.Overwrite, err +} + +func (api *KeyAPI) List(ctx context.Context) ([]iface.Key, error) { + var out struct { + Keys []keyOutput + } + if err := api.core().Request("key/list").Exec(ctx, &out); err != nil { + return nil, err + } + + res := make([]iface.Key, len(out.Keys)) + for i, k := range out.Keys { + key, err := newKey(k.Name, k.Id) + if err != nil { + return nil, err + } + res[i] = key + } + + return res, nil +} + +func (api *KeyAPI) Self(ctx context.Context) (iface.Key, error) { + var id struct{ ID string } + if err := api.core().Request("id").Exec(ctx, &id); err != nil { + return nil, err + } + + return newKey("self", id.ID) +} + +func (api *KeyAPI) Remove(ctx context.Context, name string) (iface.Key, error) { + var out struct { + Keys []keyOutput + } + if err := api.core().Request("key/rm", name).Exec(ctx, &out); err != nil { + return nil, err + } + if len(out.Keys) != 1 { + return nil, errors.New("got unexpected number of keys back") + } + + return newKey(out.Keys[0].Name, out.Keys[0].Id) +} + +func (api *KeyAPI) core() *HttpApi { + return (*HttpApi)(api) +} + +func (api *KeyAPI) Sign(ctx context.Context, name string, data []byte) (iface.Key, []byte, error) { + var out struct { + Key keyOutput + Signature string + } + + err := api.core().Request("key/sign"). + Option("key", name). + FileBody(bytes.NewReader(data)). + Exec(ctx, &out) + if err != nil { + return nil, nil, err + } + + key, err := newKey(out.Key.Name, out.Key.Id) + if err != nil { + return nil, nil, err + } + + _, signature, err := multibase.Decode(out.Signature) + if err != nil { + return nil, nil, err + } + + return key, signature, nil +} + +func (api *KeyAPI) Verify(ctx context.Context, keyOrName string, signature, data []byte) (iface.Key, bool, error) { + var out struct { + Key keyOutput + SignatureValid bool + } + + err := api.core().Request("key/verify"). + Option("key", keyOrName). + Option("signature", toMultibase(signature)). + FileBody(bytes.NewReader(data)). + Exec(ctx, &out) + if err != nil { + return nil, false, err + } + + key, err := newKey(out.Key.Name, out.Key.Id) + if err != nil { + return nil, false, err + } + + return key, out.SignatureValid, nil +} diff --git a/client/rpc/name.go b/client/rpc/name.go new file mode 100644 index 000000000..023f0cde8 --- /dev/null +++ b/client/rpc/name.go @@ -0,0 +1,134 @@ +package rpc + +import ( + "context" + "encoding/json" + "fmt" + "io" + + "github.com/ipfs/boxo/ipns" + "github.com/ipfs/boxo/namesys" + "github.com/ipfs/boxo/path" + iface "github.com/ipfs/kubo/core/coreiface" + caopts "github.com/ipfs/kubo/core/coreiface/options" +) + +type NameAPI HttpApi + +type ipnsEntry struct { + Name string `json:"Name"` + Value string `json:"Value"` +} + +func (api *NameAPI) Publish(ctx context.Context, p path.Path, opts ...caopts.NamePublishOption) (ipns.Name, error) { + options, err := caopts.NamePublishOptions(opts...) + if err != nil { + return ipns.Name{}, err + } + + req := api.core().Request("name/publish", p.String()). + Option("key", options.Key). + Option("allow-offline", options.AllowOffline). + Option("lifetime", options.ValidTime). + Option("resolve", false) + + if options.TTL != nil { + req.Option("ttl", options.TTL) + } + + var out ipnsEntry + if err := req.Exec(ctx, &out); err != nil { + return ipns.Name{}, err + } + return ipns.NameFromString(out.Name) +} + +func (api *NameAPI) Search(ctx context.Context, name string, opts ...caopts.NameResolveOption) (<-chan iface.IpnsResult, error) { + options, err := caopts.NameResolveOptions(opts...) + if err != nil { + return nil, err + } + + ropts := namesys.ProcessResolveOptions(options.ResolveOpts) + if ropts.Depth != namesys.DefaultDepthLimit && ropts.Depth != 1 { + return nil, fmt.Errorf("Name.Resolve: depth other than 1 or %d not supported", namesys.DefaultDepthLimit) + } + + req := api.core().Request("name/resolve", name). + Option("nocache", !options.Cache). + Option("recursive", ropts.Depth != 1). + Option("dht-record-count", ropts.DhtRecordCount). + Option("dht-timeout", ropts.DhtTimeout). + Option("stream", true) + resp, err := req.Send(ctx) + if err != nil { + return nil, err + } + if resp.Error != nil { + return nil, resp.Error + } + + res := make(chan iface.IpnsResult) + + go func() { + defer close(res) + defer resp.Close() + + dec := json.NewDecoder(resp.Output) + + for { + var out struct{ Path string } + err := dec.Decode(&out) + if err == io.EOF { + return + } + var ires iface.IpnsResult + if err == nil { + p, err := path.NewPath(out.Path) + if err != nil { + return + } + ires.Path = p + } + + select { + case res <- ires: + case <-ctx.Done(): + } + if err != nil { + return + } + } + }() + + return res, nil +} + +func (api *NameAPI) Resolve(ctx context.Context, name string, opts ...caopts.NameResolveOption) (path.Path, error) { + options, err := caopts.NameResolveOptions(opts...) + if err != nil { + return nil, err + } + + ropts := namesys.ProcessResolveOptions(options.ResolveOpts) + if ropts.Depth != namesys.DefaultDepthLimit && ropts.Depth != 1 { + return nil, fmt.Errorf("Name.Resolve: depth other than 1 or %d not supported", namesys.DefaultDepthLimit) + } + + req := api.core().Request("name/resolve", name). + Option("nocache", !options.Cache). + Option("recursive", ropts.Depth != 1). + Option("dht-record-count", ropts.DhtRecordCount). + Option("dht-timeout", ropts.DhtTimeout) + + var out struct{ Path string } + if err := req.Exec(ctx, &out); err != nil { + return nil, err + } + + return path.NewPath(out.Path) +} + +func (api *NameAPI) core() *HttpApi { + return (*HttpApi)(api) +} diff --git a/client/rpc/object.go b/client/rpc/object.go new file mode 100644 index 000000000..5c9d323e8 --- /dev/null +++ b/client/rpc/object.go @@ -0,0 +1,88 @@ +package rpc + +import ( + "context" + + "github.com/ipfs/boxo/path" + "github.com/ipfs/go-cid" + iface "github.com/ipfs/kubo/core/coreiface" + caopts "github.com/ipfs/kubo/core/coreiface/options" +) + +type ObjectAPI HttpApi + +type objectOut struct { + Hash string +} + +func (api *ObjectAPI) AddLink(ctx context.Context, base path.Path, name string, child path.Path, opts ...caopts.ObjectAddLinkOption) (path.ImmutablePath, error) { + options, err := caopts.ObjectAddLinkOptions(opts...) + if err != nil { + return path.ImmutablePath{}, err + } + + var out objectOut + err = api.core().Request("object/patch/add-link", base.String(), name, child.String()). + Option("create", options.Create). + Exec(ctx, &out) + if err != nil { + return path.ImmutablePath{}, err + } + + c, err := cid.Parse(out.Hash) + if err != nil { + return path.ImmutablePath{}, err + } + + return path.FromCid(c), nil +} + +func (api *ObjectAPI) RmLink(ctx context.Context, base path.Path, link string) (path.ImmutablePath, error) { + var out objectOut + err := api.core().Request("object/patch/rm-link", base.String(), link). + Exec(ctx, &out) + if err != nil { + return path.ImmutablePath{}, err + } + + c, err := cid.Parse(out.Hash) + if err != nil { + return path.ImmutablePath{}, err + } + + return path.FromCid(c), nil +} + +type change struct { + Type iface.ChangeType + Path string + Before cid.Cid + After cid.Cid +} + +func (api *ObjectAPI) Diff(ctx context.Context, a path.Path, b path.Path) ([]iface.ObjectChange, error) { + var out struct { + Changes []change + } + if err := api.core().Request("object/diff", a.String(), b.String()).Exec(ctx, &out); err != nil { + return nil, err + } + res := make([]iface.ObjectChange, len(out.Changes)) + for i, ch := range out.Changes { + res[i] = iface.ObjectChange{ + Type: ch.Type, + Path: ch.Path, + } + if ch.Before != cid.Undef { + res[i].Before = path.FromCid(ch.Before) + } + if ch.After != cid.Undef { + res[i].After = path.FromCid(ch.After) + } + } + return res, nil +} + +func (api *ObjectAPI) core() *HttpApi { + return (*HttpApi)(api) +} diff --git a/client/rpc/path.go b/client/rpc/path.go new file mode 100644 index 000000000..03d3dfa55 --- /dev/null +++ b/client/rpc/path.go @@ -0,0 +1,48 @@ +package rpc + +import ( + "context" + + "github.com/ipfs/boxo/path" + cid "github.com/ipfs/go-cid" + ipld "github.com/ipfs/go-ipld-format" +) + +func (api *HttpApi) ResolvePath(ctx context.Context, p path.Path) (path.ImmutablePath, []string, error) { + var out struct { + Cid cid.Cid + RemPath string + } + + var err error + if p.Namespace() == path.IPNSNamespace { + if p, err = api.Name().Resolve(ctx, p.String()); err != nil { + return path.ImmutablePath{}, nil, err + } + } + + if err := api.Request("dag/resolve", p.String()).Exec(ctx, &out); err != nil { + return path.ImmutablePath{}, nil, err + } + + p, err = path.NewPathFromSegments(p.Namespace(), out.Cid.String(), out.RemPath) + if err != nil { + return path.ImmutablePath{}, nil, err + } + + imPath, err := path.NewImmutablePath(p) + if err != nil { + return path.ImmutablePath{}, nil, err + } + + return imPath, path.StringToSegments(out.RemPath), nil +} + +func (api *HttpApi) ResolveNode(ctx context.Context, p path.Path) (ipld.Node, error) { + rp, _, err := api.ResolvePath(ctx, p) + if err != nil { + return nil, err + } + + return api.Dag().Get(ctx, rp.RootCid()) +} diff --git a/client/rpc/pin.go b/client/rpc/pin.go new file mode 100644 index 000000000..6e8e942ac --- /dev/null +++ b/client/rpc/pin.go @@ -0,0 +1,272 @@ +package rpc + +import ( + "context" + "encoding/json" + "errors" + "io" + "strings" + + "github.com/ipfs/boxo/path" + "github.com/ipfs/go-cid" + iface "github.com/ipfs/kubo/core/coreiface" + caopts "github.com/ipfs/kubo/core/coreiface/options" +) + +type PinAPI HttpApi + +type pinRefKeyObject struct { + Type string +} + +type pinRefKeyList struct { + Keys map[string]pinRefKeyObject +} + +type pin struct { + path path.ImmutablePath + typ string + name string + err error +} + +func (p pin) Err() error { + return p.err +} + +func (p pin) Path() path.ImmutablePath { + return p.path +} + +func (p pin) Name() string { + return p.name +} + +func (p pin) Type() string { + return p.typ +} + +func (api *PinAPI) Add(ctx context.Context, p path.Path, opts ...caopts.PinAddOption) error { + options, err := caopts.PinAddOptions(opts...) + if err != nil { + return err + } + + return api.core().Request("pin/add", p.String()). + Option("recursive", options.Recursive).Exec(ctx, nil) +} + +type pinLsObject struct { + Cid string + Name string + Type string +} + +func (api *PinAPI) Ls(ctx context.Context, opts ...caopts.PinLsOption) (<-chan iface.Pin, error) { + options, err := caopts.PinLsOptions(opts...) + if err != nil { + return nil, err + } + + res, err := api.core().Request("pin/ls"). + Option("type", options.Type). + Option("stream", true). + Send(ctx) + if err != nil { + return nil, err + } + + pins := make(chan iface.Pin) + go func(ch chan<- iface.Pin) { + defer res.Output.Close() + defer close(ch) + + dec := json.NewDecoder(res.Output) + var out pinLsObject + for { + switch err := dec.Decode(&out); err { + case nil: + case io.EOF: + return + default: + select { + case ch <- pin{err: err}: + return + case <-ctx.Done(): + return + } + } + + c, err := cid.Parse(out.Cid) + if err != nil { + select { + case ch <- pin{err: err}: + return + case <-ctx.Done(): + return + } + } + + select { + case ch <- pin{typ: out.Type, name: out.Name, path: path.FromCid(c)}: + case <-ctx.Done(): + return + } + } + }(pins) + return pins, nil +} + +// IsPinned returns whether or not the given cid is pinned +// and an explanation of why its pinned. +func (api *PinAPI) IsPinned(ctx context.Context, p path.Path, opts ...caopts.PinIsPinnedOption) (string, bool, error) { + options, err := caopts.PinIsPinnedOptions(opts...) + if err != nil { + return "", false, err + } + var out pinRefKeyList + err = api.core().Request("pin/ls"). + Option("type", options.WithType). + Option("arg", p.String()). + Exec(ctx, &out) + if err != nil { + // TODO: This error-type discrimination based on sub-string matching is brittle. + // It is addressed by this open issue: https://github.com/ipfs/go-ipfs/issues/7563 + if strings.Contains(err.Error(), "is not pinned") { + return "", false, nil + } + return "", false, err + } + + for _, obj := range out.Keys { + return obj.Type, true, nil + } + return "", false, errors.New("http api returned no error and no results") +} + +func (api *PinAPI) Rm(ctx context.Context, p path.Path, opts ...caopts.PinRmOption) error { + options, err := caopts.PinRmOptions(opts...) + if err != nil { + return err + } + + return api.core().Request("pin/rm", p.String()). + Option("recursive", options.Recursive). + Exec(ctx, nil) +} + +func (api *PinAPI) Update(ctx context.Context, from path.Path, to path.Path, opts ...caopts.PinUpdateOption) error { + options, err := caopts.PinUpdateOptions(opts...) + if err != nil { + return err + } + + return api.core().Request("pin/update", from.String(), to.String()). + Option("unpin", options.Unpin).Exec(ctx, nil) +} + +type pinVerifyRes struct { + ok bool + badNodes []iface.BadPinNode + err error +} + +func (r pinVerifyRes) Ok() bool { + return r.ok +} + +func (r pinVerifyRes) BadNodes() []iface.BadPinNode { + return r.badNodes +} + +func (r pinVerifyRes) Err() error { + return r.err +} + +type badNode struct { + err error + cid cid.Cid +} + +func (n badNode) Path() path.ImmutablePath { + return path.FromCid(n.cid) +} + +func (n badNode) Err() error { + return n.err +} + +func (api *PinAPI) Verify(ctx context.Context) (<-chan iface.PinStatus, error) { + resp, err := api.core().Request("pin/verify").Option("verbose", true).Send(ctx) + if err != nil { + return nil, err + } + if resp.Error != nil { + return nil, resp.Error + } + res := make(chan iface.PinStatus) + + go func() { + defer resp.Close() + defer close(res) + dec := json.NewDecoder(resp.Output) + for { + var out struct { + Cid string + Err string + Ok bool + + BadNodes []struct { + Cid string + Err string + } + } + if err := dec.Decode(&out); err != nil { + if err == io.EOF { + return + } + select { + case res <- pinVerifyRes{err: err}: + return + case <-ctx.Done(): + return + } + } + + if out.Err != "" { + select { + case res <- pinVerifyRes{err: errors.New(out.Err)}: + return + case <-ctx.Done(): + return + } + } + + badNodes := make([]iface.BadPinNode, len(out.BadNodes)) + for i, n := range out.BadNodes { + c, err := cid.Decode(n.Cid) + if err != nil { + badNodes[i] = badNode{cid: c, err: err} + continue + } + + if n.Err != "" { + err = errors.New(n.Err) + } + badNodes[i] = badNode{cid: c, err: err} + } + + select { + case res <- pinVerifyRes{ok: out.Ok, badNodes: badNodes}: + case <-ctx.Done(): + return + } + } + }() + + return res, nil +} + +func (api *PinAPI) core() *HttpApi { + return (*HttpApi)(api) +} diff --git a/client/rpc/pubsub.go b/client/rpc/pubsub.go new file mode 100644 index 000000000..d12d7a5de --- /dev/null +++ b/client/rpc/pubsub.go @@ -0,0 +1,213 @@ +package rpc + +import ( + "bytes" + "context" + "encoding/json" + "io" + + iface "github.com/ipfs/kubo/core/coreiface" + caopts "github.com/ipfs/kubo/core/coreiface/options" + "github.com/libp2p/go-libp2p/core/peer" + mbase "github.com/multiformats/go-multibase" +) + +type PubsubAPI HttpApi + +func (api *PubsubAPI) Ls(ctx context.Context) ([]string, error) { + var out struct { + Strings []string + } + + if err := api.core().Request("pubsub/ls").Exec(ctx, &out); err != nil { + return nil, err + } + topics := make([]string, len(out.Strings)) + for n, mb := range out.Strings { + _, topic, err := mbase.Decode(mb) + if err != nil { + return nil, err + } + topics[n] = string(topic) + } + return topics, nil +} + +func (api *PubsubAPI) Peers(ctx context.Context, opts ...caopts.PubSubPeersOption) ([]peer.ID, error) { + options, err := caopts.PubSubPeersOptions(opts...) + if err != nil { + return nil, err + } + + var out struct { + Strings []string + } + + var optionalTopic string + if len(options.Topic) > 0 { + optionalTopic = toMultibase([]byte(options.Topic)) + } + if err := api.core().Request("pubsub/peers", optionalTopic).Exec(ctx, &out); err != nil { + return nil, err + } + + res := make([]peer.ID, len(out.Strings)) + for i, sid := range out.Strings { + id, err := peer.Decode(sid) + if err != nil { + return nil, err + } + res[i] = id + } + return res, nil +} + +func (api *PubsubAPI) Publish(ctx context.Context, topic string, message []byte) error { + return api.core().Request("pubsub/pub", toMultibase([]byte(topic))). + FileBody(bytes.NewReader(message)). + Exec(ctx, nil) +} + +type pubsubSub struct { + messages chan pubsubMessage + + done chan struct{} + rcloser func() error +} + +type pubsubMessage struct { + JFrom string `json:"from,omitempty"` + JData string `json:"data,omitempty"` + JSeqno string `json:"seqno,omitempty"` + JTopicIDs []string `json:"topicIDs,omitempty"` + + // real values after unpacking from text/multibase envelopes + from peer.ID + data []byte + seqno []byte + topics []string + + err error +} + +func (msg *pubsubMessage) From() peer.ID { + return msg.from +} + +func (msg *pubsubMessage) Data() []byte { + return msg.data +} + +func (msg *pubsubMessage) Seq() []byte { + return msg.seqno +} + +// TODO: do we want to keep this interface as []string, +// or change to more correct [][]byte? +func (msg *pubsubMessage) Topics() []string { + return msg.topics +} + +func (s *pubsubSub) Next(ctx context.Context) (iface.PubSubMessage, error) { + select { + case msg, ok := <-s.messages: + if !ok { + return nil, io.EOF + } + if msg.err != nil { + return nil, msg.err + } + // unpack values from text/multibase envelopes + var err error + msg.from, err = peer.Decode(msg.JFrom) + if err != nil { + return nil, err + } + _, msg.data, err = mbase.Decode(msg.JData) + if err != nil { + return nil, err + } + _, msg.seqno, err = mbase.Decode(msg.JSeqno) + if err != nil { + return nil, err + } + for _, mbt := range msg.JTopicIDs { + _, topic, err := mbase.Decode(mbt) + if err != nil { + return nil, err + } + msg.topics = append(msg.topics, string(topic)) + } + return &msg, nil + case <-ctx.Done(): + return nil, ctx.Err() + } +} + +func (api *PubsubAPI) Subscribe(ctx context.Context, topic string, opts ...caopts.PubSubSubscribeOption) (iface.PubSubSubscription, error) { + /* right now we have no options (discover got deprecated) + options, err := caopts.PubSubSubscribeOptions(opts...) + if err != nil { + return nil, err + } + */ + resp, err := api.core().Request("pubsub/sub", toMultibase([]byte(topic))).Send(ctx) + if err != nil { + return nil, err + } + if resp.Error != nil { + return nil, resp.Error + } + + sub := &pubsubSub{ + messages: make(chan pubsubMessage), + done: make(chan struct{}), + rcloser: func() error { + return resp.Cancel() + }, + } + + dec := json.NewDecoder(resp.Output) + + go func() { + defer close(sub.messages) + + for { + var msg pubsubMessage + if err := dec.Decode(&msg); err != nil { + if err == io.EOF { + return + } + msg.err = err + } + + select { + case sub.messages <- msg: + case <-sub.done: + return + case <-ctx.Done(): + return + } + } + }() + + return sub, nil +} + +func (s *pubsubSub) Close() error { + if s.done != nil { + close(s.done) + s.done = nil + } + return s.rcloser() +} + +func (api *PubsubAPI) core() *HttpApi { + return (*HttpApi)(api) +} + +// Encodes bytes into URL-safe multibase that can be sent over HTTP RPC (URL or body). +func toMultibase(data []byte) string { + mb, _ := mbase.Encode(mbase.Base64url, data) + return mb +} diff --git a/client/rpc/request.go b/client/rpc/request.go new file mode 100644 index 000000000..675717707 --- /dev/null +++ b/client/rpc/request.go @@ -0,0 +1,36 @@ +package rpc + +import ( + "context" + "io" + "strings" +) + +type Request struct { + Ctx context.Context + ApiBase string + Command string + Args []string + Opts map[string]string + Body io.Reader + Headers map[string]string +} + +func NewRequest(ctx context.Context, url, command string, args ...string) *Request { + if !strings.HasPrefix(url, "http") { + url = "http://" + url + } + + opts := map[string]string{ + "encoding": "json", + "stream-channels": "true", + } + return &Request{ + Ctx: ctx, + ApiBase: url + "/api/v0", + Command: command, + Args: args, + Opts: opts, + Headers: make(map[string]string), + } +} diff --git a/client/rpc/requestbuilder.go b/client/rpc/requestbuilder.go new file mode 100644 index 000000000..e060c19b4 --- /dev/null +++ b/client/rpc/requestbuilder.go @@ -0,0 +1,148 @@ +package rpc + +import ( + "bytes" + "context" + "fmt" + "io" + "strconv" + "strings" + + "github.com/blang/semver/v4" + "github.com/ipfs/boxo/files" +) + +type RequestBuilder interface { + Arguments(args ...string) RequestBuilder + BodyString(body string) RequestBuilder + BodyBytes(body []byte) RequestBuilder + Body(body io.Reader) RequestBuilder + FileBody(body io.Reader) RequestBuilder + Option(key string, value interface{}) RequestBuilder + Header(name, value string) RequestBuilder + Send(ctx context.Context) (*Response, error) + Exec(ctx context.Context, res interface{}) error +} + +// encodedAbsolutePathVersion is the version from which the absolute path header in +// multipart requests is %-encoded. Before this version, its sent raw. +var encodedAbsolutePathVersion = semver.MustParse("0.23.0-dev") + +// requestBuilder is an IPFS commands request builder. +type requestBuilder struct { + command string + args []string + opts map[string]string + headers map[string]string + body io.Reader + buildError error + + shell *HttpApi +} + +// Arguments adds the arguments to the args. +func (r *requestBuilder) Arguments(args ...string) RequestBuilder { + r.args = append(r.args, args...) + return r +} + +// BodyString sets the request body to the given string. +func (r *requestBuilder) BodyString(body string) RequestBuilder { + return r.Body(strings.NewReader(body)) +} + +// BodyBytes sets the request body to the given buffer. +func (r *requestBuilder) BodyBytes(body []byte) RequestBuilder { + return r.Body(bytes.NewReader(body)) +} + +// Body sets the request body to the given reader. +func (r *requestBuilder) Body(body io.Reader) RequestBuilder { + r.body = body + return r +} + +// FileBody sets the request body to the given reader wrapped into multipartreader. +func (r *requestBuilder) FileBody(body io.Reader) RequestBuilder { + pr, _ := files.NewReaderPathFile("/dev/stdin", io.NopCloser(body), nil) + d := files.NewMapDirectory(map[string]files.Node{"": pr}) + + version, err := r.shell.loadRemoteVersion() + if err != nil { + // Unfortunately, we cannot return an error here. Changing this API is also + // not the best since we would otherwise have an inconsistent RequestBuilder. + // We save the error and return it when calling [requestBuilder.Send]. + r.buildError = err + return r + } + + useEncodedAbsPaths := version.LT(encodedAbsolutePathVersion) + r.body = files.NewMultiFileReader(d, false, useEncodedAbsPaths) + + return r +} + +// Option sets the given option. +func (r *requestBuilder) Option(key string, value interface{}) RequestBuilder { + var s string + switch v := value.(type) { + case bool: + s = strconv.FormatBool(v) + case string: + s = v + case []byte: + s = string(v) + default: + // slow case. + s = fmt.Sprint(value) + } + if r.opts == nil { + r.opts = make(map[string]string, 1) + } + r.opts[key] = s + return r +} + +// Header sets the given header. +func (r *requestBuilder) Header(name, value string) RequestBuilder { + if r.headers == nil { + r.headers = make(map[string]string, 1) + } + r.headers[name] = value + return r +} + +// Send sends the request and return the response. +func (r *requestBuilder) Send(ctx context.Context) (*Response, error) { + if r.buildError != nil { + return nil, r.buildError + } + + r.shell.applyGlobal(r) + + req := NewRequest(ctx, r.shell.url, r.command, r.args...) + req.Opts = r.opts + req.Headers = r.headers + req.Body = r.body + return req.Send(&r.shell.httpcli) +} + +// Exec sends the request a request and decodes the response. +func (r *requestBuilder) Exec(ctx context.Context, res interface{}) error { + httpRes, err := r.Send(ctx) + if err != nil { + return err + } + + if res == nil { + lateErr := httpRes.Close() + if httpRes.Error != nil { + return httpRes.Error + } + return lateErr + } + + return httpRes.decode(res) +} + +var _ RequestBuilder = &requestBuilder{} diff --git a/client/rpc/response.go b/client/rpc/response.go new file mode 100644 index 000000000..c47da4a68 --- /dev/null +++ b/client/rpc/response.go @@ -0,0 +1,169 @@ +package rpc + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "mime" + "net/http" + "net/url" + "os" + + "github.com/ipfs/boxo/files" + cmds "github.com/ipfs/go-ipfs-cmds" + cmdhttp "github.com/ipfs/go-ipfs-cmds/http" +) + +type Error = cmds.Error + +type trailerReader struct { + resp *http.Response +} + +func (r *trailerReader) Read(b []byte) (int, error) { + n, err := r.resp.Body.Read(b) + if err != nil { + if e := r.resp.Trailer.Get(cmdhttp.StreamErrHeader); e != "" { + err = errors.New(e) + } + } + return n, err +} + +func (r *trailerReader) Close() error { + return r.resp.Body.Close() +} + +type Response struct { + Output io.ReadCloser + Error *Error +} + +func (r *Response) Close() error { + if r.Output != nil { + + // drain output (response body) + _, err1 := io.Copy(io.Discard, r.Output) + err2 := r.Output.Close() + if err1 != nil { + return err1 + } + return err2 + } + return nil +} + +// Cancel aborts running request (without draining request body). +func (r *Response) Cancel() error { + if r.Output != nil { + return r.Output.Close() + } + + return nil +} + +// Decode reads request body and decodes it as json. +func (r *Response) decode(dec interface{}) error { + if r.Error != nil { + return r.Error + } + + err := json.NewDecoder(r.Output).Decode(dec) + err2 := r.Close() + if err != nil { + return err + } + + return err2 +} + +func (r *Request) Send(c *http.Client) (*Response, error) { + url := r.getURL() + req, err := http.NewRequest("POST", url, r.Body) + if err != nil { + return nil, err + } + + req = req.WithContext(r.Ctx) + + // Add any headers that were supplied via the requestBuilder. + for k, v := range r.Headers { + req.Header.Add(k, v) + } + + if fr, ok := r.Body.(*files.MultiFileReader); ok { + req.Header.Set("Content-Type", "multipart/form-data; boundary="+fr.Boundary()) + req.Header.Set("Content-Disposition", "form-data; name=\"files\"") + } + + resp, err := c.Do(req) + if err != nil { + return nil, err + } + + contentType, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return nil, err + } + + nresp := new(Response) + + nresp.Output = &trailerReader{resp} + if resp.StatusCode >= http.StatusBadRequest { + e := new(Error) + switch { + case resp.StatusCode == http.StatusNotFound: + e.Message = "command not found" + case contentType == "text/plain": + out, err := io.ReadAll(resp.Body) + if err != nil { + fmt.Fprintf(os.Stderr, "ipfs-shell: warning! response (%d) read error: %s\n", resp.StatusCode, err) + } + e.Message = string(out) + + // set special status codes. + switch resp.StatusCode { + case http.StatusNotFound, http.StatusBadRequest: + e.Code = cmds.ErrClient + case http.StatusTooManyRequests: + e.Code = cmds.ErrRateLimited + case http.StatusForbidden: + e.Code = cmds.ErrForbidden + } + case contentType == "application/json": + if err = json.NewDecoder(resp.Body).Decode(e); err != nil { + fmt.Fprintf(os.Stderr, "ipfs-shell: warning! response (%d) unmarshall error: %s\n", resp.StatusCode, err) + } + default: + // This is a server-side bug (probably). + e.Code = cmds.ErrImplementation + fmt.Fprintf(os.Stderr, "ipfs-shell: warning! unhandled response (%d) encoding: %s", resp.StatusCode, contentType) + out, err := io.ReadAll(resp.Body) + if err != nil { + fmt.Fprintf(os.Stderr, "ipfs-shell: response (%d) read error: %s\n", resp.StatusCode, err) + } + e.Message = fmt.Sprintf("unknown ipfs-shell error encoding: %q - %q", contentType, out) + } + nresp.Error = e + nresp.Output = nil + + // drain body and close + _, _ = io.Copy(io.Discard, resp.Body) + _ = resp.Body.Close() + } + + return nresp, nil +} + +func (r *Request) getURL() string { + values := make(url.Values) + for _, arg := range r.Args { + values.Add("arg", arg) + } + for k, v := range r.Opts { + values.Add(k, v) + } + + return fmt.Sprintf("%s/%s?%s", r.ApiBase, r.Command, values.Encode()) +} diff --git a/client/rpc/routing.go b/client/rpc/routing.go new file mode 100644 index 000000000..693f155c6 --- /dev/null +++ b/client/rpc/routing.go @@ -0,0 +1,161 @@ +package rpc + +import ( + "bytes" + "context" + "encoding/base64" + "encoding/json" + + "github.com/ipfs/boxo/path" + "github.com/ipfs/kubo/core/coreiface/options" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/routing" +) + +type RoutingAPI HttpApi + +func (api *RoutingAPI) Get(ctx context.Context, key string) ([]byte, error) { + resp, err := api.core().Request("routing/get", key).Send(ctx) + if err != nil { + return nil, err + } + if resp.Error != nil { + return nil, resp.Error + } + defer resp.Close() + + var out routing.QueryEvent + + dec := json.NewDecoder(resp.Output) + if err := dec.Decode(&out); err != nil { + return nil, err + } + + res, err := base64.StdEncoding.DecodeString(out.Extra) + if err != nil { + return nil, err + } + + return res, nil +} + +func (api *RoutingAPI) Put(ctx context.Context, key string, value []byte, opts ...options.RoutingPutOption) error { + var cfg options.RoutingPutSettings + for _, o := range opts { + if err := o(&cfg); err != nil { + return err + } + } + + resp, err := api.core().Request("routing/put", key). + Option("allow-offline", cfg.AllowOffline). + FileBody(bytes.NewReader(value)). + Send(ctx) + if err != nil { + return err + } + if resp.Error != nil { + return resp.Error + } + return nil +} + +func (api *RoutingAPI) FindPeer(ctx context.Context, p peer.ID) (peer.AddrInfo, error) { + var out struct { + Type routing.QueryEventType + Responses []peer.AddrInfo + } + resp, err := api.core().Request("routing/findpeer", p.String()).Send(ctx) + if err != nil { + return peer.AddrInfo{}, err + } + if resp.Error != nil { + return peer.AddrInfo{}, resp.Error + } + defer resp.Close() + dec := json.NewDecoder(resp.Output) + for { + if err := dec.Decode(&out); err != nil { + return peer.AddrInfo{}, err + } + if out.Type == routing.FinalPeer { + return out.Responses[0], nil + } + } +} + +func (api *RoutingAPI) FindProviders(ctx context.Context, p path.Path, opts ...options.RoutingFindProvidersOption) (<-chan peer.AddrInfo, error) { + options, err := options.RoutingFindProvidersOptions(opts...) + if err != nil { + return nil, err + } + + rp, _, err := api.core().ResolvePath(ctx, p) + if err != nil { + return nil, err + } + + resp, err := api.core().Request("routing/findprovs", rp.RootCid().String()). + Option("num-providers", options.NumProviders). + Send(ctx) + if err != nil { + return nil, err + } + if resp.Error != nil { + return nil, resp.Error + } + res := make(chan peer.AddrInfo) + + go func() { + defer resp.Close() + defer close(res) + dec := json.NewDecoder(resp.Output) + + for { + var out struct { + Extra string + Type routing.QueryEventType + Responses []peer.AddrInfo + } + + if err := dec.Decode(&out); err != nil { + return // todo: handle this somehow + } + if out.Type == routing.QueryError { + return // usually a 'not found' error + // todo: handle other errors + } + if out.Type == routing.Provider { + for _, pi := range out.Responses { + select { + case res <- pi: + case <-ctx.Done(): + return + } + } + } + } + }() + + return res, nil +} + +func (api *RoutingAPI) Provide(ctx context.Context, p path.Path, opts ...options.RoutingProvideOption) error { + options, err := options.RoutingProvideOptions(opts...) + if err != nil { + return err + } + + rp, _, err := api.core().ResolvePath(ctx, p) + if err != nil { + return err + } + + return api.core().Request("routing/provide", rp.RootCid().String()). + Option("recursive", options.Recursive). + Exec(ctx, nil) +} + +func (api *RoutingAPI) core() *HttpApi { + return (*HttpApi)(api) +} diff --git a/client/rpc/swarm.go b/client/rpc/swarm.go new file mode 100644 index 000000000..d54d06604 --- /dev/null +++ b/client/rpc/swarm.go @@ -0,0 +1,187 @@ +package rpc + +import ( + "context" + "time" + + iface "github.com/ipfs/kubo/core/coreiface" + "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/protocol" + "github.com/multiformats/go-multiaddr" +) + +type SwarmAPI HttpApi + +func (api *SwarmAPI) Connect(ctx context.Context, pi peer.AddrInfo) error { + pidma, err := multiaddr.NewComponent("p2p", pi.ID.String()) + if err != nil { + return err + } + + saddrs := make([]string, len(pi.Addrs)) + for i, addr := range pi.Addrs { + saddrs[i] = addr.Encapsulate(pidma).String() + } + + return api.core().Request("swarm/connect", saddrs...).Exec(ctx, nil) +} + +func (api *SwarmAPI) Disconnect(ctx context.Context, addr multiaddr.Multiaddr) error { + return api.core().Request("swarm/disconnect", addr.String()).Exec(ctx, nil) +} + +type connInfo struct { + addr multiaddr.Multiaddr + peer peer.ID + latency time.Duration + muxer string + direction network.Direction + streams []protocol.ID +} + +func (c *connInfo) ID() peer.ID { + return c.peer +} + +func (c *connInfo) Address() multiaddr.Multiaddr { + return c.addr +} + +func (c *connInfo) Direction() network.Direction { + return c.direction +} + +func (c *connInfo) Latency() (time.Duration, error) { + return c.latency, nil +} + +func (c *connInfo) Streams() ([]protocol.ID, error) { + return c.streams, nil +} + +func (api *SwarmAPI) Peers(ctx context.Context) ([]iface.ConnectionInfo, error) { + var resp struct { + Peers []struct { + Addr string + Peer string + Latency string + Muxer string + Direction network.Direction + Streams []struct { + Protocol string + } + } + } + + err := api.core().Request("swarm/peers"). + Option("streams", true). + Option("latency", true). + Exec(ctx, &resp) + if err != nil { + return nil, err + } + + res := make([]iface.ConnectionInfo, len(resp.Peers)) + for i, conn := range resp.Peers { + latency, _ := time.ParseDuration(conn.Latency) + out := &connInfo{ + latency: latency, + muxer: conn.Muxer, + direction: conn.Direction, + } + + out.peer, err = peer.Decode(conn.Peer) + if err != nil { + return nil, err + } + + out.addr, err = multiaddr.NewMultiaddr(conn.Addr) + if err != nil { + return nil, err + } + + out.streams = make([]protocol.ID, len(conn.Streams)) + for i, p := range conn.Streams { + out.streams[i] = protocol.ID(p.Protocol) + } + + res[i] = out + } + + return res, nil +} + +func (api *SwarmAPI) KnownAddrs(ctx context.Context) (map[peer.ID][]multiaddr.Multiaddr, error) { + var out struct { + Addrs map[string][]string + } + if err := api.core().Request("swarm/addrs").Exec(ctx, &out); err != nil { + return nil, err + } + res := map[peer.ID][]multiaddr.Multiaddr{} + for spid, saddrs := range out.Addrs { + addrs := make([]multiaddr.Multiaddr, len(saddrs)) + + for i, addr := range saddrs { + a, err := multiaddr.NewMultiaddr(addr) + if err != nil { + return nil, err + } + addrs[i] = a + } + + pid, err := peer.Decode(spid) + if err != nil { + return nil, err + } + + res[pid] = addrs + } + + return res, nil +} + +func (api *SwarmAPI) LocalAddrs(ctx context.Context) ([]multiaddr.Multiaddr, error) { + var out struct { + Strings []string + } + + if err := api.core().Request("swarm/addrs/local").Exec(ctx, &out); err != nil { + return nil, err + } + + res := make([]multiaddr.Multiaddr, len(out.Strings)) + for i, addr := range out.Strings { + ma, err := multiaddr.NewMultiaddr(addr) + if err != nil { + return nil, err + } + res[i] = ma + } + return res, nil +} + +func (api *SwarmAPI) ListenAddrs(ctx context.Context) ([]multiaddr.Multiaddr, error) { + var out struct { + Strings []string + } + + if err := api.core().Request("swarm/addrs/listen").Exec(ctx, &out); err != nil { + return nil, err + } + + res := make([]multiaddr.Multiaddr, len(out.Strings)) + for i, addr := range out.Strings { + ma, err := multiaddr.NewMultiaddr(addr) + if err != nil { + return nil, err + } + res[i] = ma + } + return res, nil +} + +func (api *SwarmAPI) core() *HttpApi { + return (*HttpApi)(api) +} diff --git a/client/rpc/unixfs.go b/client/rpc/unixfs.go new file mode 100644 index 000000000..3ba2c1c15 --- /dev/null +++ b/client/rpc/unixfs.go @@ -0,0 +1,243 @@ +package rpc + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "os" + "time" + + "github.com/ipfs/boxo/files" + unixfs "github.com/ipfs/boxo/ipld/unixfs" + unixfs_pb "github.com/ipfs/boxo/ipld/unixfs/pb" + "github.com/ipfs/boxo/path" + "github.com/ipfs/go-cid" + iface "github.com/ipfs/kubo/core/coreiface" + caopts "github.com/ipfs/kubo/core/coreiface/options" + mh "github.com/multiformats/go-multihash" +) + +type addEvent struct { + Name string + Hash string `json:",omitempty"` + Bytes int64 `json:",omitempty"` + Size string `json:",omitempty"` +} + +type UnixfsAPI HttpApi + +func (api *UnixfsAPI) Add(ctx context.Context, f files.Node, opts ...caopts.UnixfsAddOption) (path.ImmutablePath, error) { + options, _, err := caopts.UnixfsAddOptions(opts...) + if err != nil { + return path.ImmutablePath{}, err + } + + mht, ok := mh.Codes[options.MhType] + if !ok { + return path.ImmutablePath{}, fmt.Errorf("unknowm mhType %d", options.MhType) + } + + req := api.core().Request("add"). + Option("hash", mht). + Option("chunker", options.Chunker). + Option("cid-version", options.CidVersion). + Option("fscache", options.FsCache). + Option("inline", options.Inline). + Option("inline-limit", options.InlineLimit). + Option("nocopy", options.NoCopy). + Option("only-hash", options.OnlyHash). + Option("pin", options.Pin). + Option("silent", options.Silent). + Option("progress", options.Progress) + + if options.RawLeavesSet { + req.Option("raw-leaves", options.RawLeaves) + } + + switch options.Layout { + case caopts.BalancedLayout: + // noop, default + case caopts.TrickleLayout: + req.Option("trickle", true) + } + + d := files.NewMapDirectory(map[string]files.Node{"": f}) // unwrapped on the other side + + version, err := api.core().loadRemoteVersion() + if err != nil { + return path.ImmutablePath{}, err + } + useEncodedAbsPaths := version.LT(encodedAbsolutePathVersion) + req.Body(files.NewMultiFileReader(d, false, useEncodedAbsPaths)) + + var out addEvent + resp, err := req.Send(ctx) + if err != nil { + return path.ImmutablePath{}, err + } + if resp.Error != nil { + return path.ImmutablePath{}, resp.Error + } + defer resp.Output.Close() + dec := json.NewDecoder(resp.Output) + + for { + var evt addEvent + if err := dec.Decode(&evt); err != nil { + if errors.Is(err, io.EOF) { + break + } + return path.ImmutablePath{}, err + } + out = evt + + if options.Events != nil { + ifevt := &iface.AddEvent{ + Name: out.Name, + Size: out.Size, + Bytes: out.Bytes, + } + + if out.Hash != "" { + c, err := cid.Parse(out.Hash) + if err != nil { + return path.ImmutablePath{}, err + } + + ifevt.Path = path.FromCid(c) + } + + select { + case options.Events <- ifevt: + case <-ctx.Done(): + return path.ImmutablePath{}, ctx.Err() + } + } + } + + c, err := cid.Parse(out.Hash) + if err != nil { + return path.ImmutablePath{}, err + } + + return path.FromCid(c), nil +} + +type lsLink struct { + Name, Hash string + Size uint64 + Type unixfs_pb.Data_DataType + Target string + + Mode os.FileMode + ModTime time.Time +} + +type lsObject struct { + Hash string + Links []lsLink +} + +type lsOutput struct { + Objects []lsObject +} + +func (api *UnixfsAPI) Ls(ctx context.Context, p path.Path, opts ...caopts.UnixfsLsOption) (<-chan iface.DirEntry, error) { + options, err := caopts.UnixfsLsOptions(opts...) + if err != nil { + return nil, err + } + + resp, err := api.core().Request("ls", p.String()). + Option("resolve-type", options.ResolveChildren). + Option("size", options.ResolveChildren). + Option("stream", true). + Send(ctx) + if err != nil { + return nil, err + } + if resp.Error != nil { + return nil, resp.Error + } + + dec := json.NewDecoder(resp.Output) + out := make(chan iface.DirEntry) + + go func() { + defer resp.Close() + defer close(out) + + for { + var link lsOutput + if err := dec.Decode(&link); err != nil { + if err == io.EOF { + return + } + select { + case out <- iface.DirEntry{Err: err}: + case <-ctx.Done(): + } + return + } + + if len(link.Objects) != 1 { + select { + case out <- iface.DirEntry{Err: errors.New("unexpected Objects len")}: + case <-ctx.Done(): + } + return + } + + if len(link.Objects[0].Links) != 1 { + select { + case out <- iface.DirEntry{Err: errors.New("unexpected Links len")}: + case <-ctx.Done(): + } + return + } + + l0 := link.Objects[0].Links[0] + + c, err := cid.Decode(l0.Hash) + if err != nil { + select { + case out <- iface.DirEntry{Err: err}: + case <-ctx.Done(): + } + return + } + + var ftype iface.FileType + switch l0.Type { + case unixfs.TRaw, unixfs.TFile: + ftype = iface.TFile + case unixfs.THAMTShard, unixfs.TDirectory, unixfs.TMetadata: + ftype = iface.TDirectory + case unixfs.TSymlink: + ftype = iface.TSymlink + } + + select { + case out <- iface.DirEntry{ + Name: l0.Name, + Cid: c, + Size: l0.Size, + Type: ftype, + Target: l0.Target, + + Mode: l0.Mode, + ModTime: l0.ModTime, + }: + case <-ctx.Done(): + } + } + }() + + return out, nil +} + +func (api *UnixfsAPI) core() *HttpApi { + return (*HttpApi)(api) +} diff --git a/cmd/ipfs/dist/README.md b/cmd/ipfs/dist/README.md index f541482ca..7ff65e9f2 100644 --- a/cmd/ipfs/dist/README.md +++ b/cmd/ipfs/dist/README.md @@ -1,6 +1,7 @@ -# ipfs commandline tool +# ipfs command line tool -This is the [ipfs](http://ipfs.io) commandline tool. It contains a full ipfs node. +This is a [command line tool for interacting with Kubo](https://docs.ipfs.tech/install/command-line/), +an [IPFS](https://ipfs.tech) implementation. It contains a full IPFS node. ## Install diff --git a/cmd/ipfs/add_migrations.go b/cmd/ipfs/kubo/add_migrations.go similarity index 92% rename from cmd/ipfs/add_migrations.go rename to cmd/ipfs/kubo/add_migrations.go index 6d62b4d6b..557a8de84 100644 --- a/cmd/ipfs/add_migrations.go +++ b/cmd/ipfs/kubo/add_migrations.go @@ -1,4 +1,4 @@ -package main +package kubo import ( "context" @@ -8,18 +8,18 @@ import ( "os" "path/filepath" - files "github.com/ipfs/go-ipfs-files" - coreiface "github.com/ipfs/interface-go-ipfs-core" - "github.com/ipfs/interface-go-ipfs-core/options" - ipath "github.com/ipfs/interface-go-ipfs-core/path" + "github.com/ipfs/boxo/files" + "github.com/ipfs/boxo/path" "github.com/ipfs/kubo/core" "github.com/ipfs/kubo/core/coreapi" + coreiface "github.com/ipfs/kubo/core/coreiface" + "github.com/ipfs/kubo/core/coreiface/options" "github.com/ipfs/kubo/repo/fsrepo/migrations" "github.com/ipfs/kubo/repo/fsrepo/migrations/ipfsfetcher" "github.com/libp2p/go-libp2p/core/peer" ) -// addMigrations adds any migration downloaded by the fetcher to the IPFS node +// addMigrations adds any migration downloaded by the fetcher to the IPFS node. func addMigrations(ctx context.Context, node *core.IpfsNode, fetcher migrations.Fetcher, pin bool) error { var fetchers []migrations.Fetcher if mf, ok := fetcher.(*migrations.MultiFetcher); ok { @@ -63,7 +63,7 @@ func addMigrations(ctx context.Context, node *core.IpfsNode, fetcher migrations. return nil } -// addMigrationFiles adds the files at paths to IPFS, optionally pinning them +// addMigrationFiles adds the files at paths to IPFS, optionally pinning them. func addMigrationFiles(ctx context.Context, node *core.IpfsNode, paths []string, pin bool) error { if len(paths) == 0 { return nil @@ -98,7 +98,7 @@ func addMigrationFiles(ctx context.Context, node *core.IpfsNode, paths []string, // addMigrationPaths adds the files at paths to IPFS, optionally pinning // them. This is done after connecting to the peer. -func addMigrationPaths(ctx context.Context, node *core.IpfsNode, peerInfo peer.AddrInfo, paths []ipath.Path, pin bool) error { +func addMigrationPaths(ctx context.Context, node *core.IpfsNode, peerInfo peer.AddrInfo, paths []path.Path, pin bool) error { if len(paths) == 0 { return errors.New("nothing downloaded by ipfs fetcher") } @@ -142,7 +142,7 @@ func addMigrationPaths(ctx context.Context, node *core.IpfsNode, peerInfo peer.A return nil } -func ipfsGet(ctx context.Context, ufs coreiface.UnixfsAPI, ipfsPath ipath.Path) error { +func ipfsGet(ctx context.Context, ufs coreiface.UnixfsAPI, ipfsPath path.Path) error { nd, err := ufs.Get(ctx, ipfsPath) if err != nil { return err diff --git a/cmd/ipfs/daemon.go b/cmd/ipfs/kubo/daemon.go similarity index 70% rename from cmd/ipfs/daemon.go rename to cmd/ipfs/kubo/daemon.go index af105589d..956057fe6 100644 --- a/cmd/ipfs/daemon.go +++ b/cmd/ipfs/kubo/daemon.go @@ -1,20 +1,26 @@ -package main +package kubo import ( + "context" "errors" _ "expvar" "fmt" + "math" "net" "net/http" _ "net/http/pprof" "os" + "regexp" "runtime" "sort" + "strings" "sync" "time" multierror "github.com/hashicorp/go-multierror" + cmds "github.com/ipfs/go-ipfs-cmds" + mprome "github.com/ipfs/go-metrics-prometheus" version "github.com/ipfs/kubo" utilmain "github.com/ipfs/kubo/cmd/ipfs/util" oldcmds "github.com/ipfs/kubo/commands" @@ -24,19 +30,19 @@ import ( commands "github.com/ipfs/kubo/core/commands" "github.com/ipfs/kubo/core/coreapi" corehttp "github.com/ipfs/kubo/core/corehttp" + options "github.com/ipfs/kubo/core/coreiface/options" corerepo "github.com/ipfs/kubo/core/corerepo" libp2p "github.com/ipfs/kubo/core/node/libp2p" nodeMount "github.com/ipfs/kubo/fuse/node" fsrepo "github.com/ipfs/kubo/repo/fsrepo" "github.com/ipfs/kubo/repo/fsrepo/migrations" "github.com/ipfs/kubo/repo/fsrepo/migrations/ipfsfetcher" - pnet "github.com/libp2p/go-libp2p/core/pnet" - sockets "github.com/libp2p/go-socket-activation" - - cmds "github.com/ipfs/go-ipfs-cmds" - mprome "github.com/ipfs/go-metrics-prometheus" - options "github.com/ipfs/interface-go-ipfs-core/options" goprocess "github.com/jbenet/goprocess" + p2pcrypto "github.com/libp2p/go-libp2p/core/crypto" + pnet "github.com/libp2p/go-libp2p/core/pnet" + "github.com/libp2p/go-libp2p/core/protocol" + p2phttp "github.com/libp2p/go-libp2p/p2p/http" + sockets "github.com/libp2p/go-socket-activation" ma "github.com/multiformats/go-multiaddr" manet "github.com/multiformats/go-multiaddr/net" prometheus "github.com/prometheus/client_golang/prometheus" @@ -44,34 +50,34 @@ import ( ) const ( - adjustFDLimitKwd = "manage-fdlimit" - enableGCKwd = "enable-gc" - initOptionKwd = "init" - initConfigOptionKwd = "init-config" - initProfileOptionKwd = "init-profile" - ipfsMountKwd = "mount-ipfs" - ipnsMountKwd = "mount-ipns" - migrateKwd = "migrate" - mountKwd = "mount" - offlineKwd = "offline" // global option - routingOptionKwd = "routing" - routingOptionSupernodeKwd = "supernode" - routingOptionDHTClientKwd = "dhtclient" - routingOptionDHTKwd = "dht" - routingOptionDHTServerKwd = "dhtserver" - routingOptionNoneKwd = "none" - routingOptionCustomKwd = "custom" - routingOptionDefaultKwd = "default" - routingOptionAutoKwd = "auto" - unencryptTransportKwd = "disable-transport-encryption" - unrestrictedAPIAccessKwd = "unrestricted-api" - writableKwd = "writable" - enablePubSubKwd = "enable-pubsub-experiment" - enableIPNSPubSubKwd = "enable-namesys-pubsub" - enableMultiplexKwd = "enable-mplex-experiment" - agentVersionSuffix = "agent-version-suffix" + adjustFDLimitKwd = "manage-fdlimit" + enableGCKwd = "enable-gc" + initOptionKwd = "init" + initConfigOptionKwd = "init-config" + initProfileOptionKwd = "init-profile" + ipfsMountKwd = "mount-ipfs" + ipnsMountKwd = "mount-ipns" + migrateKwd = "migrate" + mountKwd = "mount" + offlineKwd = "offline" // global option + routingOptionKwd = "routing" + routingOptionSupernodeKwd = "supernode" + routingOptionDHTClientKwd = "dhtclient" + routingOptionDHTKwd = "dht" + routingOptionDHTServerKwd = "dhtserver" + routingOptionNoneKwd = "none" + routingOptionCustomKwd = "custom" + routingOptionDefaultKwd = "default" + routingOptionAutoKwd = "auto" + routingOptionAutoClientKwd = "autoclient" + unencryptTransportKwd = "disable-transport-encryption" + unrestrictedAPIAccessKwd = "unrestricted-api" + enablePubSubKwd = "enable-pubsub-experiment" + enableIPNSPubSubKwd = "enable-namesys-pubsub" + enableMultiplexKwd = "enable-mplex-experiment" + agentVersionSuffix = "agent-version-suffix" // apiAddrKwd = "address-api" - // swarmAddrKwd = "address-swarm" + // swarmAddrKwd = "address-swarm". ) var daemonCmd = &cmds.Command{ @@ -85,7 +91,7 @@ running, calls to 'ipfs' commands will be sent over the network to the daemon. `, LongDescription: ` -The daemon will start listening on ports on the network, which are +The Kubo daemon will start listening on ports on the network, which are documented in (and can be modified through) 'ipfs config Addresses'. For example, to change the 'Gateway' port: @@ -105,11 +111,16 @@ other computers in the network, use 0.0.0.0 as the ip address: Be careful if you expose the RPC API. It is a security risk, as anyone could control your node remotely. If you need to control the node remotely, make sure to protect the port as you would other services or database -(firewall, authenticated proxy, etc). +(firewall, authenticated proxy, etc), or at least set API.Authorizations. + +If you do not want to open any ports for RPC, and only want to use +kubo CLI client, it is possible to expose the RPC over Unix socket: + + ipfs config Addresses.API /unix/var/run/kubo.socket HTTP Headers -ipfs supports passing arbitrary headers to the RPC API and Gateway. You can +Kubo supports passing arbitrary headers to the RPC API and Gateway. You can do this by setting headers on the API.HTTPHeaders and Gateway.HTTPHeaders keys: @@ -120,7 +131,7 @@ Note that the value of the keys is an _array_ of strings. This is because headers can have more than one value, and it is convenient to pass through to other libraries. -CORS Headers (for API) +CORS Headers (for RPC API) You can setup CORS headers the same way: @@ -137,7 +148,7 @@ second signal. IPFS_PATH environment variable -ipfs uses a repository in the local file system. By default, the repo is +Kubo uses a repository in the local file system. By default, the repo is located at ~/.ipfs. To change the repo location, set the $IPFS_PATH environment variable: @@ -145,7 +156,7 @@ environment variable: DEPRECATION NOTICE -Previously, ipfs used an environment variable as seen below: +Previously, Kubo used an environment variable as seen below: export API_ORIGIN="http://localhost:8888/" @@ -156,23 +167,22 @@ Headers. }, Options: []cmds.Option{ - cmds.BoolOption(initOptionKwd, "Initialize ipfs with default settings if not already initialized"), + cmds.BoolOption(initOptionKwd, "Initialize Kubo with default settings if not already initialized"), cmds.StringOption(initConfigOptionKwd, "Path to existing configuration file to be loaded during --init"), cmds.StringOption(initProfileOptionKwd, "Configuration profiles to apply for --init. See ipfs init --help for more"), cmds.StringOption(routingOptionKwd, "Overrides the routing option").WithDefault(routingOptionDefaultKwd), cmds.BoolOption(mountKwd, "Mounts IPFS to the filesystem using FUSE (experimental)"), - cmds.BoolOption(writableKwd, "Enable writing objects (with POST, PUT and DELETE)"), cmds.StringOption(ipfsMountKwd, "Path to the mountpoint for IPFS (if using --mount). Defaults to config setting."), cmds.StringOption(ipnsMountKwd, "Path to the mountpoint for IPNS (if using --mount). Defaults to config setting."), - cmds.BoolOption(unrestrictedAPIAccessKwd, "Allow API access to unlisted hashes"), + cmds.BoolOption(unrestrictedAPIAccessKwd, "Allow RPC API access to unlisted hashes"), cmds.BoolOption(unencryptTransportKwd, "Disable transport encryption (for debugging protocols)"), cmds.BoolOption(enableGCKwd, "Enable automatic periodic repo garbage collection"), cmds.BoolOption(adjustFDLimitKwd, "Check and raise file descriptor limits if needed").WithDefault(true), cmds.BoolOption(migrateKwd, "If true, assume yes at the migrate prompt. If false, assume no."), - cmds.BoolOption(enablePubSubKwd, "Enable experimental pubsub feature. Overrides Pubsub.Enabled config."), + cmds.BoolOption(enablePubSubKwd, "DEPRECATED"), cmds.BoolOption(enableIPNSPubSubKwd, "Enable IPNS over pubsub. Implicitly enables pubsub, overrides Ipns.UsePubsub config."), cmds.BoolOption(enableMultiplexKwd, "DEPRECATED"), - cmds.StringOption(agentVersionSuffix, "Optional suffix to the AgentVersion presented by `ipfs id` and also advertised through BitSwap."), + cmds.StringOption(agentVersionSuffix, "Optional suffix to the AgentVersion presented by `ipfs id` and exposed via libp2p identify protocol."), // TODO: add way to override addresses. tricky part: updating the config if also --init. // cmds.StringOption(apiAddrKwd, "Address for the daemon rpc API (overrides config)"), @@ -370,6 +380,8 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment return err } + fmt.Printf("PeerID: %s\n", cfg.Identity.PeerID) + if !psSet { pubsub = cfg.Pubsub.Enabled.WithDefault(false) } @@ -387,7 +399,7 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment "pubsub": pubsub, "ipnsps": ipnsps, }, - //TODO(Kubuxu): refactor Online vs Offline by adding Permanent vs Ephemeral + // TODO(Kubuxu): refactor Online vs Offline by adding Permanent vs Ephemeral } routingOption, _ := req.Options[routingOptionKwd].(string) @@ -411,11 +423,9 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment case routingOptionSupernodeKwd: return errors.New("supernode routing was never fully implemented and has been removed") case routingOptionDefaultKwd, routingOptionAutoKwd: - ncfg.Routing = libp2p.ConstructDefaultRouting( - cfg.Identity.PeerID, - cfg.Addresses.Swarm, - cfg.Identity.PrivKey, - ) + ncfg.Routing = libp2p.ConstructDefaultRouting(cfg, libp2p.DHTOption) + case routingOptionAutoClientKwd: + ncfg.Routing = libp2p.ConstructDefaultRouting(cfg, libp2p.DHTClientOption) case routingOptionDHTClientKwd: ncfg.Routing = libp2p.DHTClientOption case routingOptionDHTKwd: @@ -425,20 +435,25 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment case routingOptionNoneKwd: ncfg.Routing = libp2p.NilRouterOption case routingOptionCustomKwd: + if cfg.Routing.AcceleratedDHTClient.WithDefault(config.DefaultAcceleratedDHTClient) { + return fmt.Errorf("Routing.AcceleratedDHTClient option is set even tho Routing.Type is custom, using custom .AcceleratedDHTClient needs to be set on DHT routers individually") + } ncfg.Routing = libp2p.ConstructDelegatedRouting( cfg.Routing.Routers, cfg.Routing.Methods, cfg.Identity.PeerID, - cfg.Addresses.Swarm, + cfg.Addresses, cfg.Identity.PrivKey, ) default: return fmt.Errorf("unrecognized routing option: %s", routingOption) } - agentVersionSuffixString, _ := req.Options[agentVersionSuffix].(string) - if agentVersionSuffixString != "" { - version.SetUserAgentSuffix(agentVersionSuffixString) + // Set optional agent version suffix + versionSuffixFromCli, _ := req.Options[agentVersionSuffix].(string) + versionSuffix := cfg.Version.AgentSuffix.WithDefault(versionSuffixFromCli) + if versionSuffix != "" { + version.SetUserAgentSuffix(versionSuffix) } node, err := core.NewNode(req.Context, ncfg) @@ -457,7 +472,23 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment log.Fatal("Private network does not work with Routing.Type=auto. Update your config to Routing.Type=dht (or none, and do manual peering)") } - printSwarmAddrs(node) + printLibp2pPorts(node) + + if node.PrivateKey.Type() == p2pcrypto.RSA { + fmt.Print(` +Warning: You are using an RSA Peer ID, which was replaced by Ed25519 +as the default recommended in Kubo since September 2020. Signing with +RSA Peer IDs is more CPU-intensive than with other key types. +It is recommended that you change your public key type to ed25519 +by using the following command: + + ipfs key rotate -o rsa-key-backup -t ed25519 + +After changing your key type, restart your node for the changes to +take effect. + +`) + } defer func() { // We wait for the node to close first, as the node has children @@ -532,10 +563,16 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment return err } + // add trustless gateway over libp2p + p2pGwErrc, err := serveTrustlessGatewayOverLibp2p(cctx) + if err != nil { + return err + } + // Add ipfs version info to prometheus metrics - var ipfsInfoMetric = promauto.NewGaugeVec(prometheus.GaugeOpts{ + ipfsInfoMetric := promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "ipfs_info", - Help: "IPFS version information.", + Help: "Kubo IPFS version information.", }, []string{"version", "commit"}) // Setting to 1 lets us multiply it with other stats to add the version labels @@ -549,7 +586,7 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment prometheus.MustRegister(&corehttp.IpfsNodeCollector{Node: node}) // start MFS pinning thread - startPinMFS(daemonConfigPollInterval, cctx, &ipfsPinMFSNode{node}) + startPinMFS(cctx, daemonConfigPollInterval, &ipfsPinMFSNode{node}) // The daemon is *finally* ready. fmt.Printf("Daemon is ready\n") @@ -563,12 +600,30 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment fmt.Println("(Hit ctrl-c again to force-shutdown the daemon.)") }() - // Give the user heads up if daemon running in online mode has no peers after 1 minute if !offline { + // Warn users who were victims of 'lowprofile' footgun (https://github.com/ipfs/kubo/pull/10524) + if cfg.Experimental.StrategicProviding { + fmt.Print(` +⚠️ Reprovide system is disabled due to 'Experimental.StrategicProviding=true' +⚠️ Local CIDs will not be announced to Amino DHT, making them impossible to retrieve without manual peering +⚠️ If this is not intentional, call 'ipfs config profile apply announce-on' + +`) + } else if cfg.Reprovider.Interval.WithDefault(config.DefaultReproviderInterval) == 0 { + fmt.Print(` +⚠️ Reprovider system is disabled due to 'Reprovider.Interval=0' +⚠️ Local CIDs will not be announced to Amino DHT, making them impossible to retrieve without manual peering +⚠️ If this is not intentional, call 'ipfs config profile apply announce-on', or set 'Reprovider.Interval=22h' + +`) + } + + // Give the user heads up if daemon running in online mode has no peers after 1 minute time.AfterFunc(1*time.Minute, func() { cfg, err := cctx.GetConfig() if err != nil { log.Errorf("failed to access config: %s", err) + return } if len(cfg.Bootstrap) == 0 && len(cfg.Peering.Peers) == 0 { // Skip peer check if Bootstrap and Peering lists are empty @@ -579,16 +634,26 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment ipfs, err := coreapi.NewCoreAPI(node) if err != nil { log.Errorf("failed to access CoreAPI: %v", err) + return } peers, err := ipfs.Swarm().Peers(cctx.Context()) if err != nil { log.Errorf("failed to read swarm peers: %v", err) + return } if len(peers) == 0 { log.Error("failed to bootstrap (no peers found): consider updating Bootstrap or Peering section of your config") + } else { + // After 1 minute we should have enough peers + // to run informed version check + startVersionChecker( + cctx.Context(), + node, + cfg.Version.SwarmCheckEnabled.WithDefault(true), + cfg.Version.SwarmCheckPercentThreshold.WithDefault(config.DefaultSwarmCheckPercentThreshold), + ) } }) - } // Hard deprecation notice if someone still uses IPFS_REUSEPORT @@ -599,7 +664,7 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment // collect long-running errors and block for shutdown // TODO(cryptix): our fuse currently doesn't follow this pattern for graceful shutdown var errs error - for err := range merge(apiErrc, gwErrc, gcErrc) { + for err := range merge(apiErrc, gwErrc, gcErrc, p2pGwErrc) { if err != nil { errs = multierror.Append(errs, err) } @@ -608,7 +673,7 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment return errs } -// serveHTTPApi collects options, creates listener, prints status message and starts serving requests +// serveHTTPApi collects options, creates listener, prints status message and starts serving requests. func serveHTTPApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, error) { cfg, err := cctx.GetConfig() if err != nil { @@ -651,13 +716,25 @@ func serveHTTPApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, error listeners = append(listeners, apiLis) } + if len(cfg.API.Authorizations) > 0 && len(listeners) > 0 { + fmt.Printf("RPC API access is limited by the rules defined in API.Authorizations\n") + } + for _, listener := range listeners { // we might have listened to /tcp/0 - let's see what we are listing on - fmt.Printf("API server listening on %s\n", listener.Multiaddr()) - // Browsers require TCP. + fmt.Printf("RPC API server listening on %s\n", listener.Multiaddr()) + // Browsers require TCP with explicit host. switch listener.Addr().Network() { case "tcp", "tcp4", "tcp6": - fmt.Printf("WebUI: http://%s/webui\n", listener.Addr()) + rpc := listener.Addr().String() + // replace catch-all with explicit localhost URL that works in browsers + // https://github.com/ipfs/kubo/issues/10515 + if strings.Contains(rpc, "0.0.0.0:") { + rpc = strings.Replace(rpc, "0.0.0.0:", "127.0.0.1:", 1) + } else if strings.Contains(rpc, "[::]:") { + rpc = strings.Replace(rpc, "[::]:", "[::1]:", 1) + } + fmt.Printf("WebUI: http://%s/webui\n", rpc) } } @@ -666,12 +743,12 @@ func serveHTTPApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, error // only the webui objects are allowed. // if you know what you're doing, go ahead and pass --unrestricted-api. unrestricted, _ := req.Options[unrestrictedAPIAccessKwd].(bool) - gatewayOpt := corehttp.GatewayOption(false, corehttp.WebUIPaths...) + gatewayOpt := corehttp.GatewayOption(corehttp.WebUIPaths...) if unrestricted { - gatewayOpt = corehttp.GatewayOption(true, "/ipfs", "/ipns") + gatewayOpt = corehttp.GatewayOption("/ipfs", "/ipns") } - var opts = []corehttp.ServeOption{ + opts := []corehttp.ServeOption{ corehttp.MetricsCollectionOption("api"), corehttp.MetricsOpenCensusCollectionOption(), corehttp.MetricsOpenCensusDefaultPrometheusRegistry(), @@ -698,8 +775,11 @@ func serveHTTPApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, error return nil, fmt.Errorf("serveHTTPApi: ConstructNode() failed: %s", err) } - if err := node.Repo.SetAPIAddr(rewriteMaddrToUseLocalhostIfItsAny(listeners[0].Multiaddr())); err != nil { - return nil, fmt.Errorf("serveHTTPApi: SetAPIAddr() failed: %w", err) + if len(listeners) > 0 { + // Only add an api file if the API is running. + if err := node.Repo.SetAPIAddr(rewriteMaddrToUseLocalhostIfItsAny(listeners[0].Multiaddr())); err != nil { + return nil, fmt.Errorf("serveHTTPApi: SetAPIAddr() failed: %w", err) + } } errc := make(chan error) @@ -733,8 +813,8 @@ func rewriteMaddrToUseLocalhostIfItsAny(maddr ma.Multiaddr) ma.Multiaddr { } } -// printSwarmAddrs prints the addresses of the host -func printSwarmAddrs(node *core.IpfsNode) { +// printLibp2pPorts prints which ports are opened to facilitate swarm connectivity. +func printLibp2pPorts(node *core.IpfsNode) { if !node.IsOnline { fmt.Println("Swarm not listening, running in offline mode.") return @@ -744,39 +824,58 @@ func printSwarmAddrs(node *core.IpfsNode) { if err != nil { log.Errorf("failed to read listening addresses: %s", err) } - lisAddrs := make([]string, len(ifaceAddrs)) - for i, addr := range ifaceAddrs { - lisAddrs[i] = addr.String() - } - sort.Strings(lisAddrs) - for _, addr := range lisAddrs { - fmt.Printf("Swarm listening on %s\n", addr) + + // Multiple libp2p transports can use same port. + // Deduplicate all listeners and collect unique IP:port (udp|tcp) combinations + // which is useful information for operator deploying Kubo in TCP/IP infra. + addrMap := make(map[string]map[string]struct{}) + re := regexp.MustCompile(`^/(?:ip[46]|dns(?:[46])?)/([^/]+)/(tcp|udp)/(\d+)(/.*)?$`) + for _, addr := range ifaceAddrs { + matches := re.FindStringSubmatch(addr.String()) + if matches != nil { + hostname := matches[1] + protocol := strings.ToUpper(matches[2]) + port := matches[3] + var host string + if matches[0][:4] == "/ip6" { + host = fmt.Sprintf("[%s]:%s", hostname, port) + } else { + host = fmt.Sprintf("%s:%s", hostname, port) + } + if _, ok := addrMap[host]; !ok { + addrMap[host] = make(map[string]struct{}) + } + addrMap[host][protocol] = struct{}{} + } } - nodePhostAddrs := node.PeerHost.Addrs() - addrs := make([]string, len(nodePhostAddrs)) - for i, addr := range nodePhostAddrs { - addrs[i] = addr.String() - } - sort.Strings(addrs) - for _, addr := range addrs { - fmt.Printf("Swarm announcing %s\n", addr) + // Produce a sorted host:port list + hosts := make([]string, 0, len(addrMap)) + for host := range addrMap { + hosts = append(hosts, host) } + sort.Strings(hosts) + // Print listeners + for _, host := range hosts { + protocolsSet := addrMap[host] + protocols := make([]string, 0, len(protocolsSet)) + for protocol := range protocolsSet { + protocols = append(protocols, protocol) + } + sort.Strings(protocols) + fmt.Printf("Swarm listening on %s (%s)\n", host, strings.Join(protocols, "+")) + } + fmt.Printf("Run 'ipfs id' to inspect announced and discovered multiaddrs of this node.\n") } -// serveHTTPGateway collects options, creates listener, prints status message and starts serving requests +// serveHTTPGateway collects options, creates listener, prints status message and starts serving requests. func serveHTTPGateway(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, error) { cfg, err := cctx.GetConfig() if err != nil { return nil, fmt.Errorf("serveHTTPGateway: GetConfig() failed: %s", err) } - writable, writableOptionFound := req.Options[writableKwd].(bool) - if !writableOptionFound { - writable = cfg.Gateway.Writable - } - listeners, err := sockets.TakeListeners("io.ipfs.gateway") if err != nil { return nil, fmt.Errorf("serveHTTPGateway: socket activation failed: %s", err) @@ -807,37 +906,37 @@ func serveHTTPGateway(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, e } // we might have listened to /tcp/0 - let's see what we are listing on - gwType := "readonly" - if writable { - gwType = "writable" + for _, listener := range listeners { + fmt.Printf("Gateway server listening on %s\n", listener.Multiaddr()) } - for _, listener := range listeners { - fmt.Printf("Gateway (%s) server listening on %s\n", gwType, listener.Multiaddr()) + if cfg.Gateway.ExposeRoutingAPI.WithDefault(config.DefaultExposeRoutingAPI) { + for _, listener := range listeners { + fmt.Printf("Routing V1 API exposed at http://%s/routing/v1\n", listener.Addr()) + } } cmdctx := *cctx cmdctx.Gateway = true - var opts = []corehttp.ServeOption{ + opts := []corehttp.ServeOption{ corehttp.MetricsCollectionOption("gateway"), corehttp.HostnameOption(), - corehttp.GatewayOption(writable, "/ipfs", "/ipns"), + corehttp.GatewayOption("/ipfs", "/ipns"), corehttp.VersionOption(), corehttp.CheckVersionOption(), - corehttp.CommandsROOption(cmdctx), } if cfg.Experimental.P2pHttpProxy { opts = append(opts, corehttp.P2PProxyOption()) } - if len(cfg.Gateway.RootRedirect) > 0 { - opts = append(opts, corehttp.RedirectOption("", cfg.Gateway.RootRedirect)) + if cfg.Gateway.ExposeRoutingAPI.WithDefault(config.DefaultExposeRoutingAPI) { + opts = append(opts, corehttp.RoutingOption()) } - if len(cfg.Gateway.PathPrefixes) > 0 { - log.Fatal("Support for custom Gateway.PathPrefixes was removed: https://github.com/ipfs/go-ipfs/issues/7702") + if len(cfg.Gateway.RootRedirect) > 0 { + opts = append(opts, corehttp.RedirectOption("", cfg.Gateway.RootRedirect)) } node, err := cctx.ConstructNode() @@ -873,7 +972,58 @@ func serveHTTPGateway(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, e return errc, nil } -// collects options and opens the fuse mountpoint +const gatewayProtocolID protocol.ID = "/ipfs/gateway" // FIXME: specify https://github.com/ipfs/specs/issues/433 + +func serveTrustlessGatewayOverLibp2p(cctx *oldcmds.Context) (<-chan error, error) { + node, err := cctx.ConstructNode() + if err != nil { + return nil, fmt.Errorf("serveHTTPGatewayOverLibp2p: ConstructNode() failed: %s", err) + } + cfg, err := node.Repo.Config() + if err != nil { + return nil, fmt.Errorf("could not read config: %w", err) + } + + if !cfg.Experimental.GatewayOverLibp2p { + errCh := make(chan error) + close(errCh) + return errCh, nil + } + + opts := []corehttp.ServeOption{ + corehttp.MetricsCollectionOption("libp2p-gateway"), + corehttp.Libp2pGatewayOption(), + corehttp.VersionOption(), + } + + handler, err := corehttp.MakeHandler(node, nil, opts...) + if err != nil { + return nil, err + } + + h := p2phttp.Host{ + StreamHost: node.PeerHost, + } + + h.WellKnownHandler.AddProtocolMeta(gatewayProtocolID, p2phttp.ProtocolMeta{Path: "/"}) + h.ServeMux = http.NewServeMux() + h.ServeMux.Handle("/", handler) + + errc := make(chan error, 1) + go func() { + defer close(errc) + errc <- h.Serve() + }() + + go func() { + <-node.Process.Closing() + h.Close() + }() + + return errc, nil +} + +// collects options and opens the fuse mountpoint. func mountFuse(req *cmds.Request, cctx *oldcmds.Context) error { cfg, err := cctx.GetConfig() if err != nil { @@ -952,7 +1102,11 @@ func YesNoPrompt(prompt string) bool { var s string for i := 0; i < 3; i++ { fmt.Printf("%s ", prompt) - fmt.Scanf("%s", &s) + _, err := fmt.Scanf("%s", &s) + if err != nil { + fmt.Printf("Invalid input: %v. Please try again.\n", err) + continue + } switch s { case "y", "Y": return true @@ -977,3 +1131,41 @@ func printVersion() { fmt.Printf("System version: %s\n", runtime.GOARCH+"/"+runtime.GOOS) fmt.Printf("Golang version: %s\n", runtime.Version()) } + +func startVersionChecker(ctx context.Context, nd *core.IpfsNode, enabled bool, percentThreshold int64) { + if !enabled { + return + } + ticker := time.NewTicker(time.Hour) + defer ticker.Stop() + go func() { + for { + o, err := commands.DetectNewKuboVersion(nd, percentThreshold) + if err != nil { + // The version check is best-effort, and may fail in custom + // configurations that do not run standard WAN DHT. If it + // errors here, no point in spamming logs: og once and exit. + log.Errorw("initial version check failed, will not be run again", "error", err) + return + } + if o.UpdateAvailable { + newerPercent := fmt.Sprintf("%.0f%%", math.Round(float64(o.WithGreaterVersion)/float64(o.PeersSampled)*100)) + log.Errorf(` +⚠️ A NEW VERSION OF KUBO DETECTED + +This Kubo node is running an outdated version (%s). +%s of the sampled Kubo peers are running a higher version. +Visit https://github.com/ipfs/kubo/releases or https://dist.ipfs.tech/#kubo and update to version %s or later.`, + o.RunningVersion, newerPercent, o.GreatestVersion) + } + select { + case <-ctx.Done(): + return + case <-nd.Process.Closing(): + return + case <-ticker.C: + continue + } + } + }() +} diff --git a/cmd/ipfs/daemon_linux.go b/cmd/ipfs/kubo/daemon_linux.go similarity index 95% rename from cmd/ipfs/daemon_linux.go rename to cmd/ipfs/kubo/daemon_linux.go index d06baf286..b612738a2 100644 --- a/cmd/ipfs/daemon_linux.go +++ b/cmd/ipfs/kubo/daemon_linux.go @@ -1,7 +1,7 @@ //go:build linux // +build linux -package main +package kubo import ( daemon "github.com/coreos/go-systemd/v22/daemon" diff --git a/cmd/ipfs/daemon_other.go b/cmd/ipfs/kubo/daemon_other.go similarity index 86% rename from cmd/ipfs/daemon_other.go rename to cmd/ipfs/kubo/daemon_other.go index cb96ce1b9..c5b24053d 100644 --- a/cmd/ipfs/daemon_other.go +++ b/cmd/ipfs/kubo/daemon_other.go @@ -1,7 +1,7 @@ //go:build !linux // +build !linux -package main +package kubo func notifyReady() {} diff --git a/cmd/ipfs/debug.go b/cmd/ipfs/kubo/debug.go similarity index 94% rename from cmd/ipfs/debug.go rename to cmd/ipfs/kubo/debug.go index f1b2683d1..ce07ca8e9 100644 --- a/cmd/ipfs/debug.go +++ b/cmd/ipfs/kubo/debug.go @@ -1,4 +1,4 @@ -package main +package kubo import ( "net/http" diff --git a/cmd/ipfs/dnsresolve_test.go b/cmd/ipfs/kubo/dnsresolve_test.go similarity index 98% rename from cmd/ipfs/dnsresolve_test.go rename to cmd/ipfs/kubo/dnsresolve_test.go index fcba5d697..82e4e62f5 100644 --- a/cmd/ipfs/dnsresolve_test.go +++ b/cmd/ipfs/kubo/dnsresolve_test.go @@ -1,4 +1,4 @@ -package main +package kubo import ( "context" @@ -25,7 +25,8 @@ func makeResolver(t *testing.T, n uint8) *madns.Resolver { backend := &madns.MockResolver{ IP: map[string][]net.IPAddr{ "example.com": results, - }} + }, + } resolver, err := madns.NewResolver(madns.WithDefaultResolver(backend)) if err != nil { diff --git a/cmd/ipfs/init.go b/cmd/ipfs/kubo/init.go similarity index 95% rename from cmd/ipfs/init.go rename to cmd/ipfs/kubo/init.go index 745ceb3e2..986fe90c8 100644 --- a/cmd/ipfs/init.go +++ b/cmd/ipfs/kubo/init.go @@ -1,4 +1,4 @@ -package main +package kubo import ( "context" @@ -10,24 +10,25 @@ import ( "path/filepath" "strings" - path "github.com/ipfs/go-path" - unixfs "github.com/ipfs/go-unixfs" + unixfs "github.com/ipfs/boxo/ipld/unixfs" + "github.com/ipfs/boxo/path" assets "github.com/ipfs/kubo/assets" oldcmds "github.com/ipfs/kubo/commands" core "github.com/ipfs/kubo/core" "github.com/ipfs/kubo/core/commands" fsrepo "github.com/ipfs/kubo/repo/fsrepo" + "github.com/ipfs/boxo/files" cmds "github.com/ipfs/go-ipfs-cmds" - files "github.com/ipfs/go-ipfs-files" - options "github.com/ipfs/interface-go-ipfs-core/options" config "github.com/ipfs/kubo/config" + options "github.com/ipfs/kubo/core/coreiface/options" ) const ( algorithmDefault = options.Ed25519Key algorithmOptionName = "algorithm" bitsOptionName = "bits" + emptyRepoDefault = true emptyRepoOptionName = "empty-repo" profileOptionName = "profile" ) @@ -61,7 +62,7 @@ environment variable: Options: []cmds.Option{ cmds.StringOption(algorithmOptionName, "a", "Cryptographic algorithm to use for key generation.").WithDefault(algorithmDefault), cmds.IntOption(bitsOptionName, "b", "Number of bits to use in the generated RSA private key."), - cmds.BoolOption(emptyRepoOptionName, "e", "Don't add and pin help files to the local storage."), + cmds.BoolOption(emptyRepoOptionName, "e", "Don't add and pin help files to the local storage.").WithDefault(emptyRepoDefault), cmds.StringOption(profileOptionName, "p", "Apply profile settings to config. Multiple profiles can be separated by ','"), // TODO need to decide whether to expose the override as a file or a @@ -193,7 +194,7 @@ func checkWritable(dir string) error { if os.IsNotExist(err) { // dir doesn't exist, check that we can create it - return os.Mkdir(dir, 0775) + return os.Mkdir(dir, 0o775) } if os.IsPermission(err) { @@ -251,7 +252,7 @@ func initializeIpnsKeyspace(repoRoot string) error { // pin recursively because this might already be pinned // and doing a direct pin would throw an error in that case - err = nd.Pinning.Pin(ctx, emptyDir, true) + err = nd.Pinning.Pin(ctx, emptyDir, true, "") if err != nil { return err } diff --git a/cmd/ipfs/ipfs.go b/cmd/ipfs/kubo/ipfs.go similarity index 98% rename from cmd/ipfs/ipfs.go rename to cmd/ipfs/kubo/ipfs.go index 24aea66c7..58310865e 100644 --- a/cmd/ipfs/ipfs.go +++ b/cmd/ipfs/kubo/ipfs.go @@ -1,4 +1,4 @@ -package main +package kubo import ( commands "github.com/ipfs/kubo/core/commands" @@ -14,7 +14,7 @@ var Root = &cmds.Command{ Helptext: commands.Root.Helptext, } -// commandsClientCmd is the "ipfs commands" command for local cli +// commandsClientCmd is the "ipfs commands" command for local cli. var commandsClientCmd = commands.CommandsCmd(Root) // Commands in localCommands should always be run locally (even if daemon is running). diff --git a/cmd/ipfs/pinmfs.go b/cmd/ipfs/kubo/pinmfs.go similarity index 59% rename from cmd/ipfs/pinmfs.go rename to cmd/ipfs/kubo/pinmfs.go index 57f353f1d..c9187145c 100644 --- a/cmd/ipfs/pinmfs.go +++ b/cmd/ipfs/kubo/pinmfs.go @@ -1,23 +1,24 @@ -package main +package kubo import ( "context" "fmt" + "os" "time" "github.com/libp2p/go-libp2p/core/host" peer "github.com/libp2p/go-libp2p/core/peer" + pinclient "github.com/ipfs/boxo/pinning/remote/client" cid "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" - logging "github.com/ipfs/go-log" - pinclient "github.com/ipfs/go-pinning-service-http-client" + logging "github.com/ipfs/go-log/v2" config "github.com/ipfs/kubo/config" "github.com/ipfs/kubo/core" ) -// mfslog is the logger for remote mfs pinning +// mfslog is the logger for remote mfs pinning. var mfslog = logging.Logger("remotepinning/mfs") type lastPin struct { @@ -31,7 +32,20 @@ func (x lastPin) IsValid() bool { return x != lastPin{} } -const daemonConfigPollInterval = time.Minute / 2 +var daemonConfigPollInterval = time.Minute / 2 + +func init() { + // this environment variable is solely for testing, use at your own risk + if pollDurStr := os.Getenv("MFS_PIN_POLL_INTERVAL"); pollDurStr != "" { + d, err := time.ParseDuration(pollDurStr) + if err != nil { + mfslog.Error("error parsing MFS_PIN_POLL_INTERVAL, using default:", err) + return + } + daemonConfigPollInterval = d + } +} + const defaultRepinInterval = 5 * time.Minute type pinMFSContext interface { @@ -61,56 +75,28 @@ func (x *ipfsPinMFSNode) PeerHost() host.Host { return x.node.PeerHost } -func startPinMFS(configPollInterval time.Duration, cctx pinMFSContext, node pinMFSNode) { - errCh := make(chan error) - go pinMFSOnChange(configPollInterval, cctx, node, errCh) - go func() { - for { - select { - case err, isOpen := <-errCh: - if !isOpen { - return - } - mfslog.Errorf("%v", err) - case <-cctx.Context().Done(): - return - } - } - }() +func startPinMFS(cctx pinMFSContext, configPollInterval time.Duration, node pinMFSNode) { + go pinMFSOnChange(cctx, configPollInterval, node) } -func pinMFSOnChange(configPollInterval time.Duration, cctx pinMFSContext, node pinMFSNode, errCh chan<- error) { - defer close(errCh) - - var tmo *time.Timer - defer func() { - if tmo != nil { - tmo.Stop() - } - }() +func pinMFSOnChange(cctx pinMFSContext, configPollInterval time.Duration, node pinMFSNode) { + tmo := time.NewTimer(configPollInterval) + defer tmo.Stop() lastPins := map[string]lastPin{} for { // polling sleep - if tmo == nil { - tmo = time.NewTimer(configPollInterval) - } else { - tmo.Reset(configPollInterval) - } select { case <-cctx.Context().Done(): return case <-tmo.C: + tmo.Reset(configPollInterval) } // reread the config, which may have changed in the meantime cfg, err := cctx.GetConfig() if err != nil { - select { - case errCh <- fmt.Errorf("pinning reading config (%v)", err): - case <-cctx.Context().Done(): - return - } + mfslog.Errorf("pinning reading config (%v)", err) continue } mfslog.Debugf("pinning loop is awake, %d remote services", len(cfg.Pinning.RemoteServices)) @@ -118,30 +104,29 @@ func pinMFSOnChange(configPollInterval time.Duration, cctx pinMFSContext, node p // get the most recent MFS root cid rootNode, err := node.RootNode() if err != nil { - select { - case errCh <- fmt.Errorf("pinning reading MFS root (%v)", err): - case <-cctx.Context().Done(): - return - } + mfslog.Errorf("pinning reading MFS root (%v)", err) continue } - rootCid := rootNode.Cid() // pin to all remote services in parallel - pinAllMFS(cctx.Context(), node, cfg, rootCid, lastPins, errCh) + pinAllMFS(cctx.Context(), node, cfg, rootNode.Cid(), lastPins) } } // pinAllMFS pins on all remote services in parallel to overcome DoS attacks. -func pinAllMFS(ctx context.Context, node pinMFSNode, cfg *config.Config, rootCid cid.Cid, lastPins map[string]lastPin, errCh chan<- error) { - ch := make(chan lastPin, len(cfg.Pinning.RemoteServices)) - for svcName_, svcConfig_ := range cfg.Pinning.RemoteServices { +func pinAllMFS(ctx context.Context, node pinMFSNode, cfg *config.Config, rootCid cid.Cid, lastPins map[string]lastPin) { + ch := make(chan lastPin) + var started int + + for svcName, svcConfig := range cfg.Pinning.RemoteServices { + if ctx.Err() != nil { + break + } + // skip services where MFS is not enabled - svcName, svcConfig := svcName_, svcConfig_ mfslog.Debugf("pinning MFS root considering service %q", svcName) if !svcConfig.Policies.MFS.Enable { mfslog.Debugf("pinning service %q is not enabled", svcName) - ch <- lastPin{} continue } // read mfs pin interval for this service @@ -152,11 +137,7 @@ func pinAllMFS(ctx context.Context, node pinMFSNode, cfg *config.Config, rootCid var err error repinInterval, err = time.ParseDuration(svcConfig.Policies.MFS.RepinInterval) if err != nil { - select { - case errCh <- fmt.Errorf("remote pinning service %q has invalid MFS.RepinInterval (%v)", svcName, err): - case <-ctx.Done(): - } - ch <- lastPin{} + mfslog.Errorf("remote pinning service %q has invalid MFS.RepinInterval (%v)", svcName, err) continue } } @@ -169,38 +150,30 @@ func pinAllMFS(ctx context.Context, node pinMFSNode, cfg *config.Config, rootCid } else { mfslog.Debugf("pinning MFS root to %q: skipped due to MFS.RepinInterval=%s (remaining: %s)", svcName, repinInterval.String(), (repinInterval - time.Since(last.Time)).String()) } - ch <- lastPin{} continue } } mfslog.Debugf("pinning MFS root %q to %q", rootCid, svcName) - go func() { - if r, err := pinMFS(ctx, node, rootCid, svcName, svcConfig); err != nil { - select { - case errCh <- fmt.Errorf("pinning MFS root %q to %q (%v)", rootCid, svcName, err): - case <-ctx.Done(): - } - ch <- lastPin{} - } else { - ch <- r + go func(svcName string, svcConfig config.RemotePinningService) { + r, err := pinMFS(ctx, node, rootCid, svcName, svcConfig) + if err != nil { + mfslog.Errorf("pinning MFS root %q to %q (%v)", rootCid, svcName, err) } - }() + ch <- r + }(svcName, svcConfig) + started++ } - for i := 0; i < len(cfg.Pinning.RemoteServices); i++ { + + // Collect results from all started goroutines. + for i := 0; i < started; i++ { if x := <-ch; x.IsValid() { lastPins[x.ServiceName] = x } } } -func pinMFS( - ctx context.Context, - node pinMFSNode, - cid cid.Cid, - svcName string, - svcConfig config.RemotePinningService, -) (lastPin, error) { +func pinMFS(ctx context.Context, node pinMFSNode, cid cid.Cid, svcName string, svcConfig config.RemotePinningService) (lastPin, error) { c := pinclient.NewClient(svcConfig.API.Endpoint, svcConfig.API.Key) pinName := svcConfig.Policies.MFS.PinName @@ -230,43 +203,46 @@ func pinMFS( } for range lsPinCh { // in case the prior loop exits early } - if err := <-lsErrCh; err != nil { + err := <-lsErrCh + if err != nil { return lastPin{}, fmt.Errorf("error while listing remote pins: %v", err) } - // CID of the current MFS root is already being pinned, nothing to do - if pinning { - mfslog.Debugf("pinning MFS to %q: pin for %q exists since %s, skipping", svcName, cid, pinTime.String()) - return lastPin{Time: pinTime, ServiceName: svcName, ServiceConfig: svcConfig, CID: cid}, nil - } + if !pinning { + // Prepare Pin.name + addOpts := []pinclient.AddOption{pinclient.PinOpts.WithName(pinName)} - // Prepare Pin.name - addOpts := []pinclient.AddOption{pinclient.PinOpts.WithName(pinName)} - - // Prepare Pin.origins - // Add own multiaddrs to the 'origins' array, so Pinning Service can - // use that as a hint and connect back to us (if possible) - if node.PeerHost() != nil { - addrs, err := peer.AddrInfoToP2pAddrs(host.InfoFromHost(node.PeerHost())) - if err != nil { - return lastPin{}, err + // Prepare Pin.origins + // Add own multiaddrs to the 'origins' array, so Pinning Service can + // use that as a hint and connect back to us (if possible) + if node.PeerHost() != nil { + addrs, err := peer.AddrInfoToP2pAddrs(host.InfoFromHost(node.PeerHost())) + if err != nil { + return lastPin{}, err + } + addOpts = append(addOpts, pinclient.PinOpts.WithOrigins(addrs...)) } - addOpts = append(addOpts, pinclient.PinOpts.WithOrigins(addrs...)) - } - // Create or replace pin for MFS root - if existingRequestID != "" { - mfslog.Debugf("pinning to %q: replacing existing MFS root pin with %q", svcName, cid) - _, err := c.Replace(ctx, existingRequestID, cid, addOpts...) - if err != nil { - return lastPin{}, err + // Create or replace pin for MFS root + if existingRequestID != "" { + mfslog.Debugf("pinning to %q: replacing existing MFS root pin with %q", svcName, cid) + if _, err = c.Replace(ctx, existingRequestID, cid, addOpts...); err != nil { + return lastPin{}, err + } + } else { + mfslog.Debugf("pinning to %q: creating a new MFS root pin for %q", svcName, cid) + if _, err = c.Add(ctx, cid, addOpts...); err != nil { + return lastPin{}, err + } } } else { - mfslog.Debugf("pinning to %q: creating a new MFS root pin for %q", svcName, cid) - _, err := c.Add(ctx, cid, addOpts...) - if err != nil { - return lastPin{}, err - } + mfslog.Debugf("pinning MFS to %q: pin for %q exists since %s, skipping", svcName, cid, pinTime.String()) } - return lastPin{Time: pinTime, ServiceName: svcName, ServiceConfig: svcConfig, CID: cid}, nil + + return lastPin{ + Time: pinTime, + ServiceName: svcName, + ServiceConfig: svcConfig, + CID: cid, + }, nil } diff --git a/cmd/ipfs/pinmfs_test.go b/cmd/ipfs/kubo/pinmfs_test.go similarity index 66% rename from cmd/ipfs/pinmfs_test.go rename to cmd/ipfs/kubo/pinmfs_test.go index 78c7a863b..750be9c98 100644 --- a/cmd/ipfs/pinmfs_test.go +++ b/cmd/ipfs/kubo/pinmfs_test.go @@ -1,14 +1,19 @@ -package main +package kubo import ( + "bufio" "context" + "encoding/json" + "errors" "fmt" + "io" "strings" "testing" "time" + merkledag "github.com/ipfs/boxo/ipld/merkledag" ipld "github.com/ipfs/go-ipld-format" - merkledag "github.com/ipfs/go-merkledag" + logging "github.com/ipfs/go-log/v2" config "github.com/ipfs/kubo/config" "github.com/libp2p/go-libp2p/core/host" peer "github.com/libp2p/go-libp2p/core/peer" @@ -60,25 +65,37 @@ func isErrorSimilar(e1, e2 error) bool { } func TestPinMFSConfigError(t *testing.T) { - ctx := &testPinMFSContext{ - ctx: context.Background(), + ctx, cancel := context.WithTimeout(context.Background(), 2*testConfigPollInterval) + defer cancel() + + cctx := &testPinMFSContext{ + ctx: ctx, cfg: nil, err: fmt.Errorf("couldn't read config"), } node := &testPinMFSNode{} - errCh := make(chan error) - go pinMFSOnChange(testConfigPollInterval, ctx, node, errCh) - if !isErrorSimilar(<-errCh, ctx.err) { - t.Errorf("error did not propagate") + + logReader := logging.NewPipeReader() + go func() { + pinMFSOnChange(cctx, testConfigPollInterval, node) + logReader.Close() + }() + + level, msg := readLogLine(t, logReader) + if level != "error" { + t.Error("expected error to be logged") } - if !isErrorSimilar(<-errCh, ctx.err) { + if !isErrorSimilar(errors.New(msg), cctx.err) { t.Errorf("error did not propagate") } } func TestPinMFSRootNodeError(t *testing.T) { - ctx := &testPinMFSContext{ - ctx: context.Background(), + ctx, cancel := context.WithTimeout(context.Background(), 2*testConfigPollInterval) + defer cancel() + + cctx := &testPinMFSContext{ + ctx: ctx, cfg: &config.Config{ Pinning: config.Pinning{}, }, @@ -87,12 +104,16 @@ func TestPinMFSRootNodeError(t *testing.T) { node := &testPinMFSNode{ err: fmt.Errorf("cannot create root node"), } - errCh := make(chan error) - go pinMFSOnChange(testConfigPollInterval, ctx, node, errCh) - if !isErrorSimilar(<-errCh, node.err) { - t.Errorf("error did not propagate") + logReader := logging.NewPipeReader() + go func() { + pinMFSOnChange(cctx, testConfigPollInterval, node) + logReader.Close() + }() + level, msg := readLogLine(t, logReader) + if level != "error" { + t.Error("expected error to be logged") } - if !isErrorSimilar(<-errCh, node.err) { + if !isErrorSimilar(errors.New(msg), node.err) { t.Errorf("error did not propagate") } } @@ -155,7 +176,8 @@ func TestPinMFSService(t *testing.T) { } func testPinMFSServiceWithError(t *testing.T, cfg *config.Config, expectedErrorPrefix string) { - goctx, cancel := context.WithCancel(context.Background()) + goctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() ctx := &testPinMFSContext{ ctx: goctx, cfg: cfg, @@ -164,16 +186,36 @@ func testPinMFSServiceWithError(t *testing.T, cfg *config.Config, expectedErrorP node := &testPinMFSNode{ err: nil, } - errCh := make(chan error) - go pinMFSOnChange(testConfigPollInterval, ctx, node, errCh) - defer cancel() - // first pass through the pinning loop - err := <-errCh - if !strings.Contains((err).Error(), expectedErrorPrefix) { - t.Errorf("expecting error containing %q", expectedErrorPrefix) + logReader := logging.NewPipeReader() + go func() { + pinMFSOnChange(ctx, testConfigPollInterval, node) + logReader.Close() + }() + level, msg := readLogLine(t, logReader) + if level != "error" { + t.Error("expected error to be logged") } - // second pass through the pinning loop - if !strings.Contains((err).Error(), expectedErrorPrefix) { + if !strings.Contains(msg, expectedErrorPrefix) { t.Errorf("expecting error containing %q", expectedErrorPrefix) } } + +func readLogLine(t *testing.T, logReader io.Reader) (string, string) { + t.Helper() + + r := bufio.NewReader(logReader) + data, err := r.ReadBytes('\n') + if err != nil { + t.Fatal(err) + } + + logInfo := struct { + Level string `json:"level"` + Msg string `json:"msg"` + }{} + err = json.Unmarshal(data, &logInfo) + if err != nil { + t.Fatal(err) + } + return logInfo.Level, logInfo.Msg +} diff --git a/cmd/ipfs/kubo/start.go b/cmd/ipfs/kubo/start.go new file mode 100644 index 000000000..474045e71 --- /dev/null +++ b/cmd/ipfs/kubo/start.go @@ -0,0 +1,497 @@ +// cmd/ipfs/kubo implements the primary CLI binary for kubo +package kubo + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net" + "net/http" + "os" + "runtime/pprof" + "strings" + "time" + + "github.com/blang/semver/v4" + "github.com/google/uuid" + u "github.com/ipfs/boxo/util" + cmds "github.com/ipfs/go-ipfs-cmds" + "github.com/ipfs/go-ipfs-cmds/cli" + cmdhttp "github.com/ipfs/go-ipfs-cmds/http" + logging "github.com/ipfs/go-log" + ipfs "github.com/ipfs/kubo" + "github.com/ipfs/kubo/client/rpc/auth" + "github.com/ipfs/kubo/cmd/ipfs/util" + oldcmds "github.com/ipfs/kubo/commands" + config "github.com/ipfs/kubo/config" + "github.com/ipfs/kubo/core" + corecmds "github.com/ipfs/kubo/core/commands" + "github.com/ipfs/kubo/core/corehttp" + "github.com/ipfs/kubo/plugin/loader" + "github.com/ipfs/kubo/repo" + "github.com/ipfs/kubo/repo/fsrepo" + "github.com/ipfs/kubo/tracing" + ma "github.com/multiformats/go-multiaddr" + madns "github.com/multiformats/go-multiaddr-dns" + manet "github.com/multiformats/go-multiaddr/net" + "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" + "go.opentelemetry.io/contrib/propagators/autoprop" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/trace" +) + +// log is the command logger. +var ( + log = logging.Logger("cmd/ipfs") + tracer trace.Tracer +) + +// declared as a var for testing purposes. +var dnsResolver = madns.DefaultResolver + +const ( + EnvEnableProfiling = "IPFS_PROF" + cpuProfile = "ipfs.cpuprof" + heapProfile = "ipfs.memprof" +) + +type PluginPreloader func(*loader.PluginLoader) error + +func loadPlugins(repoPath string, preload PluginPreloader) (*loader.PluginLoader, error) { + plugins, err := loader.NewPluginLoader(repoPath) + if err != nil { + return nil, fmt.Errorf("error loading plugins: %s", err) + } + + if preload != nil { + if err := preload(plugins); err != nil { + return nil, fmt.Errorf("error loading plugins (preload): %s", err) + } + } + + if err := plugins.Initialize(); err != nil { + return nil, fmt.Errorf("error initializing plugins: %s", err) + } + + if err := plugins.Inject(); err != nil { + return nil, fmt.Errorf("error initializing plugins: %s", err) + } + return plugins, nil +} + +func printErr(err error) int { + fmt.Fprintf(os.Stderr, "Error: %s\n", err.Error()) + return 1 +} + +func newUUID(key string) logging.Metadata { + ids := "#UUID-ERROR#" + if id, err := uuid.NewRandom(); err == nil { + ids = id.String() + } + return logging.Metadata{ + key: ids, + } +} + +func BuildDefaultEnv(ctx context.Context, req *cmds.Request) (cmds.Environment, error) { + return BuildEnv(nil)(ctx, req) +} + +// BuildEnv creates an environment to be used with the kubo CLI. Note: the plugin preloader should only call functions +// associated with preloaded plugins (i.e. Load). +func BuildEnv(pl PluginPreloader) func(ctx context.Context, req *cmds.Request) (cmds.Environment, error) { + return func(ctx context.Context, req *cmds.Request) (cmds.Environment, error) { + checkDebug(req) + repoPath, err := getRepoPath(req) + if err != nil { + return nil, err + } + log.Debugf("config path is %s", repoPath) + + plugins, err := loadPlugins(repoPath, pl) + if err != nil { + return nil, err + } + + // this sets up the function that will initialize the node + // this is so that we can construct the node lazily. + return &oldcmds.Context{ + ConfigRoot: repoPath, + ReqLog: &oldcmds.ReqLog{}, + Plugins: plugins, + ConstructNode: func() (n *core.IpfsNode, err error) { + if req == nil { + return nil, errors.New("constructing node without a request") + } + + r, err := fsrepo.Open(repoPath) + if err != nil { // repo is owned by the node + return nil, err + } + + // ok everything is good. set it on the invocation (for ownership) + // and return it. + n, err = core.NewNode(ctx, &core.BuildCfg{ + Repo: r, + }) + if err != nil { + return nil, err + } + + return n, nil + }, + }, nil + } +} + +// Start roadmap: +// - parse the commandline to get a cmdInvocation +// - if user requests help, print it and exit. +// - run the command invocation +// - output the response +// - if anything fails, print error, maybe with help. +func Start(buildEnv func(ctx context.Context, req *cmds.Request) (cmds.Environment, error)) (exitCode int) { + ctx := logging.ContextWithLoggable(context.Background(), newUUID("session")) + + tp, err := tracing.NewTracerProvider(ctx) + if err != nil { + return printErr(err) + } + defer func() { + if err := tp.Shutdown(ctx); err != nil { + exitCode = printErr(err) + } + }() + otel.SetTracerProvider(tp) + otel.SetTextMapPropagator(autoprop.NewTextMapPropagator()) + tracer = tp.Tracer("Kubo-cli") + + stopFunc, err := profileIfEnabled() + if err != nil { + return printErr(err) + } + defer stopFunc() // to be executed as late as possible + + intrh, ctx := util.SetupInterruptHandler(ctx) + defer intrh.Close() + + // Handle `ipfs version` or `ipfs help` + if len(os.Args) > 1 { + // Handle `ipfs --version' + if os.Args[1] == "--version" { + os.Args[1] = "version" + } + + // Handle `ipfs help` and `ipfs help ` + if os.Args[1] == "help" { + if len(os.Args) > 2 { + os.Args = append(os.Args[:1], os.Args[2:]...) + // Handle `ipfs help --help` + // append `--help`,when the command is not `ipfs help --help` + if os.Args[1] != "--help" { + os.Args = append(os.Args, "--help") + } + } else { + os.Args[1] = "--help" + } + } + } else if insideGUI() { // if no args were passed, and we're in a GUI environment + // launch the daemon instead of launching a ghost window + os.Args = append(os.Args, "daemon", "--init") + } + + // output depends on executable name passed in os.Args + // so we need to make sure it's stable + os.Args[0] = "ipfs" + + err = cli.Run(ctx, Root, os.Args, os.Stdin, os.Stdout, os.Stderr, buildEnv, makeExecutor) + if err != nil { + return 1 + } + + // everything went better than expected :) + return 0 +} + +func insideGUI() bool { + return util.InsideGUI() +} + +func checkDebug(req *cmds.Request) { + // check if user wants to debug. option OR env var. + debug, _ := req.Options["debug"].(bool) + if debug || os.Getenv("IPFS_LOGGING") == "debug" { + u.Debug = true + logging.SetDebugLogging() + } + if u.GetenvBool("DEBUG") { + u.Debug = true + } +} + +func apiAddrOption(req *cmds.Request) (ma.Multiaddr, error) { + apiAddrStr, apiSpecified := req.Options[corecmds.ApiOption].(string) + if !apiSpecified { + return nil, nil + } + return ma.NewMultiaddr(apiAddrStr) +} + +// encodedAbsolutePathVersion is the version from which the absolute path header in +// multipart requests is %-encoded. Before this version, its sent raw. +var encodedAbsolutePathVersion = semver.MustParse("0.23.0-dev") + +func makeExecutor(req *cmds.Request, env interface{}) (cmds.Executor, error) { + exe := tracingWrappedExecutor{cmds.NewExecutor(req.Root)} + cctx := env.(*oldcmds.Context) + + // Check if the command is disabled. + if req.Command.NoLocal && req.Command.NoRemote { + return nil, fmt.Errorf("command disabled: %v", req.Path) + } + + // Can we just run this locally? + if !req.Command.NoLocal { + if doesNotUseRepo, ok := corecmds.GetDoesNotUseRepo(req.Command.Extra); doesNotUseRepo && ok { + return exe, nil + } + } + + // Get the API option from the commandline. + apiAddr, err := apiAddrOption(req) + if err != nil { + return nil, err + } + + // Require that the command be run on the daemon when the API flag is + // passed (unless we're trying to _run_ the daemon). + daemonRequested := apiAddr != nil && req.Command != daemonCmd + + // Run this on the client if required. + if req.Command.NoRemote { + if daemonRequested { + // User requested that the command be run on the daemon but we can't. + // NOTE: We drop this check for the `ipfs daemon` command. + return nil, errors.New("api flag specified but command cannot be run on the daemon") + } + return exe, nil + } + + // Finally, look in the repo for an API file. + if apiAddr == nil { + var err error + apiAddr, err = fsrepo.APIAddr(cctx.ConfigRoot) + switch err { + case nil, repo.ErrApiNotRunning: + default: + return nil, err + } + } + + // Still no api specified? Run it on the client or fail. + if apiAddr == nil { + if req.Command.NoLocal { + return nil, fmt.Errorf("command must be run on the daemon: %v", req.Path) + } + return exe, nil + } + + // Resolve the API addr. + // + // Do not replace apiAddr with the resolved addr so that the requested + // hostname is kept for use in the request's HTTP header. + _, err = resolveAddr(req.Context, apiAddr) + if err != nil { + return nil, err + } + network, host, err := manet.DialArgs(apiAddr) + if err != nil { + return nil, err + } + + // Construct the executor. + opts := []cmdhttp.ClientOpt{ + cmdhttp.ClientWithAPIPrefix(corehttp.APIPath), + } + + // Fallback on a local executor if we (a) have a repo and (b) aren't + // forcing a daemon. + if !daemonRequested && fsrepo.IsInitialized(cctx.ConfigRoot) { + opts = append(opts, cmdhttp.ClientWithFallback(exe)) + } + + var tpt http.RoundTripper + switch network { + case "tcp", "tcp4", "tcp6": + tpt = http.DefaultTransport + case "unix": + path := host + host = "unix" + tpt = &http.Transport{ + DialContext: func(_ context.Context, _, _ string) (net.Conn, error) { + return net.Dial("unix", path) + }, + } + default: + return nil, fmt.Errorf("unsupported API address: %s", apiAddr) + } + + apiAuth, specified := req.Options[corecmds.ApiAuthOption].(string) + if specified { + authorization := config.ConvertAuthSecret(apiAuth) + tpt = auth.NewAuthorizedRoundTripper(authorization, tpt) + } + + httpClient := &http.Client{ + Transport: otelhttp.NewTransport(tpt), + } + opts = append(opts, cmdhttp.ClientWithHTTPClient(httpClient)) + + // Fetch remove version, as some feature compatibility might change depending on it. + remoteVersion, err := getRemoteVersion(tracingWrappedExecutor{cmdhttp.NewClient(host, opts...)}) + if err != nil { + return nil, err + } + opts = append(opts, cmdhttp.ClientWithRawAbsPath(remoteVersion.LT(encodedAbsolutePathVersion))) + + return tracingWrappedExecutor{cmdhttp.NewClient(host, opts...)}, nil +} + +type tracingWrappedExecutor struct { + exec cmds.Executor +} + +func (twe tracingWrappedExecutor) Execute(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment) error { + ctx, span := tracer.Start(req.Context, "cmds."+strings.Join(req.Path, "."), trace.WithAttributes(attribute.StringSlice("Arguments", req.Arguments))) + defer span.End() + req.Context = ctx + + err := twe.exec.Execute(req, re, env) + if err != nil { + span.SetStatus(codes.Error, err.Error()) + } + return err +} + +func getRepoPath(req *cmds.Request) (string, error) { + repoOpt, found := req.Options[corecmds.RepoDirOption].(string) + if found && repoOpt != "" { + return repoOpt, nil + } + + repoPath, err := fsrepo.BestKnownPath() + if err != nil { + return "", err + } + return repoPath, nil +} + +// startProfiling begins CPU profiling and returns a `stop` function to be +// executed as late as possible. The stop function captures the memprofile. +func startProfiling() (func(), error) { + // start CPU profiling as early as possible + ofi, err := os.Create(cpuProfile) + if err != nil { + return nil, err + } + err = pprof.StartCPUProfile(ofi) + if err != nil { + ofi.Close() + return nil, err + } + go func() { + for range time.NewTicker(time.Second * 30).C { + err := writeHeapProfileToFile() + if err != nil { + log.Error(err) + } + } + }() + + stopProfiling := func() { + pprof.StopCPUProfile() + ofi.Close() // captured by the closure + } + return stopProfiling, nil +} + +func writeHeapProfileToFile() error { + mprof, err := os.Create(heapProfile) + if err != nil { + return err + } + defer mprof.Close() // _after_ writing the heap profile + return pprof.WriteHeapProfile(mprof) +} + +func profileIfEnabled() (func(), error) { + // FIXME this is a temporary hack so profiling of asynchronous operations + // works as intended. + if os.Getenv(EnvEnableProfiling) != "" { + stopProfilingFunc, err := startProfiling() // TODO maybe change this to its own option... profiling makes it slower. + if err != nil { + return nil, err + } + return stopProfilingFunc, nil + } + return func() {}, nil +} + +func resolveAddr(ctx context.Context, addr ma.Multiaddr) (ma.Multiaddr, error) { + ctx, cancelFunc := context.WithTimeout(ctx, 10*time.Second) + defer cancelFunc() + + addrs, err := dnsResolver.Resolve(ctx, addr) + if err != nil { + return nil, err + } + + if len(addrs) == 0 { + return nil, errors.New("non-resolvable API endpoint") + } + + return addrs[0], nil +} + +type nopWriter struct { + io.Writer +} + +func (nw nopWriter) Close() error { + return nil +} + +func getRemoteVersion(exe cmds.Executor) (*semver.Version, error) { + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Second*30)) + defer cancel() + + req, err := cmds.NewRequest(ctx, []string{"version"}, nil, nil, nil, Root) + if err != nil { + return nil, err + } + + var buf bytes.Buffer + re, err := cmds.NewWriterResponseEmitter(nopWriter{&buf}, req) + if err != nil { + return nil, err + } + + err = exe.Execute(req, re, nil) + if err != nil { + return nil, err + } + + var out ipfs.VersionInfo + dec := json.NewDecoder(&buf) + if err := dec.Decode(&out); err != nil { + return nil, err + } + + return semver.New(out.Version) +} diff --git a/cmd/ipfs/main.go b/cmd/ipfs/main.go index 8b9822c99..ae7eedc0f 100644 --- a/cmd/ipfs/main.go +++ b/cmd/ipfs/main.go @@ -1,394 +1,11 @@ -// cmd/ipfs implements the primary CLI binary for ipfs package main import ( - "context" - "errors" - "fmt" - "math/rand" - "net" - "net/http" "os" - "runtime/pprof" - "time" - "github.com/ipfs/kubo/cmd/ipfs/util" - oldcmds "github.com/ipfs/kubo/commands" - "github.com/ipfs/kubo/core" - corecmds "github.com/ipfs/kubo/core/commands" - "github.com/ipfs/kubo/core/corehttp" - "github.com/ipfs/kubo/plugin/loader" - "github.com/ipfs/kubo/repo" - "github.com/ipfs/kubo/repo/fsrepo" - "github.com/ipfs/kubo/tracing" - - cmds "github.com/ipfs/go-ipfs-cmds" - "github.com/ipfs/go-ipfs-cmds/cli" - cmdhttp "github.com/ipfs/go-ipfs-cmds/http" - u "github.com/ipfs/go-ipfs-util" - logging "github.com/ipfs/go-log" - ma "github.com/multiformats/go-multiaddr" - madns "github.com/multiformats/go-multiaddr-dns" - manet "github.com/multiformats/go-multiaddr/net" - - "github.com/google/uuid" - "go.opentelemetry.io/otel" + "github.com/ipfs/kubo/cmd/ipfs/kubo" ) -// log is the command logger -var log = logging.Logger("cmd/ipfs") - -// declared as a var for testing purposes -var dnsResolver = madns.DefaultResolver - -const ( - EnvEnableProfiling = "IPFS_PROF" - cpuProfile = "ipfs.cpuprof" - heapProfile = "ipfs.memprof" -) - -func loadPlugins(repoPath string) (*loader.PluginLoader, error) { - plugins, err := loader.NewPluginLoader(repoPath) - if err != nil { - return nil, fmt.Errorf("error loading plugins: %s", err) - } - - if err := plugins.Initialize(); err != nil { - return nil, fmt.Errorf("error initializing plugins: %s", err) - } - - if err := plugins.Inject(); err != nil { - return nil, fmt.Errorf("error initializing plugins: %s", err) - } - return plugins, nil -} - -// main roadmap: -// - parse the commandline to get a cmdInvocation -// - if user requests help, print it and exit. -// - run the command invocation -// - output the response -// - if anything fails, print error, maybe with help func main() { - os.Exit(mainRet()) -} - -func printErr(err error) int { - fmt.Fprintf(os.Stderr, "Error: %s\n", err.Error()) - return 1 -} - -func newUUID(key string) logging.Metadata { - ids := "#UUID-ERROR#" - if id, err := uuid.NewRandom(); err == nil { - ids = id.String() - } - return logging.Metadata{ - key: ids, - } -} - -func mainRet() (exitCode int) { - rand.Seed(time.Now().UnixNano()) - ctx := logging.ContextWithLoggable(context.Background(), newUUID("session")) - var err error - - tp, err := tracing.NewTracerProvider(ctx) - if err != nil { - return printErr(err) - } - defer func() { - if err := tp.Shutdown(ctx); err != nil { - exitCode = printErr(err) - } - }() - otel.SetTracerProvider(tp) - - stopFunc, err := profileIfEnabled() - if err != nil { - return printErr(err) - } - defer stopFunc() // to be executed as late as possible - - intrh, ctx := util.SetupInterruptHandler(ctx) - defer intrh.Close() - - // Handle `ipfs version` or `ipfs help` - if len(os.Args) > 1 { - // Handle `ipfs --version' - if os.Args[1] == "--version" { - os.Args[1] = "version" - } - - // Handle `ipfs help` and `ipfs help ` - if os.Args[1] == "help" { - if len(os.Args) > 2 { - os.Args = append(os.Args[:1], os.Args[2:]...) - // Handle `ipfs help --help` - // append `--help`,when the command is not `ipfs help --help` - if os.Args[1] != "--help" { - os.Args = append(os.Args, "--help") - } - } else { - os.Args[1] = "--help" - } - } - } else if insideGUI() { // if no args were passed, and we're in a GUI environment - // launch the daemon instead of launching a ghost window - os.Args = append(os.Args, "daemon", "--init") - } - - // output depends on executable name passed in os.Args - // so we need to make sure it's stable - os.Args[0] = "ipfs" - - buildEnv := func(ctx context.Context, req *cmds.Request) (cmds.Environment, error) { - checkDebug(req) - repoPath, err := getRepoPath(req) - if err != nil { - return nil, err - } - log.Debugf("config path is %s", repoPath) - - plugins, err := loadPlugins(repoPath) - if err != nil { - return nil, err - } - - // this sets up the function that will initialize the node - // this is so that we can construct the node lazily. - return &oldcmds.Context{ - ConfigRoot: repoPath, - ReqLog: &oldcmds.ReqLog{}, - Plugins: plugins, - ConstructNode: func() (n *core.IpfsNode, err error) { - if req == nil { - return nil, errors.New("constructing node without a request") - } - - r, err := fsrepo.Open(repoPath) - if err != nil { // repo is owned by the node - return nil, err - } - - // ok everything is good. set it on the invocation (for ownership) - // and return it. - n, err = core.NewNode(ctx, &core.BuildCfg{ - Repo: r, - }) - if err != nil { - return nil, err - } - - return n, nil - }, - }, nil - } - - err = cli.Run(ctx, Root, os.Args, os.Stdin, os.Stdout, os.Stderr, buildEnv, makeExecutor) - if err != nil { - return 1 - } - - // everything went better than expected :) - return 0 -} - -func insideGUI() bool { - return util.InsideGUI() -} - -func checkDebug(req *cmds.Request) { - // check if user wants to debug. option OR env var. - debug, _ := req.Options["debug"].(bool) - if debug || os.Getenv("IPFS_LOGGING") == "debug" { - u.Debug = true - logging.SetDebugLogging() - } - if u.GetenvBool("DEBUG") { - u.Debug = true - } -} - -func apiAddrOption(req *cmds.Request) (ma.Multiaddr, error) { - apiAddrStr, apiSpecified := req.Options[corecmds.ApiOption].(string) - if !apiSpecified { - return nil, nil - } - return ma.NewMultiaddr(apiAddrStr) -} - -func makeExecutor(req *cmds.Request, env interface{}) (cmds.Executor, error) { - exe := cmds.NewExecutor(req.Root) - cctx := env.(*oldcmds.Context) - - // Check if the command is disabled. - if req.Command.NoLocal && req.Command.NoRemote { - return nil, fmt.Errorf("command disabled: %v", req.Path) - } - - // Can we just run this locally? - if !req.Command.NoLocal { - if doesNotUseRepo, ok := corecmds.GetDoesNotUseRepo(req.Command.Extra); doesNotUseRepo && ok { - return exe, nil - } - } - - // Get the API option from the commandline. - apiAddr, err := apiAddrOption(req) - if err != nil { - return nil, err - } - - // Require that the command be run on the daemon when the API flag is - // passed (unless we're trying to _run_ the daemon). - daemonRequested := apiAddr != nil && req.Command != daemonCmd - - // Run this on the client if required. - if req.Command.NoRemote { - if daemonRequested { - // User requested that the command be run on the daemon but we can't. - // NOTE: We drop this check for the `ipfs daemon` command. - return nil, errors.New("api flag specified but command cannot be run on the daemon") - } - return exe, nil - } - - // Finally, look in the repo for an API file. - if apiAddr == nil { - var err error - apiAddr, err = fsrepo.APIAddr(cctx.ConfigRoot) - switch err { - case nil, repo.ErrApiNotRunning: - default: - return nil, err - } - } - - // Still no api specified? Run it on the client or fail. - if apiAddr == nil { - if req.Command.NoLocal { - return nil, fmt.Errorf("command must be run on the daemon: %v", req.Path) - } - return exe, nil - } - - // Resolve the API addr. - apiAddr, err = resolveAddr(req.Context, apiAddr) - if err != nil { - return nil, err - } - network, host, err := manet.DialArgs(apiAddr) - if err != nil { - return nil, err - } - - // Construct the executor. - opts := []cmdhttp.ClientOpt{ - cmdhttp.ClientWithAPIPrefix(corehttp.APIPath), - } - - // Fallback on a local executor if we (a) have a repo and (b) aren't - // forcing a daemon. - if !daemonRequested && fsrepo.IsInitialized(cctx.ConfigRoot) { - opts = append(opts, cmdhttp.ClientWithFallback(exe)) - } - - switch network { - case "tcp", "tcp4", "tcp6": - case "unix": - path := host - host = "unix" - opts = append(opts, cmdhttp.ClientWithHTTPClient(&http.Client{ - Transport: &http.Transport{ - DialContext: func(_ context.Context, _, _ string) (net.Conn, error) { - return net.Dial("unix", path) - }, - }, - })) - default: - return nil, fmt.Errorf("unsupported API address: %s", apiAddr) - } - - return cmdhttp.NewClient(host, opts...), nil -} - -func getRepoPath(req *cmds.Request) (string, error) { - repoOpt, found := req.Options[corecmds.RepoDirOption].(string) - if found && repoOpt != "" { - return repoOpt, nil - } - - repoPath, err := fsrepo.BestKnownPath() - if err != nil { - return "", err - } - return repoPath, nil -} - -// startProfiling begins CPU profiling and returns a `stop` function to be -// executed as late as possible. The stop function captures the memprofile. -func startProfiling() (func(), error) { - // start CPU profiling as early as possible - ofi, err := os.Create(cpuProfile) - if err != nil { - return nil, err - } - err = pprof.StartCPUProfile(ofi) - if err != nil { - ofi.Close() - return nil, err - } - go func() { - for range time.NewTicker(time.Second * 30).C { - err := writeHeapProfileToFile() - if err != nil { - log.Error(err) - } - } - }() - - stopProfiling := func() { - pprof.StopCPUProfile() - ofi.Close() // captured by the closure - } - return stopProfiling, nil -} - -func writeHeapProfileToFile() error { - mprof, err := os.Create(heapProfile) - if err != nil { - return err - } - defer mprof.Close() // _after_ writing the heap profile - return pprof.WriteHeapProfile(mprof) -} - -func profileIfEnabled() (func(), error) { - // FIXME this is a temporary hack so profiling of asynchronous operations - // works as intended. - if os.Getenv(EnvEnableProfiling) != "" { - stopProfilingFunc, err := startProfiling() // TODO maybe change this to its own option... profiling makes it slower. - if err != nil { - return nil, err - } - return stopProfilingFunc, nil - } - return func() {}, nil -} - -func resolveAddr(ctx context.Context, addr ma.Multiaddr) (ma.Multiaddr, error) { - ctx, cancelFunc := context.WithTimeout(ctx, 10*time.Second) - defer cancelFunc() - - addrs, err := dnsResolver.Resolve(ctx, addr) - if err != nil { - return nil, err - } - - if len(addrs) == 0 { - return nil, errors.New("non-resolvable API endpoint") - } - - return addrs[0], nil + os.Exit(kubo.Start(kubo.BuildDefaultEnv)) } diff --git a/cmd/ipfs/runmain_test.go b/cmd/ipfs/runmain_test.go index d91879112..a37ec194c 100644 --- a/cmd/ipfs/runmain_test.go +++ b/cmd/ipfs/runmain_test.go @@ -1,30 +1,33 @@ //go:build testrunmain // +build testrunmain -package main +package main_test import ( "flag" "fmt" "os" "testing" + + "github.com/ipfs/kubo/cmd/ipfs/kubo" ) // this abuses go so much that I felt dirty writing this code // but it is the only way to do it without writing custom compiler that would -// be a clone of go-build with go-test +// be a clone of go-build with go-test. func TestRunMain(t *testing.T) { args := flag.Args() os.Args = append([]string{os.Args[0]}, args...) - ret := mainRet() + + ret := kubo.Start(kubo.BuildDefaultEnv) p := os.Getenv("IPFS_COVER_RET_FILE") if len(p) != 0 { - os.WriteFile(p, []byte(fmt.Sprintf("%d\n", ret)), 0777) + os.WriteFile(p, []byte(fmt.Sprintf("%d\n", ret)), 0o777) } // close outputs so go testing doesn't print anything - null, _ := os.Open(os.DevNull) + null, _ := os.OpenFile(os.DevNull, os.O_RDWR, 0755) os.Stderr = null os.Stdout = null } diff --git a/cmd/ipfs/util/ulimit.go b/cmd/ipfs/util/ulimit.go index e024ced84..188444d67 100644 --- a/cmd/ipfs/util/ulimit.go +++ b/cmd/ipfs/util/ulimit.go @@ -14,19 +14,19 @@ var log = logging.Logger("ulimit") var ( supportsFDManagement = false - // getlimit returns the soft and hard limits of file descriptors counts + // getlimit returns the soft and hard limits of file descriptors counts. getLimit func() (uint64, uint64, error) - // set limit sets the soft and hard limits of file descriptors counts + // set limit sets the soft and hard limits of file descriptors counts. setLimit func(uint64, uint64) error ) -// minimum file descriptor limit before we complain +// minimum file descriptor limit before we complain. const minFds = 2048 // default max file descriptor limit. const maxFds = 8192 -// userMaxFDs returns the value of IPFS_FD_MAX +// userMaxFDs returns the value of IPFS_FD_MAX. func userMaxFDs() uint64 { // check if the IPFS_FD_MAX is set up and if it does // not have a valid fds number notify the user @@ -42,7 +42,7 @@ func userMaxFDs() uint64 { } // ManageFdLimit raise the current max file descriptor count -// of the process based on the IPFS_FD_MAX value +// of the process based on the IPFS_FD_MAX value. func ManageFdLimit() (changed bool, newLimit uint64, err error) { if !supportsFDManagement { return false, 0, nil @@ -82,7 +82,7 @@ func ManageFdLimit() (changed bool, newLimit uint64, err error) { // set the soft value err = setLimit(targetLimit, hard) if err != nil { - err = fmt.Errorf("error setting ulimit without hard limit: %s", err) + err = fmt.Errorf("error setting ulimit without hard limit: %w", err) break } newLimit = targetLimit @@ -107,7 +107,7 @@ func ManageFdLimit() (changed bool, newLimit uint64, err error) { break } default: - err = fmt.Errorf("error setting: ulimit: %s", err) + err = fmt.Errorf("error setting: ulimit: %w", err) } return newLimit > 0, newLimit, err diff --git a/cmd/ipfswatch/main.go b/cmd/ipfswatch/main.go index d555f14b9..6850f6423 100644 --- a/cmd/ipfswatch/main.go +++ b/cmd/ipfswatch/main.go @@ -16,17 +16,19 @@ import ( core "github.com/ipfs/kubo/core" coreapi "github.com/ipfs/kubo/core/coreapi" corehttp "github.com/ipfs/kubo/core/corehttp" + "github.com/ipfs/kubo/misc/fsutil" fsrepo "github.com/ipfs/kubo/repo/fsrepo" fsnotify "github.com/fsnotify/fsnotify" - files "github.com/ipfs/go-ipfs-files" + "github.com/ipfs/boxo/files" process "github.com/jbenet/goprocess" - homedir "github.com/mitchellh/go-homedir" ) -var http = flag.Bool("http", false, "expose IPFS HTTP API") -var repoPath = flag.String("repo", os.Getenv("IPFS_PATH"), "IPFS_PATH to use") -var watchPath = flag.String("path", ".", "the path to watch") +var ( + http = flag.Bool("http", false, "expose IPFS HTTP API") + repoPath = flag.String("repo", os.Getenv("IPFS_PATH"), "IPFS_PATH to use") + watchPath = flag.String("path", ".", "the path to watch") +) func main() { flag.Parse() @@ -52,11 +54,10 @@ func main() { } func run(ipfsPath, watchPath string) error { - proc := process.WithParent(process.Background()) log.Printf("running IPFSWatch on '%s' using repo at '%s'...", watchPath, ipfsPath) - ipfsPath, err := homedir.Expand(ipfsPath) + ipfsPath, err := fsutil.ExpandHome(ipfsPath) if err != nil { return err } @@ -93,8 +94,8 @@ func run(ipfsPath, watchPath string) error { if *http { addr := "/ip4/127.0.0.1/tcp/5001" - var opts = []corehttp.ServeOption{ - corehttp.GatewayOption(true, "/ipfs", "/ipns"), + opts := []corehttp.ServeOption{ + corehttp.GatewayOption("/ipfs", "/ipns"), corehttp.WebUIOption, corehttp.CommandsOption(cmdCtx(node, ipfsPath)), } diff --git a/codecov.yml b/codecov.yml index 71f774147..8405abb90 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,9 +1,9 @@ codecov: ci: - - "ci/circle-ci" - "!travis-ci.org" - "!ci.ipfs.team:8111" - "!ci.ipfs.team" + - "github.com" notify: require_ci_to_pass: no after_n_builds: 2 diff --git a/commands/context.go b/commands/context.go index 64e6ec55d..cc95d55f4 100644 --- a/commands/context.go +++ b/commands/context.go @@ -12,14 +12,14 @@ import ( cmds "github.com/ipfs/go-ipfs-cmds" logging "github.com/ipfs/go-log" - coreiface "github.com/ipfs/interface-go-ipfs-core" - options "github.com/ipfs/interface-go-ipfs-core/options" config "github.com/ipfs/kubo/config" + coreiface "github.com/ipfs/kubo/core/coreiface" + options "github.com/ipfs/kubo/core/coreiface/options" ) var log = logging.Logger("command") -// Context represents request context +// Context represents request context. type Context struct { ConfigRoot string ReqLog *ReqLog @@ -54,7 +54,7 @@ func (c *Context) GetNode() (*core.IpfsNode, error) { } // GetAPI returns CoreAPI instance backed by ipfs node. -// It may construct the node with the provided function +// It may construct the node with the provided function. func (c *Context) GetAPI() (coreiface.CoreAPI, error) { if c.api == nil { n, err := c.GetNode() diff --git a/commands/reqlog.go b/commands/reqlog.go index 14c10a7c0..444bbcd3e 100644 --- a/commands/reqlog.go +++ b/commands/reqlog.go @@ -5,7 +5,7 @@ import ( "time" ) -// ReqLogEntry is an entry in the request log +// ReqLogEntry is an entry in the request log. type ReqLogEntry struct { StartTime time.Time EndTime time.Time @@ -18,14 +18,14 @@ type ReqLogEntry struct { log *ReqLog } -// Copy returns a copy of the ReqLogEntry +// Copy returns a copy of the ReqLogEntry. func (r *ReqLogEntry) Copy() *ReqLogEntry { out := *r out.log = nil return &out } -// ReqLog is a log of requests +// ReqLog is a log of requests. type ReqLog struct { Requests []*ReqLogEntry nextID int @@ -33,7 +33,7 @@ type ReqLog struct { keep time.Duration } -// AddEntry adds an entry to the log +// AddEntry adds an entry to the log. func (rl *ReqLog) AddEntry(rle *ReqLogEntry) { rl.lock.Lock() defer rl.lock.Unlock() @@ -47,7 +47,7 @@ func (rl *ReqLog) AddEntry(rle *ReqLogEntry) { } } -// ClearInactive removes stale entries +// ClearInactive removes stale entries. func (rl *ReqLog) ClearInactive() { rl.lock.Lock() defer rl.lock.Unlock() @@ -79,14 +79,14 @@ func (rl *ReqLog) cleanup() { rl.Requests = rl.Requests[:i] } -// SetKeepTime sets a duration after which an entry will be considered inactive +// SetKeepTime sets a duration after which an entry will be considered inactive. func (rl *ReqLog) SetKeepTime(t time.Duration) { rl.lock.Lock() defer rl.lock.Unlock() rl.keep = t } -// Report generates a copy of all the entries in the requestlog +// Report generates a copy of all the entries in the requestlog. func (rl *ReqLog) Report() []*ReqLogEntry { rl.lock.Lock() defer rl.lock.Unlock() @@ -99,7 +99,7 @@ func (rl *ReqLog) Report() []*ReqLogEntry { return out } -// Finish marks an entry in the log as finished +// Finish marks an entry in the log as finished. func (rl *ReqLog) Finish(rle *ReqLogEntry) { rl.lock.Lock() defer rl.lock.Unlock() diff --git a/config/api.go b/config/api.go index b36b10803..a626a272a 100644 --- a/config/api.go +++ b/config/api.go @@ -1,5 +1,63 @@ package config -type API struct { - HTTPHeaders map[string][]string // HTTP headers to return with the API. +import ( + "encoding/base64" + "strings" +) + +const ( + APITag = "API" + AuthorizationTag = "Authorizations" +) + +type RPCAuthScope struct { + // AuthSecret is the secret that will be compared to the HTTP "Authorization". + // header. A secret is in the format "type:value". Check the documentation for + // supported types. + AuthSecret string + + // AllowedPaths is an explicit list of RPC path prefixes to allow. + // By default, none are allowed. ["/api/v0"] exposes all RPCs. + AllowedPaths []string +} + +type API struct { + // HTTPHeaders are the HTTP headers to return with the API. + HTTPHeaders map[string][]string + + // Authorization is a map of authorizations used to authenticate in the API. + // If the map is empty, then the RPC API is exposed to everyone. Check the + // documentation for more details. + Authorizations map[string]*RPCAuthScope `json:",omitempty"` +} + +// ConvertAuthSecret converts the given secret in the format "type:value" into an +// HTTP Authorization header value. It can handle 'bearer' and 'basic' as type. +// If type exists and is not known, an empty string is returned. If type does not +// exist, 'bearer' type is assumed. +func ConvertAuthSecret(secret string) string { + if secret == "" { + return secret + } + + split := strings.SplitN(secret, ":", 2) + if len(split) < 2 { + // No prefix: assume bearer token. + return "Bearer " + secret + } + + if strings.HasPrefix(secret, "basic:") { + if strings.Contains(split[1], ":") { + // Assume basic:user:password + return "Basic " + base64.StdEncoding.EncodeToString([]byte(split[1])) + } else { + // Assume already base64 encoded. + return "Basic " + split[1] + } + } else if strings.HasPrefix(secret, "bearer:") { + return "Bearer " + split[1] + } + + // Unknown. Type is present, but we can't handle it. + return "" } diff --git a/config/api_test.go b/config/api_test.go new file mode 100644 index 000000000..daf8a5375 --- /dev/null +++ b/config/api_test.go @@ -0,0 +1,22 @@ +package config + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestConvertAuthSecret(t *testing.T) { + for _, testCase := range []struct { + input string + output string + }{ + {"", ""}, + {"someToken", "Bearer someToken"}, + {"bearer:someToken", "Bearer someToken"}, + {"basic:user:pass", "Basic dXNlcjpwYXNz"}, + {"basic:dXNlcjpwYXNz", "Basic dXNlcjpwYXNz"}, + } { + assert.Equal(t, testCase.output, ConvertAuthSecret(testCase.input)) + } +} diff --git a/config/autonat.go b/config/autonat.go index 64856faa6..e8940d14e 100644 --- a/config/autonat.go +++ b/config/autonat.go @@ -20,6 +20,9 @@ const ( // AutoNATServiceDisabled indicates that the user has disabled the // AutoNATService. AutoNATServiceDisabled + // AutoNATServiceEnabledV1Only forces use of V1 and disables V2 + // (used for testing) + AutoNATServiceEnabledV1Only ) func (m *AutoNATServiceMode) UnmarshalText(text []byte) error { @@ -30,6 +33,8 @@ func (m *AutoNATServiceMode) UnmarshalText(text []byte) error { *m = AutoNATServiceEnabled case "disabled": *m = AutoNATServiceDisabled + case "legacy-v1": + *m = AutoNATServiceEnabledV1Only default: return fmt.Errorf("unknown autonat mode: %s", string(text)) } @@ -44,6 +49,8 @@ func (m AutoNATServiceMode) MarshalText() ([]byte, error) { return []byte("enabled"), nil case AutoNATServiceDisabled: return []byte("disabled"), nil + case AutoNATServiceEnabledV1Only: + return []byte("legacy-v1"), nil default: return nil, fmt.Errorf("unknown autonat mode: %d", m) } @@ -64,7 +71,7 @@ type AutoNATConfig struct { Throttle *AutoNATThrottleConfig `json:",omitempty"` } -// AutoNATThrottleConfig configures the throttle limites +// AutoNATThrottleConfig configures the throttle limites. type AutoNATThrottleConfig struct { // GlobalLimit and PeerLimit sets the global and per-peer dialback // limits. The AutoNAT service will only perform the specified number of diff --git a/config/autotls.go b/config/autotls.go new file mode 100644 index 000000000..67ada23ab --- /dev/null +++ b/config/autotls.go @@ -0,0 +1,30 @@ +package config + +import p2pforge "github.com/ipshipyard/p2p-forge/client" + +// AutoTLS includes optional configuration of p2p-forge client of service +// for obtaining a domain and TLS certificate to improve connectivity for web +// browser clients. More: https://github.com/ipshipyard/p2p-forge#readme +type AutoTLS struct { + // Enables the p2p-forge feature + Enabled Flag `json:",omitempty"` + + // Optional override of the parent domain that will be used + DomainSuffix *OptionalString `json:",omitempty"` + + // Optional override of HTTP API that acts as ACME DNS-01 Challenge broker + RegistrationEndpoint *OptionalString `json:",omitempty"` + + // Optional Authorization token, used with private/test instances of p2p-forge + RegistrationToken *OptionalString `json:",omitempty"` + + // Optional override of CA ACME API used by p2p-forge system + CAEndpoint *OptionalString `json:",omitempty"` +} + +const ( + DefaultAutoTLSEnabled = false // experimental, opt-in for now (https://github.com/ipfs/kubo/pull/10521) + DefaultDomainSuffix = p2pforge.DefaultForgeDomain + DefaultRegistrationEndpoint = p2pforge.DefaultForgeEndpoint + DefaultCAEndpoint = p2pforge.DefaultCAEndpoint +) diff --git a/config/bootstrap_peers.go b/config/bootstrap_peers.go index 6222cd623..1671d9f81 100644 --- a/config/bootstrap_peers.go +++ b/config/bootstrap_peers.go @@ -19,8 +19,8 @@ var DefaultBootstrapAddresses = []string{ "/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa", "/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb", "/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt", - "/ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", // mars.i.ipfs.io - "/ip4/104.131.131.82/udp/4001/quic/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", // mars.i.ipfs.io + "/ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", // mars.i.ipfs.io + "/ip4/104.131.131.82/udp/4001/quic-v1/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", // mars.i.ipfs.io } // ErrInvalidPeerAddr signals an address is not a valid peer address. @@ -36,7 +36,7 @@ func (c *Config) BootstrapPeers() ([]peer.AddrInfo, error) { func DefaultBootstrapPeers() ([]peer.AddrInfo, error) { ps, err := ParseBootstrapPeers(DefaultBootstrapAddresses) if err != nil { - return nil, fmt.Errorf(`failed to parse hardcoded bootstrap peers: %s + return nil, fmt.Errorf(`failed to parse hardcoded bootstrap peers: %w This is a problem with the ipfs codebase. Please report it to the dev team`, err) } return ps, nil diff --git a/config/config.go b/config/config.go index 93494265d..91fb219a2 100644 --- a/config/config.go +++ b/config/config.go @@ -9,7 +9,7 @@ import ( "path/filepath" "strings" - "github.com/mitchellh/go-homedir" + "github.com/ipfs/kubo/misc/fsutil" ) // Config is used to load ipfs config files. @@ -26,6 +26,7 @@ type Config struct { API API // local node's API settings Swarm SwarmConfig AutoNAT AutoNATConfig + AutoTLS AutoTLS Pubsub PubsubConfig Peering Peering DNS DNS @@ -36,27 +37,29 @@ type Config struct { Experimental Experiments Plugins Plugins Pinning Pinning + Import Import + Version Version Internal Internal // experimental/unstable options } const ( - // DefaultPathName is the default config dir name + // DefaultPathName is the default config dir name. DefaultPathName = ".ipfs" // DefaultPathRoot is the path to the default config dir location. DefaultPathRoot = "~/" + DefaultPathName - // DefaultConfigFile is the filename of the configuration file + // DefaultConfigFile is the filename of the configuration file. DefaultConfigFile = "config" // EnvDir is the environment variable used to change the path root. EnvDir = "IPFS_PATH" ) -// PathRoot returns the default configuration root directory +// PathRoot returns the default configuration root directory. func PathRoot() (string, error) { dir := os.Getenv(EnvDir) var err error if len(dir) == 0 { - dir, err = homedir.Expand(DefaultPathRoot) + dir, err = fsutil.ExpandHome(DefaultPathRoot) } return dir, err } @@ -83,7 +86,7 @@ func Path(configroot, extension string) (string, error) { // - If the user-provided configuration file path is only a file name, use the // configuration root directory, otherwise use only the user-provided path // and ignore the configuration root. -func Filename(configroot string, userConfigFile string) (string, error) { +func Filename(configroot, userConfigFile string) (string, error) { if userConfigFile == "" { return Path(configroot, DefaultConfigFile) } @@ -95,7 +98,7 @@ func Filename(configroot string, userConfigFile string) (string, error) { return userConfigFile, nil } -// HumanOutput gets a config value ready for printing +// HumanOutput gets a config value ready for printing. func HumanOutput(value interface{}) ([]byte, error) { s, ok := value.(string) if ok { @@ -104,7 +107,7 @@ func HumanOutput(value interface{}) ([]byte, error) { return Marshal(value) } -// Marshal configuration with JSON +// Marshal configuration with JSON. func Marshal(value interface{}) ([]byte, error) { // need to prettyprint, hence MarshalIndent, instead of Encoder return json.MarshalIndent(value, "", " ") @@ -117,7 +120,7 @@ func FromMap(v map[string]interface{}) (*Config, error) { } var conf Config if err := json.NewDecoder(buf).Decode(&conf); err != nil { - return nil, fmt.Errorf("failure to decode config: %s", err) + return nil, fmt.Errorf("failure to decode config: %w", err) } return &conf, nil } @@ -129,7 +132,7 @@ func ToMap(conf *Config) (map[string]interface{}, error) { } var m map[string]interface{} if err := json.NewDecoder(buf).Decode(&m); err != nil { - return nil, fmt.Errorf("failure to decode config: %s", err) + return nil, fmt.Errorf("failure to decode config: %w", err) } return m, nil } @@ -140,11 +143,11 @@ func (c *Config) Clone() (*Config, error) { var buf bytes.Buffer if err := json.NewEncoder(&buf).Encode(c); err != nil { - return nil, fmt.Errorf("failure to encode config: %s", err) + return nil, fmt.Errorf("failure to encode config: %w", err) } if err := json.NewDecoder(&buf).Decode(&newConfig); err != nil { - return nil, fmt.Errorf("failure to decode config: %s", err) + return nil, fmt.Errorf("failure to decode config: %w", err) } return &newConfig, nil diff --git a/config/datastore.go b/config/datastore.go index 2b2bcb518..1a5994a17 100644 --- a/config/datastore.go +++ b/config/datastore.go @@ -26,7 +26,7 @@ type Datastore struct { } // DataStorePath returns the default data store path given a configuration root -// (set an empty string to have the default configuration root) +// (set an empty string to have the default configuration root). func DataStorePath(configroot string) (string, error) { return Path(configroot, DefaultDataStoreDirectory) } diff --git a/config/discovery.go b/config/discovery.go index 274ae051c..4ddc3a82f 100644 --- a/config/discovery.go +++ b/config/discovery.go @@ -6,8 +6,4 @@ type Discovery struct { type MDNS struct { Enabled bool - - // DEPRECATED: the time between discovery rounds is no longer configurable - // See: https://github.com/ipfs/go-ipfs/pull/9048#discussion_r906814717 - Interval *OptionalInteger `json:",omitempty"` } diff --git a/config/dns.go b/config/dns.go index 444d99088..8e1fc85a5 100644 --- a/config/dns.go +++ b/config/dns.go @@ -1,6 +1,6 @@ package config -// DNS specifies DNS resolution rules using custom resolvers +// DNS specifies DNS resolution rules using custom resolvers. type DNS struct { // Resolvers is a map of FQDNs to URLs for custom DNS resolution. // URLs starting with `https://` indicate DoH endpoints. diff --git a/config/experiments.go b/config/experiments.go index 7ad87c853..fab1f953c 100644 --- a/config/experiments.go +++ b/config/experiments.go @@ -1,12 +1,16 @@ package config type Experiments struct { - FilestoreEnabled bool - UrlstoreEnabled bool - ShardingEnabled bool `json:",omitempty"` // deprecated by autosharding: https://github.com/ipfs/kubo/pull/8527 - GraphsyncEnabled bool - Libp2pStreamMounting bool - P2pHttpProxy bool //nolint - StrategicProviding bool - AcceleratedDHTClient bool + FilestoreEnabled bool + UrlstoreEnabled bool + ShardingEnabled bool `json:",omitempty"` // deprecated by autosharding: https://github.com/ipfs/kubo/pull/8527 + Libp2pStreamMounting bool + P2pHttpProxy bool //nolint + StrategicProviding bool + OptimisticProvide bool + OptimisticProvideJobsPoolSize int + GatewayOverLibp2p bool `json:",omitempty"` + + GraphsyncEnabled graphsyncEnabled `json:",omitempty"` + AcceleratedDHTClient experimentalAcceleratedDHTClient `json:",omitempty"` } diff --git a/config/gateway.go b/config/gateway.go index ad01b263b..35af598b4 100644 --- a/config/gateway.go +++ b/config/gateway.go @@ -1,10 +1,15 @@ package config -const DefaultInlineDNSLink = false +const ( + DefaultInlineDNSLink = false + DefaultDeserializedResponses = true + DefaultDisableHTMLErrors = false + DefaultExposeRoutingAPI = false +) type GatewaySpec struct { // Paths is explicit list of path prefixes that should be handled by - // this gateway. Example: `["/ipfs", "/ipns", "/api"]` + // this gateway. Example: `["/ipfs", "/ipns"]` Paths []string // UseSubdomains indicates whether or not this gateway uses subdomains @@ -25,11 +30,15 @@ type GatewaySpec struct { // (FQDN) into a single DNS label in order to interop with wildcard TLS certs // and Origin per CID isolation provided by rules like https://publicsuffix.org InlineDNSLink Flag + + // DeserializedResponses configures this gateway to respond to deserialized + // responses. Disabling this option enables a Trustless Gateway, as per: + // https://specs.ipfs.tech/http-gateways/trustless-gateway/. + DeserializedResponses Flag } // Gateway contains options for the HTTP gateway server. type Gateway struct { - // HTTPHeaders configures the headers that should be returned by this // gateway. HTTPHeaders map[string][]string // HTTP headers to return with the gateway @@ -38,16 +47,6 @@ type Gateway struct { // should be redirected. RootRedirect string - // Writable enables PUT/POST request handling by this gateway. Usually, - // writing is done through the API, not the gateway. - Writable bool - - // PathPrefixes was removed: https://github.com/ipfs/go-ipfs/issues/7702 - PathPrefixes []string - - // FIXME: Not yet implemented: https://github.com/ipfs/kubo/issues/8059 - APICommands []string - // NoFetch configures the gateway to _not_ fetch blocks in response to // requests. NoFetch bool @@ -57,7 +56,21 @@ type Gateway struct { // This flag can be overridden per FQDN in PublicGateways. NoDNSLink bool + // DeserializedResponses configures this gateway to respond to deserialized + // requests. Disabling this option enables a Trustless only gateway, as per: + // https://specs.ipfs.tech/http-gateways/trustless-gateway/. This can + // be overridden per FQDN in PublicGateways. + DeserializedResponses Flag + + // DisableHTMLErrors disables pretty HTML pages when an error occurs. Instead, a `text/plain` + // page will be sent with the raw error message. + DisableHTMLErrors Flag + // PublicGateways configures behavior of known public gateways. // Each key is a fully qualified domain name (FQDN). PublicGateways map[string]*GatewaySpec + + // ExposeRoutingAPI configures the gateway port to expose + // routing system as HTTP API at /routing/v1 (https://specs.ipfs.tech/routing/http-routing-v1/). + ExposeRoutingAPI Flag } diff --git a/config/identity.go b/config/identity.go index 6117365c9..5b661dfb4 100644 --- a/config/identity.go +++ b/config/identity.go @@ -6,9 +6,11 @@ import ( ic "github.com/libp2p/go-libp2p/core/crypto" ) -const IdentityTag = "Identity" -const PrivKeyTag = "PrivKey" -const PrivKeySelector = IdentityTag + "." + PrivKeyTag +const ( + IdentityTag = "Identity" + PrivKeyTag = "PrivKey" + PrivKeySelector = IdentityTag + "." + PrivKeyTag +) // Identity tracks the configuration of the local node's identity. type Identity struct { @@ -16,7 +18,7 @@ type Identity struct { PrivKey string `json:",omitempty"` } -// DecodePrivateKey is a helper to decode the users PrivateKey +// DecodePrivateKey is a helper to decode the users PrivateKey. func (i *Identity) DecodePrivateKey(passphrase string) (ic.PrivKey, error) { pkb, err := base64.StdEncoding.DecodeString(i.PrivKey) if err != nil { diff --git a/config/import.go b/config/import.go new file mode 100644 index 000000000..10af4edfa --- /dev/null +++ b/config/import.go @@ -0,0 +1,17 @@ +package config + +const ( + DefaultCidVersion = 0 + DefaultUnixFSRawLeaves = false + DefaultUnixFSChunker = "size-262144" + DefaultHashFunction = "sha2-256" +) + +// Import configures the default options for ingesting data. This affects commands +// that ingest data, such as 'ipfs add', 'ipfs dag put, 'ipfs block put', 'ipfs files write'. +type Import struct { + CidVersion OptionalInteger + UnixFSRawLeaves Flag + UnixFSChunker OptionalString + HashFunction OptionalString +} diff --git a/config/init.go b/config/init.go index e91b24871..6099712f4 100644 --- a/config/init.go +++ b/config/init.go @@ -7,7 +7,7 @@ import ( "io" "time" - "github.com/ipfs/interface-go-ipfs-core/options" + "github.com/ipfs/kubo/core/coreiface/options" "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/peer" ) @@ -65,15 +65,8 @@ func InitWithIdentity(identity Identity) (*Config, error) { Gateway: Gateway{ RootRedirect: "", - Writable: false, NoFetch: false, - PathPrefixes: []string{}, - HTTPHeaders: map[string][]string{ - "Access-Control-Allow-Origin": {"*"}, - "Access-Control-Allow-Methods": {"GET"}, - "Access-Control-Allow-Headers": {"X-Requested-With", "Range", "User-Agent"}, - }, - APICommands: []string{}, + HTTPHeaders: map[string][]string{}, }, Reprovider: Reprovider{ Interval: nil, @@ -95,30 +88,34 @@ func InitWithIdentity(identity Identity) (*Config, error) { } // DefaultConnMgrHighWater is the default value for the connection managers -// 'high water' mark +// 'high water' mark. const DefaultConnMgrHighWater = 96 // DefaultConnMgrLowWater is the default value for the connection managers 'low -// water' mark +// water' mark. const DefaultConnMgrLowWater = 32 // DefaultConnMgrGracePeriod is the default value for the connection managers -// grace period +// grace period. const DefaultConnMgrGracePeriod = time.Second * 20 // DefaultConnMgrType is the default value for the connection managers // type. const DefaultConnMgrType = "basic" +// DefaultResourceMgrMinInboundConns is a MAGIC number that probably a good +// enough number of inbound conns to be a good network citizen. +const DefaultResourceMgrMinInboundConns = 800 + func addressesConfig() Addresses { return Addresses{ Swarm: []string{ "/ip4/0.0.0.0/tcp/4001", "/ip6/::/tcp/4001", - "/ip4/0.0.0.0/udp/4001/quic", + "/ip4/0.0.0.0/udp/4001/webrtc-direct", "/ip4/0.0.0.0/udp/4001/quic-v1", "/ip4/0.0.0.0/udp/4001/quic-v1/webtransport", - "/ip6/::/udp/4001/quic", + "/ip6/::/udp/4001/webrtc-direct", "/ip6/::/udp/4001/quic-v1", "/ip6/::/udp/4001/quic-v1/webtransport", }, @@ -141,6 +138,17 @@ func DefaultDatastoreConfig() Datastore { } } +func pebbleSpec() map[string]interface{} { + return map[string]interface{}{ + "type": "measure", + "prefix": "pebble.datastore", + "child": map[string]interface{}{ + "type": "pebbleds", + "path": "pebbleds", + }, + } +} + func badgerSpec() map[string]interface{} { return map[string]interface{}{ "type": "measure", @@ -240,7 +248,7 @@ func CreateIdentity(out io.Writer, opts []options.KeyGenerateOption) (Identity, if err != nil { return ident, err } - ident.PeerID = id.Pretty() + ident.PeerID = id.String() fmt.Fprintf(out, "peer identity: %s\n", ident.PeerID) return ident, nil } diff --git a/config/init_test.go b/config/init_test.go index b48b360c3..8a6888de3 100644 --- a/config/init_test.go +++ b/config/init_test.go @@ -4,7 +4,7 @@ import ( "bytes" "testing" - "github.com/ipfs/interface-go-ipfs-core/options" + "github.com/ipfs/kubo/core/coreiface/options" crypto_pb "github.com/libp2p/go-libp2p/core/crypto/pb" ) diff --git a/config/internal.go b/config/internal.go index a860c02c9..f43746534 100644 --- a/config/internal.go +++ b/config/internal.go @@ -2,9 +2,10 @@ package config type Internal struct { // All marked as omitempty since we are expecting to make changes to all subcomponents of Internal - Bitswap *InternalBitswap `json:",omitempty"` - UnixFSShardingSizeThreshold *OptionalString `json:",omitempty"` - Libp2pForceReachability *OptionalString `json:",omitempty"` + Bitswap *InternalBitswap `json:",omitempty"` + UnixFSShardingSizeThreshold *OptionalString `json:",omitempty"` + Libp2pForceReachability *OptionalString `json:",omitempty"` + BackupBootstrapInterval *OptionalDuration `json:",omitempty"` } type InternalBitswap struct { @@ -13,4 +14,5 @@ type InternalBitswap struct { EngineTaskWorkerCount OptionalInteger MaxOutstandingBytesPerPeer OptionalInteger ProviderSearchDelay OptionalDuration + WantHaveReplaceSize OptionalInteger } diff --git a/config/ipns.go b/config/ipns.go index d51910884..288421973 100644 --- a/config/ipns.go +++ b/config/ipns.go @@ -1,11 +1,23 @@ package config +import ( + "math" + "time" +) + +const ( + DefaultIpnsMaxCacheTTL = time.Duration(math.MaxInt64) +) + type Ipns struct { RepublishPeriod string RecordLifetime string ResolveCacheSize int + // MaxCacheTTL is the maximum duration IPNS entries are valid in the cache. + MaxCacheTTL *OptionalDuration `json:",omitempty"` + // Enable namesys pubsub (--enable-namesys-pubsub) UsePubsub Flag `json:",omitempty"` } diff --git a/config/migration.go b/config/migration.go index 27d4b3c70..e172988a9 100644 --- a/config/migration.go +++ b/config/migration.go @@ -5,7 +5,7 @@ const DefaultMigrationKeep = "cache" var DefaultMigrationDownloadSources = []string{"HTTPS", "IPFS"} // Migration configures how migrations are downloaded and if the downloads are -// added to IPFS locally +// added to IPFS locally. type Migration struct { // Sources in order of preference, where "IPFS" means use IPFS and "HTTPS" // means use default gateways. Any other values are interpreted as diff --git a/config/mounts.go b/config/mounts.go index b23d30b2e..dfdd1e5bf 100644 --- a/config/mounts.go +++ b/config/mounts.go @@ -1,6 +1,6 @@ package config -// Mounts stores the (string) mount points +// Mounts stores the (string) mount points. type Mounts struct { IPFS string IPNS string diff --git a/config/profile.go b/config/profile.go index 1a4013918..3a4329639 100644 --- a/config/profile.go +++ b/config/profile.go @@ -6,10 +6,10 @@ import ( "time" ) -// Transformer is a function which takes configuration and applies some filter to it +// Transformer is a function which takes configuration and applies some filter to it. type Transformer func(c *Config) error -// Profile contains the profile transformer the description of the profile +// Profile contains the profile transformer the description of the profile. type Profile struct { // Description briefly describes the functionality of the profile. Description string @@ -43,7 +43,7 @@ var defaultServerFilters = []string{ "/ip6/fe80::/ipcidr/10", } -// Profiles is a map holding configuration transformers. Docs are in docs/config.md +// Profiles is a map holding configuration transformers. Docs are in docs/config.md. var Profiles = map[string]Profile{ "server": { Description: `Disables local host discovery, recommended when @@ -82,6 +82,7 @@ is useful when using the daemon in test environments.`, } c.Swarm.DisableNatPortMap = true + c.Routing.LoopbackAddressesOnLanDHT = True c.Bootstrap = []string{} c.Discovery.MDNS.Enabled = false @@ -123,7 +124,7 @@ This profile may only be applied when first initializing the node. "flatfs": { Description: `Configures the node to use the flatfs datastore. -This is the most battle-tested and reliable datastore. +This is the most battle-tested and reliable datastore. You should use this datastore if: * You need a very simple and very reliable datastore, and you trust your @@ -134,7 +135,11 @@ You should use this datastore if: * You want to minimize memory usage. * You are ok with the default speed of data import, or prefer to use --nocopy. -This profile may only be applied when first initializing the node. +See configuration documentation at: +https://github.com/ipfs/kubo/blob/master/docs/datastores.md#flatfs + +NOTE: This profile may only be applied when first initializing node at IPFS_PATH + via 'ipfs init --profile flatfs' `, InitOnly: true, @@ -143,24 +148,54 @@ This profile may only be applied when first initializing the node. return nil }, }, - "badgerds": { - Description: `Configures the node to use the experimental badger datastore. + "pebbleds": { + Description: `Configures the node to use the pebble high-performance datastore. -Use this datastore if some aspects of performance, -especially the speed of adding many gigabytes of files, are critical. -However, be aware that: +Pebble is a LevelDB/RocksDB inspired key-value store focused on performance +and internal usage by CockroachDB. +You should use this datastore if: + +- You need a datastore that is focused on performance. +- You need reliability by default, but may choose to disable WAL for maximum performance when reliability is not critical. +- This datastore is good for multi-terabyte data sets. +- May benefit from tuning depending on read/write patterns and throughput. +- Performance is helped significantly by running on a system with plenty of memory. + +See configuration documentation at: +https://github.com/ipfs/kubo/blob/master/docs/datastores.md#pebbleds + +NOTE: This profile may only be applied when first initializing node at IPFS_PATH + via 'ipfs init --profile pebbleds' +`, + + InitOnly: true, + Transform: func(c *Config) error { + c.Datastore.Spec = pebbleSpec() + return nil + }, + }, + "badgerds": { + Description: `Configures the node to use the legacy badgerv1 datastore. + +NOTE: this is badger 1.x, which has known bugs and is no longer supported by the upstream team. +It is provided here only for pre-existing users, allowing them to migrate away to more modern datastore. + +Other caveats: * This datastore will not properly reclaim space when your datastore is smaller than several gigabytes. If you run IPFS with --enable-gc, you plan on storing very little data in your IPFS node, and disk usage is more critical than performance, consider using flatfs. -* This datastore uses up to several gigabytes of memory. +* This datastore uses up to several gigabytes of memory. * Good for medium-size datastores, but may run into performance issues if your dataset is bigger than a terabyte. -* The current implementation is based on old badger 1.x - which is no longer supported by the upstream team. -This profile may only be applied when first initializing the node.`, +See configuration documentation at: +https://github.com/ipfs/kubo/blob/master/docs/datastores.md#badgerds + +NOTE: This profile may only be applied when first initializing node at IPFS_PATH + via 'ipfs init --profile badgerds' +`, InitOnly: true, Transform: func(c *Config) error { @@ -174,10 +209,12 @@ functionality - performance of content discovery and data fetching may be degraded. `, Transform: func(c *Config) error { - c.Routing.Type = NewOptionalString("dhtclient") // TODO: https://github.com/ipfs/kubo/issues/9480 + // Disable "server" services (dht, autonat, limited relay) + c.Routing.Type = NewOptionalString("autoclient") c.AutoNAT.ServiceMode = AutoNATServiceDisabled - c.Reprovider.Interval = NewOptionalDuration(0) + c.Swarm.RelayService.Enabled = False + // Keep bare minimum connections around lowWater := int64(20) highWater := int64(40) gracePeriod := time.Minute @@ -188,6 +225,29 @@ fetching may be degraded. return nil }, }, + "announce-off": { + Description: `Disables Reprovide system (and announcing to Amino DHT). + + USE WITH CAUTION: + The main use case for this is setups with manual Peering.Peers config. + Data from this node will not be announced on the DHT. This will make + DHT-based routing an data retrieval impossible if this node is the only + one hosting it, and other peers are not already connected to it. +`, + Transform: func(c *Config) error { + c.Reprovider.Interval = NewOptionalDuration(0) // 0 disables periodic reprovide + c.Experimental.StrategicProviding = true // this is not a typo (the name is counter-intuitive) + return nil + }, + }, + "announce-on": { + Description: `Re-enables Reprovide system (reverts announce-off profile).`, + Transform: func(c *Config) error { + c.Reprovider.Interval = NewOptionalDuration(DefaultReproviderInterval) // have to apply explicit default because nil would be ignored + c.Experimental.StrategicProviding = false // this is not a typo (the name is counter-intuitive) + return nil + }, + }, "randomports": { Description: `Use a random port number for swarm.`, @@ -203,6 +263,28 @@ fetching may be degraded. return nil }, }, + "legacy-cid-v0": { + Description: `Makes UnixFS import produce legacy CIDv0 with no raw leaves, sha2-256 and 256 KiB chunks.`, + + Transform: func(c *Config) error { + c.Import.CidVersion = *NewOptionalInteger(0) + c.Import.UnixFSRawLeaves = False + c.Import.UnixFSChunker = *NewOptionalString("size-262144") + c.Import.HashFunction = *NewOptionalString("sha2-256") + return nil + }, + }, + "test-cid-v1": { + Description: `Makes UnixFS import produce modern CIDv1 with raw leaves, sha2-256 and 1 MiB chunks.`, + + Transform: func(c *Config) error { + c.Import.CidVersion = *NewOptionalInteger(1) + c.Import.UnixFSRawLeaves = True + c.Import.UnixFSChunker = *NewOptionalString("size-1048576") + c.Import.HashFunction = *NewOptionalString("sha2-256") + return nil + }, + }, } func getAvailablePort() (port int, err error) { diff --git a/config/pubsub.go b/config/pubsub.go index ba8084300..36f9a9881 100644 --- a/config/pubsub.go +++ b/config/pubsub.go @@ -1,5 +1,24 @@ package config +const ( + // LastSeenMessagesStrategy is a strategy that calculates the TTL countdown + // based on the last time a Pubsub message is seen. This means that if a message + // is received and then seen again within the specified TTL window, it + // won't be emitted until the TTL countdown expires from the last time the + // message was seen. + LastSeenMessagesStrategy = "last-seen" + + // FirstSeenMessagesStrategy is a strategy that calculates the TTL + // countdown based on the first time a Pubsub message is seen. This means that if + // a message is received and then seen again within the specified TTL + // window, it won't be emitted. + FirstSeenMessagesStrategy = "first-seen" + + // DefaultSeenMessagesStrategy is the strategy that is used by default if + // no Pubsub.SeenMessagesStrategy is specified. + DefaultSeenMessagesStrategy = LastSeenMessagesStrategy +) + type PubsubConfig struct { // Router can be either floodsub (legacy) or gossipsub (new and // backwards compatible). @@ -12,7 +31,11 @@ type PubsubConfig struct { // Enable pubsub (--enable-pubsub-experiment) Enabled Flag `json:",omitempty"` - // SeenMessagesTTL configures the duration after which a previously seen - // message ID can be forgotten about. + // SeenMessagesTTL is a value that controls the time window within which + // duplicate messages will be identified and won't be emitted. SeenMessagesTTL *OptionalDuration `json:",omitempty"` + + // SeenMessagesStrategy is a setting that determines how the time-to-live + // (TTL) countdown for deduplicating messages is calculated. + SeenMessagesStrategy *OptionalString `json:",omitempty"` } diff --git a/config/reprovider.go b/config/reprovider.go index 19ee1c58d..dae9ae6de 100644 --- a/config/reprovider.go +++ b/config/reprovider.go @@ -2,8 +2,10 @@ package config import "time" -const DefaultReproviderInterval = time.Hour * 22 // https://github.com/ipfs/kubo/pull/9326 -const DefaultReproviderStrategy = "all" +const ( + DefaultReproviderInterval = time.Hour * 22 // https://github.com/ipfs/kubo/pull/9326 + DefaultReproviderStrategy = "all" +) type Reprovider struct { Interval *OptionalDuration `json:",omitempty"` // Time period to reprovide locally stored objects to the network diff --git a/config/routing.go b/config/routing.go index 983e8606d..52d8b7296 100644 --- a/config/routing.go +++ b/config/routing.go @@ -3,41 +3,64 @@ package config import ( "encoding/json" "fmt" + "os" "runtime" + "strings" ) -// Routing defines configuration options for libp2p routing +const ( + DefaultAcceleratedDHTClient = false + DefaultLoopbackAddressesOnLanDHT = false +) + +var ( + // Default HTTP routers used in parallel to DHT when Routing.Type = "auto" + DefaultHTTPRouters = getEnvOrDefault("IPFS_HTTP_ROUTERS", []string{ + "https://cid.contact", // https://github.com/ipfs/kubo/issues/9422#issuecomment-1338142084 + }) + + // Default filter-protocols to pass along with delegated routing requests (as defined in IPIP-484) + // and also filter out locally + DefaultHTTPRoutersFilterProtocols = getEnvOrDefault("IPFS_HTTP_ROUTERS_FILTER_PROTOCOLS", []string{ + "unknown", // allow results without protocol list, we can do libp2p identify to test them + "transport-bitswap", + // TODO: add 'transport-ipfs-gateway-http' once https://github.com/ipfs/rainbow/issues/125 is addressed + }) +) + +// Routing defines configuration options for libp2p routing. type Routing struct { // Type sets default daemon routing mode. // - // Can be one of "auto", "dht", "dhtclient", "dhtserver", "none", or "custom". + // Can be one of "auto", "autoclient", "dht", "dhtclient", "dhtserver", "none", or "custom". // When unset or set to "auto", DHT and implicit routers are used. // When "custom" is set, user-provided Routing.Routers is used. Type *OptionalString `json:",omitempty"` + AcceleratedDHTClient Flag `json:",omitempty"` + + LoopbackAddressesOnLanDHT Flag `json:",omitempty"` + Routers Routers Methods Methods } type Router struct { - - // Currenly supported Types are "reframe", "dht", "parallel", "sequential". - // Reframe type allows to add other resolvers using the Reframe spec: - // https://github.com/ipfs/specs/tree/main/reframe - // In the future we will support "dht" and other Types here. + // Router type ID. See RouterType for more info. Type RouterType // Parameters are extra configuration that this router might need. - // A common one for reframe router is "Endpoint". + // A common one for HTTP router is "Endpoint". Parameters interface{} } -type Routers map[string]RouterParser -type Methods map[MethodName]Method +type ( + Routers map[string]RouterParser + Methods map[MethodName]Method +) func (m Methods) Check() error { - // Check supported methods for _, mn := range MethodNameList { _, ok := m[mn] @@ -82,8 +105,6 @@ func (r *RouterParser) UnmarshalJSON(b []byte) error { switch out.Type { case RouterTypeHTTP: p = &HTTPRouterParams{} - case RouterTypeReframe: - p = &ReframeRouterParams{} case RouterTypeDHT: p = &DHTRouterParams{} case RouterTypeSequential: @@ -107,11 +128,10 @@ func (r *RouterParser) UnmarshalJSON(b []byte) error { type RouterType string const ( - RouterTypeReframe RouterType = "reframe" - RouterTypeHTTP RouterType = "http" - RouterTypeDHT RouterType = "dht" - RouterTypeSequential RouterType = "sequential" - RouterTypeParallel RouterType = "parallel" + RouterTypeHTTP RouterType = "http" // HTTP JSON API for delegated routing systems (IPIP-337). + RouterTypeDHT RouterType = "dht" // DHT router. + RouterTypeSequential RouterType = "sequential" // Router helper to execute several routers sequentially. + RouterTypeParallel RouterType = "parallel" // Router helper to execute several routers in parallel. ) type DHTMode string @@ -134,12 +154,6 @@ const ( var MethodNameList = []MethodName{MethodNameProvide, MethodNameFindPeers, MethodNameFindProviders, MethodNameGetIPNS, MethodNamePutIPNS} -type ReframeRouterParams struct { - // Endpoint is the URL where the routing implementation will point to get the information. - // Usually used for reframe Routers. - Endpoint string -} - type HTTPRouterParams struct { // Endpoint is the URL where the routing implementation will point to get the information. Endpoint string @@ -183,3 +197,13 @@ type ConfigRouter struct { type Method struct { RouterName string } + +// getEnvOrDefault reads space or comma separated strings from env if present, +// and uses provided defaultValue as a fallback +func getEnvOrDefault(key string, defaultValue []string) []string { + if value, exists := os.LookupEnv(key); exists { + splitFunc := func(r rune) bool { return r == ',' || r == ' ' } + return strings.FieldsFunc(value, splitFunc) + } + return defaultValue +} diff --git a/config/routing_test.go b/config/routing_test.go index 49068f976..87a4f8cce 100644 --- a/config/routing_test.go +++ b/config/routing_test.go @@ -23,53 +23,51 @@ func TestRouterParameters(t *testing.T) { PublicIPNetwork: false, }, }}, - "router-reframe": {Router{ - Type: RouterTypeReframe, - Parameters: ReframeRouterParams{ - Endpoint: "reframe-endpoint", + "router-parallel": { + Router{ + Type: RouterTypeParallel, + Parameters: ComposableRouterParams{ + Routers: []ConfigRouter{ + { + RouterName: "router-dht", + Timeout: Duration{10 * time.Second}, + IgnoreErrors: true, + }, + { + RouterName: "router-dht", + Timeout: Duration{10 * time.Second}, + IgnoreErrors: false, + ExecuteAfter: &OptionalDuration{&sec}, + }, + }, + Timeout: &OptionalDuration{&min}, + }, }, - }}, - "router-parallel": {Router{ - Type: RouterTypeParallel, - Parameters: ComposableRouterParams{ - Routers: []ConfigRouter{ - { - RouterName: "router-dht", - Timeout: Duration{10 * time.Second}, - IgnoreErrors: true, - }, - { - RouterName: "router-reframe", - Timeout: Duration{10 * time.Second}, - IgnoreErrors: false, - ExecuteAfter: &OptionalDuration{&sec}, - }, - }, - Timeout: &OptionalDuration{&min}, - }}, }, - "router-sequential": {Router{ - Type: RouterTypeSequential, - Parameters: ComposableRouterParams{ - Routers: []ConfigRouter{ - { - RouterName: "router-dht", - Timeout: Duration{10 * time.Second}, - IgnoreErrors: true, - }, - { - RouterName: "router-reframe", - Timeout: Duration{10 * time.Second}, - IgnoreErrors: false, + "router-sequential": { + Router{ + Type: RouterTypeSequential, + Parameters: ComposableRouterParams{ + Routers: []ConfigRouter{ + { + RouterName: "router-dht", + Timeout: Duration{10 * time.Second}, + IgnoreErrors: true, + }, + { + RouterName: "router-dht", + Timeout: Duration{10 * time.Second}, + IgnoreErrors: false, + }, }, + Timeout: &OptionalDuration{&min}, }, - Timeout: &OptionalDuration{&min}, - }}, + }, }, }, Methods: Methods{ MethodNameFindPeers: { - RouterName: "router-reframe", + RouterName: "router-dht", }, MethodNameFindProviders: { RouterName: "router-dht", @@ -99,9 +97,6 @@ func TestRouterParameters(t *testing.T) { dhtp := r2.Routers["router-dht"].Parameters require.IsType(&DHTRouterParams{}, dhtp) - rp := r2.Routers["router-reframe"].Parameters - require.IsType(&ReframeRouterParams{}, rp) - sp := r2.Routers["router-sequential"].Parameters require.IsType(&ComposableRouterParams{}, sp) @@ -109,68 +104,24 @@ func TestRouterParameters(t *testing.T) { require.IsType(&ComposableRouterParams{}, pp) } -func TestRouterMissingParameters(t *testing.T) { - require := require.New(t) - - r := Routing{ - Type: NewOptionalString("custom"), - Routers: map[string]RouterParser{ - "router-wrong-reframe": {Router{ - Type: RouterTypeReframe, - Parameters: DHTRouterParams{ - Mode: "auto", - AcceleratedDHTClient: true, - PublicIPNetwork: false, - }, - }}, - }, - Methods: Methods{ - MethodNameFindPeers: { - RouterName: "router-wrong-reframe", - }, - MethodNameFindProviders: { - RouterName: "router-wrong-reframe", - }, - MethodNameGetIPNS: { - RouterName: "router-wrong-reframe", - }, - MethodNameProvide: { - RouterName: "router-wrong-reframe", - }, - MethodNamePutIPNS: { - RouterName: "router-wrong-reframe", - }, - }, - } - - out, err := json.Marshal(r) - require.NoError(err) - - r2 := &Routing{} - - err = json.Unmarshal(out, r2) - require.NoError(err) - require.Empty(r2.Routers["router-wrong-reframe"].Parameters.(*ReframeRouterParams).Endpoint) -} - func TestMethods(t *testing.T) { require := require.New(t) methodsOK := Methods{ MethodNameFindPeers: { - RouterName: "router-wrong-reframe", + RouterName: "router-wrong", }, MethodNameFindProviders: { - RouterName: "router-wrong-reframe", + RouterName: "router-wrong", }, MethodNameGetIPNS: { - RouterName: "router-wrong-reframe", + RouterName: "router-wrong", }, MethodNameProvide: { - RouterName: "router-wrong-reframe", + RouterName: "router-wrong", }, MethodNamePutIPNS: { - RouterName: "router-wrong-reframe", + RouterName: "router-wrong", }, } @@ -178,16 +129,16 @@ func TestMethods(t *testing.T) { methodsMissing := Methods{ MethodNameFindPeers: { - RouterName: "router-wrong-reframe", + RouterName: "router-wrong", }, MethodNameGetIPNS: { - RouterName: "router-wrong-reframe", + RouterName: "router-wrong", }, MethodNameProvide: { - RouterName: "router-wrong-reframe", + RouterName: "router-wrong", }, MethodNamePutIPNS: { - RouterName: "router-wrong-reframe", + RouterName: "router-wrong", }, } diff --git a/config/serialize/serialize.go b/config/serialize/serialize.go index d20e48118..7cb479f6b 100644 --- a/config/serialize/serialize.go +++ b/config/serialize/serialize.go @@ -28,19 +28,19 @@ func ReadConfigFile(filename string, cfg interface{}) error { } defer f.Close() if err := json.NewDecoder(f).Decode(cfg); err != nil { - return fmt.Errorf("failure to decode config: %s", err) + return fmt.Errorf("failure to decode config: %w", err) } return nil } // WriteConfigFile writes the config from `cfg` into `filename`. func WriteConfigFile(filename string, cfg interface{}) error { - err := os.MkdirAll(filepath.Dir(filename), 0755) + err := os.MkdirAll(filepath.Dir(filename), 0o755) if err != nil { return err } - f, err := atomicfile.New(filename, 0600) + f, err := atomicfile.New(filename, 0o600) if err != nil { return err } @@ -49,7 +49,7 @@ func WriteConfigFile(filename string, cfg interface{}) error { return encode(f, cfg) } -// encode configuration with JSON +// encode configuration with JSON. func encode(w io.Writer, value interface{}) error { // need to prettyprint, hence MarshalIndent, instead of Encoder buf, err := config.Marshal(value) diff --git a/config/serialize/serialize_test.go b/config/serialize/serialize_test.go index cc161c80d..bf4b8b16f 100644 --- a/config/serialize/serialize_test.go +++ b/config/serialize/serialize_test.go @@ -30,7 +30,7 @@ func TestConfig(t *testing.T) { } if runtime.GOOS != "windows" { // see https://golang.org/src/os/types_windows.go - if g := st.Mode().Perm(); g&0117 != 0 { + if g := st.Mode().Perm(); g&0o117 != 0 { t.Fatalf("config file should not be executable or accessible to world: %v", g) } } diff --git a/config/swarm.go b/config/swarm.go index d8fd17e94..4a04a0054 100644 --- a/config/swarm.go +++ b/config/swarm.go @@ -1,7 +1,5 @@ package config -import rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager" - type SwarmConfig struct { // AddrFilters specifies a set libp2p addresses that we should never // dial or receive connections from. @@ -15,26 +13,6 @@ type SwarmConfig struct { // DisableNatPortMap turns off NAT port mapping (UPnP, etc.). DisableNatPortMap bool - // DisableRelay explicitly disables the relay transport. - // - // Deprecated: This flag is deprecated and is overridden by - // `Swarm.Transports.Relay` if specified. - DisableRelay bool `json:",omitempty"` - - // EnableRelayHop makes this node act as a public relay v1 - // - // Deprecated: The circuit v1 protocol is deprecated. - // Use `Swarm.RelayService` to configure the circuit v2 relay. - EnableRelayHop bool `json:",omitempty"` - - // EnableAutoRelay enables the "auto relay user" feature. - // Node will find and use advertised public relays when it determines that - // it's not reachable from the public internet. - // - // Deprecated: This flag is deprecated and is overridden by - // `Swarm.RelayClient.Enabled` if specified. - EnableAutoRelay bool `json:",omitempty"` - // RelayClient controls the client side of "auto relay" feature. // When enabled, the node will use relays if it is not publicly reachable. RelayClient RelayClient @@ -87,8 +65,6 @@ type RelayService struct { // BufferSize is the size of the relayed connection buffers. BufferSize *OptionalInteger `json:",omitempty"` - // MaxReservationsPerPeer is the maximum number of reservations originating from the same peer. - MaxReservationsPerPeer *OptionalInteger `json:",omitempty"` // MaxReservationsPerIP is the maximum number of reservations originating from the same IP address. MaxReservationsPerIP *OptionalInteger `json:",omitempty"` // MaxReservationsPerASN is the maximum number of reservations origination from the same ASN. @@ -100,12 +76,13 @@ type Transports struct { // listen on a transport, add the transport to your Addresses.Swarm. Network struct { // All default to on. - QUIC Flag `json:",omitempty"` - TCP Flag `json:",omitempty"` - Websocket Flag `json:",omitempty"` - Relay Flag `json:",omitempty"` - // except WebTransport which is experimental and optin. + QUIC Flag `json:",omitempty"` + TCP Flag `json:",omitempty"` + Websocket Flag `json:",omitempty"` + Relay Flag `json:",omitempty"` WebTransport Flag `json:",omitempty"` + // except WebRTCDirect which is experimental and opt-in. + WebRTCDirect Flag `json:",omitempty"` } // Security specifies the transports used to encrypt insecure network @@ -113,8 +90,6 @@ type Transports struct { Security struct { // Defaults to 100. TLS Priority `json:",omitempty"` - // Defaults to 200. - SECIO Priority `json:",omitempty"` // Defaults to 300. Noise Priority `json:",omitempty"` } @@ -124,12 +99,10 @@ type Transports struct { Multiplexers struct { // Defaults to 100. Yamux Priority `json:",omitempty"` - // Defaults to 200. - Mplex Priority `json:",omitempty"` } } -// ConnMgr defines configuration options for the libp2p connection manager +// ConnMgr defines configuration options for the libp2p connection manager. type ConnMgr struct { Type *OptionalString `json:",omitempty"` LowWater *OptionalInteger `json:",omitempty"` @@ -141,8 +114,8 @@ type ConnMgr struct { // type ResourceMgr struct { // Enables the Network Resource Manager feature, default to on. - Enabled Flag `json:",omitempty"` - Limits *rcmgr.LimitConfig `json:",omitempty"` + Enabled Flag `json:",omitempty"` + Limits swarmLimits `json:",omitempty"` MaxMemory *OptionalString `json:",omitempty"` MaxFileDescriptors *OptionalInteger `json:",omitempty"` diff --git a/config/types.go b/config/types.go index 1af244f9c..506139318 100644 --- a/config/types.go +++ b/config/types.go @@ -1,8 +1,10 @@ package config import ( + "bytes" "encoding/json" "fmt" + "io" "strings" "time" ) @@ -40,8 +42,10 @@ func (o Strings) MarshalJSON() ([]byte, error) { } } -var _ json.Unmarshaler = (*Strings)(nil) -var _ json.Marshaler = (*Strings)(nil) +var ( + _ json.Unmarshaler = (*Strings)(nil) + _ json.Marshaler = (*Strings)(nil) +) // Flag represents a ternary value: false (-1), default (0), or true (+1). // @@ -111,8 +115,10 @@ func (f Flag) String() string { } } -var _ json.Unmarshaler = (*Flag)(nil) -var _ json.Marshaler = (*Flag)(nil) +var ( + _ json.Unmarshaler = (*Flag)(nil) + _ json.Marshaler = (*Flag)(nil) +) // Priority represents a value with a priority where 0 means "default" and -1 // means "disabled". @@ -208,17 +214,19 @@ func (p Priority) String() string { } } -var _ json.Unmarshaler = (*Priority)(nil) -var _ json.Marshaler = (*Priority)(nil) +var ( + _ json.Unmarshaler = (*Priority)(nil) + _ json.Marshaler = (*Priority)(nil) +) // OptionalDuration wraps time.Duration to provide json serialization and deserialization. // -// NOTE: the zero value encodes to JSON nill +// NOTE: the zero value encodes to JSON nill. type OptionalDuration struct { value *time.Duration } -// NewOptionalDuration returns an OptionalDuration from a string +// NewOptionalDuration returns an OptionalDuration from a string. func NewOptionalDuration(d time.Duration) *OptionalDuration { return &OptionalDuration{value: &d} } @@ -264,8 +272,10 @@ func (d OptionalDuration) String() string { return d.value.String() } -var _ json.Unmarshaler = (*OptionalDuration)(nil) -var _ json.Marshaler = (*OptionalDuration)(nil) +var ( + _ json.Unmarshaler = (*OptionalDuration)(nil) + _ json.Marshaler = (*OptionalDuration)(nil) +) type Duration struct { time.Duration @@ -296,16 +306,23 @@ func (d *Duration) UnmarshalJSON(b []byte) error { } } -var _ json.Unmarshaler = (*Duration)(nil) -var _ json.Marshaler = (*Duration)(nil) +var ( + _ json.Unmarshaler = (*Duration)(nil) + _ json.Marshaler = (*Duration)(nil) +) // OptionalInteger represents an integer that has a default value // -// When encoded in json, Default is encoded as "null" +// When encoded in json, Default is encoded as "null". type OptionalInteger struct { value *int64 } +// NewOptionalInteger returns an OptionalInteger from a int64. +func NewOptionalInteger(v int64) *OptionalInteger { + return &OptionalInteger{value: &v} +} + // WithDefault resolves the integer with the given default. func (p *OptionalInteger) WithDefault(defaultValue int64) (value int64) { if p == nil || p.value == nil { @@ -314,7 +331,7 @@ func (p *OptionalInteger) WithDefault(defaultValue int64) (value int64) { return *p.value } -// IsDefault returns if this is a default optional integer +// IsDefault returns if this is a default optional integer. func (p *OptionalInteger) IsDefault() bool { return p == nil || p.value == nil } @@ -345,20 +362,22 @@ func (p OptionalInteger) String() string { if p.value == nil { return "default" } - return fmt.Sprintf("%d", p.value) + return fmt.Sprintf("%d", *p.value) } -var _ json.Unmarshaler = (*OptionalInteger)(nil) -var _ json.Marshaler = (*OptionalInteger)(nil) +var ( + _ json.Unmarshaler = (*OptionalInteger)(nil) + _ json.Marshaler = (*OptionalInteger)(nil) +) // OptionalString represents a string that has a default value // -// When encoded in json, Default is encoded as "null" +// When encoded in json, Default is encoded as "null". type OptionalString struct { value *string } -// NewOptionalString returns an OptionalString from a string +// NewOptionalString returns an OptionalString from a string. func NewOptionalString(s string) *OptionalString { return &OptionalString{value: &s} } @@ -371,7 +390,7 @@ func (p *OptionalString) WithDefault(defaultValue string) (value string) { return *p.value } -// IsDefault returns if this is a default optional integer +// IsDefault returns if this is a default optional integer. func (p *OptionalString) IsDefault() bool { return p == nil || p.value == nil } @@ -405,5 +424,84 @@ func (p OptionalString) String() string { return *p.value } -var _ json.Unmarshaler = (*OptionalInteger)(nil) -var _ json.Marshaler = (*OptionalInteger)(nil) +var ( + _ json.Unmarshaler = (*OptionalInteger)(nil) + _ json.Marshaler = (*OptionalInteger)(nil) +) + +type swarmLimits doNotUse + +var _ json.Unmarshaler = swarmLimits(false) + +func (swarmLimits) UnmarshalJSON(b []byte) error { + d := json.NewDecoder(bytes.NewReader(b)) + for { + switch tok, err := d.Token(); err { + case io.EOF: + return nil + case nil: + switch tok { + case json.Delim('{'), json.Delim('}'): + // accept empty objects + continue + } + //nolint + return fmt.Errorf("The Swarm.ResourceMgr.Limits configuration has been removed in Kubo 0.19 and should be empty or not present. To set custom libp2p limits, read https://github.com/ipfs/kubo/blob/master/docs/libp2p-resource-management.md#user-supplied-override-limits") + default: + return err + } + } +} + +type experimentalAcceleratedDHTClient doNotUse + +var _ json.Unmarshaler = experimentalAcceleratedDHTClient(false) + +func (experimentalAcceleratedDHTClient) UnmarshalJSON(b []byte) error { + d := json.NewDecoder(bytes.NewReader(b)) + for { + switch tok, err := d.Token(); err { + case io.EOF: + return nil + case nil: + switch tok { + case json.Delim('{'), json.Delim('}'): + // accept empty objects + continue + } + //nolint + return fmt.Errorf("The Experimental.AcceleratedDHTClient key has been moved to Routing.AcceleratedDHTClient in Kubo 0.21, please use this new key and remove the old one.") + default: + return err + } + } +} + +// doNotUse is a type you must not use, it should be struct{} but encoding/json +// does not support omitempty on structs and I can't be bothered to write custom +// marshalers on all structs that have a doNotUse field. +type doNotUse bool + +type graphsyncEnabled doNotUse + +var _ json.Unmarshaler = graphsyncEnabled(false) + +func (graphsyncEnabled) UnmarshalJSON(b []byte) error { + d := json.NewDecoder(bytes.NewReader(b)) + for { + switch tok, err := d.Token(); err { + case io.EOF: + return nil + case nil: + switch tok { + case json.Delim('{'), json.Delim('}'), false: + // accept empty objects and false + continue + } + //nolint + return fmt.Errorf("Support for Experimental.GraphsyncEnabled has been removed in Kubo 0.25.0, please remove this key. For more details see https://github.com/ipfs/kubo/pull/9747.") + default: + return err + } + } +} diff --git a/config/types_test.go b/config/types_test.go index caef2b112..7ea7506f1 100644 --- a/config/types_test.go +++ b/config/types_test.go @@ -129,7 +129,6 @@ func TestOneStrings(t *testing.T) { out, err := json.Marshal(Strings{"one"}) if err != nil { t.Fatal(err) - } expected := "\"one\"" if string(out) != expected { @@ -141,7 +140,6 @@ func TestNoStrings(t *testing.T) { out, err := json.Marshal(Strings{}) if err != nil { t.Fatal(err) - } expected := "null" if string(out) != expected { @@ -153,7 +151,6 @@ func TestManyStrings(t *testing.T) { out, err := json.Marshal(Strings{"one", "two"}) if err != nil { t.Fatal(err) - } expected := "[\"one\",\"two\"]" if string(out) != expected { diff --git a/config/version.go b/config/version.go new file mode 100644 index 000000000..8096107bb --- /dev/null +++ b/config/version.go @@ -0,0 +1,14 @@ +package config + +const DefaultSwarmCheckPercentThreshold = 5 + +// Version allows controling things like custom user agent and update checks. +type Version struct { + // Optional suffix to the AgentVersion presented by `ipfs id` and exposed + // via libp2p identify protocol. + AgentSuffix *OptionalString `json:",omitempty"` + + // Detect when to warn about new version when observed via libp2p identify + SwarmCheckEnabled Flag `json:",omitempty"` + SwarmCheckPercentThreshold *OptionalInteger `json:",omitempty"` +} diff --git a/core/bootstrap/bootstrap.go b/core/bootstrap/bootstrap.go deleted file mode 100644 index daa0a44d3..000000000 --- a/core/bootstrap/bootstrap.go +++ /dev/null @@ -1,209 +0,0 @@ -package bootstrap - -import ( - "context" - "errors" - "fmt" - "io" - "math/rand" - "sync" - "time" - - logging "github.com/ipfs/go-log" - "github.com/jbenet/goprocess" - "github.com/jbenet/goprocess/context" - "github.com/jbenet/goprocess/periodic" - "github.com/libp2p/go-libp2p/core/host" - "github.com/libp2p/go-libp2p/core/network" - "github.com/libp2p/go-libp2p/core/peer" - "github.com/libp2p/go-libp2p/core/peerstore" - "github.com/libp2p/go-libp2p/core/routing" -) - -var log = logging.Logger("bootstrap") - -// ErrNotEnoughBootstrapPeers signals that we do not have enough bootstrap -// peers to bootstrap correctly. -var ErrNotEnoughBootstrapPeers = errors.New("not enough bootstrap peers to bootstrap") - -// BootstrapConfig specifies parameters used in an IpfsNode's network -// bootstrapping process. -type BootstrapConfig struct { - // MinPeerThreshold governs whether to bootstrap more connections. If the - // node has less open connections than this number, it will open connections - // to the bootstrap nodes. From there, the routing system should be able - // to use the connections to the bootstrap nodes to connect to even more - // peers. Routing systems like the IpfsDHT do so in their own Bootstrap - // process, which issues random queries to find more peers. - MinPeerThreshold int - - // Period governs the periodic interval at which the node will - // attempt to bootstrap. The bootstrap process is not very expensive, so - // this threshold can afford to be small (<=30s). - Period time.Duration - - // ConnectionTimeout determines how long to wait for a bootstrap - // connection attempt before cancelling it. - ConnectionTimeout time.Duration - - // BootstrapPeers is a function that returns a set of bootstrap peers - // for the bootstrap process to use. This makes it possible for clients - // to control the peers the process uses at any moment. - BootstrapPeers func() []peer.AddrInfo -} - -// DefaultBootstrapConfig specifies default sane parameters for bootstrapping. -var DefaultBootstrapConfig = BootstrapConfig{ - MinPeerThreshold: 4, - Period: 30 * time.Second, - ConnectionTimeout: (30 * time.Second) / 3, // Perod / 3 -} - -func BootstrapConfigWithPeers(pis []peer.AddrInfo) BootstrapConfig { - cfg := DefaultBootstrapConfig - cfg.BootstrapPeers = func() []peer.AddrInfo { - return pis - } - return cfg -} - -// Bootstrap kicks off IpfsNode bootstrapping. This function will periodically -// check the number of open connections and -- if there are too few -- initiate -// connections to well-known bootstrap peers. It also kicks off subsystem -// bootstrapping (i.e. routing). -func Bootstrap(id peer.ID, host host.Host, rt routing.Routing, cfg BootstrapConfig) (io.Closer, error) { - - // make a signal to wait for one bootstrap round to complete. - doneWithRound := make(chan struct{}) - - if len(cfg.BootstrapPeers()) == 0 { - // We *need* to bootstrap but we have no bootstrap peers - // configured *at all*, inform the user. - log.Warn("no bootstrap nodes configured: go-ipfs may have difficulty connecting to the network") - } - - // the periodic bootstrap function -- the connection supervisor - periodic := func(worker goprocess.Process) { - ctx := goprocessctx.OnClosingContext(worker) - - if err := bootstrapRound(ctx, host, cfg); err != nil { - log.Debugf("%s bootstrap error: %s", id, err) - } - - <-doneWithRound - } - - // kick off the node's periodic bootstrapping - proc := periodicproc.Tick(cfg.Period, periodic) - proc.Go(periodic) // run one right now. - - // kick off Routing.Bootstrap - if rt != nil { - ctx := goprocessctx.OnClosingContext(proc) - if err := rt.Bootstrap(ctx); err != nil { - proc.Close() - return nil, err - } - } - - doneWithRound <- struct{}{} - close(doneWithRound) // it no longer blocks periodic - return proc, nil -} - -func bootstrapRound(ctx context.Context, host host.Host, cfg BootstrapConfig) error { - - ctx, cancel := context.WithTimeout(ctx, cfg.ConnectionTimeout) - defer cancel() - id := host.ID() - - // get bootstrap peers from config. retrieving them here makes - // sure we remain observant of changes to client configuration. - peers := cfg.BootstrapPeers() - // determine how many bootstrap connections to open - connected := host.Network().Peers() - if len(connected) >= cfg.MinPeerThreshold { - log.Debugf("%s core bootstrap skipped -- connected to %d (> %d) nodes", - id, len(connected), cfg.MinPeerThreshold) - return nil - } - numToDial := cfg.MinPeerThreshold - len(connected) - - // filter out bootstrap nodes we are already connected to - var notConnected []peer.AddrInfo - for _, p := range peers { - if host.Network().Connectedness(p.ID) != network.Connected { - notConnected = append(notConnected, p) - } - } - - // if connected to all bootstrap peer candidates, exit - if len(notConnected) < 1 { - log.Debugf("%s no more bootstrap peers to create %d connections", id, numToDial) - return ErrNotEnoughBootstrapPeers - } - - // connect to a random susbset of bootstrap candidates - randSubset := randomSubsetOfPeers(notConnected, numToDial) - - log.Debugf("%s bootstrapping to %d nodes: %s", id, numToDial, randSubset) - return bootstrapConnect(ctx, host, randSubset) -} - -func bootstrapConnect(ctx context.Context, ph host.Host, peers []peer.AddrInfo) error { - if len(peers) < 1 { - return ErrNotEnoughBootstrapPeers - } - - errs := make(chan error, len(peers)) - var wg sync.WaitGroup - for _, p := range peers { - - // performed asynchronously because when performed synchronously, if - // one `Connect` call hangs, subsequent calls are more likely to - // fail/abort due to an expiring context. - // Also, performed asynchronously for dial speed. - - wg.Add(1) - go func(p peer.AddrInfo) { - defer wg.Done() - log.Debugf("%s bootstrapping to %s", ph.ID(), p.ID) - - ph.Peerstore().AddAddrs(p.ID, p.Addrs, peerstore.PermanentAddrTTL) - if err := ph.Connect(ctx, p); err != nil { - log.Debugf("failed to bootstrap with %v: %s", p.ID, err) - errs <- err - return - } - log.Infof("bootstrapped with %v", p.ID) - }(p) - } - wg.Wait() - - // our failure condition is when no connection attempt succeeded. - // So drain the errs channel, counting the results. - close(errs) - count := 0 - var err error - for err = range errs { - if err != nil { - count++ - } - } - if count == len(peers) { - return fmt.Errorf("failed to bootstrap. %s", err) - } - return nil -} - -func randomSubsetOfPeers(in []peer.AddrInfo, max int) []peer.AddrInfo { - if max > len(in) { - max = len(in) - } - - out := make([]peer.AddrInfo, max) - for i, val := range rand.Perm(len(in))[:max] { - out[i] = in[val] - } - return out -} diff --git a/core/bootstrap/bootstrap_test.go b/core/bootstrap/bootstrap_test.go deleted file mode 100644 index 98a4a7827..000000000 --- a/core/bootstrap/bootstrap_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package bootstrap - -import ( - "testing" - - "github.com/libp2p/go-libp2p/core/peer" - "github.com/libp2p/go-libp2p/core/test" -) - -func TestSubsetWhenMaxIsGreaterThanLengthOfSlice(t *testing.T) { - var ps []peer.AddrInfo - sizeofSlice := 100 - for i := 0; i < sizeofSlice; i++ { - pid, err := test.RandPeerID() - if err != nil { - t.Fatal(err) - } - - ps = append(ps, peer.AddrInfo{ID: pid}) - } - out := randomSubsetOfPeers(ps, 2*sizeofSlice) - if len(out) != len(ps) { - t.Fail() - } -} diff --git a/core/builder.go b/core/builder.go index 7a1d6fef2..4c54ddf8c 100644 --- a/core/builder.go +++ b/core/builder.go @@ -7,7 +7,7 @@ import ( "sync" "time" - "github.com/ipfs/kubo/core/bootstrap" + "github.com/ipfs/boxo/bootstrap" "github.com/ipfs/kubo/core/node" "github.com/ipfs/go-metrics-interface" diff --git a/core/commands/add.go b/core/commands/add.go index 9f686e14b..908613025 100644 --- a/core/commands/add.go +++ b/core/commands/add.go @@ -5,29 +5,53 @@ import ( "fmt" "io" "os" - "path" + gopath "path" + "strconv" "strings" + "time" + "github.com/ipfs/kubo/config" "github.com/ipfs/kubo/core/commands/cmdenv" "github.com/cheggaaa/pb" + "github.com/ipfs/boxo/files" + mfs "github.com/ipfs/boxo/mfs" + "github.com/ipfs/boxo/path" cmds "github.com/ipfs/go-ipfs-cmds" - files "github.com/ipfs/go-ipfs-files" ipld "github.com/ipfs/go-ipld-format" - mfs "github.com/ipfs/go-mfs" - coreiface "github.com/ipfs/interface-go-ipfs-core" - "github.com/ipfs/interface-go-ipfs-core/options" + coreiface "github.com/ipfs/kubo/core/coreiface" + "github.com/ipfs/kubo/core/coreiface/options" mh "github.com/multiformats/go-multihash" ) // ErrDepthLimitExceeded indicates that the max depth has been exceeded. var ErrDepthLimitExceeded = fmt.Errorf("depth limit exceeded") +type TimeParts struct { + t *time.Time +} + +func (t TimeParts) MarshalJSON() ([]byte, error) { + return t.t.MarshalJSON() +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +// The time is expected to be a quoted string in RFC 3339 format. +func (t *TimeParts) UnmarshalJSON(data []byte) (err error) { + // Fractional seconds are handled implicitly by Parse. + tt, err := time.Parse("\"2006-01-02T15:04:05Z\"", string(data)) + *t = TimeParts{&tt} + return +} + type AddEvent struct { - Name string - Hash string `json:",omitempty"` - Bytes int64 `json:",omitempty"` - Size string `json:",omitempty"` + Name string + Hash string `json:",omitempty"` + Bytes int64 `json:",omitempty"` + Size string `json:",omitempty"` + Mode string `json:",omitempty"` + Mtime int64 `json:",omitempty"` + MtimeNsecs int `json:",omitempty"` } const ( @@ -48,6 +72,12 @@ const ( inlineOptionName = "inline" inlineLimitOptionName = "inline-limit" toFilesOptionName = "to-files" + + preserveModeOptionName = "preserve-mode" + preserveMtimeOptionName = "preserve-mtime" + modeOptionName = "mode" + mtimeOptionName = "mtime" + mtimeNsecsOptionName = "mtime-nsecs" ) const adderOutChanSize = 8 @@ -154,32 +184,34 @@ See 'dag export' and 'dag import' for more information. cmds.BoolOption(trickleOptionName, "t", "Use trickle-dag format for dag generation."), cmds.BoolOption(onlyHashOptionName, "n", "Only chunk and hash - do not write to disk."), cmds.BoolOption(wrapOptionName, "w", "Wrap files with a directory object."), - cmds.StringOption(chunkerOptionName, "s", "Chunking algorithm, size-[bytes], rabin-[min]-[avg]-[max] or buzhash").WithDefault("size-262144"), + cmds.StringOption(chunkerOptionName, "s", "Chunking algorithm, size-[bytes], rabin-[min]-[avg]-[max] or buzhash"), cmds.BoolOption(rawLeavesOptionName, "Use raw blocks for leaf nodes."), cmds.BoolOption(noCopyOptionName, "Add the file using filestore. Implies raw-leaves. (experimental)"), cmds.BoolOption(fstoreCacheOptionName, "Check the filestore for pre-existing blocks. (experimental)"), cmds.IntOption(cidVersionOptionName, "CID version. Defaults to 0 unless an option that depends on CIDv1 is passed. Passing version 1 will cause the raw-leaves option to default to true."), - cmds.StringOption(hashOptionName, "Hash function to use. Implies CIDv1 if not sha2-256. (experimental)").WithDefault("sha2-256"), + cmds.StringOption(hashOptionName, "Hash function to use. Implies CIDv1 if not sha2-256. (experimental)"), cmds.BoolOption(inlineOptionName, "Inline small blocks into CIDs. (experimental)"), cmds.IntOption(inlineLimitOptionName, "Maximum block size to inline. (experimental)").WithDefault(32), cmds.BoolOption(pinOptionName, "Pin locally to protect added files from garbage collection.").WithDefault(true), cmds.StringOption(toFilesOptionName, "Add reference to Files API (MFS) at the provided path."), + cmds.BoolOption(preserveModeOptionName, "Apply existing POSIX permissions to created UnixFS entries. Disables raw-leaves. (experimental)"), + cmds.BoolOption(preserveMtimeOptionName, "Apply existing POSIX modification time to created UnixFS entries. Disables raw-leaves. (experimental)"), + cmds.UintOption(modeOptionName, "Custom POSIX file mode to store in created UnixFS entries. Disables raw-leaves. (experimental)"), + cmds.Int64Option(mtimeOptionName, "Custom POSIX modification time to store in created UnixFS entries (seconds before or after the Unix Epoch). Disables raw-leaves. (experimental)"), + cmds.UintOption(mtimeNsecsOptionName, "Custom POSIX modification time (optional time fraction in nanoseconds)"), }, PreRun: func(req *cmds.Request, env cmds.Environment) error { quiet, _ := req.Options[quietOptionName].(bool) quieter, _ := req.Options[quieterOptionName].(bool) quiet = quiet || quieter - silent, _ := req.Options[silentOptionName].(bool) - if quiet || silent { - return nil - } - - // ipfs cli progress bar defaults to true unless quiet or silent is used - _, found := req.Options[progressOptionName].(bool) - if !found { - req.Options[progressOptionName] = true + if !quiet && !silent { + // ipfs cli progress bar defaults to true unless quiet or silent is used + _, found := req.Options[progressOptionName].(bool) + if !found { + req.Options[progressOptionName] = true + } } return nil @@ -190,10 +222,20 @@ See 'dag export' and 'dag import' for more information. return err } + nd, err := cmdenv.GetNode(env) + if err != nil { + return err + } + + cfg, err := nd.Repo.Config() + if err != nil { + return err + } + progress, _ := req.Options[progressOptionName].(bool) trickle, _ := req.Options[trickleOptionName].(bool) wrap, _ := req.Options[wrapOptionName].(bool) - hash, _ := req.Options[onlyHashOptionName].(bool) + onlyHash, _ := req.Options[onlyHashOptionName].(bool) silent, _ := req.Options[silentOptionName].(bool) chunker, _ := req.Options[chunkerOptionName].(string) dopin, _ := req.Options[pinOptionName].(bool) @@ -205,6 +247,46 @@ See 'dag export' and 'dag import' for more information. inline, _ := req.Options[inlineOptionName].(bool) inlineLimit, _ := req.Options[inlineLimitOptionName].(int) toFilesStr, toFilesSet := req.Options[toFilesOptionName].(string) + preserveMode, _ := req.Options[preserveModeOptionName].(bool) + preserveMtime, _ := req.Options[preserveMtimeOptionName].(bool) + mode, _ := req.Options[modeOptionName].(uint) + mtime, _ := req.Options[mtimeOptionName].(int64) + mtimeNsecs, _ := req.Options[mtimeNsecsOptionName].(uint) + + if chunker == "" { + chunker = cfg.Import.UnixFSChunker.WithDefault(config.DefaultUnixFSChunker) + } + + if hashFunStr == "" { + hashFunStr = cfg.Import.HashFunction.WithDefault(config.DefaultHashFunction) + } + + if !cidVerSet && !cfg.Import.CidVersion.IsDefault() { + cidVerSet = true + cidVer = int(cfg.Import.CidVersion.WithDefault(config.DefaultCidVersion)) + } + + if !rbset && cfg.Import.UnixFSRawLeaves != config.Default { + rbset = true + rawblks = cfg.Import.UnixFSRawLeaves.WithDefault(config.DefaultUnixFSRawLeaves) + } + + // Storing optional mode or mtime (UnixFS 1.5) requires root block + // to always be 'dag-pb' and not 'raw'. Below adjusts raw-leaves setting, if possible. + if preserveMode || preserveMtime || mode != 0 || mtime != 0 { + // Error if --raw-leaves flag was explicitly passed by the user. + // (let user make a decision to manually disable it and retry) + if rbset && rawblks { + return fmt.Errorf("%s can't be used with UnixFS metadata like mode or modification time", rawLeavesOptionName) + } + // No explicit preference from user, disable raw-leaves and continue + rbset = true + rawblks = false + } + + if onlyHash && toFilesSet { + return fmt.Errorf("%s and %s options are not compatible", onlyHashOptionName, toFilesOptionName) + } hashFunCode, ok := mh.Names[strings.ToLower(hashFunStr)] if !ok { @@ -232,12 +314,25 @@ See 'dag export' and 'dag import' for more information. options.Unixfs.Chunker(chunker), options.Unixfs.Pin(dopin), - options.Unixfs.HashOnly(hash), + options.Unixfs.HashOnly(onlyHash), options.Unixfs.FsCache(fscache), options.Unixfs.Nocopy(nocopy), options.Unixfs.Progress(progress), options.Unixfs.Silent(silent), + + options.Unixfs.PreserveMode(preserveMode), + options.Unixfs.PreserveMtime(preserveMtime), + } + + if mode != 0 { + opts = append(opts, options.Unixfs.Mode(os.FileMode(mode))) + } + + if mtime != 0 { + opts = append(opts, options.Unixfs.Mtime(mtime, uint32(mtimeNsecs))) + } else if mtimeNsecs != 0 { + return fmt.Errorf("option %q requires %q to be provided as well", mtimeNsecsOptionName, mtimeOptionName) } if cidVerSet { @@ -301,7 +396,7 @@ See 'dag export' and 'dag import' for more information. return } // if MFS destination is a dir, append filename to the dir path - toFilesDst += path.Base(addit.Name()) + toFilesDst += gopath.Base(addit.Name()) } // error if we try to overwrite a preexisting file destination @@ -310,14 +405,14 @@ See 'dag export' and 'dag import' for more information. return } - _, err = mfs.Lookup(ipfsNode.FilesRoot, path.Dir(toFilesDst)) + _, err = mfs.Lookup(ipfsNode.FilesRoot, gopath.Dir(toFilesDst)) if err != nil { - errCh <- fmt.Errorf("%s: MFS destination parent %q %q does not exist: %w", toFilesOptionName, toFilesDst, path.Dir(toFilesDst), err) + errCh <- fmt.Errorf("%s: MFS destination parent %q %q does not exist: %w", toFilesOptionName, toFilesDst, gopath.Dir(toFilesDst), err) return } var nodeAdded ipld.Node - nodeAdded, err = api.Dag().Get(req.Context, pathAdded.Cid()) + nodeAdded, err = api.Dag().Get(req.Context, pathAdded.RootCid()) if err != nil { errCh <- err return @@ -339,22 +434,43 @@ See 'dag export' and 'dag import' for more information. } h := "" - if output.Path != nil { - h = enc.Encode(output.Path.Cid()) + if (output.Path != path.ImmutablePath{}) { + h = enc.Encode(output.Path.RootCid()) } if !dir && addit.Name() != "" { output.Name = addit.Name() } else { - output.Name = path.Join(addit.Name(), output.Name) + output.Name = gopath.Join(addit.Name(), output.Name) } - if err := res.Emit(&AddEvent{ - Name: output.Name, - Hash: h, - Bytes: output.Bytes, - Size: output.Size, - }); err != nil { + output.Mode = addit.Node().Mode() + if ts := addit.Node().ModTime(); !ts.IsZero() { + output.Mtime = addit.Node().ModTime().Unix() + output.MtimeNsecs = addit.Node().ModTime().Nanosecond() + } + + addEvent := AddEvent{ + Name: output.Name, + Hash: h, + Bytes: output.Bytes, + Size: output.Size, + Mtime: output.Mtime, + MtimeNsecs: output.MtimeNsecs, + } + + if output.Mode != 0 { + addEvent.Mode = "0" + strconv.FormatUint(uint64(output.Mode), 8) + } + + if output.Mtime > 0 { + addEvent.Mtime = output.Mtime + if output.MtimeNsecs > 0 { + addEvent.MtimeNsecs = output.MtimeNsecs + } + } + + if err := res.Emit(&addEvent); err != nil { return err } } diff --git a/core/commands/bitswap.go b/core/commands/bitswap.go index d524263be..07f91fb0f 100644 --- a/core/commands/bitswap.go +++ b/core/commands/bitswap.go @@ -8,8 +8,8 @@ import ( e "github.com/ipfs/kubo/core/commands/e" humanize "github.com/dustin/go-humanize" - bitswap "github.com/ipfs/go-bitswap" - decision "github.com/ipfs/go-bitswap/decision" + bitswap "github.com/ipfs/boxo/bitswap" + "github.com/ipfs/boxo/bitswap/server" cidutil "github.com/ipfs/go-cidutil" cmds "github.com/ipfs/go-ipfs-cmds" peer "github.com/libp2p/go-libp2p/core/peer" @@ -109,7 +109,7 @@ var bitswapStatCmd = &cmds.Command{ } if !nd.IsOnline { - return cmds.Errorf(cmds.ErrClient, ErrNotOnline.Error()) + return cmds.Errorf(cmds.ErrClient, "unable to run offline: %s", ErrNotOnline) } bs, ok := nd.Exchange.(*bitswap.Bitswap) @@ -179,7 +179,7 @@ prints the ledger associated with a given peer. Arguments: []cmds.Argument{ cmds.StringArg("peer", true, false, "The PeerID (B58) of the ledger to inspect."), }, - Type: decision.Receipt{}, + Type: server.Receipt{}, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { nd, err := cmdenv.GetNode(env) if err != nil { @@ -203,7 +203,7 @@ prints the ledger associated with a given peer. return cmds.EmitOnce(res, bs.LedgerForPeer(partner)) }, Encoders: cmds.EncoderMap{ - cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *decision.Receipt) error { + cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *server.Receipt) error { fmt.Fprintf(w, "Ledger for %s\n"+ "Debt ratio:\t%f\n"+ "Exchanges:\t%d\n"+ diff --git a/core/commands/block.go b/core/commands/block.go index b0591b002..b4b0fd204 100644 --- a/core/commands/block.go +++ b/core/commands/block.go @@ -6,14 +6,15 @@ import ( "io" "os" - files "github.com/ipfs/go-ipfs-files" + "github.com/ipfs/boxo/files" + "github.com/ipfs/kubo/config" cmdenv "github.com/ipfs/kubo/core/commands/cmdenv" "github.com/ipfs/kubo/core/commands/cmdutils" + options "github.com/ipfs/kubo/core/coreiface/options" + cmds "github.com/ipfs/go-ipfs-cmds" - options "github.com/ipfs/interface-go-ipfs-core/options" - path "github.com/ipfs/interface-go-ipfs-core/path" mh "github.com/multiformats/go-multihash" ) @@ -66,13 +67,18 @@ on raw IPFS blocks. It outputs the following to stdout: return err } - b, err := api.Block().Stat(req.Context, path.New(req.Arguments[0])) + p, err := cmdutils.PathOrCidPath(req.Arguments[0]) + if err != nil { + return err + } + + b, err := api.Block().Stat(req.Context, p) if err != nil { return err } return cmds.EmitOnce(res, &BlockStat{ - Key: b.Path().Cid().String(), + Key: b.Path().RootCid().String(), Size: b.Size(), }) }, @@ -103,7 +109,12 @@ It takes a , and outputs the block to stdout. return err } - r, err := api.Block().Get(req.Context, path.New(req.Arguments[0])) + p, err := cmdutils.PathOrCidPath(req.Arguments[0]) + if err != nil { + return err + } + + r, err := api.Block().Get(req.Context, p) if err != nil { return err } @@ -143,7 +154,7 @@ only for backward compatibility when a legacy CIDv0 is required (--format=v0). }, Options: []cmds.Option{ cmds.StringOption(blockCidCodecOptionName, "Multicodec to use in returned CID").WithDefault("raw"), - cmds.StringOption(mhtypeOptionName, "Multihash hash function").WithDefault("sha2-256"), + cmds.StringOption(mhtypeOptionName, "Multihash hash function"), cmds.IntOption(mhlenOptionName, "Multihash hash length").WithDefault(-1), cmds.BoolOption(pinOptionName, "Pin added blocks recursively").WithDefault(false), cmdutils.AllowBigBlockOption, @@ -155,7 +166,21 @@ only for backward compatibility when a legacy CIDv0 is required (--format=v0). return err } + nd, err := cmdenv.GetNode(env) + if err != nil { + return err + } + + cfg, err := nd.Repo.Config() + if err != nil { + return err + } + mhtype, _ := req.Options[mhtypeOptionName].(string) + if mhtype == "" { + mhtype = cfg.Import.HashFunction.WithDefault(config.DefaultHashFunction) + } + mhtval, ok := mh.Names[mhtype] if !ok { return fmt.Errorf("unrecognized multihash function: %s", mhtype) @@ -169,7 +194,7 @@ only for backward compatibility when a legacy CIDv0 is required (--format=v0). cidCodec, _ := req.Options[blockCidCodecOptionName].(string) format, _ := req.Options[blockFormatOptionName].(string) // deprecated - // use of legacy 'format' needs to supress 'cid-codec' + // use of legacy 'format' needs to suppress 'cid-codec' if format != "" { if cidCodec != "" && cidCodec != "raw" { return fmt.Errorf("unable to use %q (deprecated) and a custom %q at the same time", blockFormatOptionName, blockCidCodecOptionName) @@ -200,7 +225,7 @@ only for backward compatibility when a legacy CIDv0 is required (--format=v0). } err = res.Emit(&BlockStat{ - Key: p.Path().Cid().String(), + Key: p.Path().RootCid().String(), Size: p.Size(), }) if err != nil { @@ -255,7 +280,12 @@ It takes a list of CIDs to remove from the local datastore.. // TODO: use batching coreapi when done for _, b := range req.Arguments { - rp, err := api.ResolvePath(req.Context, path.New(b)) + p, err := cmdutils.PathOrCidPath(b) + if err != nil { + return err + } + + rp, _, err := api.ResolvePath(req.Context, p) if err != nil { return err } @@ -263,7 +293,7 @@ It takes a list of CIDs to remove from the local datastore.. err = api.Block().Rm(req.Context, rp, options.Block.Force(force)) if err != nil { if err := res.Emit(&removedBlock{ - Hash: rp.Cid().String(), + Hash: rp.RootCid().String(), Error: err.Error(), }); err != nil { return err @@ -273,7 +303,7 @@ It takes a list of CIDs to remove from the local datastore.. if !quiet { err := res.Emit(&removedBlock{ - Hash: rp.Cid().String(), + Hash: rp.RootCid().String(), }) if err != nil { return err diff --git a/core/commands/bootstrap.go b/core/commands/bootstrap.go index 07cdcbe79..decf2b271 100644 --- a/core/commands/bootstrap.go +++ b/core/commands/bootstrap.go @@ -374,9 +374,7 @@ func bootstrapRemove(r repo.Repo, cfg *config.Config, toRemove []string) ([]stri removed = append(removed, p) continue } - var ( - keptAddrs, removedAddrs []ma.Multiaddr - ) + var keptAddrs, removedAddrs []ma.Multiaddr // remove specific addresses filter: for _, addr := range p.Addrs { diff --git a/core/commands/cat.go b/core/commands/cat.go index 151ac126e..6fa1f71b7 100644 --- a/core/commands/cat.go +++ b/core/commands/cat.go @@ -7,12 +7,12 @@ import ( "os" "github.com/ipfs/kubo/core/commands/cmdenv" + "github.com/ipfs/kubo/core/commands/cmdutils" "github.com/cheggaaa/pb" - "github.com/ipfs/go-ipfs-cmds" - "github.com/ipfs/go-ipfs-files" - "github.com/ipfs/interface-go-ipfs-core" - "github.com/ipfs/interface-go-ipfs-core/path" + "github.com/ipfs/boxo/files" + cmds "github.com/ipfs/go-ipfs-cmds" + iface "github.com/ipfs/kubo/core/coreiface" ) const ( @@ -127,8 +127,13 @@ func cat(ctx context.Context, api iface.CoreAPI, paths []string, offset int64, m if max == 0 { return nil, 0, nil } - for _, p := range paths { - f, err := api.Unixfs().Get(ctx, path.New(p)) + for _, pString := range paths { + p, err := cmdutils.PathOrCidPath(pString) + if err != nil { + return nil, 0, err + } + + f, err := api.Unixfs().Get(ctx, p) if err != nil { return nil, 0, err } diff --git a/core/commands/cid.go b/core/commands/cid.go index a6406a3e0..b2e8f131d 100644 --- a/core/commands/cid.go +++ b/core/commands/cid.go @@ -7,10 +7,10 @@ import ( "strings" "unicode" + verifcid "github.com/ipfs/boxo/verifcid" cid "github.com/ipfs/go-cid" cidutil "github.com/ipfs/go-cidutil" cmds "github.com/ipfs/go-ipfs-cmds" - verifcid "github.com/ipfs/go-verifcid" ipldmulticodec "github.com/ipld/go-ipld-prime/multicodec" mbase "github.com/multiformats/go-multibase" mc "github.com/multiformats/go-multicodec" @@ -80,11 +80,16 @@ The optional format string is a printf style format string: switch verStr { case "": - // noop + if baseStr != "" { + opts.verConv = toCidV1 + } case "0": if opts.newCodec != 0 && opts.newCodec != cid.DagProtobuf { return fmt.Errorf("cannot convert to CIDv0 with any codec other than dag-pb") } + if baseStr != "" && baseStr != "base58btc" { + return fmt.Errorf("cannot convert to CIDv0 with any multibase other than the implicit base58btc") + } opts.verConv = toCidV0 case "1": opts.verConv = toCidV1 @@ -377,7 +382,7 @@ var hashesCmd = &cmds.Command{ var res []CodeAndName // use mhash.Codes in case at some point there are multiple names for a given code for code, name := range mhash.Codes { - if !verifcid.IsGoodHash(code) { + if !verifcid.DefaultAllowlist.IsAllowed(code) { continue } res = append(res, CodeAndName{int(code), name}) diff --git a/core/commands/cid_test.go b/core/commands/cid_test.go new file mode 100644 index 000000000..106296282 --- /dev/null +++ b/core/commands/cid_test.go @@ -0,0 +1,111 @@ +package commands + +import ( + "testing" + + cmds "github.com/ipfs/go-ipfs-cmds" + "github.com/multiformats/go-multibase" +) + +func TestCidFmtCmd(t *testing.T) { + t.Parallel() + + // Test 'error when -v 0 is present and a custom -b is passed' + t.Run("ipfs cid format -b z -v 0", func(t *testing.T) { + t.Parallel() + + type testV0PresentAndCustomBaseCase struct { + MultibaseName string + ExpectedErrMsg string + } + + var testV0PresentAndCustomBaseCases []testV0PresentAndCustomBaseCase + + for _, e := range multibase.EncodingToStr { + var testCase testV0PresentAndCustomBaseCase + + if e == "base58btc" { + testCase.MultibaseName = e + testCase.ExpectedErrMsg = "" + testV0PresentAndCustomBaseCases = append(testV0PresentAndCustomBaseCases, testCase) + continue + } + testCase.MultibaseName = e + testCase.ExpectedErrMsg = "cannot convert to CIDv0 with any multibase other than the implicit base58btc" + testV0PresentAndCustomBaseCases = append(testV0PresentAndCustomBaseCases, testCase) + } + + for _, e := range testV0PresentAndCustomBaseCases { + + // Mock request + req := &cmds.Request{ + Options: map[string]interface{}{ + cidVerisonOptionName: "0", + cidMultibaseOptionName: e.MultibaseName, + cidFormatOptionName: "%s", + }, + } + + // Response emitter + resp := cmds.ResponseEmitter(nil) + + // Call the CidFmtCmd function with the mock request and response + err := cidFmtCmd.Run(req, resp, nil) + if err == nil && e.MultibaseName == "base58btc" { + continue + } + + errMsg := err.Error() + if errMsg != e.ExpectedErrMsg { + t.Errorf("Expected %s, got %s instead", e.ExpectedErrMsg, errMsg) + } + } + }) + + // Test 'upgrade CID to v1 when passing a custom -b and no -v is specified' + t.Run("ipfs cid format -b z", func(t *testing.T) { + t.Parallel() + + type testImplicitVersionAndCustomMultibaseCase struct { + Ver string + CidV1 string + CidV0 string + MultibaseName string + } + + var testCases = []testImplicitVersionAndCustomMultibaseCase{ + { + Ver: "", + CidV1: "zdj7WWwMSWGoyxYkkT7mHgYvr6tV8CYd77aYxxqSbg9HsiMcE", + CidV0: "QmPr755CxWUwt39C2Yiw4UGKrv16uZhSgeZJmoHUUS9TSJ", + MultibaseName: "z", + }, + { + Ver: "", + CidV1: "CAFYBEIDI7ZABPGG3S63QW3AJG2XAZNE4NJQPN777WLWYRAIDG3TE5QFN3A======", + CidV0: "QmVQVyEijmLb2cBQrowNQsaPbnUnJhfDK1sYe3wepm6ySf", + MultibaseName: "base32padupper", + }, + } + for _, e := range testCases { + // Mock request + req := &cmds.Request{ + Options: map[string]interface{}{ + cidVerisonOptionName: e.Ver, + cidMultibaseOptionName: e.MultibaseName, + cidFormatOptionName: "%s", + }, + } + + // Response emitter + resp := cmds.ResponseEmitter(nil) + + // Call the CidFmtCmd function with the mock request and response + err := cidFmtCmd.Run(req, resp, nil) + + if err != nil { + t.Error(err) + } + } + }) +} diff --git a/core/commands/cmdenv/cidbase.go b/core/commands/cmdenv/cidbase.go index 32f2c2157..55815f524 100644 --- a/core/commands/cmdenv/cidbase.go +++ b/core/commands/cmdenv/cidbase.go @@ -10,8 +10,10 @@ import ( mbase "github.com/multiformats/go-multibase" ) -var OptionCidBase = cmds.StringOption("cid-base", "Multibase encoding used for version 1 CIDs in output.") -var OptionUpgradeCidV0InOutput = cmds.BoolOption("upgrade-cidv0-in-output", "Upgrade version 0 to version 1 CIDs in output.") +var ( + OptionCidBase = cmds.StringOption("cid-base", "Multibase encoding used for version 1 CIDs in output.") + OptionUpgradeCidV0InOutput = cmds.BoolOption("upgrade-cidv0-in-output", "Upgrade version 0 to version 1 CIDs in output.") +) // GetCidEncoder processes the `cid-base` and `output-cidv1` options and // returns a encoder to use based on those parameters. diff --git a/core/commands/cmdenv/env.go b/core/commands/cmdenv/env.go index 2c5845890..fb538dc12 100644 --- a/core/commands/cmdenv/env.go +++ b/core/commands/cmdenv/env.go @@ -10,8 +10,8 @@ import ( cmds "github.com/ipfs/go-ipfs-cmds" logging "github.com/ipfs/go-log" - coreiface "github.com/ipfs/interface-go-ipfs-core" - options "github.com/ipfs/interface-go-ipfs-core/options" + coreiface "github.com/ipfs/kubo/core/coreiface" + options "github.com/ipfs/kubo/core/coreiface/options" ) var log = logging.Logger("core/commands/cmdenv") diff --git a/core/commands/cmdenv/file.go b/core/commands/cmdenv/file.go index 50ce748eb..8167ba54d 100644 --- a/core/commands/cmdenv/file.go +++ b/core/commands/cmdenv/file.go @@ -3,7 +3,7 @@ package cmdenv import ( "fmt" - files "github.com/ipfs/go-ipfs-files" + "github.com/ipfs/boxo/files" ) // GetFileArg returns the next file from the directory or an error diff --git a/core/commands/cmdutils/utils.go b/core/commands/cmdutils/utils.go index f4b6b40f4..be295f9e3 100644 --- a/core/commands/cmdutils/utils.go +++ b/core/commands/cmdutils/utils.go @@ -5,8 +5,9 @@ import ( cmds "github.com/ipfs/go-ipfs-cmds" + "github.com/ipfs/boxo/path" "github.com/ipfs/go-cid" - coreiface "github.com/ipfs/interface-go-ipfs-core" + coreiface "github.com/ipfs/kubo/core/coreiface" ) const ( @@ -47,5 +48,20 @@ func CheckBlockSize(req *cmds.Request, size uint64) error { return fmt.Errorf("produced block is over 1MiB: big blocks can't be exchanged with other peers. consider using UnixFS for automatic chunking of bigger files, or pass --allow-big-block to override") } return nil - +} + +// PathOrCidPath returns a path.Path built from the argument. It keeps the old +// behaviour by building a path from a CID string. +func PathOrCidPath(str string) (path.Path, error) { + p, err := path.NewPath(str) + if err == nil { + return p, nil + } + + if p, err := path.NewPath("/ipfs/" + str); err == nil { + return p, nil + } + + // Send back original err. + return nil, err } diff --git a/core/commands/commands.go b/core/commands/commands.go index 794e1a592..249f0ffbe 100644 --- a/core/commands/commands.go +++ b/core/commands/commands.go @@ -13,7 +13,7 @@ import ( "sort" "strings" - "github.com/ipfs/go-ipfs-cmds" + cmds "github.com/ipfs/go-ipfs-cmds" ) type commandEncoder struct { @@ -169,6 +169,33 @@ To install the completions permanently, they can be moved to return res.Emit(&buf) }, }, + "zsh": { + Helptext: cmds.HelpText{ + Tagline: "Generate zsh shell completions.", + ShortDescription: "Generates command completions for the zsh shell.", + LongDescription: ` +Generates command completions for the zsh shell. + +The simplest way to see it working is write the completions +to a file and then source it: + + > ipfs commands completion zsh > ipfs-completion.zsh + > source ./ipfs-completion.zsh + +To install the completions permanently, they can be moved to +/etc/zsh/completions or sourced from your ~/.zshrc file. +`, + }, + NoRemote: true, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + var buf bytes.Buffer + if err := writeZshCompletions(root, &buf); err != nil { + return err + } + res.SetLength(uint64(buf.Len())) + return res.Emit(&buf) + }, + }, "fish": { Helptext: cmds.HelpText{ Tagline: "Generate fish shell completions.", diff --git a/core/commands/commands_test.go b/core/commands/commands_test.go index ec4cc1bb6..b04a5459b 100644 --- a/core/commands/commands_test.go +++ b/core/commands/commands_test.go @@ -15,62 +15,6 @@ func collectPaths(prefix string, cmd *cmds.Command, out map[string]struct{}) { } } -func TestROCommands(t *testing.T) { - list := []string{ - "/block", - "/block/get", - "/block/stat", - "/cat", - "/commands", - "/commands/completion", - "/commands/completion/bash", - "/commands/completion/fish", - "/dag", - "/dag/get", - "/dag/resolve", - "/dag/stat", - "/dag/export", - "/dns", - "/get", - "/ls", - "/name", - "/name/resolve", - "/object", - "/object/data", - "/object/get", - "/object/links", - "/object/stat", - "/refs", - "/resolve", - "/version", - } - - cmdSet := make(map[string]struct{}) - collectPaths("", RootRO, cmdSet) - - for _, path := range list { - if _, ok := cmdSet[path]; !ok { - t.Errorf("%q not in result", path) - } else { - delete(cmdSet, path) - } - } - - for path := range cmdSet { - t.Errorf("%q in result but shouldn't be", path) - } - - for _, path := range list { - path = path[1:] // remove leading slash - split := strings.Split(path, "/") - sub, err := RootRO.Get(split) - if err != nil { - t.Errorf("error getting subcommand %q: %v", path, err) - } else if sub == nil { - t.Errorf("subcommand %q is nil even though there was no error", path) - } - } -} func TestCommands(t *testing.T) { list := []string{ "/add", @@ -101,6 +45,7 @@ func TestCommands(t *testing.T) { "/commands/completion", "/commands/completion/bash", "/commands/completion/fish", + "/commands/completion/zsh", "/config", "/config/edit", "/config/profile", @@ -115,12 +60,12 @@ func TestCommands(t *testing.T) { "/dag/resolve", "/dag/stat", "/dht", - "/dht/findpeer", + "/dht/query", "/dht/findprovs", + "/dht/findpeer", "/dht/get", "/dht/provide", "/dht/put", - "/dht/query", "/routing", "/routing/put", "/routing/get", @@ -133,9 +78,6 @@ func TestCommands(t *testing.T) { "/diag/cmds/set-time", "/diag/profile", "/diag/sys", - "/dns", - "/file", - "/file/ls", "/files", "/files/chcid", "/files/cp", @@ -147,6 +89,8 @@ func TestCommands(t *testing.T) { "/files/rm", "/files/stat", "/files/write", + "/files/chmod", + "/files/touch", "/filestore", "/filestore/dups", "/filestore/ls", @@ -161,6 +105,8 @@ func TestCommands(t *testing.T) { "/key/rename", "/key/rm", "/key/rotate", + "/key/sign", + "/key/verify", "/log", "/log/level", "/log/ls", @@ -173,6 +119,7 @@ func TestCommands(t *testing.T) { "/multibase/transcode", "/multibase/list", "/name", + "/name/inspect", "/name/publish", "/name/pubsub", "/name/pubsub/cancel", @@ -223,7 +170,6 @@ func TestCommands(t *testing.T) { "/refs", "/refs/local", "/repo", - "/repo/fsck", "/repo/gc", "/repo/migrate", "/repo/stat", @@ -247,20 +193,15 @@ func TestCommands(t *testing.T) { "/swarm/filters", "/swarm/filters/add", "/swarm/filters/rm", - "/swarm/limit", "/swarm/peers", "/swarm/peering", "/swarm/peering/add", "/swarm/peering/ls", "/swarm/peering/rm", - "/swarm/stats", - "/tar", - "/tar/add", - "/tar/cat", + "/swarm/resources", "/update", - "/urlstore", - "/urlstore/add", "/version", + "/version/check", "/version/deps", } diff --git a/core/commands/completion.go b/core/commands/completion.go index c4fb1ba41..2f5b8b61e 100644 --- a/core/commands/completion.go +++ b/core/commands/completion.go @@ -83,7 +83,7 @@ func commandToCompletions(name string, fullName string, cmd *cmds.Command) *comp return parsed } -var bashCompletionTemplate, fishCompletionTemplate *template.Template +var bashCompletionTemplate, fishCompletionTemplate, zshCompletionTemplate *template.Template func init() { commandTemplate := template.Must(template.New("command").Parse(` @@ -153,6 +153,28 @@ _ipfs() { {{ template "command" . }} } complete -o nosort -o nospace -o default -F _ipfs ipfs +`)) + + zshCompletionTemplate = template.Must(commandTemplate.New("root").Parse(`#!bin/zsh +autoload bashcompinit +bashcompinit +_ipfs_compgen() { +local oldifs="$IFS" +IFS=$'\n' +while read -r line; do + COMPREPLY+=("$line") +done < <(compgen "$@") +IFS="$oldifs" +} + +_ipfs() { +COMPREPLY=() +local index=1 +local argidx=0 +local word="${COMP_WORDS[COMP_CWORD]}" +{{ template "command" . }} +} +complete -o nosort -o nospace -o default -F _ipfs ipfs `)) fishCommandTemplate := template.Must(template.New("command").Parse(` @@ -208,7 +230,6 @@ complete -c ipfs --keep-order --no-files {{ template "command" . }} `)) - } // writeBashCompletions generates a bash completion script for the given command tree. @@ -222,3 +243,8 @@ func writeFishCompletions(cmd *cmds.Command, out io.Writer) error { cmds := commandToCompletions("ipfs", "", cmd) return fishCompletionTemplate.Execute(out, cmds) } + +func writeZshCompletions(cmd *cmds.Command, out io.Writer) error { + cmds := commandToCompletions("ipfs", "", cmd) + return zshCompletionTemplate.Execute(out, cmds) +} diff --git a/core/commands/config.go b/core/commands/config.go index 4d2ea4f0d..b52c05af2 100644 --- a/core/commands/config.go +++ b/core/commands/config.go @@ -208,6 +208,11 @@ NOTE: For security reasons, this command will omit your private key and remote s return err } + cfg, err = scrubValue(cfg, []string{config.APITag, config.AuthorizationTag}) + if err != nil { + return err + } + cfg, err = scrubOptionalValue(cfg, config.PinningConcealSelector) if err != nil { return err @@ -581,5 +586,4 @@ func getRemotePinningServices(r repo.Repo) (map[string]config.RemotePinningServi } } return oldServices, nil - } diff --git a/core/commands/config_test.go b/core/commands/config_test.go index 9e244f54f..5eb79c153 100644 --- a/core/commands/config_test.go +++ b/core/commands/config_test.go @@ -12,6 +12,5 @@ func TestScrubMapInternalDelete(t *testing.T) { } if len(m) != 0 { t.Errorf("expecting an empty map, got a non-empty map") - } } diff --git a/core/commands/dag/dag.go b/core/commands/dag/dag.go index df7762c00..ce5edb641 100644 --- a/core/commands/dag/dag.go +++ b/core/commands/dag/dag.go @@ -1,8 +1,11 @@ package dagcmd import ( + "encoding/csv" + "encoding/json" "fmt" "io" + "path" "github.com/ipfs/kubo/core/commands/cmdenv" "github.com/ipfs/kubo/core/commands/cmdutils" @@ -10,10 +13,6 @@ import ( cid "github.com/ipfs/go-cid" cidenc "github.com/ipfs/go-cidutil/cidenc" cmds "github.com/ipfs/go-ipfs-cmds" - ipfspath "github.com/ipfs/go-path" - //gipfree "github.com/ipld/go-ipld-prime/impl/free" - //gipselector "github.com/ipld/go-ipld-prime/traversal/selector" - //gipselectorbuilder "github.com/ipld/go-ipld-prime/traversal/selector/builder" ) const ( @@ -88,7 +87,7 @@ into an object of the specified format. cmds.StringOption("store-codec", "Codec that the stored object will be encoded with").WithDefault("dag-cbor"), cmds.StringOption("input-codec", "Codec that the input object is encoded in").WithDefault("dag-json"), cmds.BoolOption("pin", "Pin this object when adding."), - cmds.StringOption("hash", "Hash function to use").WithDefault("sha2-256"), + cmds.StringOption("hash", "Hash function to use"), cmdutils.AllowBigBlockOption, }, Run: dagPut, @@ -158,7 +157,7 @@ var DagResolveCmd = &cmds.Command{ } p := enc.Encode(out.Cid) if out.RemPath != "" { - p = ipfspath.Join([]string{p, out.RemPath}) + p = path.Join(p, out.RemPath) } fmt.Fprint(w, p) @@ -168,13 +167,6 @@ var DagResolveCmd = &cmds.Command{ Type: ResolveOutput{}, } -type importResult struct { - blockCount uint64 - blockBytesCount uint64 - roots map[cid.Cid]struct{} - err error -} - // DagImportCmd is a command for importing a car to ipfs var DagImportCmd = &cmds.Command{ Helptext: cmds.HelpText{ @@ -214,7 +206,6 @@ Specification of CAR formats: https://ipld.io/specs/transport/car/ Run: dagImport, Encoders: cmds.EncoderMap{ cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, event *CarImportOutput) error { - silent, _ := req.Options[silentOptionName].(bool) if silent { return nil @@ -242,11 +233,11 @@ Specification of CAR formats: https://ipld.io/specs/transport/car/ } if event.Root.PinErrorMsg != "" { - event.Root.PinErrorMsg = fmt.Sprintf("FAILED: %s", event.Root.PinErrorMsg) - } else { - event.Root.PinErrorMsg = "success" + return fmt.Errorf("pinning root %q FAILED: %s", enc.Encode(event.Root.Cid), event.Root.PinErrorMsg) } + event.Root.PinErrorMsg = "success" + _, err = fmt.Fprintf( w, "Pinned root\t%s\t%s\n", @@ -283,12 +274,83 @@ CAR file follows the CARv1 format: https://ipld.io/specs/transport/car/carv1/ // DagStat is a dag stat command response type DagStat struct { - Size uint64 - NumBlocks int64 + Cid cid.Cid `json:",omitempty"` + Size uint64 `json:",omitempty"` + NumBlocks int64 `json:",omitempty"` } func (s *DagStat) String() string { - return fmt.Sprintf("Size: %d, NumBlocks: %d", s.Size, s.NumBlocks) + return fmt.Sprintf("%s %d %d", s.Cid.String()[:20], s.Size, s.NumBlocks) +} + +func (s *DagStat) MarshalJSON() ([]byte, error) { + type Alias DagStat + /* + We can't rely on cid.Cid.MarshalJSON since it uses the {"/": "..."} + format. To make the output consistent and follow the Kubo API patterns + we use the Cid.String method + */ + return json.Marshal(struct { + Cid string `json:"Cid"` + *Alias + }{ + Cid: s.Cid.String(), + Alias: (*Alias)(s), + }) +} + +func (s *DagStat) UnmarshalJSON(data []byte) error { + /* + We can't rely on cid.Cid.UnmarshalJSON since it uses the {"/": "..."} + format. To make the output consistent and follow the Kubo API patterns + we use the Cid.Parse method + */ + type Alias DagStat + aux := struct { + Cid string `json:"Cid"` + *Alias + }{ + Alias: (*Alias)(s), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + Cid, err := cid.Parse(aux.Cid) + if err != nil { + return err + } + s.Cid = Cid + return nil +} + +type DagStatSummary struct { + redundantSize uint64 `json:"-"` + UniqueBlocks int `json:",omitempty"` + TotalSize uint64 `json:",omitempty"` + SharedSize uint64 `json:",omitempty"` + Ratio float32 `json:",omitempty"` + DagStatsArray []*DagStat `json:"DagStats,omitempty"` +} + +func (s *DagStatSummary) String() string { + return fmt.Sprintf("Total Size: %d\nUnique Blocks: %d\nShared Size: %d\nRatio: %f", s.TotalSize, s.UniqueBlocks, s.SharedSize, s.Ratio) +} + +func (s *DagStatSummary) incrementTotalSize(size uint64) { + s.TotalSize += size +} + +func (s *DagStatSummary) incrementRedundantSize(size uint64) { + s.redundantSize += size +} + +func (s *DagStatSummary) appendStats(stats *DagStat) { + s.DagStatsArray = append(s.DagStatsArray, stats) +} + +func (s *DagStatSummary) calculateSummary() { + s.Ratio = float32(s.redundantSize) / float32(s.TotalSize) + s.SharedSize = s.redundantSize - s.TotalSize } // DagStatCmd is a command for getting size information about an ipfs-stored dag @@ -303,24 +365,50 @@ Note: This command skips duplicate blocks in reporting both size and the number `, }, Arguments: []cmds.Argument{ - cmds.StringArg("root", true, false, "CID of a DAG root to get statistics for").EnableStdin(), + cmds.StringArg("root", true, true, "CID of a DAG root to get statistics for").EnableStdin(), }, Options: []cmds.Option{ cmds.BoolOption(progressOptionName, "p", "Return progressive data while reading through the DAG").WithDefault(true), }, Run: dagStat, - Type: DagStat{}, + Type: DagStatSummary{}, PostRun: cmds.PostRunMap{ cmds.CLI: finishCLIStat, }, Encoders: cmds.EncoderMap{ - cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, event *DagStat) error { + cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, event *DagStatSummary) error { + fmt.Fprintln(w) + csvWriter := csv.NewWriter(w) + csvWriter.Comma = '\t' + cidSpacing := len(event.DagStatsArray[0].Cid.String()) + header := []string{fmt.Sprintf("%-*s", cidSpacing, "CID"), fmt.Sprintf("%-15s", "Blocks"), "Size"} + if err := csvWriter.Write(header); err != nil { + return err + } + for _, dagStat := range event.DagStatsArray { + numBlocksStr := fmt.Sprint(dagStat.NumBlocks) + err := csvWriter.Write([]string{ + dagStat.Cid.String(), + fmt.Sprintf("%-15s", numBlocksStr), + fmt.Sprint(dagStat.Size), + }) + if err != nil { + return err + } + } + csvWriter.Flush() + fmt.Fprint(w, "\nSummary\n") _, err := fmt.Fprintf( w, "%v\n", event, ) + fmt.Fprint(w, "\n\n") return err }), + cmds.JSON: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, event *DagStatSummary) error { + return json.NewEncoder(w).Encode(event) + }, + ), }, } diff --git a/core/commands/dag/export.go b/core/commands/dag/export.go index e9d120e87..a729cf752 100644 --- a/core/commands/dag/export.go +++ b/core/commands/dag/export.go @@ -12,8 +12,9 @@ import ( blocks "github.com/ipfs/go-block-format" cid "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" - iface "github.com/ipfs/interface-go-ipfs-core" "github.com/ipfs/kubo/core/commands/cmdenv" + "github.com/ipfs/kubo/core/commands/cmdutils" + iface "github.com/ipfs/kubo/core/coreiface" cmds "github.com/ipfs/go-ipfs-cmds" gocar "github.com/ipld/go-car" @@ -21,12 +22,10 @@ import ( ) func dagExport(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { - c, err := cid.Decode(req.Arguments[0]) + // Accept CID or a content path + p, err := cmdutils.PathOrCidPath(req.Arguments[0]) if err != nil { - return fmt.Errorf( - "unable to parse root specification (currently only bare CIDs are supported): %s", - err, - ) + return err } api, err := cmdenv.GetApi(env, req) @@ -34,6 +33,13 @@ func dagExport(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment return err } + // Resolve path and confirm the root block is available, fail fast if not + b, err := api.Block().Stat(req.Context, p) + if err != nil { + return err + } + c := b.Path().RootCid() + pipeR, pipeW := io.Pipe() errCh := make(chan error, 2) // we only report the 1st error @@ -79,7 +85,6 @@ func dagExport(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment } func finishCLIExport(res cmds.Response, re cmds.ResponseEmitter) error { - var showProgress bool val, specified := res.Request().Options[progressOptionName] if !specified { diff --git a/core/commands/dag/get.go b/core/commands/dag/get.go index 79ad98a49..d15d2b69e 100644 --- a/core/commands/dag/get.go +++ b/core/commands/dag/get.go @@ -4,9 +4,10 @@ import ( "fmt" "io" + "github.com/ipfs/boxo/path" ipldlegacy "github.com/ipfs/go-ipld-legacy" - "github.com/ipfs/interface-go-ipfs-core/path" "github.com/ipfs/kubo/core/commands/cmdenv" + "github.com/ipfs/kubo/core/commands/cmdutils" "github.com/ipld/go-ipld-prime" "github.com/ipld/go-ipld-prime/multicodec" @@ -28,12 +29,17 @@ func dagGet(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) e return err } - rp, err := api.ResolvePath(req.Context, path.New(req.Arguments[0])) + p, err := cmdutils.PathOrCidPath(req.Arguments[0]) if err != nil { return err } - obj, err := api.Dag().Get(req.Context, rp.Cid()) + rp, remainder, err := api.ResolvePath(req.Context, p) + if err != nil { + return err + } + + obj, err := api.Dag().Get(req.Context, rp.RootCid()) if err != nil { return err } @@ -45,8 +51,8 @@ func dagGet(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) e finalNode := universal.(ipld.Node) - if len(rp.Remainder()) > 0 { - remainderPath := ipld.ParsePath(rp.Remainder()) + if len(remainder) > 0 { + remainderPath := ipld.ParsePath(path.SegmentsToString(remainder...)) finalNode, err = traversal.Get(finalNode, remainderPath) if err != nil { diff --git a/core/commands/dag/import.go b/core/commands/dag/import.go index 87daaae11..5e39393c1 100644 --- a/core/commands/dag/import.go +++ b/core/commands/dag/import.go @@ -5,21 +5,20 @@ import ( "fmt" "io" + "github.com/ipfs/boxo/files" + blocks "github.com/ipfs/go-block-format" cid "github.com/ipfs/go-cid" - files "github.com/ipfs/go-ipfs-files" + cmds "github.com/ipfs/go-ipfs-cmds" ipld "github.com/ipfs/go-ipld-format" ipldlegacy "github.com/ipfs/go-ipld-legacy" - iface "github.com/ipfs/interface-go-ipfs-core" - "github.com/ipfs/interface-go-ipfs-core/options" + "github.com/ipfs/kubo/core/coreiface/options" + gocarv2 "github.com/ipld/go-car/v2" + "github.com/ipfs/kubo/core/commands/cmdenv" "github.com/ipfs/kubo/core/commands/cmdutils" - - cmds "github.com/ipfs/go-ipfs-cmds" - gocarv2 "github.com/ipld/go-car/v2" ) func dagImport(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { - node, err := cmdenv.GetNode(env) if err != nil { return err @@ -30,6 +29,8 @@ func dagImport(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment return err } + blockDecoder := ipldlegacy.NewDecoder() + // on import ensure we do not reach out to the network for any reason // if a pin based on what is imported + what is in the blockstore // isn't possible: tough luck @@ -38,82 +39,129 @@ func dagImport(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment return err } + doPinRoots, _ := req.Options[pinRootsOptionName].(bool) + // grab a pinlock ( which doubles as a GC lock ) so that regardless of the // size of the streamed-in cars nothing will disappear on us before we had // a chance to roots that may show up at the very end // This is especially important for use cases like dagger: // ipfs dag import $( ... | ipfs-dagger --stdout=carfifos ) // - unlocker := node.Blockstore.PinLock(req.Context) - defer unlocker.Unlock(req.Context) + if doPinRoots { + unlocker := node.Blockstore.PinLock(req.Context) + defer unlocker.Unlock(req.Context) + } - doPinRoots, _ := req.Options[pinRootsOptionName].(bool) + // this is *not* a transaction + // it is simply a way to relieve pressure on the blockstore + // similar to pinner.Pin/pinner.Flush + batch := ipld.NewBatch(req.Context, api.Dag()) - retCh := make(chan importResult, 1) - go importWorker(req, res, api, retCh) + roots := cid.NewSet() + var blockCount, blockBytesCount uint64 - done := <-retCh - if done.err != nil { - return done.err + // remember last valid block and provide a meaningful error message + // when a truncated/mangled CAR is being imported + importError := func(previous blocks.Block, current blocks.Block, err error) error { + if current != nil { + return fmt.Errorf("import failed at block %q: %w", current.Cid(), err) + } + if previous != nil { + return fmt.Errorf("import failed after block %q: %w", previous.Cid(), err) + } + return fmt.Errorf("import failed: %w", err) + } + + it := req.Files.Entries() + for it.Next() { + file := files.FileFromEntry(it) + if file == nil { + return errors.New("expected a file handle") + } + + // import blocks + err = func() error { + // wrap a defer-closer-scope + // + // every single file in it() is already open before we start + // just close here sooner rather than later for neatness + // and to surface potential errors writing on closed fifos + // this won't/can't help with not running out of handles + defer file.Close() + + var previous blocks.Block + + car, err := gocarv2.NewBlockReader(file) + if err != nil { + return err + } + + for _, c := range car.Roots { + roots.Add(c) + } + + for { + block, err := car.Next() + if err != nil && err != io.EOF { + return importError(previous, block, err) + } else if block == nil { + break + } + if err := cmdutils.CheckBlockSize(req, uint64(len(block.RawData()))); err != nil { + return importError(previous, block, err) + } + + // the double-decode is suboptimal, but we need it for batching + nd, err := blockDecoder.DecodeNode(req.Context, block) + if err != nil { + return importError(previous, block, err) + } + + if err := batch.Add(req.Context, nd); err != nil { + return importError(previous, block, err) + } + blockCount++ + blockBytesCount += uint64(len(block.RawData())) + previous = block + } + return nil + }() + if err != nil { + return err + } + } + + if err := batch.Commit(); err != nil { + return err } // It is not guaranteed that a root in a header is actually present in the same ( or any ) - // .car file. This is the case in version 1, and ideally in further versions too + // .car file. This is the case in version 1, and ideally in further versions too. // Accumulate any root CID seen in a header, and supplement its actual node if/when encountered - // We will attempt a pin *only* at the end in case all car files were well formed - // - // The boolean value indicates whether we have encountered the root within the car file's - roots := done.roots + // We will attempt a pin *only* at the end in case all car files were well-formed. // opportunistic pinning: try whatever sticks if doPinRoots { - - var failedPins int - for c := range roots { - - // We need to re-retrieve a block, convert it to ipld, and feed it - // to the Pinning interface, sigh... - // - // If we didn't have the problem of inability to take multiple pinlocks, - // we could use the api directly like so (though internally it does the same): - // - // // not ideal, but the pinning api takes only paths :( - // rp := path.NewResolvedPath( - // ipfspath.FromCid(c), - // c, - // c, - // "", - // ) - // - // if err := api.Pin().Add(req.Context, rp, options.Pin.Recursive(true)); err != nil { - + err = roots.ForEach(func(c cid.Cid) error { ret := RootMeta{Cid: c} + // This will trigger a full read of the DAG in the pinner, to make sure we have all blocks. + // Ideally we would do colloring of the pinning state while importing the blocks + // and ensure the gray bucket is empty at the end (or use the network to download missing blocks). if block, err := node.Blockstore.Get(req.Context, c); err != nil { ret.PinErrorMsg = err.Error() - } else if nd, err := ipldlegacy.DecodeNode(req.Context, block); err != nil { + } else if nd, err := blockDecoder.DecodeNode(req.Context, block); err != nil { ret.PinErrorMsg = err.Error() - } else if err := node.Pinning.Pin(req.Context, nd, true); err != nil { + } else if err := node.Pinning.Pin(req.Context, nd, true, ""); err != nil { ret.PinErrorMsg = err.Error() } else if err := node.Pinning.Flush(req.Context); err != nil { ret.PinErrorMsg = err.Error() } - if ret.PinErrorMsg != "" { - failedPins++ - } - - if err := res.Emit(&CarImportOutput{Root: &ret}); err != nil { - return err - } - } - - if failedPins > 0 { - return fmt.Errorf( - "unable to pin all roots: %d out of %d failed", - failedPins, - len(roots), - ) + return res.Emit(&CarImportOutput{Root: &ret}) + }) + if err != nil { + return err } } @@ -121,8 +169,8 @@ func dagImport(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment if stats { err = res.Emit(&CarImportOutput{ Stats: &CarImportStats{ - BlockCount: done.blockCount, - BlockBytesCount: done.blockBytesCount, + BlockCount: blockCount, + BlockBytesCount: blockBytesCount, }, }) if err != nil { @@ -132,89 +180,3 @@ func dagImport(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment return nil } - -func importWorker(req *cmds.Request, re cmds.ResponseEmitter, api iface.CoreAPI, ret chan importResult) { - - // this is *not* a transaction - // it is simply a way to relieve pressure on the blockstore - // similar to pinner.Pin/pinner.Flush - batch := ipld.NewBatch(req.Context, api.Dag()) - - roots := make(map[cid.Cid]struct{}) - var blockCount, blockBytesCount uint64 - - it := req.Files.Entries() - for it.Next() { - - file := files.FileFromEntry(it) - if file == nil { - ret <- importResult{err: errors.New("expected a file handle")} - return - } - - // wrap a defer-closer-scope - // - // every single file in it() is already open before we start - // just close here sooner rather than later for neatness - // and to surface potential errors writing on closed fifos - // this won't/can't help with not running out of handles - err := func() error { - defer file.Close() - - car, err := gocarv2.NewBlockReader(file) - if err != nil { - return err - } - - for _, c := range car.Roots { - roots[c] = struct{}{} - } - - for { - block, err := car.Next() - if err != nil && err != io.EOF { - return err - } else if block == nil { - break - } - if err := cmdutils.CheckBlockSize(req, uint64(len(block.RawData()))); err != nil { - return err - } - - // the double-decode is suboptimal, but we need it for batching - nd, err := ipldlegacy.DecodeNode(req.Context, block) - if err != nil { - return err - } - - if err := batch.Add(req.Context, nd); err != nil { - return err - } - blockCount++ - blockBytesCount += uint64(len(block.RawData())) - } - - return nil - }() - - if err != nil { - ret <- importResult{err: err} - return - } - } - - if err := it.Err(); err != nil { - ret <- importResult{err: err} - return - } - - if err := batch.Commit(); err != nil { - ret <- importResult{err: err} - return - } - - ret <- importResult{ - blockCount: blockCount, - blockBytesCount: blockBytesCount, - roots: roots} -} diff --git a/core/commands/dag/put.go b/core/commands/dag/put.go index d8dbaa3f1..fb719916c 100644 --- a/core/commands/dag/put.go +++ b/core/commands/dag/put.go @@ -7,13 +7,14 @@ import ( blocks "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" ipldlegacy "github.com/ipfs/go-ipld-legacy" + "github.com/ipfs/kubo/config" "github.com/ipfs/kubo/core/commands/cmdenv" "github.com/ipfs/kubo/core/commands/cmdutils" "github.com/ipld/go-ipld-prime/multicodec" basicnode "github.com/ipld/go-ipld-prime/node/basic" + "github.com/ipfs/boxo/files" cmds "github.com/ipfs/go-ipfs-cmds" - files "github.com/ipfs/go-ipfs-files" ipld "github.com/ipfs/go-ipld-format" mc "github.com/multiformats/go-multicodec" @@ -32,11 +33,25 @@ func dagPut(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) e return err } + nd, err := cmdenv.GetNode(env) + if err != nil { + return err + } + + cfg, err := nd.Repo.Config() + if err != nil { + return err + } + inputCodec, _ := req.Options["input-codec"].(string) storeCodec, _ := req.Options["store-codec"].(string) hash, _ := req.Options["hash"].(string) dopin, _ := req.Options["pin"].(bool) + if hash == "" { + hash = cfg.Import.HashFunction.WithDefault(config.DefaultHashFunction) + } + var icodec mc.Code if err := icodec.Set(inputCodec); err != nil { return err diff --git a/core/commands/dag/resolve.go b/core/commands/dag/resolve.go index 9b5ae2ccd..f8a4b36cd 100644 --- a/core/commands/dag/resolve.go +++ b/core/commands/dag/resolve.go @@ -1,8 +1,9 @@ package dagcmd import ( - "github.com/ipfs/interface-go-ipfs-core/path" + "github.com/ipfs/boxo/path" "github.com/ipfs/kubo/core/commands/cmdenv" + "github.com/ipfs/kubo/core/commands/cmdutils" cmds "github.com/ipfs/go-ipfs-cmds" ) @@ -13,13 +14,18 @@ func dagResolve(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environmen return err } - rp, err := api.ResolvePath(req.Context, path.New(req.Arguments[0])) + p, err := cmdutils.PathOrCidPath(req.Arguments[0]) + if err != nil { + return err + } + + rp, remainder, err := api.ResolvePath(req.Context, p) if err != nil { return err } return cmds.EmitOnce(res, &ResolveOutput{ - Cid: rp.Cid(), - RemPath: rp.Remainder(), + Cid: rp.RootCid(), + RemPath: path.SegmentsToString(remainder...), }) } diff --git a/core/commands/dag/stat.go b/core/commands/dag/stat.go index fa573f57d..bb9be7e0d 100644 --- a/core/commands/dag/stat.go +++ b/core/commands/dag/stat.go @@ -5,71 +5,86 @@ import ( "io" "os" - "github.com/ipfs/go-merkledag/traverse" - "github.com/ipfs/interface-go-ipfs-core/path" - "github.com/ipfs/kubo/core/commands/cmdenv" - "github.com/ipfs/kubo/core/commands/e" - + mdag "github.com/ipfs/boxo/ipld/merkledag" + "github.com/ipfs/boxo/ipld/merkledag/traverse" + cid "github.com/ipfs/go-cid" cmds "github.com/ipfs/go-ipfs-cmds" - mdag "github.com/ipfs/go-merkledag" + "github.com/ipfs/kubo/core/commands/cmdenv" + "github.com/ipfs/kubo/core/commands/cmdutils" + "github.com/ipfs/kubo/core/commands/e" ) +// TODO cache every cid traversal in a dp cache +// if the cid exists in the cache, don't traverse it, and use the cached result +// to compute the new state + func dagStat(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { progressive := req.Options[progressOptionName].(bool) - api, err := cmdenv.GetApi(env, req) if err != nil { return err } - - rp, err := api.ResolvePath(req.Context, path.New(req.Arguments[0])) - if err != nil { - return err - } - - if len(rp.Remainder()) > 0 { - return fmt.Errorf("cannot return size for anything other than a DAG with a root CID") - } - nodeGetter := mdag.NewSession(req.Context, api.Dag()) - obj, err := nodeGetter.Get(req.Context, rp.Cid()) - if err != nil { - return err - } - dagstats := &DagStat{} - err = traverse.Traverse(obj, traverse.Options{ - DAG: nodeGetter, - Order: traverse.DFSPre, - Func: func(current traverse.State) error { - dagstats.Size += uint64(len(current.Node.RawData())) - dagstats.NumBlocks++ - - if progressive { - if err := res.Emit(dagstats); err != nil { - return err - } - } - return nil - }, - ErrFunc: nil, - SkipDuplicates: true, - }) - if err != nil { - return fmt.Errorf("error traversing DAG: %w", err) - } - - if !progressive { - if err := res.Emit(dagstats); err != nil { + cidSet := cid.NewSet() + dagStatSummary := &DagStatSummary{DagStatsArray: []*DagStat{}} + for _, a := range req.Arguments { + p, err := cmdutils.PathOrCidPath(a) + if err != nil { return err } + rp, remainder, err := api.ResolvePath(req.Context, p) + if err != nil { + return err + } + if len(remainder) > 0 { + return fmt.Errorf("cannot return size for anything other than a DAG with a root CID") + } + + obj, err := nodeGetter.Get(req.Context, rp.RootCid()) + if err != nil { + return err + } + dagstats := &DagStat{Cid: rp.RootCid()} + dagStatSummary.appendStats(dagstats) + err = traverse.Traverse(obj, traverse.Options{ + DAG: nodeGetter, + Order: traverse.DFSPre, + Func: func(current traverse.State) error { + currentNodeSize := uint64(len(current.Node.RawData())) + dagstats.Size += currentNodeSize + dagstats.NumBlocks++ + if !cidSet.Has(current.Node.Cid()) { + dagStatSummary.incrementTotalSize(currentNodeSize) + } + dagStatSummary.incrementRedundantSize(currentNodeSize) + cidSet.Add(current.Node.Cid()) + if progressive { + if err := res.Emit(dagStatSummary); err != nil { + return err + } + } + return nil + }, + ErrFunc: nil, + SkipDuplicates: true, + }) + if err != nil { + return fmt.Errorf("error traversing DAG: %w", err) + } } + dagStatSummary.UniqueBlocks = cidSet.Len() + dagStatSummary.calculateSummary() + + if err := res.Emit(dagStatSummary); err != nil { + return err + } return nil } func finishCLIStat(res cmds.Response, re cmds.ResponseEmitter) error { - var dagStats *DagStat + var dagStats *DagStatSummary for { v, err := res.Next() if err != nil { @@ -78,13 +93,20 @@ func finishCLIStat(res cmds.Response, re cmds.ResponseEmitter) error { } return err } - - out, ok := v.(*DagStat) - if !ok { + switch out := v.(type) { + case *DagStatSummary: + dagStats = out + if dagStats.Ratio == 0 { + length := len(dagStats.DagStatsArray) + if length > 0 { + currentStat := dagStats.DagStatsArray[length-1] + fmt.Fprintf(os.Stderr, "CID: %s, Size: %d, NumBlocks: %d\n", currentStat.Cid, currentStat.Size, currentStat.NumBlocks) + } + } + default: return e.TypeErr(out, v) + } - dagStats = out - fmt.Fprintf(os.Stderr, "%v\r", out) } return re.Emit(dagStats) } diff --git a/core/commands/dht.go b/core/commands/dht.go index 95ac187f5..1d4620181 100644 --- a/core/commands/dht.go +++ b/core/commands/dht.go @@ -15,6 +15,7 @@ import ( var ErrNotDHT = errors.New("routing service is not a DHT") var DhtCmd = &cmds.Command{ + Status: cmds.Deprecated, Helptext: cmds.HelpText{ Tagline: "Issue commands directly through the DHT.", ShortDescription: ``, @@ -22,64 +23,14 @@ var DhtCmd = &cmds.Command{ Subcommands: map[string]*cmds.Command{ "query": queryDhtCmd, - "findprovs": findProvidersDhtCmd, - "findpeer": findPeerDhtCmd, - "get": getValueDhtCmd, - "put": putValueDhtCmd, - "provide": provideRefDhtCmd, + "findprovs": RemovedDHTCmd, + "findpeer": RemovedDHTCmd, + "get": RemovedDHTCmd, + "put": RemovedDHTCmd, + "provide": RemovedDHTCmd, }, } -var findProvidersDhtCmd = &cmds.Command{ - Helptext: findProvidersRoutingCmd.Helptext, - Arguments: findProvidersRoutingCmd.Arguments, - Options: findProvidersRoutingCmd.Options, - Run: findProvidersRoutingCmd.Run, - Encoders: findProvidersRoutingCmd.Encoders, - Type: findProvidersRoutingCmd.Type, - Status: cmds.Deprecated, -} - -var findPeerDhtCmd = &cmds.Command{ - Helptext: findPeerRoutingCmd.Helptext, - Arguments: findPeerRoutingCmd.Arguments, - Options: findPeerRoutingCmd.Options, - Run: findPeerRoutingCmd.Run, - Encoders: findPeerRoutingCmd.Encoders, - Type: findPeerRoutingCmd.Type, - Status: cmds.Deprecated, -} - -var getValueDhtCmd = &cmds.Command{ - Helptext: getValueRoutingCmd.Helptext, - Arguments: getValueRoutingCmd.Arguments, - Options: getValueRoutingCmd.Options, - Run: getValueRoutingCmd.Run, - Encoders: getValueRoutingCmd.Encoders, - Type: getValueRoutingCmd.Type, - Status: cmds.Deprecated, -} - -var putValueDhtCmd = &cmds.Command{ - Helptext: putValueRoutingCmd.Helptext, - Arguments: putValueRoutingCmd.Arguments, - Options: putValueRoutingCmd.Options, - Run: putValueRoutingCmd.Run, - Encoders: putValueRoutingCmd.Encoders, - Type: putValueRoutingCmd.Type, - Status: cmds.Deprecated, -} - -var provideRefDhtCmd = &cmds.Command{ - Helptext: provideRefRoutingCmd.Helptext, - Arguments: provideRefRoutingCmd.Arguments, - Options: provideRefRoutingCmd.Options, - Run: provideRefRoutingCmd.Run, - Encoders: provideRefRoutingCmd.Encoders, - Type: provideRefRoutingCmd.Type, - Status: cmds.Deprecated, -} - // kademlia extends the routing interface with a command to get the peers closest to the target type kademlia interface { routing.Routing @@ -87,6 +38,7 @@ type kademlia interface { } var queryDhtCmd = &cmds.Command{ + Status: cmds.Deprecated, Helptext: cmds.HelpText{ Tagline: "Find the closest Peer IDs to a given Peer ID by querying the DHT.", ShortDescription: "Outputs a list of newline-delimited Peer IDs.", @@ -169,3 +121,12 @@ var queryDhtCmd = &cmds.Command{ }, Type: routing.QueryEvent{}, } +var RemovedDHTCmd = &cmds.Command{ + Status: cmds.Removed, + Helptext: cmds.HelpText{ + Tagline: "Removed, use 'ipfs routing' instead.", + }, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + return errors.New("removed, use 'ipfs routing' instead") + }, +} diff --git a/core/commands/dht_test.go b/core/commands/dht_test.go index 5c027a2f4..f0f7de703 100644 --- a/core/commands/dht_test.go +++ b/core/commands/dht_test.go @@ -3,23 +3,23 @@ package commands import ( "testing" - "github.com/ipfs/go-namesys" + "github.com/ipfs/boxo/namesys" - ipns "github.com/ipfs/go-ipns" + ipns "github.com/ipfs/boxo/ipns" "github.com/libp2p/go-libp2p/core/test" ) func TestKeyTranslation(t *testing.T) { pid := test.RandPeerIDFatal(t) - pkname := namesys.PkKeyForID(pid) - ipnsname := ipns.RecordKey(pid) + pkname := namesys.PkRoutingKey(pid) + ipnsname := ipns.NameFromPeer(pid).RoutingKey() - pkk, err := escapeDhtKey("/pk/" + pid.Pretty()) + pkk, err := escapeDhtKey("/pk/" + pid.String()) if err != nil { t.Fatal(err) } - ipnsk, err := escapeDhtKey("/ipns/" + pid.Pretty()) + ipnsk, err := escapeDhtKey("/ipns/" + pid.String()) if err != nil { t.Fatal(err) } @@ -28,7 +28,7 @@ func TestKeyTranslation(t *testing.T) { t.Fatal("keys didn't match!") } - if ipnsk != ipnsname { + if ipnsk != string(ipnsname) { t.Fatal("keys didn't match!") } } diff --git a/core/commands/dns.go b/core/commands/dns.go deleted file mode 100644 index 81854e1c1..000000000 --- a/core/commands/dns.go +++ /dev/null @@ -1,68 +0,0 @@ -package commands - -import ( - "fmt" - "io" - - namesys "github.com/ipfs/go-namesys" - nsopts "github.com/ipfs/interface-go-ipfs-core/options/namesys" - cmdenv "github.com/ipfs/kubo/core/commands/cmdenv" - ncmd "github.com/ipfs/kubo/core/commands/name" - - cmds "github.com/ipfs/go-ipfs-cmds" -) - -const ( - dnsRecursiveOptionName = "recursive" -) - -var DNSCmd = &cmds.Command{ - Status: cmds.Deprecated, // https://github.com/ipfs/kubo/issues/8607 - Helptext: cmds.HelpText{ - Tagline: "Resolve DNSLink records.", - ShortDescription: ` -This command can only recursively resolve DNSLink TXT records. -It will fail to recursively resolve through IPNS keys etc. - -DEPRECATED: superseded by 'ipfs resolve' - -For general-purpose recursive resolution, use 'ipfs resolve -r'. -It will work across multiple DNSLinks and IPNS keys. -`, - }, - - Arguments: []cmds.Argument{ - cmds.StringArg("domain-name", true, false, "The domain-name name to resolve.").EnableStdin(), - }, - Options: []cmds.Option{ - cmds.BoolOption(dnsRecursiveOptionName, "r", "Resolve until the result is not a DNS link.").WithDefault(true), - }, - Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { - node, err := cmdenv.GetNode(env) - if err != nil { - return err - } - - recursive, _ := req.Options[dnsRecursiveOptionName].(bool) - name := req.Arguments[0] - resolver := namesys.NewDNSResolver(node.DNSResolver.LookupTXT) - - var routing []nsopts.ResolveOpt - if !recursive { - routing = append(routing, nsopts.Depth(1)) - } - - output, err := resolver.Resolve(req.Context, name, routing...) - if err != nil && (recursive || err != namesys.ErrResolveRecursion) { - return err - } - return cmds.EmitOnce(res, &ncmd.ResolvedPath{Path: output}) - }, - Encoders: cmds.EncoderMap{ - cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *ncmd.ResolvedPath) error { - fmt.Fprintln(w, cmdenv.EscNonPrint(out.Path.String())) - return nil - }), - }, - Type: ncmd.ResolvedPath{}, -} diff --git a/core/commands/external.go b/core/commands/external.go index 0eb4de036..65ddcea4d 100644 --- a/core/commands/external.go +++ b/core/commands/external.go @@ -41,7 +41,7 @@ func ExternalBinary(instructions string) *cmds.Command { cmd := exec.Command(binname, req.Arguments...) // TODO: make commands lib be able to pass stdin through daemon - //cmd.Stdin = req.Stdin() + // cmd.Stdin = req.Stdin() cmd.Stdin = io.LimitReader(nil, 0) cmd.Stdout = w cmd.Stderr = w diff --git a/core/commands/files.go b/core/commands/files.go index bba14a1e1..d1a52cbfb 100644 --- a/core/commands/files.go +++ b/core/commands/files.go @@ -2,30 +2,34 @@ package commands import ( "context" + "encoding/json" "errors" "fmt" "io" "os" gopath "path" "sort" + "strconv" "strings" + "time" humanize "github.com/dustin/go-humanize" + "github.com/ipfs/kubo/config" "github.com/ipfs/kubo/core" "github.com/ipfs/kubo/core/commands/cmdenv" - bservice "github.com/ipfs/go-blockservice" + bservice "github.com/ipfs/boxo/blockservice" + offline "github.com/ipfs/boxo/exchange/offline" + dag "github.com/ipfs/boxo/ipld/merkledag" + ft "github.com/ipfs/boxo/ipld/unixfs" + mfs "github.com/ipfs/boxo/mfs" + "github.com/ipfs/boxo/path" cid "github.com/ipfs/go-cid" cidenc "github.com/ipfs/go-cidutil/cidenc" cmds "github.com/ipfs/go-ipfs-cmds" - offline "github.com/ipfs/go-ipfs-exchange-offline" ipld "github.com/ipfs/go-ipld-format" logging "github.com/ipfs/go-log" - dag "github.com/ipfs/go-merkledag" - mfs "github.com/ipfs/go-mfs" - ft "github.com/ipfs/go-unixfs" - iface "github.com/ipfs/interface-go-ipfs-core" - path "github.com/ipfs/interface-go-ipfs-core/path" + iface "github.com/ipfs/kubo/core/coreiface" mh "github.com/multiformats/go-multihash" ) @@ -80,6 +84,8 @@ operations. "rm": filesRmCmd, "flush": filesFlushCmd, "chcid": filesChcidCmd, + "chmod": filesChmodCmd, + "touch": filesTouchCmd, }, } @@ -88,8 +94,10 @@ const ( filesHashOptionName = "hash" ) -var cidVersionOption = cmds.IntOption(filesCidVersionOptionName, "cid-ver", "Cid version to use. (experimental)") -var hashOption = cmds.StringOption(filesHashOptionName, "Hash function to use. Will set Cid version to 1 if used. (experimental)") +var ( + cidVersionOption = cmds.IntOption(filesCidVersionOptionName, "cid-ver", "Cid version to use. (experimental)") + hashOption = cmds.StringOption(filesHashOptionName, "Hash function to use. Will set Cid version to 1 if used. (experimental)") +) var errFormat = errors.New("format was set by multiple options. Only one format option is allowed") @@ -102,6 +110,43 @@ type statOutput struct { WithLocality bool `json:",omitempty"` Local bool `json:",omitempty"` SizeLocal uint64 `json:",omitempty"` + Mode uint32 `json:",omitempty"` + Mtime int64 `json:",omitempty"` + MtimeNsecs int `json:",omitempty"` +} + +func (s *statOutput) MarshalJSON() ([]byte, error) { + type so statOutput + out := &struct { + *so + Mode string `json:",omitempty"` + }{so: (*so)(s)} + + if s.Mode != 0 { + out.Mode = fmt.Sprintf("%04o", s.Mode) + } + return json.Marshal(out) +} + +func (s *statOutput) UnmarshalJSON(data []byte) error { + var err error + type so statOutput + tmp := &struct { + *so + Mode string `json:",omitempty"` + }{so: (*so)(s)} + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + if tmp.Mode != "" { + mode, err := strconv.ParseUint(tmp.Mode, 8, 32) + if err == nil { + s.Mode = uint32(mode) + } + } + return err } const ( @@ -109,10 +154,13 @@ const ( Size: CumulativeSize: ChildBlocks: -Type: ` +Type: +Mode: () +Mtime: ` filesFormatOptionName = "format" filesSizeOptionName = "size" filesWithLocalOptionName = "with-local" + filesStatUnspecified = "not set" ) var filesStatCmd = &cmds.Command{ @@ -125,16 +173,16 @@ var filesStatCmd = &cmds.Command{ }, Options: []cmds.Option{ cmds.StringOption(filesFormatOptionName, "Print statistics in given format. Allowed tokens: "+ - " . Conflicts with other format options.").WithDefault(defaultStatFormat), + " and optional ."+ + "Conflicts with other format options.").WithDefault(defaultStatFormat), cmds.BoolOption(filesHashOptionName, "Print only hash. Implies '--format='. Conflicts with other format options."), cmds.BoolOption(filesSizeOptionName, "Print only size. Implies '--format='. Conflicts with other format options."), cmds.BoolOption(filesWithLocalOptionName, "Compute the amount of the dag that is local, and if possible the total size"), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { - _, err := statGetFormatOptions(req) if err != nil { - return cmds.Errorf(cmds.ErrClient, err.Error()) + return cmds.Errorf(cmds.ErrClient, "invalid parameters: %s", err) } node, err := cmdenv.GetNode(env) @@ -197,12 +245,29 @@ var filesStatCmd = &cmds.Command{ }, Encoders: cmds.EncoderMap{ cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *statOutput) error { + mode, modeo := filesStatUnspecified, filesStatUnspecified + if out.Mode != 0 { + mode = strings.ToLower(os.FileMode(out.Mode).String()) + modeo = "0" + strconv.FormatInt(int64(out.Mode&0x1FF), 8) + } + mtime, mtimes, mtimens := filesStatUnspecified, filesStatUnspecified, filesStatUnspecified + if out.Mtime > 0 { + mtime = time.Unix(out.Mtime, int64(out.MtimeNsecs)).UTC().Format("2 Jan 2006, 15:04:05 MST") + mtimes = strconv.FormatInt(out.Mtime, 10) + mtimens = strconv.Itoa(out.MtimeNsecs) + } + s, _ := statGetFormatOptions(req) s = strings.Replace(s, "", out.Hash, -1) s = strings.Replace(s, "", fmt.Sprintf("%d", out.Size), -1) s = strings.Replace(s, "", fmt.Sprintf("%d", out.CumulativeSize), -1) s = strings.Replace(s, "", fmt.Sprintf("%d", out.Blocks), -1) s = strings.Replace(s, "", out.Type, -1) + s = strings.Replace(s, "", mode, -1) + s = strings.Replace(s, "", modeo, -1) + s = strings.Replace(s, "", mtime, -1) + s = strings.Replace(s, "", mtimes, -1) + s = strings.Replace(s, "", mtimens, -1) fmt.Fprintln(w, s) @@ -225,7 +290,6 @@ func moreThanOne(a, b, c bool) bool { } func statGetFormatOptions(req *cmds.Request) (string, error) { - hash, _ := req.Options[filesHashOptionName].(bool) size, _ := req.Options[filesSizeOptionName].(bool) format, _ := req.Options[filesFormatOptionName].(string) @@ -253,28 +317,7 @@ func statNode(nd ipld.Node, enc cidenc.Encoder) (*statOutput, error) { switch n := nd.(type) { case *dag.ProtoNode: - d, err := ft.FSNodeFromBytes(n.Data()) - if err != nil { - return nil, err - } - - var ndtype string - switch d.Type() { - case ft.TDirectory, ft.THAMTShard: - ndtype = "directory" - case ft.TFile, ft.TMetadata, ft.TRaw: - ndtype = "file" - default: - return nil, fmt.Errorf("unrecognized node type: %s", d.Type()) - } - - return &statOutput{ - Hash: enc.Encode(c), - Blocks: len(nd.Links()), - Size: d.FileSize(), - CumulativeSize: cumulsize, - Type: ndtype, - }, nil + return statProtoNode(n, enc, c, cumulsize) case *dag.RawNode: return &statOutput{ Hash: enc.Encode(c), @@ -288,6 +331,44 @@ func statNode(nd ipld.Node, enc cidenc.Encoder) (*statOutput, error) { } } +func statProtoNode(n *dag.ProtoNode, enc cidenc.Encoder, cid cid.Cid, cumulsize uint64) (*statOutput, error) { + d, err := ft.FSNodeFromBytes(n.Data()) + if err != nil { + return nil, err + } + + stat := statOutput{ + Hash: enc.Encode(cid), + Blocks: len(n.Links()), + Size: d.FileSize(), + CumulativeSize: cumulsize, + } + + switch d.Type() { + case ft.TDirectory, ft.THAMTShard: + stat.Type = "directory" + case ft.TFile, ft.TSymlink, ft.TMetadata, ft.TRaw: + stat.Type = "file" + default: + return nil, fmt.Errorf("unrecognized node type: %s", d.Type()) + } + + if mode := d.Mode(); mode != 0 { + stat.Mode = uint32(mode) + } else if d.Type() == ft.TSymlink { + stat.Mode = uint32(os.ModeSymlink | 0x1FF) + } + + if mt := d.ModTime(); !mt.IsZero() { + stat.Mtime = mt.Unix() + if ns := mt.Nanosecond(); ns > 0 { + stat.MtimeNsecs = ns + } + } + + return &stat, nil +} + func walkBlock(ctx context.Context, dagserv ipld.DAGService, nd ipld.Node) (bool, uint64, error) { // Start with the block data size sizeLocal := uint64(len(nd.RawData())) @@ -307,7 +388,6 @@ func walkBlock(ctx context.Context, dagserv ipld.DAGService, nd ipld.Node) (bool } childLocal, childLocalSize, err := walkBlock(ctx, dagserv, child) - if err != nil { return local, sizeLocal, err } @@ -341,7 +421,7 @@ $ ipfs add --quieter --pin=false $ ipfs files cp /ipfs/ /your/desired/mfs/path If you wish to fully copy content from a different IPFS peer into MFS, do not -forget to force IPFS to fetch to full DAG after doing the "cp" operation. i.e: +forget to force IPFS to fetch the full DAG after doing a "cp" operation. i.e: $ ipfs files cp /ipfs/ /your/desired/mfs/path $ ipfs pin add @@ -424,7 +504,12 @@ being GC'ed. func getNodeFromPath(ctx context.Context, node *core.IpfsNode, api iface.CoreAPI, p string) (ipld.Node, error) { switch { case strings.HasPrefix(p, "/ipfs/"): - return api.ResolveNode(ctx, path.New(p)) + pth, err := path.NewPath(p) + if err != nil { + return nil, err + } + + return api.ResolveNode(ctx, pth) default: fsn, err := mfs.Lookup(node.FilesRoot, p) if err != nil { @@ -798,18 +883,28 @@ See '--to-files' in 'ipfs add --help' for more information. return err } + nd, err := cmdenv.GetNode(env) + if err != nil { + return err + } + + cfg, err := nd.Repo.Config() + if err != nil { + return err + } + create, _ := req.Options[filesCreateOptionName].(bool) mkParents, _ := req.Options[filesParentsOptionName].(bool) trunc, _ := req.Options[filesTruncateOptionName].(bool) flush, _ := req.Options[filesFlushOptionName].(bool) rawLeaves, rawLeavesDef := req.Options[filesRawLeavesOptionName].(bool) - prefix, err := getPrefixNew(req) - if err != nil { - return err + if !rawLeavesDef && cfg.Import.UnixFSRawLeaves != config.Default { + rawLeavesDef = true + rawLeaves = cfg.Import.UnixFSRawLeaves.WithDefault(config.DefaultUnixFSRawLeaves) } - nd, err := cmdenv.GetNode(env) + prefix, err := getPrefixNew(req) if err != nil { return err } @@ -1298,3 +1393,86 @@ func getParentDir(root *mfs.Root, dir string) (*mfs.Directory, error) { } return pdir, nil } + +var filesChmodCmd = &cmds.Command{ + Status: cmds.Experimental, + Helptext: cmds.HelpText{ + Tagline: "Change optional POSIX mode permissions", + ShortDescription: ` +The mode argument must be specified in Unix numeric notation. + + $ ipfs files chmod 0644 /foo + $ ipfs files stat /foo + ... + Type: file + Mode: -rw-r--r-- (0644) + ... +`, + }, + Arguments: []cmds.Argument{ + cmds.StringArg("mode", true, false, "Mode to apply to node (numeric notation)"), + cmds.StringArg("path", true, false, "Path to apply mode"), + }, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + nd, err := cmdenv.GetNode(env) + if err != nil { + return err + } + + path, err := checkPath(req.Arguments[1]) + if err != nil { + return err + } + + mode, err := strconv.ParseInt(req.Arguments[0], 8, 32) + if err != nil { + return err + } + + return mfs.Chmod(nd.FilesRoot, path, os.FileMode(mode)) + }, +} + +var filesTouchCmd = &cmds.Command{ + Status: cmds.Experimental, + Helptext: cmds.HelpText{ + Tagline: "Set or change optional POSIX modification times.", + ShortDescription: ` +Examples: + # set modification time to now. + $ ipfs files touch /foo + # set a custom modification time. + $ ipfs files touch --mtime=1630937926 /foo +`, + }, + Arguments: []cmds.Argument{ + cmds.StringArg("path", true, false, "Path of target to update."), + }, + Options: []cmds.Option{ + cmds.Int64Option(mtimeOptionName, "Modification time in seconds before or since the Unix Epoch to apply to created UnixFS entries."), + cmds.UintOption(mtimeNsecsOptionName, "Modification time fraction in nanoseconds"), + }, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + nd, err := cmdenv.GetNode(env) + if err != nil { + return err + } + + path, err := checkPath(req.Arguments[0]) + if err != nil { + return err + } + + mtime, _ := req.Options[mtimeOptionName].(int64) + nsecs, _ := req.Options[mtimeNsecsOptionName].(uint) + + var ts time.Time + if mtime != 0 { + ts = time.Unix(mtime, int64(nsecs)).UTC() + } else { + ts = time.Now().UTC() + } + + return mfs.Touch(nd.FilesRoot, path, ts) + }, +} diff --git a/core/commands/filestore.go b/core/commands/filestore.go index 7a271526b..0c9dbee0a 100644 --- a/core/commands/filestore.go +++ b/core/commands/filestore.go @@ -6,7 +6,7 @@ import ( "io" "os" - filestore "github.com/ipfs/go-filestore" + filestore "github.com/ipfs/boxo/filestore" cmds "github.com/ipfs/go-ipfs-cmds" core "github.com/ipfs/kubo/core" cmdenv "github.com/ipfs/kubo/core/commands/cmdenv" diff --git a/core/commands/get.go b/core/commands/get.go index 7e915ea29..12a3ea8ca 100644 --- a/core/commands/get.go +++ b/core/commands/get.go @@ -1,6 +1,7 @@ package commands import ( + gotar "archive/tar" "bufio" "compress/gzip" "errors" @@ -12,13 +13,13 @@ import ( "strings" "github.com/ipfs/kubo/core/commands/cmdenv" + "github.com/ipfs/kubo/core/commands/cmdutils" "github.com/ipfs/kubo/core/commands/e" "github.com/cheggaaa/pb" + "github.com/ipfs/boxo/files" + "github.com/ipfs/boxo/tar" cmds "github.com/ipfs/go-ipfs-cmds" - files "github.com/ipfs/go-ipfs-files" - "github.com/ipfs/go-libipfs/tar" - "github.com/ipfs/interface-go-ipfs-core/path" ) var ErrInvalidCompressionLevel = errors.New("compression level must be between 1 and 9") @@ -72,7 +73,10 @@ may also specify the level of compression by specifying '-l=<1-9>'. return err } - p := path.New(req.Arguments[0]) + p, err := cmdutils.PathOrCidPath(req.Arguments[0]) + if err != nil { + return err + } file, err := api.Unixfs().Get(ctx, p) if err != nil { @@ -328,7 +332,8 @@ func fileArchive(f files.Node, name string, archive bool, compression int) (io.R closeGzwAndPipe() // everything seems to be ok }() } else { - // the case for 1. archive, and 2. not archived and not compressed, in which tar is used anyway as a transport format + // the case for 1. archive, and 2. not archived and not compressed, in + // which tar is used anyway as a transport format // construct the tar writer w, err := files.NewTarWriter(maybeGzw) @@ -336,6 +341,11 @@ func fileArchive(f files.Node, name string, archive bool, compression int) (io.R return nil, err } + // if not creating an archive set the format to PAX in order to preserve nanoseconds + if !archive { + w.SetFormat(gotar.FormatPAX) + } + go func() { // write all the nodes recursively if err := w.WriteFile(f, filename); checkErrAndClosePipe(err) { diff --git a/core/commands/id.go b/core/commands/id.go index 887f93efe..3446fc267 100644 --- a/core/commands/id.go +++ b/core/commands/id.go @@ -10,28 +10,27 @@ import ( "strings" version "github.com/ipfs/kubo" - core "github.com/ipfs/kubo/core" - cmdenv "github.com/ipfs/kubo/core/commands/cmdenv" + "github.com/ipfs/kubo/core" + "github.com/ipfs/kubo/core/commands/cmdenv" cmds "github.com/ipfs/go-ipfs-cmds" ke "github.com/ipfs/kubo/core/commands/keyencode" kb "github.com/libp2p/go-libp2p-kbucket" ic "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/host" - peer "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/peer" pstore "github.com/libp2p/go-libp2p/core/peerstore" - identify "github.com/libp2p/go-libp2p/p2p/protocol/identify" + "github.com/libp2p/go-libp2p/core/protocol" ) const offlineIDErrorMessage = "'ipfs id' cannot query information on remote peers without a running daemon; if you only want to convert --peerid-base, pass --offline option" -type IdOutput struct { //nolint - ID string - PublicKey string - Addresses []string - AgentVersion string - ProtocolVersion string - Protocols []string +type IdOutput struct { // nolint + ID string + PublicKey string + Addresses []string + AgentVersion string + Protocols []protocol.ID } const ( @@ -126,10 +125,9 @@ EXAMPLE: output := format output = strings.Replace(output, "", out.ID, -1) output = strings.Replace(output, "", out.AgentVersion, -1) - output = strings.Replace(output, "", out.ProtocolVersion, -1) output = strings.Replace(output, "", out.PublicKey, -1) output = strings.Replace(output, "", strings.Join(out.Addresses, "\n"), -1) - output = strings.Replace(output, "", strings.Join(out.Protocols, "\n"), -1) + output = strings.Replace(output, "", strings.Join(protocol.ConvertToStrings(out.Protocols), "\n"), -1) output = strings.Replace(output, "\\n", "\n", -1) output = strings.Replace(output, "\\t", "\t", -1) fmt.Fprint(w, output) @@ -175,16 +173,9 @@ func printPeer(keyEnc ke.KeyEncoder, ps pstore.Peerstore, p peer.ID) (interface{ sort.Strings(info.Addresses) protocols, _ := ps.GetProtocols(p) // don't care about errors here. - for _, p := range protocols { - info.Protocols = append(info.Protocols, string(p)) - } - sort.Strings(info.Protocols) + info.Protocols = append(info.Protocols, protocols...) + sort.Slice(info.Protocols, func(i, j int) bool { return info.Protocols[i] < info.Protocols[j] }) - if v, err := ps.Get(p, "ProtocolVersion"); err == nil { - if vs, ok := v.(string); ok { - info.ProtocolVersion = vs - } - } if v, err := ps.Get(p, "AgentVersion"); err == nil { if vs, ok := v.(string); ok { info.AgentVersion = vs @@ -216,9 +207,8 @@ func printSelf(keyEnc ke.KeyEncoder, node *core.IpfsNode) (interface{}, error) { } sort.Strings(info.Addresses) info.Protocols = node.PeerHost.Mux().Protocols() - sort.Strings(info.Protocols) + sort.Slice(info.Protocols, func(i, j int) bool { return info.Protocols[i] < info.Protocols[j] }) } - info.ProtocolVersion = identify.DefaultProtocolVersion info.AgentVersion = version.GetUserAgentVersion() return info, nil } diff --git a/core/commands/keystore.go b/core/commands/keystore.go index f530d5eda..a86fb281a 100644 --- a/core/commands/keystore.go +++ b/core/commands/keystore.go @@ -12,18 +12,19 @@ import ( "strings" "text/tabwriter" + keystore "github.com/ipfs/boxo/keystore" cmds "github.com/ipfs/go-ipfs-cmds" - keystore "github.com/ipfs/go-ipfs-keystore" - options "github.com/ipfs/interface-go-ipfs-core/options" oldcmds "github.com/ipfs/kubo/commands" config "github.com/ipfs/kubo/config" cmdenv "github.com/ipfs/kubo/core/commands/cmdenv" "github.com/ipfs/kubo/core/commands/e" ke "github.com/ipfs/kubo/core/commands/keyencode" + options "github.com/ipfs/kubo/core/coreiface/options" fsrepo "github.com/ipfs/kubo/repo/fsrepo" migrations "github.com/ipfs/kubo/repo/fsrepo/migrations" "github.com/libp2p/go-libp2p/core/crypto" peer "github.com/libp2p/go-libp2p/core/peer" + mbase "github.com/multiformats/go-multibase" ) var KeyCmd = &cmds.Command{ @@ -51,6 +52,8 @@ publish'. "rename": keyRenameCmd, "rm": keyRmCmd, "rotate": keyRotateCmd, + "sign": keySignCmd, + "verify": keyVerifyCmd, }, } @@ -118,7 +121,6 @@ var keyGenCmd = &cmds.Command{ } key, err := api.Key().Generate(req.Context, name, opts...) - if err != nil { return err } @@ -211,7 +213,6 @@ elsewhere. For example, using openssl to get a PEM with public key: stdKey, err := crypto.PrivKeyToStdKey(sk) if err != nil { return fmt.Errorf("converting libp2p private key to std Go key: %w", err) - } // For some reason the ed25519.PrivateKey does not use pointer // receivers, so we need to convert it for MarshalPKCS8PrivateKey. @@ -375,7 +376,6 @@ The PEM format allows for key generation outside of the IPFS node: sk, _, err = crypto.KeyPairFromStdKey(stdKey) if err != nil { return fmt.Errorf("converting std Go key to libp2p key: %w", err) - } case keyFormatLibp2pCleartextOption: sk, err = crypto.UnmarshalPrivateKey(data) @@ -691,6 +691,139 @@ func keyOutputListEncoders() cmds.EncoderFunc { }) } +type KeySignOutput struct { + Key KeyOutput + Signature string +} + +var keySignCmd = &cmds.Command{ + Status: cmds.Experimental, + Helptext: cmds.HelpText{ + Tagline: "Generates a signature for the given data with a specified key. Useful for proving the key ownership.", + LongDescription: ` +Sign arbitrary bytes, such as to prove ownership of a Peer ID or an IPNS Name. +To avoid signature reuse, the signed payload is always prefixed with +"libp2p-key signed message:". +`, + }, + Options: []cmds.Option{ + cmds.StringOption("key", "k", "The name of the key to use for signing."), + ke.OptionIPNSBase, + }, + Arguments: []cmds.Argument{ + cmds.FileArg("data", true, false, "The data to sign.").EnableStdin(), + }, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + api, err := cmdenv.GetApi(env, req) + if err != nil { + return err + } + keyEnc, err := ke.KeyEncoderFromString(req.Options[ke.OptionIPNSBase.Name()].(string)) + if err != nil { + return err + } + + name, _ := req.Options["key"].(string) + + file, err := cmdenv.GetFileArg(req.Files.Entries()) + if err != nil { + return err + } + defer file.Close() + + data, err := io.ReadAll(file) + if err != nil { + return err + } + + key, signature, err := api.Key().Sign(req.Context, name, data) + if err != nil { + return err + } + + encodedSignature, err := mbase.Encode(mbase.Base64url, signature) + if err != nil { + return err + } + + return res.Emit(&KeySignOutput{ + Key: KeyOutput{ + Name: key.Name(), + Id: keyEnc.FormatID(key.ID()), + }, + Signature: encodedSignature, + }) + }, + Type: KeySignOutput{}, +} + +type KeyVerifyOutput struct { + Key KeyOutput + SignatureValid bool +} + +var keyVerifyCmd = &cmds.Command{ + Status: cmds.Experimental, + Helptext: cmds.HelpText{ + Tagline: "Verify that the given data and signature match.", + LongDescription: ` +Verify if the given data and signatures match. To avoid the signature reuse, +the signed payload is always prefixed with "libp2p-key signed message:". +`, + }, + Options: []cmds.Option{ + cmds.StringOption("key", "k", "The name of the key to use for signing."), + cmds.StringOption("signature", "s", "Multibase-encoded signature to verify."), + ke.OptionIPNSBase, + }, + Arguments: []cmds.Argument{ + cmds.FileArg("data", true, false, "The data to verify against the given signature.").EnableStdin(), + }, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + api, err := cmdenv.GetApi(env, req) + if err != nil { + return err + } + keyEnc, err := ke.KeyEncoderFromString(req.Options[ke.OptionIPNSBase.Name()].(string)) + if err != nil { + return err + } + + name, _ := req.Options["key"].(string) + encodedSignature, _ := req.Options["signature"].(string) + + _, signature, err := mbase.Decode(encodedSignature) + if err != nil { + return err + } + + file, err := cmdenv.GetFileArg(req.Files.Entries()) + if err != nil { + return err + } + defer file.Close() + + data, err := io.ReadAll(file) + if err != nil { + return err + } + + key, valid, err := api.Key().Verify(req.Context, name, signature, data) + if err != nil { + return err + } + + return res.Emit(&KeyVerifyOutput{ + Key: KeyOutput{ + Name: key.Name(), + Id: keyEnc.FormatID(key.ID()), + }, + SignatureValid: valid, + }) + }, + Type: KeyVerifyOutput{}, +} + // DaemonNotRunning checks to see if the ipfs repo is locked, indicating that // the daemon is running, and returns and error if the daemon is running. func DaemonNotRunning(req *cmds.Request, env cmds.Environment) error { diff --git a/core/commands/ls.go b/core/commands/ls.go index a8d01a594..ab914bb0e 100644 --- a/core/commands/ls.go +++ b/core/commands/ls.go @@ -6,15 +6,16 @@ import ( "os" "sort" "text/tabwriter" + "time" cmdenv "github.com/ipfs/kubo/core/commands/cmdenv" + "github.com/ipfs/kubo/core/commands/cmdutils" + unixfs "github.com/ipfs/boxo/ipld/unixfs" + unixfs_pb "github.com/ipfs/boxo/ipld/unixfs/pb" cmds "github.com/ipfs/go-ipfs-cmds" - unixfs "github.com/ipfs/go-unixfs" - unixfs_pb "github.com/ipfs/go-unixfs/pb" - iface "github.com/ipfs/interface-go-ipfs-core" - options "github.com/ipfs/interface-go-ipfs-core/options" - path "github.com/ipfs/interface-go-ipfs-core/path" + iface "github.com/ipfs/kubo/core/coreiface" + options "github.com/ipfs/kubo/core/coreiface/options" ) // LsLink contains printable data for a single ipld link in ls output @@ -23,6 +24,8 @@ type LsLink struct { Size uint64 Type unixfs_pb.Data_DataType Target string + Mode os.FileMode + ModTime time.Time } // LsObject is an element of LsOutput @@ -131,7 +134,12 @@ The JSON output contains type information. } for i, fpath := range paths { - results, err := api.Unixfs().Ls(req.Context, path.New(fpath), + pth, err := cmdutils.PathOrCidPath(fpath) + if err != nil { + return err + } + + results, err := api.Unixfs().Ls(req.Context, pth, options.Unixfs.ResolveChildren(resolveSize || resolveType)) if err != nil { return err @@ -158,6 +166,9 @@ The JSON output contains type information. Size: link.Size, Type: ftype, Target: link.Target, + + Mode: link.Mode, + ModTime: link.ModTime, } if err := processLink(paths[i], lsLink); err != nil { return err @@ -251,6 +262,7 @@ func tabularOutput(req *cmds.Request, w io.Writer, out *LsOutput, lastObjectHash } } + // TODO: Print link.Mode and link.ModTime? fmt.Fprintf(tw, s, link.Hash, link.Size, cmdenv.EscNonPrint(link.Name)) } } diff --git a/core/commands/name/ipns.go b/core/commands/name/ipns.go index 3c6935331..92cbb59a3 100644 --- a/core/commands/name/ipns.go +++ b/core/commands/name/ipns.go @@ -7,20 +7,18 @@ import ( "strings" "time" - namesys "github.com/ipfs/go-namesys" - cmdenv "github.com/ipfs/kubo/core/commands/cmdenv" - + "github.com/ipfs/boxo/namesys" + "github.com/ipfs/boxo/path" cmds "github.com/ipfs/go-ipfs-cmds" logging "github.com/ipfs/go-log" - path "github.com/ipfs/go-path" - options "github.com/ipfs/interface-go-ipfs-core/options" - nsopts "github.com/ipfs/interface-go-ipfs-core/options/namesys" + cmdenv "github.com/ipfs/kubo/core/commands/cmdenv" + options "github.com/ipfs/kubo/core/coreiface/options" ) var log = logging.Logger("core/commands/ipns") type ResolvedPath struct { - Path path.Path + Path string } const ( @@ -75,8 +73,8 @@ Resolve the value of a dnslink: Options: []cmds.Option{ cmds.BoolOption(recursiveOptionName, "r", "Resolve until the result is not an IPNS name.").WithDefault(true), cmds.BoolOption(nocacheOptionName, "n", "Do not use cached entries."), - cmds.UintOption(dhtRecordCountOptionName, "dhtrc", "Number of records to request for DHT resolution."), - cmds.StringOption(dhtTimeoutOptionName, "dhtt", "Max time to collect values during DHT resolution eg \"30s\". Pass 0 for no timeout."), + cmds.UintOption(dhtRecordCountOptionName, "dhtrc", "Number of records to request for DHT resolution.").WithDefault(uint(namesys.DefaultResolverDhtRecordCount)), + cmds.StringOption(dhtTimeoutOptionName, "dhtt", "Max time to collect values during DHT resolution e.g. \"30s\". Pass 0 for no timeout.").WithDefault(namesys.DefaultResolverDhtTimeout.String()), cmds.BoolOption(streamOptionName, "s", "Stream entries as they are found."), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { @@ -93,7 +91,7 @@ Resolve the value of a dnslink: if err != nil { return err } - name = self.ID().Pretty() + name = self.ID().String() } else { name = req.Arguments[0] } @@ -108,10 +106,10 @@ Resolve the value of a dnslink: } if !recursive { - opts = append(opts, options.Name.ResolveOption(nsopts.Depth(1))) + opts = append(opts, options.Name.ResolveOption(namesys.ResolveWithDepth(1))) } if rcok { - opts = append(opts, options.Name.ResolveOption(nsopts.DhtRecordCount(rc))) + opts = append(opts, options.Name.ResolveOption(namesys.ResolveWithDhtRecordCount(rc))) } if dhttok { d, err := time.ParseDuration(dhtt) @@ -121,7 +119,7 @@ Resolve the value of a dnslink: if d < 0 { return errors.New("DHT timeout value must be >= 0") } - opts = append(opts, options.Name.ResolveOption(nsopts.DhtTimeout(d))) + opts = append(opts, options.Name.ResolveOption(namesys.ResolveWithDhtTimeout(d))) } if !strings.HasPrefix(name, "/ipns/") { @@ -134,7 +132,12 @@ Resolve the value of a dnslink: return err } - return cmds.EmitOnce(res, &ResolvedPath{path.FromString(output.String())}) + pth, err := path.NewPath(output.String()) + if err != nil { + return err + } + + return cmds.EmitOnce(res, &ResolvedPath{pth.String()}) } output, err := api.Name().Search(req.Context, name, opts...) @@ -146,7 +149,7 @@ Resolve the value of a dnslink: if v.Err != nil && (recursive || v.Err != namesys.ErrResolveRecursion) { return v.Err } - if err := res.Emit(&ResolvedPath{path.FromString(v.Path.String())}); err != nil { + if err := res.Emit(&ResolvedPath{v.Path.String()}); err != nil { return err } diff --git a/core/commands/name/ipnsps.go b/core/commands/name/ipnsps.go index f57173eea..e465549e0 100644 --- a/core/commands/name/ipnsps.go +++ b/core/commands/name/ipnsps.go @@ -135,7 +135,7 @@ var ipnspsCancelCmd = &cmds.Command{ name = strings.TrimPrefix(name, "/ipns/") pid, err := peer.Decode(name) if err != nil { - return cmds.Errorf(cmds.ErrClient, err.Error()) + return cmds.Errorf(cmds.ErrClient, "not a valid IPNS name: %s", err) } ok, err := n.PSRouter.Cancel("/ipns/" + string(pid)) diff --git a/core/commands/name/name.go b/core/commands/name/name.go index d9e3de57f..912629d68 100644 --- a/core/commands/name/name.go +++ b/core/commands/name/name.go @@ -1,7 +1,18 @@ package name import ( - "github.com/ipfs/go-ipfs-cmds" + "bytes" + "encoding/hex" + "fmt" + "io" + "text/tabwriter" + "time" + + "github.com/ipfs/boxo/ipns" + ipns_pb "github.com/ipfs/boxo/ipns/pb" + cmds "github.com/ipfs/go-ipfs-cmds" + cmdenv "github.com/ipfs/kubo/core/commands/cmdenv" + "google.golang.org/protobuf/proto" ) type IpnsEntry struct { @@ -62,5 +73,197 @@ Resolve the value of a dnslink: "publish": PublishCmd, "resolve": IpnsCmd, "pubsub": IpnsPubsubCmd, + "inspect": IpnsInspectCmd, + }, +} + +type IpnsInspectValidation struct { + Valid bool + Reason string + Name string +} + +// IpnsInspectEntry contains the deserialized values from an IPNS Entry: +// https://github.com/ipfs/specs/blob/main/ipns/IPNS.md#record-serialization-format +type IpnsInspectEntry struct { + Value string + ValidityType *ipns.ValidityType + Validity *time.Time + Sequence *uint64 + TTL *time.Duration +} + +type IpnsInspectResult struct { + Entry IpnsInspectEntry + PbSize int + SignatureType string + HexDump string + Validation *IpnsInspectValidation +} + +var IpnsInspectCmd = &cmds.Command{ + Status: cmds.Experimental, + Helptext: cmds.HelpText{ + Tagline: "Inspects an IPNS Record", + ShortDescription: ` +Prints values inside of IPNS Record protobuf and its DAG-CBOR Data field. +Passing --verify will verify signature against provided public key. +`, + LongDescription: ` +Prints values inside of IPNS Record protobuf and its DAG-CBOR Data field. + +The input can be a file or STDIN, the output can be JSON: + + $ ipfs routing get "/ipns/$PEERID" > ipns_record + $ ipfs name inspect --enc=json < ipns_record + +Values in PublicKey, SignatureV1 and SignatureV2 fields are raw bytes encoded +in Multibase. The Data field is DAG-CBOR represented as DAG-JSON. + +Passing --verify will verify signature against provided public key. + +`, + }, + Arguments: []cmds.Argument{ + cmds.FileArg("record", true, false, "The IPNS record payload to be verified.").EnableStdin(), + }, + Options: []cmds.Option{ + cmds.StringOption("verify", "CID of the public IPNS key to validate against."), + cmds.BoolOption("dump", "Include a full hex dump of the raw Protobuf record.").WithDefault(true), + }, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + file, err := cmdenv.GetFileArg(req.Files.Entries()) + if err != nil { + return err + } + defer file.Close() + + var b bytes.Buffer + + _, err = io.Copy(&b, file) + if err != nil { + return err + } + + rec, err := ipns.UnmarshalRecord(b.Bytes()) + if err != nil { + return err + } + + result := &IpnsInspectResult{ + Entry: IpnsInspectEntry{}, + } + + // Best effort to get the fields. Show everything we can. + if v, err := rec.Value(); err == nil { + result.Entry.Value = v.String() + } + + if v, err := rec.ValidityType(); err == nil { + result.Entry.ValidityType = &v + } + + if v, err := rec.Validity(); err == nil { + result.Entry.Validity = &v + } + + if v, err := rec.Sequence(); err == nil { + result.Entry.Sequence = &v + } + + if v, err := rec.TTL(); err == nil { + result.Entry.TTL = &v + } + + // Here we need the raw protobuf just to decide the version. + var pbRecord ipns_pb.IpnsRecord + err = proto.Unmarshal(b.Bytes(), &pbRecord) + if err != nil { + return err + } + if len(pbRecord.SignatureV1) != 0 || len(pbRecord.Value) != 0 { + result.SignatureType = "V1+V2" + } else if pbRecord.Data != nil { + result.SignatureType = "V2" + } else { + result.SignatureType = "Unknown" + } + result.PbSize = proto.Size(&pbRecord) + + if verify, ok := req.Options["verify"].(string); ok { + name, err := ipns.NameFromString(verify) + if err != nil { + return err + } + + result.Validation = &IpnsInspectValidation{ + Name: name.String(), + } + + err = ipns.ValidateWithName(rec, name) + if err == nil { + result.Validation.Valid = true + } else { + result.Validation.Reason = err.Error() + } + } + + if dump, ok := req.Options["dump"].(bool); ok && dump { + result.HexDump = hex.Dump(b.Bytes()) + } + + return cmds.EmitOnce(res, result) + }, + Type: IpnsInspectResult{}, + Encoders: cmds.EncoderMap{ + cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *IpnsInspectResult) error { + tw := tabwriter.NewWriter(w, 0, 0, 1, ' ', 0) + defer tw.Flush() + + if out.Entry.Value != "" { + fmt.Fprintf(tw, "Value:\t%q\n", out.Entry.Value) + } + + if out.Entry.ValidityType != nil { + fmt.Fprintf(tw, "Validity Type:\t%q\n", *out.Entry.ValidityType) + } + + if out.Entry.Validity != nil { + fmt.Fprintf(tw, "Validity:\t%q\n", out.Entry.Validity.Format(time.RFC3339Nano)) + } + + if out.Entry.Sequence != nil { + fmt.Fprintf(tw, "Sequence:\t%d\n", *out.Entry.Sequence) + } + + if out.Entry.TTL != nil { + fmt.Fprintf(tw, "TTL:\t%s\n", out.Entry.TTL.String()) + } + + fmt.Fprintf(tw, "Protobuf Size:\t%d\n", out.PbSize) + fmt.Fprintf(tw, "Signature Type:\t%s\n", out.SignatureType) + + if out.Validation == nil { + tw.Flush() + fmt.Fprintf(w, "\nThis record was not verified. Pass '--verify k51...' to verify.\n") + } else { + tw.Flush() + fmt.Fprintf(w, "\nValidation results:\n") + + fmt.Fprintf(tw, "\tValid:\t%v\n", out.Validation.Valid) + if out.Validation.Reason != "" { + fmt.Fprintf(tw, "\tReason:\t%s\n", out.Validation.Reason) + } + fmt.Fprintf(tw, "\tName:\t%s\n", out.Validation.Name) + } + + if out.HexDump != "" { + tw.Flush() + + fmt.Fprintf(w, "\nHex Dump:\n%s", out.HexDump) + } + + return nil + }), }, } diff --git a/core/commands/name/publish.go b/core/commands/name/publish.go index 3619a23d5..168d7fb44 100644 --- a/core/commands/name/publish.go +++ b/core/commands/name/publish.go @@ -7,18 +7,16 @@ import ( "time" cmdenv "github.com/ipfs/kubo/core/commands/cmdenv" + "github.com/ipfs/kubo/core/commands/cmdutils" + ipns "github.com/ipfs/boxo/ipns" cmds "github.com/ipfs/go-ipfs-cmds" - iface "github.com/ipfs/interface-go-ipfs-core" - options "github.com/ipfs/interface-go-ipfs-core/options" - path "github.com/ipfs/interface-go-ipfs-core/path" ke "github.com/ipfs/kubo/core/commands/keyencode" - peer "github.com/libp2p/go-libp2p/core/peer" + iface "github.com/ipfs/kubo/core/coreiface" + options "github.com/ipfs/kubo/core/coreiface/options" ) -var ( - errAllowOffline = errors.New("can't publish while offline: pass `--allow-offline` to override") -) +var errAllowOffline = errors.New("can't publish while offline: pass `--allow-offline` to override") const ( ipfsPathOptionName = "ipfs-path" @@ -28,6 +26,7 @@ const ( ttlOptionName = "ttl" keyOptionName = "key" quieterOptionName = "quieter" + v1compatOptionName = "v1compat" ) var PublishCmd = &cmds.Command{ @@ -61,7 +60,7 @@ Publish an with another name, added by an 'ipfs key' command: > ipfs name publish --key=mykey /ipfs/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy Published to QmSrPmbaUKA3ZodhzPWZnpFgcPMFWF4QsxXbkWfEptTBJd: /ipfs/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy -Alternatively, publish an using a valid PeerID (as listed by +Alternatively, publish an using a valid PeerID (as listed by 'ipfs key list -l'): > ipfs name publish --key=QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n /ipfs/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy @@ -74,15 +73,13 @@ Alternatively, publish an using a valid PeerID (as listed by cmds.StringArg(ipfsPathOptionName, true, false, "ipfs path of the object to be published.").EnableStdin(), }, Options: []cmds.Option{ - cmds.BoolOption(resolveOptionName, "Check if the given path can be resolved before publishing.").WithDefault(true), - cmds.StringOption(lifeTimeOptionName, "t", - `Time duration that the record will be valid for. <> - This accepts durations such as "300s", "1.5h" or "2h45m". Valid time units are - "ns", "us" (or "µs"), "ms", "s", "m", "h".`).WithDefault("24h"), - cmds.BoolOption(allowOfflineOptionName, "When offline, save the IPNS record to the the local datastore without broadcasting to the network instead of simply failing."), - cmds.StringOption(ttlOptionName, "Time duration this record should be cached for. Uses the same syntax as the lifetime option. (caution: experimental)"), cmds.StringOption(keyOptionName, "k", "Name of the key to be used or a valid PeerID, as listed by 'ipfs key list -l'.").WithDefault("self"), - cmds.BoolOption(quieterOptionName, "Q", "Write only final hash."), + cmds.BoolOption(resolveOptionName, "Check if the given path can be resolved before publishing.").WithDefault(true), + cmds.StringOption(lifeTimeOptionName, "t", `Time duration the signed record will be valid for. Accepts durations such as "300s", "1.5h" or "7d2h45m"`).WithDefault(ipns.DefaultRecordLifetime.String()), + cmds.StringOption(ttlOptionName, "Time duration hint, akin to --lifetime, indicating how long to cache this record before checking for updates.").WithDefault(ipns.DefaultRecordTTL.String()), + cmds.BoolOption(quieterOptionName, "Q", "Write only final IPNS Name encoded as CIDv1 (for use in /ipns content paths)."), + cmds.BoolOption(v1compatOptionName, "Produce a backward-compatible IPNS Record by including fields for both V1 and V2 signatures.").WithDefault(true), + cmds.BoolOption(allowOfflineOptionName, "When --offline, save the IPNS record to the local datastore without broadcasting to the network (instead of failing)."), ke.OptionIPNSBase, }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { @@ -90,12 +87,9 @@ Alternatively, publish an using a valid PeerID (as listed by if err != nil { return err } - keyEnc, err := ke.KeyEncoderFromString(req.Options[ke.OptionIPNSBase.Name()].(string)) - if err != nil { - return err - } allowOffline, _ := req.Options[allowOfflineOptionName].(bool) + compatibleWithV1, _ := req.Options[v1compatOptionName].(bool) kname, _ := req.Options[keyOptionName].(string) validTimeOpt, _ := req.Options[lifeTimeOptionName].(string) @@ -108,6 +102,7 @@ Alternatively, publish an using a valid PeerID (as listed by options.Name.AllowOffline(allowOffline), options.Name.Key(kname), options.Name.ValidTime(validTime), + options.Name.CompatibleWithV1(compatibleWithV1), } if ttl, found := req.Options[ttlOptionName].(string); found { @@ -119,7 +114,10 @@ Alternatively, publish an using a valid PeerID (as listed by opts = append(opts, options.Name.TTL(d)) } - p := path.New(req.Arguments[0]) + p, err := cmdutils.PathOrCidPath(req.Arguments[0]) + if err != nil { + return err + } if verifyExists, _ := req.Options[resolveOptionName].(bool); verifyExists { _, err := api.ResolveNode(req.Context, p) @@ -128,7 +126,7 @@ Alternatively, publish an using a valid PeerID (as listed by } } - out, err := api.Name().Publish(req.Context, p, opts...) + name, err := api.Name().Publish(req.Context, p, opts...) if err != nil { if err == iface.ErrOffline { err = errAllowOffline @@ -136,15 +134,9 @@ Alternatively, publish an using a valid PeerID (as listed by return err } - // parse path, extract cid, re-base cid, reconstruct path - pid, err := peer.Decode(out.Name()) - if err != nil { - return err - } - return cmds.EmitOnce(res, &IpnsEntry{ - Name: keyEnc.FormatID(pid), - Value: out.Value().String(), + Name: name.String(), + Value: p.String(), }) }, Encoders: cmds.EncoderMap{ diff --git a/core/commands/object/diff.go b/core/commands/object/diff.go index 8d3caafc5..275f465d8 100644 --- a/core/commands/object/diff.go +++ b/core/commands/object/diff.go @@ -4,11 +4,12 @@ import ( "fmt" "io" + "github.com/ipfs/boxo/ipld/merkledag/dagutils" + "github.com/ipfs/boxo/path" cmds "github.com/ipfs/go-ipfs-cmds" - "github.com/ipfs/go-merkledag/dagutils" - path "github.com/ipfs/interface-go-ipfs-core/path" cmdenv "github.com/ipfs/kubo/core/commands/cmdenv" + "github.com/ipfs/kubo/core/commands/cmdutils" ) const ( @@ -60,8 +61,15 @@ Example: return err } - pa := path.New(req.Arguments[0]) - pb := path.New(req.Arguments[1]) + pa, err := cmdutils.PathOrCidPath(req.Arguments[0]) + if err != nil { + return err + } + + pb, err := cmdutils.PathOrCidPath(req.Arguments[1]) + if err != nil { + return err + } changes, err := api.Object().Diff(req.Context, pa, pb) if err != nil { @@ -75,12 +83,12 @@ Example: Path: change.Path, } - if change.Before != nil { - out[i].Before = change.Before.Cid() + if (change.Before != path.ImmutablePath{}) { + out[i].Before = change.Before.RootCid() } - if change.After != nil { - out[i].After = change.After.Cid() + if (change.After != path.ImmutablePath{}) { + out[i].After = change.After.RootCid() } } diff --git a/core/commands/object/object.go b/core/commands/object/object.go index 44864d046..380ca2533 100644 --- a/core/commands/object/object.go +++ b/core/commands/object/object.go @@ -1,28 +1,11 @@ package objectcmd import ( - "encoding/base64" "errors" - "fmt" - "io" - "text/tabwriter" cmds "github.com/ipfs/go-ipfs-cmds" - "github.com/ipfs/kubo/core/commands/cmdenv" - - humanize "github.com/dustin/go-humanize" - "github.com/ipfs/go-cid" - ipld "github.com/ipfs/go-ipld-format" - dag "github.com/ipfs/go-merkledag" - "github.com/ipfs/interface-go-ipfs-core/options" - path "github.com/ipfs/interface-go-ipfs-core/path" ) -type Node struct { - Links []Link - Data string -} - type Link struct { Name, Hash string Size uint64 @@ -35,16 +18,6 @@ type Object struct { var ErrDataEncoding = errors.New("unknown data field encoding") -const ( - headersOptionName = "headers" - encodingOptionName = "data-encoding" - inputencOptionName = "inputenc" - datafieldencOptionName = "datafieldenc" - pinOptionName = "pin" - quietOptionName = "quiet" - humanOptionName = "human" -) - var ObjectCmd = &cmds.Command{ Status: cmds.Deprecated, // https://github.com/ipfs/kubo/issues/7936 Helptext: cmds.HelpText{ @@ -55,502 +28,23 @@ directly. Deprecated, use more modern 'ipfs dag' and 'ipfs files' instead.`, }, Subcommands: map[string]*cmds.Command{ - "data": ObjectDataCmd, + "data": RemovedObjectCmd, "diff": ObjectDiffCmd, - "get": ObjectGetCmd, - "links": ObjectLinksCmd, - "new": ObjectNewCmd, + "get": RemovedObjectCmd, + "links": RemovedObjectCmd, + "new": RemovedObjectCmd, "patch": ObjectPatchCmd, - "put": ObjectPutCmd, - "stat": ObjectStatCmd, + "put": RemovedObjectCmd, + "stat": RemovedObjectCmd, }, } -// ObjectDataCmd object data command -var ObjectDataCmd = &cmds.Command{ - Status: cmds.Deprecated, // https://github.com/ipfs/kubo/issues/7936 +var RemovedObjectCmd = &cmds.Command{ + Status: cmds.Removed, Helptext: cmds.HelpText{ - Tagline: "Deprecated way to read the raw bytes of a dag-pb object: use 'dag get' instead.", - ShortDescription: ` -'ipfs object data' is a deprecated plumbing command for retrieving the raw -bytes stored in a dag-pb node. It outputs to stdout, and is a base58 -encoded multihash. Provided for legacy reasons. Use 'ipfs dag get' instead. -`, - LongDescription: ` -'ipfs object data' is a deprecated plumbing command for retrieving the raw -bytes stored in a dag-pb node. It outputs to stdout, and is a base58 -encoded multihash. Provided for legacy reasons. Use 'ipfs dag get' instead. - -Note that the "--encoding" option does not affect the output, since the output -is the raw data of the object. -`, - }, - - Arguments: []cmds.Argument{ - cmds.StringArg("key", true, false, "Key of the object to retrieve, in base58-encoded multihash format.").EnableStdin(), + Tagline: "Removed, use 'ipfs dag' or 'ipfs files' instead.", }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { - api, err := cmdenv.GetApi(env, req) - if err != nil { - return err - } - - path := path.New(req.Arguments[0]) - - data, err := api.Object().Data(req.Context, path) - if err != nil { - return err - } - - return res.Emit(data) + return errors.New("removed, use 'ipfs dag' or 'ipfs files' instead") }, } - -// ObjectLinksCmd object links command -var ObjectLinksCmd = &cmds.Command{ - Status: cmds.Deprecated, // https://github.com/ipfs/kubo/issues/7936 - Helptext: cmds.HelpText{ - Tagline: "Deprecated way to output links in the specified dag-pb object: use 'dag get' instead.", - ShortDescription: ` -'ipfs object links' is a plumbing command for retrieving the links from -a dag-pb node. It outputs to stdout, and is a base58 encoded -multihash. Provided for legacy reasons. Use 'ipfs dag get' instead. -`, - }, - - Arguments: []cmds.Argument{ - cmds.StringArg("key", true, false, "Key of the dag-pb object to retrieve, in base58-encoded multihash format.").EnableStdin(), - }, - Options: []cmds.Option{ - cmds.BoolOption(headersOptionName, "v", "Print table headers (Hash, Size, Name)."), - }, - Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { - api, err := cmdenv.GetApi(env, req) - if err != nil { - return err - } - - enc, err := cmdenv.GetLowLevelCidEncoder(req) - if err != nil { - return err - } - - path := path.New(req.Arguments[0]) - - rp, err := api.ResolvePath(req.Context, path) - if err != nil { - return err - } - - links, err := api.Object().Links(req.Context, rp) - if err != nil { - return err - } - - outLinks := make([]Link, len(links)) - for i, link := range links { - outLinks[i] = Link{ - Hash: enc.Encode(link.Cid), - Name: link.Name, - Size: link.Size, - } - } - - out := &Object{ - Hash: enc.Encode(rp.Cid()), - Links: outLinks, - } - - return cmds.EmitOnce(res, out) - }, - Encoders: cmds.EncoderMap{ - cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *Object) error { - tw := tabwriter.NewWriter(w, 1, 2, 1, ' ', 0) - headers, _ := req.Options[headersOptionName].(bool) - if headers { - fmt.Fprintln(tw, "Hash\tSize\tName") - } - for _, link := range out.Links { - fmt.Fprintf(tw, "%s\t%v\t%s\n", link.Hash, link.Size, cmdenv.EscNonPrint(link.Name)) - } - tw.Flush() - - return nil - }), - }, - Type: &Object{}, -} - -// ObjectGetCmd object get command -var ObjectGetCmd = &cmds.Command{ - Status: cmds.Deprecated, // https://github.com/ipfs/kubo/issues/7936 - Helptext: cmds.HelpText{ - Tagline: "Deprecated way to get and serialize the dag-pb node. Use 'dag get' instead", - ShortDescription: ` -'ipfs object get' is a plumbing command for retrieving dag-pb nodes. -It serializes the DAG node to the format specified by the "--encoding" -flag. It outputs to stdout, and is a base58 encoded multihash. - -DEPRECATED and provided for legacy reasons. Use 'ipfs dag get' instead. -`, - }, - - Arguments: []cmds.Argument{ - cmds.StringArg("key", true, false, "Key of the dag-pb object to retrieve, in base58-encoded multihash format.").EnableStdin(), - }, - Options: []cmds.Option{ - cmds.StringOption(encodingOptionName, "Encoding type of the data field, either \"text\" or \"base64\".").WithDefault("text"), - }, - Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { - api, err := cmdenv.GetApi(env, req) - if err != nil { - return err - } - - enc, err := cmdenv.GetLowLevelCidEncoder(req) - if err != nil { - return err - } - - path := path.New(req.Arguments[0]) - - datafieldenc, _ := req.Options[encodingOptionName].(string) - if err != nil { - return err - } - - nd, err := api.Object().Get(req.Context, path) - if err != nil { - return err - } - - r, err := api.Object().Data(req.Context, path) - if err != nil { - return err - } - - data, err := io.ReadAll(r) - if err != nil { - return err - } - - out, err := encodeData(data, datafieldenc) - if err != nil { - return err - } - - node := &Node{ - Links: make([]Link, len(nd.Links())), - Data: out, - } - - for i, link := range nd.Links() { - node.Links[i] = Link{ - Hash: enc.Encode(link.Cid), - Name: link.Name, - Size: link.Size, - } - } - - return cmds.EmitOnce(res, node) - }, - Type: Node{}, - Encoders: cmds.EncoderMap{ - cmds.Protobuf: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *Node) error { - // deserialize the Data field as text as this was the standard behaviour - object, err := deserializeNode(out, "text") - if err != nil { - return nil - } - - marshaled, err := object.Marshal() - if err != nil { - return err - } - _, err = w.Write(marshaled) - return err - }), - }, -} - -// ObjectStatCmd object stat command -var ObjectStatCmd = &cmds.Command{ - Status: cmds.Deprecated, // https://github.com/ipfs/kubo/issues/7936 - Helptext: cmds.HelpText{ - Tagline: "Deprecated way to read stats for the dag-pb node. Use 'files stat' instead.", - ShortDescription: ` -'ipfs object stat' is a plumbing command to print dag-pb node statistics. - is a base58 encoded multihash. - -DEPRECATED: modern replacements are 'files stat' and 'dag stat' -`, - LongDescription: ` -'ipfs object stat' is a plumbing command to print dag-pb node statistics. - is a base58 encoded multihash. It outputs to stdout: - - NumLinks int number of links in link table - BlockSize int size of the raw, encoded data - LinksSize int size of the links segment - DataSize int size of the data segment - CumulativeSize int cumulative size of object and its references - -DEPRECATED: Provided for legacy reasons. Modern replacements: - - For unixfs, 'ipfs files stat' can be used: - - $ ipfs files stat --with-local /ipfs/QmWfVY9y3xjsixTgbd9AorQxH7VtMpzfx2HaWtsoUYecaX - QmWfVY9y3xjsixTgbd9AorQxH7VtMpzfx2HaWtsoUYecaX - Size: 5 - CumulativeSize: 13 - ChildBlocks: 0 - Type: file - Local: 13 B of 13 B (100.00%) - - Reported sizes are based on metadata present in root block, and should not be - trusted. A slower, but more secure alternative is 'ipfs dag stat', which - will work for every DAG type. It comes with a benefit of calculating the - size by walking the DAG: - - $ ipfs dag stat /ipfs/QmWfVY9y3xjsixTgbd9AorQxH7VtMpzfx2HaWtsoUYecaX - Size: 13, NumBlocks: 1 -`, - }, - - Arguments: []cmds.Argument{ - cmds.StringArg("key", true, false, "Key of the object to retrieve, in base58-encoded multihash format.").EnableStdin(), - }, - Options: []cmds.Option{ - cmds.BoolOption(humanOptionName, "Print sizes in human readable format (e.g., 1K 234M 2G)"), - }, - Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { - api, err := cmdenv.GetApi(env, req) - if err != nil { - return err - } - - enc, err := cmdenv.GetLowLevelCidEncoder(req) - if err != nil { - return err - } - - ns, err := api.Object().Stat(req.Context, path.New(req.Arguments[0])) - if err != nil { - return err - } - - oldStat := &ipld.NodeStat{ - Hash: enc.Encode(ns.Cid), - NumLinks: ns.NumLinks, - BlockSize: ns.BlockSize, - LinksSize: ns.LinksSize, - DataSize: ns.DataSize, - CumulativeSize: ns.CumulativeSize, - } - - return cmds.EmitOnce(res, oldStat) - }, - Type: ipld.NodeStat{}, - Encoders: cmds.EncoderMap{ - cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *ipld.NodeStat) error { - wtr := tabwriter.NewWriter(w, 0, 0, 1, ' ', 0) - defer wtr.Flush() - fw := func(s string, n int) { - fmt.Fprintf(wtr, "%s:\t%d\n", s, n) - } - human, _ := req.Options[humanOptionName].(bool) - fw("NumLinks", out.NumLinks) - fw("BlockSize", out.BlockSize) - fw("LinksSize", out.LinksSize) - fw("DataSize", out.DataSize) - if human { - fmt.Fprintf(wtr, "%s:\t%s\n", "CumulativeSize", humanize.Bytes(uint64(out.CumulativeSize))) - } else { - fw("CumulativeSize", out.CumulativeSize) - } - - return nil - }), - }, -} - -// ObjectPutCmd object put command -var ObjectPutCmd = &cmds.Command{ - Status: cmds.Deprecated, // https://github.com/ipfs/kubo/issues/7936 - Helptext: cmds.HelpText{ - Tagline: "Deprecated way to store input as a DAG object. Use 'dag put' instead.", - ShortDescription: ` -'ipfs object put' is a plumbing command for storing dag-pb nodes. -It reads from stdin, and the output is a base58 encoded multihash. - -DEPRECATED and provided for legacy reasons. Use 'ipfs dag put' instead. -`, - }, - - Arguments: []cmds.Argument{ - cmds.FileArg("data", true, false, "Data to be stored as a dag-pb object.").EnableStdin(), - }, - Options: []cmds.Option{ - cmds.StringOption(inputencOptionName, "Encoding type of input data. One of: {\"protobuf\", \"json\"}.").WithDefault("json"), - cmds.StringOption(datafieldencOptionName, "Encoding type of the data field, either \"text\" or \"base64\".").WithDefault("text"), - cmds.BoolOption(pinOptionName, "Pin this object when adding."), - cmds.BoolOption(quietOptionName, "q", "Write minimal output."), - }, - Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { - api, err := cmdenv.GetApi(env, req) - if err != nil { - return err - } - - enc, err := cmdenv.GetLowLevelCidEncoder(req) - if err != nil { - return err - } - - file, err := cmdenv.GetFileArg(req.Files.Entries()) - if err != nil { - return err - } - - inputenc, _ := req.Options[inputencOptionName].(string) - if err != nil { - return err - } - - datafieldenc, _ := req.Options[datafieldencOptionName].(string) - if err != nil { - return err - } - - dopin, _ := req.Options[pinOptionName].(bool) - if err != nil { - return err - } - - p, err := api.Object().Put(req.Context, file, - options.Object.DataType(datafieldenc), - options.Object.InputEnc(inputenc), - options.Object.Pin(dopin)) - if err != nil { - return err - } - - return cmds.EmitOnce(res, &Object{Hash: enc.Encode(p.Cid())}) - }, - Encoders: cmds.EncoderMap{ - cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *Object) error { - quiet, _ := req.Options[quietOptionName].(bool) - - o := out.Hash - if !quiet { - o = "added " + o - } - - fmt.Fprintln(w, o) - - return nil - }), - }, - Type: Object{}, -} - -// ObjectNewCmd object new command -var ObjectNewCmd = &cmds.Command{ - Status: cmds.Deprecated, // https://github.com/ipfs/kubo/issues/7936 - Helptext: cmds.HelpText{ - Tagline: "Deprecated way to create a new dag-pb object from a template.", - ShortDescription: ` -'ipfs object new' is a plumbing command for creating new dag-pb nodes. -DEPRECATED and provided for legacy reasons. Use 'dag put' and 'files' instead. -`, - LongDescription: ` -'ipfs object new' is a plumbing command for creating new dag-pb nodes. -By default it creates and returns a new empty merkledag node, but -you may pass an optional template argument to create a preformatted -node. - -Available templates: - * unixfs-dir - -DEPRECATED and provided for legacy reasons. Use 'dag put' and 'files' instead. -`, - }, - Arguments: []cmds.Argument{ - cmds.StringArg("template", false, false, "Template to use. Optional."), - }, - Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { - api, err := cmdenv.GetApi(env, req) - if err != nil { - return err - } - - enc, err := cmdenv.GetLowLevelCidEncoder(req) - if err != nil { - return err - } - - template := "empty" - if len(req.Arguments) == 1 { - template = req.Arguments[0] - } - - nd, err := api.Object().New(req.Context, options.Object.Type(template)) - if err != nil && err != io.EOF { - return err - } - - return cmds.EmitOnce(res, &Object{Hash: enc.Encode(nd.Cid())}) - }, - Encoders: cmds.EncoderMap{ - cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *Object) error { - fmt.Fprintln(w, out.Hash) - return nil - }), - }, - Type: Object{}, -} - -// converts the Node object into a real dag.ProtoNode -func deserializeNode(nd *Node, dataFieldEncoding string) (*dag.ProtoNode, error) { - dagnode := new(dag.ProtoNode) - switch dataFieldEncoding { - case "text": - dagnode.SetData([]byte(nd.Data)) - case "base64": - data, err := base64.StdEncoding.DecodeString(nd.Data) - if err != nil { - return nil, err - } - dagnode.SetData(data) - default: - return nil, ErrDataEncoding - } - - links := make([]*ipld.Link, len(nd.Links)) - for i, link := range nd.Links { - c, err := cid.Decode(link.Hash) - if err != nil { - return nil, err - } - links[i] = &ipld.Link{ - Name: link.Name, - Size: link.Size, - Cid: c, - } - } - if err := dagnode.SetLinks(links); err != nil { - return nil, err - } - - return dagnode, nil -} - -func encodeData(data []byte, encoding string) (string, error) { - switch encoding { - case "text": - return string(data), nil - case "base64": - return base64.StdEncoding.EncodeToString(data), nil - } - - return "", ErrDataEncoding -} diff --git a/core/commands/object/patch.go b/core/commands/object/patch.go index 6368562bc..5a82dfe0b 100644 --- a/core/commands/object/patch.go +++ b/core/commands/object/patch.go @@ -8,8 +8,7 @@ import ( "github.com/ipfs/kubo/core/commands/cmdenv" "github.com/ipfs/kubo/core/commands/cmdutils" - "github.com/ipfs/interface-go-ipfs-core/options" - "github.com/ipfs/interface-go-ipfs-core/path" + "github.com/ipfs/kubo/core/coreiface/options" ) var ObjectPatchCmd = &cmds.Command{ @@ -38,122 +37,16 @@ For modern use cases, use MFS with 'files' commands: 'ipfs files --help'. }, Arguments: []cmds.Argument{}, Subcommands: map[string]*cmds.Command{ - "append-data": patchAppendDataCmd, + "append-data": RemovedObjectCmd, "add-link": patchAddLinkCmd, "rm-link": patchRmLinkCmd, - "set-data": patchSetDataCmd, + "set-data": RemovedObjectCmd, }, Options: []cmds.Option{ cmdutils.AllowBigBlockOption, }, } -var patchAppendDataCmd = &cmds.Command{ - Status: cmds.Deprecated, // https://github.com/ipfs/kubo/issues/7936 - Helptext: cmds.HelpText{ - Tagline: "Deprecated way to append data to the data segment of a DAG node.", - ShortDescription: ` -Append data to what already exists in the data segment in the given object. - -Example: - - $ echo "hello" | ipfs object patch $HASH append-data - -NOTE: This does not append data to a file - it modifies the actual raw -data within a dag-pb object. Blocks have a max size of 1MiB and objects larger than -the limit will not be respected by the network. - -DEPRECATED and provided for legacy reasons. Use 'ipfs add' or 'ipfs files' instead. -`, - }, - Arguments: []cmds.Argument{ - cmds.StringArg("root", true, false, "The hash of the node to modify."), - cmds.FileArg("data", true, false, "Data to append.").EnableStdin(), - }, - Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { - api, err := cmdenv.GetApi(env, req) - if err != nil { - return err - } - - root := path.New(req.Arguments[0]) - - file, err := cmdenv.GetFileArg(req.Files.Entries()) - if err != nil { - return err - } - - p, err := api.Object().AppendData(req.Context, root, file) - if err != nil { - return err - } - - if err := cmdutils.CheckCIDSize(req, p.Cid(), api.Dag()); err != nil { - return err - } - - return cmds.EmitOnce(res, &Object{Hash: p.Cid().String()}) - }, - Type: &Object{}, - Encoders: cmds.EncoderMap{ - cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, obj *Object) error { - _, err := fmt.Fprintln(w, obj.Hash) - return err - }), - }, -} - -var patchSetDataCmd = &cmds.Command{ - Status: cmds.Deprecated, // https://github.com/ipfs/kubo/issues/7936 - Helptext: cmds.HelpText{ - Tagline: "Deprecated way to set the data field of dag-pb object.", - ShortDescription: ` -Set the data of an IPFS object from stdin or with the contents of a file. - -Example: - - $ echo "my data" | ipfs object patch $MYHASH set-data - -DEPRECATED and provided for legacy reasons. Use 'files cp' and 'dag put' instead. -`, - }, - Arguments: []cmds.Argument{ - cmds.StringArg("root", true, false, "The hash of the node to modify."), - cmds.FileArg("data", true, false, "The data to set the object to.").EnableStdin(), - }, - Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { - api, err := cmdenv.GetApi(env, req) - if err != nil { - return err - } - - root := path.New(req.Arguments[0]) - - file, err := cmdenv.GetFileArg(req.Files.Entries()) - if err != nil { - return err - } - - p, err := api.Object().SetData(req.Context, root, file) - if err != nil { - return err - } - - if err := cmdutils.CheckCIDSize(req, p.Cid(), api.Dag()); err != nil { - return err - } - - return cmds.EmitOnce(res, &Object{Hash: p.Cid().String()}) - }, - Type: Object{}, - Encoders: cmds.EncoderMap{ - cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *Object) error { - fmt.Fprintln(w, out.Hash) - return nil - }), - }, -} - var patchRmLinkCmd = &cmds.Command{ Status: cmds.Deprecated, // https://github.com/ipfs/kubo/issues/7936 Helptext: cmds.HelpText{ @@ -174,7 +67,10 @@ DEPRECATED and provided for legacy reasons. Use 'files rm' instead. return err } - root := path.New(req.Arguments[0]) + root, err := cmdutils.PathOrCidPath(req.Arguments[0]) + if err != nil { + return err + } name := req.Arguments[1] p, err := api.Object().RmLink(req.Context, root, name) @@ -182,11 +78,11 @@ DEPRECATED and provided for legacy reasons. Use 'files rm' instead. return err } - if err := cmdutils.CheckCIDSize(req, p.Cid(), api.Dag()); err != nil { + if err := cmdutils.CheckCIDSize(req, p.RootCid(), api.Dag()); err != nil { return err } - return cmds.EmitOnce(res, &Object{Hash: p.Cid().String()}) + return cmds.EmitOnce(res, &Object{Hash: p.RootCid().String()}) }, Type: Object{}, Encoders: cmds.EncoderMap{ @@ -238,9 +134,17 @@ Use MFS and 'files' commands instead: return err } - root := path.New(req.Arguments[0]) + root, err := cmdutils.PathOrCidPath(req.Arguments[0]) + if err != nil { + return err + } + name := req.Arguments[1] - child := path.New(req.Arguments[2]) + + child, err := cmdutils.PathOrCidPath(req.Arguments[2]) + if err != nil { + return err + } create, _ := req.Options[createOptionName].(bool) if err != nil { @@ -253,11 +157,11 @@ Use MFS and 'files' commands instead: return err } - if err := cmdutils.CheckCIDSize(req, p.Cid(), api.Dag()); err != nil { + if err := cmdutils.CheckCIDSize(req, p.RootCid(), api.Dag()); err != nil { return err } - return cmds.EmitOnce(res, &Object{Hash: p.Cid().String()}) + return cmds.EmitOnce(res, &Object{Hash: p.RootCid().String()}) }, Type: Object{}, Encoders: cmds.EncoderMap{ diff --git a/core/commands/p2p.go b/core/commands/p2p.go index 7aaa7fee2..7b8b416e5 100644 --- a/core/commands/p2p.go +++ b/core/commands/p2p.go @@ -370,9 +370,7 @@ var p2pCloseCmd = &cmds.Command{ proto := protocol.ID(protoOpt) - var ( - target, listen ma.Multiaddr - ) + var target, listen ma.Multiaddr if l { listen, err = ma.NewMultiaddr(listenOpt) diff --git a/core/commands/pin/pin.go b/core/commands/pin/pin.go index 89e56f687..b87760aaf 100644 --- a/core/commands/pin/pin.go +++ b/core/commands/pin/pin.go @@ -8,19 +8,19 @@ import ( "os" "time" - bserv "github.com/ipfs/go-blockservice" + bserv "github.com/ipfs/boxo/blockservice" + offline "github.com/ipfs/boxo/exchange/offline" + dag "github.com/ipfs/boxo/ipld/merkledag" + verifcid "github.com/ipfs/boxo/verifcid" cid "github.com/ipfs/go-cid" cidenc "github.com/ipfs/go-cidutil/cidenc" cmds "github.com/ipfs/go-ipfs-cmds" - offline "github.com/ipfs/go-ipfs-exchange-offline" - dag "github.com/ipfs/go-merkledag" - verifcid "github.com/ipfs/go-verifcid" - coreiface "github.com/ipfs/interface-go-ipfs-core" - options "github.com/ipfs/interface-go-ipfs-core/options" - "github.com/ipfs/interface-go-ipfs-core/path" + coreiface "github.com/ipfs/kubo/core/coreiface" + options "github.com/ipfs/kubo/core/coreiface/options" core "github.com/ipfs/kubo/core" cmdenv "github.com/ipfs/kubo/core/commands/cmdenv" + "github.com/ipfs/kubo/core/commands/cmdutils" e "github.com/ipfs/kubo/core/commands/e" ) @@ -57,6 +57,26 @@ var addPinCmd = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "Pin objects to local storage.", ShortDescription: "Stores an IPFS object(s) from a given path locally to disk.", + LongDescription: ` +Create a pin for the given object, protecting resolved CID from being garbage +collected. + +An optional name can be provided, and read back via 'ipfs pin ls --names'. + +Be mindful of defaults: + +Default pin type is 'recursive' (entire DAG). +Pass -r=false to create a direct pin for a single block. +Use 'pin ls -t recursive' to only list roots of recursively pinned DAGs +(significantly faster when many big DAGs are pinned recursively) + +Default pin name is empty. Pass '--name' to 'pin add' to set one +and use 'pin ls --names' to see it. Pinning a second time with a different +name will update the name of the pin. + +If daemon is running, any missing blocks will be retrieved from the network. +It may take some time. Pass '--progress' to track the progress. +`, }, Arguments: []cmds.Argument{ @@ -64,6 +84,7 @@ var addPinCmd = &cmds.Command{ }, Options: []cmds.Option{ cmds.BoolOption(pinRecursiveOptionName, "r", "Recursively pin the object linked to by the specified object(s).").WithDefault(true), + cmds.StringOption(pinNameOptionName, "n", "An optional name for created pin(s)."), cmds.BoolOption(pinProgressOptionName, "Show progress"), }, Type: AddPinOutput{}, @@ -75,6 +96,7 @@ var addPinCmd = &cmds.Command{ // set recursive flag recursive, _ := req.Options[pinRecursiveOptionName].(bool) + name, _ := req.Options[pinNameOptionName].(string) showProgress, _ := req.Options[pinProgressOptionName].(bool) if err := req.ParseBodyArgs(); err != nil { @@ -87,7 +109,7 @@ var addPinCmd = &cmds.Command{ } if !showProgress { - added, err := pinAddMany(req.Context, api, enc, req.Arguments, recursive) + added, err := pinAddMany(req.Context, api, enc, req.Arguments, recursive, name) if err != nil { return err } @@ -105,7 +127,7 @@ var addPinCmd = &cmds.Command{ ch := make(chan pinResult, 1) go func() { - added, err := pinAddMany(ctx, api, enc, req.Arguments, recursive) + added, err := pinAddMany(ctx, api, enc, req.Arguments, recursive, name) ch <- pinResult{pins: added, err: err} }() @@ -181,18 +203,23 @@ var addPinCmd = &cmds.Command{ }, } -func pinAddMany(ctx context.Context, api coreiface.CoreAPI, enc cidenc.Encoder, paths []string, recursive bool) ([]string, error) { +func pinAddMany(ctx context.Context, api coreiface.CoreAPI, enc cidenc.Encoder, paths []string, recursive bool, name string) ([]string, error) { added := make([]string, len(paths)) for i, b := range paths { - rp, err := api.ResolvePath(ctx, path.New(b)) + p, err := cmdutils.PathOrCidPath(b) if err != nil { return nil, err } - if err := api.Pin().Add(ctx, rp, options.Pin.Recursive(recursive)); err != nil { + rp, _, err := api.ResolvePath(ctx, p) + if err != nil { return nil, err } - added[i] = enc.Encode(rp.Cid()) + + if err := api.Pin().Add(ctx, rp, options.Pin.Recursive(recursive), options.Pin.Name(name)); err != nil { + return nil, err + } + added[i] = enc.Encode(rp.RootCid()) } return added, nil @@ -242,12 +269,17 @@ ipfs pin ls -t indirect pins := make([]string, 0, len(req.Arguments)) for _, b := range req.Arguments { - rp, err := api.ResolvePath(req.Context, path.New(b)) + p, err := cmdutils.PathOrCidPath(b) if err != nil { return err } - id := enc.Encode(rp.Cid()) + rp, _, err := api.ResolvePath(req.Context, p) + if err != nil { + return err + } + + id := enc.Encode(rp.RootCid()) pins = append(pins, id) if err := api.Pin().Rm(req.Context, rp, options.Pin.RmRecursive(recursive)); err != nil { return err @@ -271,6 +303,7 @@ const ( pinTypeOptionName = "type" pinQuietOptionName = "quiet" pinStreamOptionName = "stream" + pinNamesOptionName = "names" ) var listPinCmd = &cmds.Command{ @@ -284,6 +317,7 @@ respectively. `, LongDescription: ` Returns a list of objects that are pinned locally. + By default, all pinned objects are returned, but the '--type' flag or arguments can restrict that to a specific pin type or to some specific objects respectively. @@ -292,10 +326,13 @@ Use --type= to specify the type of pinned keys to list. Valid values are: * "direct": pin that specific object. * "recursive": pin that specific object, and indirectly pin all its - descendants + descendants * "indirect": pinned indirectly by an ancestor (like a refcount) * "all" +By default, pin names are not included (returned as empty). +Pass '--names' flag to return pin names (set with '--name' from 'pin add'). + With arguments, the command fails if any of the arguments is not a pinned object. And if --type= is additionally used, the command will also fail if any of the arguments is not of the specified type. @@ -322,8 +359,10 @@ Example: }, Options: []cmds.Option{ cmds.StringOption(pinTypeOptionName, "t", "The type of pinned keys to list. Can be \"direct\", \"indirect\", \"recursive\", or \"all\".").WithDefault("all"), - cmds.BoolOption(pinQuietOptionName, "q", "Write just hashes of objects."), + cmds.BoolOption(pinQuietOptionName, "q", "Output only the CIDs of pins."), + cmds.StringOption(pinNameOptionName, "n", "Limit returned pins to ones with names that contain the value provided (case-sensitive, partial match). Implies --names=true."), cmds.BoolOption(pinStreamOptionName, "s", "Enable streaming of pins as they are discovered."), + cmds.BoolOption(pinNamesOptionName, "Include pin names in the output (slower, disabled by default)."), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { api, err := cmdenv.GetApi(env, req) @@ -333,6 +372,8 @@ Example: typeStr, _ := req.Options[pinTypeOptionName].(string) stream, _ := req.Options[pinStreamOptionName].(bool) + displayNames, _ := req.Options[pinNamesOptionName].(bool) + name, _ := req.Options[pinNameOptionName].(string) switch typeStr { case "all", "direct", "indirect", "recursive": @@ -342,36 +383,39 @@ Example: } // For backward compatibility, we accumulate the pins in the same output type as before. - emit := res.Emit + var emit func(PinLsOutputWrapper) error lgcList := map[string]PinLsType{} if !stream { - emit = func(v interface{}) error { - obj := v.(*PinLsOutputWrapper) - lgcList[obj.PinLsObject.Cid] = PinLsType{Type: obj.PinLsObject.Type} + emit = func(v PinLsOutputWrapper) error { + lgcList[v.PinLsObject.Cid] = PinLsType{Type: v.PinLsObject.Type, Name: v.PinLsObject.Name} return nil } + } else { + emit = func(v PinLsOutputWrapper) error { + return res.Emit(v) + } } if len(req.Arguments) > 0 { err = pinLsKeys(req, typeStr, api, emit) } else { - err = pinLsAll(req, typeStr, api, emit) + err = pinLsAll(req, typeStr, displayNames || name != "", name, api, emit) } if err != nil { return err } if !stream { - return cmds.EmitOnce(res, &PinLsOutputWrapper{ + return cmds.EmitOnce(res, PinLsOutputWrapper{ PinLsList: PinLsList{Keys: lgcList}, }) } return nil }, - Type: &PinLsOutputWrapper{}, + Type: PinLsOutputWrapper{}, Encoders: cmds.EncoderMap{ - cmds.JSON: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *PinLsOutputWrapper) error { + cmds.JSON: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out PinLsOutputWrapper) error { stream, _ := req.Options[pinStreamOptionName].(bool) enc := json.NewEncoder(w) @@ -382,15 +426,17 @@ Example: return enc.Encode(out.PinLsList) }), - cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *PinLsOutputWrapper) error { + cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out PinLsOutputWrapper) error { quiet, _ := req.Options[pinQuietOptionName].(bool) stream, _ := req.Options[pinStreamOptionName].(bool) if stream { if quiet { fmt.Fprintf(w, "%s\n", out.PinLsObject.Cid) - } else { + } else if out.PinLsObject.Name == "" { fmt.Fprintf(w, "%s %s\n", out.PinLsObject.Cid, out.PinLsObject.Type) + } else { + fmt.Fprintf(w, "%s %s %s\n", out.PinLsObject.Cid, out.PinLsObject.Type, out.PinLsObject.Name) } return nil } @@ -398,8 +444,10 @@ Example: for k, v := range out.PinLsList.Keys { if quiet { fmt.Fprintf(w, "%s\n", k) - } else { + } else if v.Name == "" { fmt.Fprintf(w, "%s %s\n", k, v.Type) + } else { + fmt.Fprintf(w, "%s %s %s\n", k, v.Type, v.Name) } } @@ -418,21 +466,23 @@ type PinLsOutputWrapper struct { // PinLsList is a set of pins with their type type PinLsList struct { - Keys map[string]PinLsType + Keys map[string]PinLsType `json:",omitempty"` } // PinLsType contains the type of a pin type PinLsType struct { Type string + Name string } // PinLsObject contains the description of a pin type PinLsObject struct { Cid string `json:",omitempty"` + Name string `json:",omitempty"` Type string `json:",omitempty"` } -func pinLsKeys(req *cmds.Request, typeStr string, api coreiface.CoreAPI, emit func(value interface{}) error) error { +func pinLsKeys(req *cmds.Request, typeStr string, api coreiface.CoreAPI, emit func(value PinLsOutputWrapper) error) error { enc, err := cmdenv.GetCidEncoder(req) if err != nil { return err @@ -450,7 +500,12 @@ func pinLsKeys(req *cmds.Request, typeStr string, api coreiface.CoreAPI, emit fu } for _, p := range req.Arguments { - rp, err := api.ResolvePath(req.Context, path.New(p)) + p, err := cmdutils.PathOrCidPath(p) + if err != nil { + return err + } + + rp, _, err := api.ResolvePath(req.Context, p) if err != nil { return err } @@ -470,10 +525,10 @@ func pinLsKeys(req *cmds.Request, typeStr string, api coreiface.CoreAPI, emit fu pinType = "indirect through " + pinType } - err = emit(&PinLsOutputWrapper{ + err = emit(PinLsOutputWrapper{ PinLsObject: PinLsObject{ Type: pinType, - Cid: enc.Encode(rp.Cid()), + Cid: enc.Encode(rp.RootCid()), }, }) if err != nil { @@ -484,7 +539,7 @@ func pinLsKeys(req *cmds.Request, typeStr string, api coreiface.CoreAPI, emit fu return nil } -func pinLsAll(req *cmds.Request, typeStr string, api coreiface.CoreAPI, emit func(value interface{}) error) error { +func pinLsAll(req *cmds.Request, typeStr string, detailed bool, name string, api coreiface.CoreAPI, emit func(value PinLsOutputWrapper) error) error { enc, err := cmdenv.GetCidEncoder(req) if err != nil { return err @@ -502,7 +557,7 @@ func pinLsAll(req *cmds.Request, typeStr string, api coreiface.CoreAPI, emit fun panic("unhandled pin type") } - pins, err := api.Pin().Ls(req.Context, opt) + pins, err := api.Pin().Ls(req.Context, opt, options.Pin.Ls.Detailed(detailed), options.Pin.Ls.Name(name)) if err != nil { return err } @@ -511,10 +566,11 @@ func pinLsAll(req *cmds.Request, typeStr string, api coreiface.CoreAPI, emit fun if err := p.Err(); err != nil { return err } - err = emit(&PinLsOutputWrapper{ + err = emit(PinLsOutputWrapper{ PinLsObject: PinLsObject{ Type: p.Type(), - Cid: enc.Encode(p.Path().Cid()), + Name: p.Name(), + Cid: enc.Encode(p.Path().RootCid()), }, }) if err != nil { @@ -565,12 +621,22 @@ pin. unpin, _ := req.Options[pinUnpinOptionName].(bool) - // Resolve the paths ahead of time so we can return the actual CIDs - from, err := api.ResolvePath(req.Context, path.New(req.Arguments[0])) + fromPath, err := cmdutils.PathOrCidPath(req.Arguments[0]) if err != nil { return err } - to, err := api.ResolvePath(req.Context, path.New(req.Arguments[1])) + + toPath, err := cmdutils.PathOrCidPath(req.Arguments[1]) + if err != nil { + return err + } + + // Resolve the paths ahead of time so we can return the actual CIDs + from, _, err := api.ResolvePath(req.Context, fromPath) + if err != nil { + return err + } + to, _, err := api.ResolvePath(req.Context, toPath) if err != nil { return err } @@ -580,7 +646,7 @@ pin. return err } - return cmds.EmitOnce(res, &PinOutput{Pins: []string{enc.Encode(from.Cid()), enc.Encode(to.Cid())}}) + return cmds.EmitOnce(res, &PinOutput{Pins: []string{enc.Encode(from.RootCid()), enc.Encode(to.RootCid())}}) }, Encoders: cmds.EncoderMap{ cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *PinOutput) error { @@ -648,13 +714,14 @@ var verifyPinCmd = &cmds.Command{ // PinVerifyRes is the result returned for each pin checked in "pin verify" type PinVerifyRes struct { - Cid string + Cid string `json:",omitempty"` + Err string `json:",omitempty"` PinStatus } // PinStatus is part of PinVerifyRes, do not use directly type PinStatus struct { - Ok bool + Ok bool `json:",omitempty"` BadNodes []BadNode `json:",omitempty"` } @@ -669,16 +736,13 @@ type pinVerifyOpts struct { includeOk bool } -func pinVerify(ctx context.Context, n *core.IpfsNode, opts pinVerifyOpts, enc cidenc.Encoder) (<-chan interface{}, error) { +// FIXME: this implementation is duplicated sith core/coreapi.PinAPI.Verify, remove this one and exclusively rely on CoreAPI. +func pinVerify(ctx context.Context, n *core.IpfsNode, opts pinVerifyOpts, enc cidenc.Encoder) (<-chan any, error) { visited := make(map[cid.Cid]PinStatus) bs := n.Blocks.Blockstore() DAG := dag.NewDAGService(bserv.New(bs, offline.Exchange(bs))) getLinks := dag.GetLinksWithDAG(DAG) - recPins, err := n.Pinning.RecursiveKeys(ctx) - if err != nil { - return nil, err - } var checkPin func(root cid.Cid) PinStatus checkPin = func(root cid.Cid) PinStatus { @@ -687,7 +751,7 @@ func pinVerify(ctx context.Context, n *core.IpfsNode, opts pinVerifyOpts, enc ci return status } - if err := verifcid.ValidateCid(root); err != nil { + if err := verifcid.ValidateCid(verifcid.DefaultAllowlist, root); err != nil { status := PinStatus{Ok: false} if opts.explain { status.BadNodes = []BadNode{{Cid: enc.Encode(key), Err: err.Error()}} @@ -719,14 +783,18 @@ func pinVerify(ctx context.Context, n *core.IpfsNode, opts pinVerifyOpts, enc ci return status } - out := make(chan interface{}) + out := make(chan any) go func() { defer close(out) - for _, cid := range recPins { - pinStatus := checkPin(cid) + for p := range n.Pinning.RecursiveKeys(ctx, false) { + if p.Err != nil { + out <- PinVerifyRes{Err: p.Err.Error()} + return + } + pinStatus := checkPin(p.Pin.Key) if !pinStatus.Ok || opts.includeOk { select { - case out <- &PinVerifyRes{enc.Encode(cid), pinStatus}: + case out <- PinVerifyRes{Cid: enc.Encode(p.Pin.Key), PinStatus: pinStatus}: case <-ctx.Done(): return } @@ -739,12 +807,18 @@ func pinVerify(ctx context.Context, n *core.IpfsNode, opts pinVerifyOpts, enc ci // Format formats PinVerifyRes func (r PinVerifyRes) Format(out io.Writer) { + if r.Err != "" { + fmt.Fprintf(out, "error: %s\n", r.Err) + return + } + if r.Ok { fmt.Fprintf(out, "%s ok\n", r.Cid) - } else { - fmt.Fprintf(out, "%s broken\n", r.Cid) - for _, e := range r.BadNodes { - fmt.Fprintf(out, " %s: %s\n", e.Cid, e.Err) - } + return + } + + fmt.Fprintf(out, "%s broken\n", r.Cid) + for _, e := range r.BadNodes { + fmt.Fprintf(out, " %s: %s\n", e.Cid, e.Err) } } diff --git a/core/commands/pin/remotepin.go b/core/commands/pin/remotepin.go index 95b17a315..3721913e7 100644 --- a/core/commands/pin/remotepin.go +++ b/core/commands/pin/remotepin.go @@ -15,13 +15,13 @@ import ( "golang.org/x/sync/errgroup" + pinclient "github.com/ipfs/boxo/pinning/remote/client" cid "github.com/ipfs/go-cid" cmds "github.com/ipfs/go-ipfs-cmds" logging "github.com/ipfs/go-log" - pinclient "github.com/ipfs/go-pinning-service-http-client" - path "github.com/ipfs/interface-go-ipfs-core/path" config "github.com/ipfs/kubo/config" "github.com/ipfs/kubo/core/commands/cmdenv" + "github.com/ipfs/kubo/core/commands/cmdutils" fsrepo "github.com/ipfs/kubo/repo/fsrepo" "github.com/libp2p/go-libp2p/core/host" peer "github.com/libp2p/go-libp2p/core/peer" @@ -54,16 +54,18 @@ var remotePinServiceCmd = &cmds.Command{ }, } -const pinNameOptionName = "name" -const pinCIDsOptionName = "cid" -const pinStatusOptionName = "status" -const pinServiceNameOptionName = "service" -const pinServiceNameArgName = pinServiceNameOptionName -const pinServiceEndpointArgName = "endpoint" -const pinServiceKeyArgName = "key" -const pinServiceStatOptionName = "stat" -const pinBackgroundOptionName = "background" -const pinForceOptionName = "force" +const ( + pinNameOptionName = "name" + pinCIDsOptionName = "cid" + pinStatusOptionName = "status" + pinServiceNameOptionName = "service" + pinServiceNameArgName = pinServiceNameOptionName + pinServiceEndpointArgName = "endpoint" + pinServiceKeyArgName = "key" + pinServiceStatOptionName = "stat" + pinBackgroundOptionName = "background" + pinForceOptionName = "force" +) type RemotePinOutput struct { Status string @@ -155,7 +157,12 @@ NOTE: a comma-separated notation is supported in CLI for convenience: if err != nil { return err } - rp, err := api.ResolvePath(ctx, path.New(req.Arguments[0])) + p, err := cmdutils.PathOrCidPath(req.Arguments[0]) + if err != nil { + return err + } + + rp, _, err := api.ResolvePath(ctx, p) if err != nil { return err } @@ -175,7 +182,7 @@ NOTE: a comma-separated notation is supported in CLI for convenience: return err } - isInBlockstore, err := node.Blockstore.Has(req.Context, rp.Cid()) + isInBlockstore, err := node.Blockstore.Has(req.Context, rp.RootCid()) if err != nil { return err } @@ -192,7 +199,7 @@ NOTE: a comma-separated notation is supported in CLI for convenience: // Execute remote pin request // TODO: fix panic when pinning service is down - ps, err := c.Add(ctx, rp.Cid(), opts...) + ps, err := c.Add(ctx, rp.RootCid(), opts...) if err != nil { return err } @@ -214,6 +221,8 @@ NOTE: a comma-separated notation is supported in CLI for convenience: // Block unless --background=true is passed if !req.Options[pinBackgroundOptionName].(bool) { + const pinWaitTime = 500 * time.Millisecond + var timer *time.Timer requestID := ps.GetRequestId() for { ps, err = c.GetStatusByID(ctx, requestID) @@ -230,10 +239,15 @@ NOTE: a comma-separated notation is supported in CLI for convenience: if s == pinclient.StatusFailed { return fmt.Errorf("remote service failed to pin requestid=%q", requestID) } - tmr := time.NewTimer(time.Second / 2) + if timer == nil { + timer = time.NewTimer(pinWaitTime) + } else { + timer.Reset(pinWaitTime) + } select { - case <-tmr.C: + case <-timer.C: case <-ctx.Done(): + timer.Stop() return fmt.Errorf("waiting for pin interrupted, requestid=%q remains on remote service", requestID) } } diff --git a/core/commands/pin/remotepin_test.go b/core/commands/pin/remotepin_test.go index d1dfa8fd5..98bb707a8 100644 --- a/core/commands/pin/remotepin_test.go +++ b/core/commands/pin/remotepin_test.go @@ -63,5 +63,4 @@ func TestNormalizeEndpoint(t *testing.T) { continue } } - } diff --git a/core/commands/ping.go b/core/commands/ping.go index 26e2e3d12..dabc1a248 100644 --- a/core/commands/ping.go +++ b/core/commands/ping.go @@ -79,7 +79,7 @@ trip latency information. if len(n.Peerstore.Addrs(pid)) == 0 { // Make sure we can find the node in question if err := res.Emit(&PingResult{ - Text: fmt.Sprintf("Looking up peer %s", pid.Pretty()), + Text: fmt.Sprintf("Looking up peer %s", pid), Success: true, }); err != nil { return err @@ -95,7 +95,7 @@ trip latency information. } if err := res.Emit(&PingResult{ - Text: fmt.Sprintf("PING %s.", pid.Pretty()), + Text: fmt.Sprintf("PING %s.", pid), Success: true, }); err != nil { return err diff --git a/core/commands/profile.go b/core/commands/profile.go index 3875cfdcb..9f54e0612 100644 --- a/core/commands/profile.go +++ b/core/commands/profile.go @@ -49,7 +49,8 @@ The output file includes: - A list of running goroutines. - A CPU profile. -- A heap profile. +- A heap inuse profile. +- A heap allocation profile. - A mutex profile. - A block profile. - Your copy of go-ipfs. @@ -79,10 +80,12 @@ However, it could reveal: profile.CollectorGoroutinesPprof, profile.CollectorVersion, profile.CollectorHeap, + profile.CollectorAllocs, profile.CollectorBin, profile.CollectorCPU, profile.CollectorMutex, profile.CollectorBlock, + profile.CollectorTrace, }), cmds.StringOption(profileTimeOption, "The amount of time spent profiling. If this is set to 0, then sampling profiles are skipped.").WithDefault("30s"), cmds.IntOption(mutexProfileFractionOption, "The fraction 1/n of mutex contention events that are reported in the mutex profile.").WithDefault(4), diff --git a/core/commands/pubsub.go b/core/commands/pubsub.go index ef8afcb44..d50e651b2 100644 --- a/core/commands/pubsub.go +++ b/core/commands/pubsub.go @@ -2,6 +2,7 @@ package commands import ( "context" + "errors" "fmt" "io" "net/http" @@ -9,21 +10,20 @@ import ( cmdenv "github.com/ipfs/kubo/core/commands/cmdenv" mbase "github.com/multiformats/go-multibase" - "github.com/pkg/errors" cmds "github.com/ipfs/go-ipfs-cmds" - options "github.com/ipfs/interface-go-ipfs-core/options" + options "github.com/ipfs/kubo/core/coreiface/options" ) var PubsubCmd = &cmds.Command{ - Status: cmds.Experimental, + Status: cmds.Deprecated, Helptext: cmds.HelpText{ Tagline: "An experimental publish-subscribe system on ipfs.", ShortDescription: ` ipfs pubsub allows you to publish messages to a given topic, and also to subscribe to new messages on a given topic. -EXPERIMENTAL FEATURE +DEPRECATED FEATURE (see https://github.com/ipfs/kubo/issues/9717) It is not intended in its current state to be used in a production environment. To use, the daemon must be run with @@ -46,13 +46,13 @@ type pubsubMessage struct { } var PubsubSubCmd = &cmds.Command{ - Status: cmds.Experimental, + Status: cmds.Deprecated, Helptext: cmds.HelpText{ Tagline: "Subscribe to messages on a given topic.", ShortDescription: ` ipfs pubsub sub subscribes to messages on a given topic. -EXPERIMENTAL FEATURE +DEPRECATED FEATURE (see https://github.com/ipfs/kubo/issues/9717) It is not intended in its current state to be used in a production environment. To use, the daemon must be run with @@ -110,7 +110,7 @@ TOPIC AND DATA ENCODING encoder, _ := mbase.EncoderByName("base64url") psm := pubsubMessage{ Data: encoder.Encode(msg.Data()), - From: msg.From().Pretty(), + From: msg.From().String(), Seqno: encoder.Encode(msg.Seq()), } for _, topic := range msg.Topics() { @@ -145,14 +145,14 @@ TOPIC AND DATA ENCODING } var PubsubPubCmd = &cmds.Command{ - Status: cmds.Experimental, + Status: cmds.Deprecated, Helptext: cmds.HelpText{ Tagline: "Publish data to a given pubsub topic.", ShortDescription: ` ipfs pubsub pub publishes a message to a specified topic. It reads binary data from stdin or a file. -EXPERIMENTAL FEATURE +DEPRECATED FEATURE (see https://github.com/ipfs/kubo/issues/9717) It is not intended in its current state to be used in a production environment. To use, the daemon must be run with @@ -201,13 +201,13 @@ HTTP RPC ENCODING } var PubsubLsCmd = &cmds.Command{ - Status: cmds.Experimental, + Status: cmds.Deprecated, Helptext: cmds.HelpText{ Tagline: "List subscribed topics by name.", ShortDescription: ` ipfs pubsub ls lists out the names of topics you are currently subscribed to. -EXPERIMENTAL FEATURE +DEPRECATED FEATURE (see https://github.com/ipfs/kubo/issues/9717) It is not intended in its current state to be used in a production environment. To use, the daemon must be run with @@ -273,7 +273,7 @@ func safeTextListEncoder(req *cmds.Request, w io.Writer, list *stringList) error } var PubsubPeersCmd = &cmds.Command{ - Status: cmds.Experimental, + Status: cmds.Deprecated, Helptext: cmds.HelpText{ Tagline: "List peers we are currently pubsubbing with.", ShortDescription: ` @@ -281,7 +281,7 @@ ipfs pubsub peers with no arguments lists out the pubsub peers you are currently connected to. If given a topic, it will list connected peers who are subscribed to the named topic. -EXPERIMENTAL FEATURE +DEPRECATED FEATURE (see https://github.com/ipfs/kubo/issues/9717) It is not intended in its current state to be used in a production environment. To use, the daemon must be run with @@ -323,7 +323,7 @@ TOPIC AND DATA ENCODING list := &stringList{make([]string, 0, len(peers))} for _, peer := range peers { - list.Strings = append(list.Strings, peer.Pretty()) + list.Strings = append(list.Strings, peer.String()) } sort.Strings(list.Strings) return cmds.EmitOnce(res, list) @@ -351,7 +351,7 @@ func urlArgsDecoder(req *cmds.Request, env cmds.Environment) error { for n, arg := range req.Arguments { encoding, data, err := mbase.Decode(arg) if err != nil { - return errors.Wrap(err, "URL arg must be multibase encoded") + return fmt.Errorf("URL arg must be multibase encoded: %w", err) } // Enforce URL-safe encoding is used for data passed via URL arguments diff --git a/core/commands/refs.go b/core/commands/refs.go index 81c13cbda..53c92c3df 100644 --- a/core/commands/refs.go +++ b/core/commands/refs.go @@ -8,20 +8,20 @@ import ( "strings" cmdenv "github.com/ipfs/kubo/core/commands/cmdenv" + "github.com/ipfs/kubo/core/commands/cmdutils" + merkledag "github.com/ipfs/boxo/ipld/merkledag" cid "github.com/ipfs/go-cid" cidenc "github.com/ipfs/go-cidutil/cidenc" cmds "github.com/ipfs/go-ipfs-cmds" ipld "github.com/ipfs/go-ipld-format" - merkledag "github.com/ipfs/go-merkledag" - iface "github.com/ipfs/interface-go-ipfs-core" - path "github.com/ipfs/interface-go-ipfs-core/path" + iface "github.com/ipfs/kubo/core/coreiface" ) var refsEncoderMap = cmds.EncoderMap{ cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *RefWrapper) error { if out.Err != "" { - return fmt.Errorf(out.Err) + return errors.New(out.Err) } fmt.Fprintln(w, out.Ref) @@ -171,11 +171,15 @@ Displays the hashes of all local objects. NOTE: This treats all local objects as func objectsForPaths(ctx context.Context, n iface.CoreAPI, paths []string) ([]cid.Cid, error) { roots := make([]cid.Cid, len(paths)) for i, sp := range paths { - o, err := n.ResolvePath(ctx, path.New(sp)) + p, err := cmdutils.PathOrCidPath(sp) if err != nil { return nil, err } - roots[i] = o.Cid() + o, _, err := n.ResolvePath(ctx, p) + if err != nil { + return nil, err + } + roots[i] = o.RootCid() } return roots, nil } diff --git a/core/commands/repo.go b/core/commands/repo.go index 5c4c7b947..77ce68590 100644 --- a/core/commands/repo.go +++ b/core/commands/repo.go @@ -19,8 +19,8 @@ import ( "github.com/ipfs/kubo/repo/fsrepo/migrations/ipfsfetcher" humanize "github.com/dustin/go-humanize" + bstore "github.com/ipfs/boxo/blockstore" cid "github.com/ipfs/go-cid" - bstore "github.com/ipfs/go-ipfs-blockstore" cmds "github.com/ipfs/go-ipfs-cmds" ) @@ -39,7 +39,6 @@ var RepoCmd = &cmds.Command{ Subcommands: map[string]*cmds.Command{ "stat": repoStatCmd, "gc": repoGcCmd, - "fsck": repoFsckCmd, "version": repoVersionCmd, "verify": repoVerifyCmd, "migrate": repoMigrateCmd, @@ -227,27 +226,6 @@ Version string The repo version. }, } -var repoFsckCmd = &cmds.Command{ - Status: cmds.Deprecated, // https://github.com/ipfs/kubo/issues/6435 - Helptext: cmds.HelpText{ - Tagline: "Remove repo lockfiles.", - ShortDescription: ` -'ipfs repo fsck' is now a no-op. -`, - }, - NoRemote: true, - Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { - return cmds.EmitOnce(res, &MessageOutput{"`ipfs repo fsck` is deprecated and does nothing.\n"}) - }, - Type: MessageOutput{}, - Encoders: cmds.EncoderMap{ - cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *MessageOutput) error { - fmt.Fprintf(w, out.Message) - return nil - }), - }, -} - type VerifyProgress struct { Msg string Progress int diff --git a/core/commands/resolve.go b/core/commands/resolve.go index 3bdd6924e..b786ed23d 100644 --- a/core/commands/resolve.go +++ b/core/commands/resolve.go @@ -7,16 +7,15 @@ import ( "strings" "time" - ns "github.com/ipfs/go-namesys" + ns "github.com/ipfs/boxo/namesys" + cidenc "github.com/ipfs/go-cidutil/cidenc" cmdenv "github.com/ipfs/kubo/core/commands/cmdenv" + "github.com/ipfs/kubo/core/commands/cmdutils" ncmd "github.com/ipfs/kubo/core/commands/name" - cidenc "github.com/ipfs/go-cidutil/cidenc" + "github.com/ipfs/boxo/path" cmds "github.com/ipfs/go-ipfs-cmds" - ipfspath "github.com/ipfs/go-path" - options "github.com/ipfs/interface-go-ipfs-core/options" - nsopts "github.com/ipfs/interface-go-ipfs-core/options/namesys" - path "github.com/ipfs/interface-go-ipfs-core/path" + options "github.com/ipfs/kubo/core/coreiface/options" ) const ( @@ -71,7 +70,7 @@ Resolve the value of an IPFS DAG path: Options: []cmds.Option{ cmds.BoolOption(resolveRecursiveOptionName, "r", "Resolve until the result is an IPFS name.").WithDefault(true), cmds.IntOption(resolveDhtRecordCountOptionName, "dhtrc", "Number of records to request for DHT resolution."), - cmds.StringOption(resolveDhtTimeoutOptionName, "dhtt", "Max time to collect values during DHT resolution eg \"30s\". Pass 0 for no timeout."), + cmds.StringOption(resolveDhtTimeoutOptionName, "dhtt", "Max time to collect values during DHT resolution e.g. \"30s\". Pass 0 for no timeout."), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { api, err := cmdenv.GetApi(env, req) @@ -87,11 +86,11 @@ Resolve the value of an IPFS DAG path: rc, rcok := req.Options[resolveDhtRecordCountOptionName].(uint) dhtt, dhttok := req.Options[resolveDhtTimeoutOptionName].(string) ropts := []options.NameResolveOption{ - options.Name.ResolveOption(nsopts.Depth(1)), + options.Name.ResolveOption(ns.ResolveWithDepth(1)), } if rcok { - ropts = append(ropts, options.Name.ResolveOption(nsopts.DhtRecordCount(rc))) + ropts = append(ropts, options.Name.ResolveOption(ns.ResolveWithDhtRecordCount(rc))) } if dhttok { d, err := time.ParseDuration(dhtt) @@ -101,14 +100,14 @@ Resolve the value of an IPFS DAG path: if d < 0 { return errors.New("DHT timeout value must be >= 0") } - ropts = append(ropts, options.Name.ResolveOption(nsopts.DhtTimeout(d))) + ropts = append(ropts, options.Name.ResolveOption(ns.ResolveWithDhtTimeout(d))) } p, err := api.Name().Resolve(req.Context, name, ropts...) // ErrResolveRecursion is fine if err != nil && err != ns.ErrResolveRecursion { return err } - return cmds.EmitOnce(res, &ncmd.ResolvedPath{Path: ipfspath.Path(p.String())}) + return cmds.EmitOnce(res, &ncmd.ResolvedPath{Path: p.String()}) } var enc cidenc.Encoder @@ -128,22 +127,34 @@ Resolve the value of an IPFS DAG path: } } - // else, ipfs path or ipns with recursive flag - rp, err := api.ResolvePath(req.Context, path.New(name)) + p, err := cmdutils.PathOrCidPath(name) if err != nil { return err } - encoded := "/" + rp.Namespace() + "/" + enc.Encode(rp.Cid()) - if remainder := rp.Remainder(); remainder != "" { - encoded += "/" + remainder + // else, ipfs path or ipns with recursive flag + rp, remainder, err := api.ResolvePath(req.Context, p) + if err != nil { + return err } - return cmds.EmitOnce(res, &ncmd.ResolvedPath{Path: ipfspath.Path(encoded)}) + // Trick to encode path with correct encoding. + encodedPath := "/" + rp.Namespace() + "/" + enc.Encode(rp.RootCid()) + if len(remainder) != 0 { + encodedPath += path.SegmentsToString(remainder...) + } + + // Ensure valid and sanitized. + ep, err := path.NewPath(encodedPath) + if err != nil { + return err + } + + return cmds.EmitOnce(res, &ncmd.ResolvedPath{Path: ep.String()}) }, Encoders: cmds.EncoderMap{ cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, rp *ncmd.ResolvedPath) error { - fmt.Fprintln(w, rp.Path.String()) + fmt.Fprintln(w, rp.Path) return nil }), }, diff --git a/core/commands/root.go b/core/commands/root.go index cfa2708e6..d062e75b4 100644 --- a/core/commands/root.go +++ b/core/commands/root.go @@ -8,7 +8,6 @@ import ( name "github.com/ipfs/kubo/core/commands/name" ocmd "github.com/ipfs/kubo/core/commands/object" "github.com/ipfs/kubo/core/commands/pin" - unixfs "github.com/ipfs/kubo/core/commands/unixfs" cmds "github.com/ipfs/go-ipfs-cmds" logging "github.com/ipfs/go-log" @@ -16,7 +15,10 @@ import ( var log = logging.Logger("core/commands") -var ErrNotOnline = errors.New("this command must be run in online mode. Try running 'ipfs daemon' first") +var ( + ErrNotOnline = errors.New("this command must be run in online mode. Try running 'ipfs daemon' first") + ErrSelfUnsupported = errors.New("finding your own node in the DHT is currently not supported") +) const ( RepoDirOption = "repo-dir" @@ -25,7 +27,8 @@ const ( DebugOption = "debug" LocalOption = "local" // DEPRECATED: use OfflineOption OfflineOption = "offline" - ApiOption = "api" //nolint + ApiOption = "api" //nolint + ApiAuthOption = "api-auth" //nolint ) var Root = &cmds.Command{ @@ -107,6 +110,7 @@ The CLI will exit with one of the following values: cmds.BoolOption(LocalOption, "L", "Run the command locally, instead of using the daemon. DEPRECATED: use --offline."), cmds.BoolOption(OfflineOption, "Run the command offline."), cmds.StringOption(ApiOption, "Use a specific API instance (defaults to /ip4/127.0.0.1/tcp/5001)"), + cmds.StringOption(ApiAuthOption, "Optional RPC API authorization secret (defined as AuthSecret in API.Authorizations config)"), // global options, added to every command cmdenv.OptionCidBase, @@ -138,7 +142,6 @@ var rootSubcommands = map[string]*cmds.Command{ "dht": DhtCmd, "routing": RoutingCmd, "diag": DiagCmd, - "dns": DNSCmd, "id": IDCmd, "key": KeyCmd, "log": LogCmd, @@ -152,83 +155,16 @@ var rootSubcommands = map[string]*cmds.Command{ "refs": RefsCmd, "resolve": ResolveCmd, "swarm": SwarmCmd, - "tar": TarCmd, - "file": unixfs.UnixFSCmd, "update": ExternalBinary("Please see https://github.com/ipfs/ipfs-update/blob/master/README.md#install for installation instructions."), - "urlstore": urlStoreCmd, "version": VersionCmd, "shutdown": daemonShutdownCmd, "cid": CidCmd, "multibase": MbaseCmd, } -// RootRO is the readonly version of Root -var RootRO = &cmds.Command{} - -var CommandsDaemonROCmd = CommandsCmd(RootRO) - -// RefsROCmd is `ipfs refs` command -var RefsROCmd = &cmds.Command{} - -// VersionROCmd is `ipfs version` command (without deps). -var VersionROCmd = &cmds.Command{} - -var rootROSubcommands = map[string]*cmds.Command{ - "commands": CommandsDaemonROCmd, - "cat": CatCmd, - "block": { - Subcommands: map[string]*cmds.Command{ - "stat": blockStatCmd, - "get": blockGetCmd, - }, - }, - "get": GetCmd, - "dns": DNSCmd, - "ls": LsCmd, - "name": { - Subcommands: map[string]*cmds.Command{ - "resolve": name.IpnsCmd, - }, - }, - "object": { - Subcommands: map[string]*cmds.Command{ - "data": ocmd.ObjectDataCmd, - "links": ocmd.ObjectLinksCmd, - "get": ocmd.ObjectGetCmd, - "stat": ocmd.ObjectStatCmd, - }, - }, - "dag": { - Subcommands: map[string]*cmds.Command{ - "get": dag.DagGetCmd, - "resolve": dag.DagResolveCmd, - "stat": dag.DagStatCmd, - "export": dag.DagExportCmd, - }, - }, - "resolve": ResolveCmd, -} - func init() { Root.ProcessHelp() - *RootRO = *Root - - // this was in the big map definition above before, - // but if we leave it there lgc.NewCommand will be executed - // before the value is updated (:/sanitize readonly refs command/) - - // sanitize readonly refs command - *RefsROCmd = *RefsCmd - RefsROCmd.Subcommands = map[string]*cmds.Command{} - rootROSubcommands["refs"] = RefsROCmd - - // sanitize readonly version command (no need to expose precise deps) - *VersionROCmd = *VersionCmd - VersionROCmd.Subcommands = map[string]*cmds.Command{} - rootROSubcommands["version"] = VersionROCmd - Root.Subcommands = rootSubcommands - RootRO.Subcommands = rootROSubcommands } type MessageOutput struct { diff --git a/core/commands/root_test.go b/core/commands/root_test.go index f5e5c248b..d1bf2e610 100644 --- a/core/commands/root_test.go +++ b/core/commands/root_test.go @@ -18,5 +18,4 @@ func TestCommandTree(t *testing.T) { } } printErrors(Root.DebugValidate()) - printErrors(RootRO.DebugValidate()) } diff --git a/core/commands/routing.go b/core/commands/routing.go index bf7d2a699..3e503b014 100644 --- a/core/commands/routing.go +++ b/core/commands/routing.go @@ -6,19 +6,30 @@ import ( "errors" "fmt" "io" + "strings" "time" cmdenv "github.com/ipfs/kubo/core/commands/cmdenv" + dag "github.com/ipfs/boxo/ipld/merkledag" + "github.com/ipfs/boxo/ipns" cid "github.com/ipfs/go-cid" cmds "github.com/ipfs/go-ipfs-cmds" ipld "github.com/ipfs/go-ipld-format" - dag "github.com/ipfs/go-merkledag" - path "github.com/ipfs/go-path" + iface "github.com/ipfs/kubo/core/coreiface" + "github.com/ipfs/kubo/core/coreiface/options" peer "github.com/libp2p/go-libp2p/core/peer" routing "github.com/libp2p/go-libp2p/core/routing" ) +var errAllowOffline = errors.New("can't put while offline: pass `--allow-offline` to override") + +const ( + dhtVerboseOptionName = "verbose" + numProvidersOptionName = "num-providers" + allowOfflineOptionName = "allow-offline" +) + var RoutingCmd = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "Issue routing commands.", @@ -34,14 +45,6 @@ var RoutingCmd = &cmds.Command{ }, } -const ( - dhtVerboseOptionName = "verbose" -) - -const ( - numProvidersOptionName = "num-providers" -) - var findProvidersRoutingCmd = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "Find peers that can provide a specific value, given a key.", @@ -71,7 +74,6 @@ var findProvidersRoutingCmd = &cmds.Command{ } c, err := cid.Parse(req.Arguments[0]) - if err != nil { return err } @@ -79,10 +81,9 @@ var findProvidersRoutingCmd = &cmds.Command{ ctx, cancel := context.WithCancel(req.Context) ctx, events := routing.RegisterForQueryEvents(ctx) - pchan := n.Routing.FindProvidersAsync(ctx, c, numProviders) - go func() { defer cancel() + pchan := n.Routing.FindProvidersAsync(ctx, c, numProviders) for p := range pchan { np := p routing.PublishQueryEvent(ctx, &routing.QueryEvent{ @@ -113,7 +114,7 @@ var findProvidersRoutingCmd = &cmds.Command{ if verbose { fmt.Fprintf(out, "provider: ") } - fmt.Fprintf(out, "%s\n", prov.ID.Pretty()) + fmt.Fprintf(out, "%s\n", prov.ID) if verbose { for _, a := range prov.Addrs { fmt.Fprintf(out, "\t%s\n", a) @@ -135,6 +136,7 @@ const ( ) var provideRefRoutingCmd = &cmds.Command{ + Status: cmds.Experimental, Helptext: cmds.HelpText{ Tagline: "Announce to the network that you are providing given values.", }, @@ -296,6 +298,10 @@ var findPeerRoutingCmd = &cmds.Command{ return err } + if pid == nd.Identity { + return ErrSelfUnsupported + } + ctx, cancel := context.WithCancel(req.Context) ctx, events := routing.RegisterForQueryEvents(ctx) @@ -346,6 +352,7 @@ var findPeerRoutingCmd = &cmds.Command{ } var getValueRoutingCmd = &cmds.Command{ + Status: cmds.Experimental, Helptext: cmds.HelpText{ Tagline: "Given a key, query the routing system for its best value.", ShortDescription: ` @@ -362,78 +369,37 @@ Different key types can specify other 'best' rules. Arguments: []cmds.Argument{ cmds.StringArg("key", true, true, "The key to find a value for."), }, - Options: []cmds.Option{ - cmds.BoolOption(dhtVerboseOptionName, "v", "Print extra information."), - }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { - nd, err := cmdenv.GetNode(env) + api, err := cmdenv.GetApi(env, req) if err != nil { return err } - if !nd.IsOnline { - return ErrNotOnline - } - - dhtkey, err := escapeDhtKey(req.Arguments[0]) + r, err := api.Routing().Get(req.Context, req.Arguments[0]) if err != nil { return err } - ctx, cancel := context.WithCancel(req.Context) - ctx, events := routing.RegisterForQueryEvents(ctx) - - var getErr error - go func() { - defer cancel() - var val []byte - val, getErr = nd.Routing.GetValue(ctx, dhtkey) - if getErr != nil { - routing.PublishQueryEvent(ctx, &routing.QueryEvent{ - Type: routing.QueryError, - Extra: getErr.Error(), - }) - } else { - routing.PublishQueryEvent(ctx, &routing.QueryEvent{ - Type: routing.Value, - Extra: base64.StdEncoding.EncodeToString(val), - }) - } - }() - - for e := range events { - if err := res.Emit(e); err != nil { - return err - } - } - - return getErr + return res.Emit(routing.QueryEvent{ + Extra: base64.StdEncoding.EncodeToString(r), + Type: routing.Value, + }) }, Encoders: cmds.EncoderMap{ - cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *routing.QueryEvent) error { - pfm := pfuncMap{ - routing.Value: func(obj *routing.QueryEvent, out io.Writer, verbose bool) error { - if verbose { - _, err := fmt.Fprintf(out, "got value: '%s'\n", obj.Extra) - return err - } - res, err := base64.StdEncoding.DecodeString(obj.Extra) - if err != nil { - return err - } - _, err = out.Write(res) - return err - }, + cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, obj *routing.QueryEvent) error { + res, err := base64.StdEncoding.DecodeString(obj.Extra) + if err != nil { + return err } - - verbose, _ := req.Options[dhtVerboseOptionName].(bool) - return printEvent(out, w, verbose, pfm) + _, err = w.Write(res) + return err }), }, Type: routing.QueryEvent{}, } var putValueRoutingCmd = &cmds.Command{ + Status: cmds.Experimental, Helptext: cmds.HelpText{ Tagline: "Write a key/value pair to the routing system.", ShortDescription: ` @@ -460,19 +426,10 @@ identified by QmFoo. cmds.FileArg("value-file", true, false, "A path to a file containing the value to store.").EnableStdin(), }, Options: []cmds.Option{ - cmds.BoolOption(dhtVerboseOptionName, "v", "Print extra information."), + cmds.BoolOption(allowOfflineOptionName, "When offline, save the IPNS record to the local datastore without broadcasting to the network instead of simply failing."), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { - nd, err := cmdenv.GetNode(env) - if err != nil { - return err - } - - if !nd.IsOnline { - return ErrNotOnline - } - - key, err := escapeDhtKey(req.Arguments[0]) + api, err := cmdenv.GetApi(env, req) if err != nil { return err } @@ -488,28 +445,29 @@ identified by QmFoo. return err } - ctx, cancel := context.WithCancel(req.Context) - ctx, events := routing.RegisterForQueryEvents(ctx) + allowOffline, _ := req.Options[allowOfflineOptionName].(bool) - var putErr error - go func() { - defer cancel() - putErr = nd.Routing.PutValue(ctx, key, []byte(data)) - if putErr != nil { - routing.PublishQueryEvent(ctx, &routing.QueryEvent{ - Type: routing.QueryError, - Extra: putErr.Error(), - }) - } - }() - - for e := range events { - if err := res.Emit(e); err != nil { - return err - } + opts := []options.RoutingPutOption{ + options.Put.AllowOffline(allowOffline), } - return putErr + ipnsName, err := ipns.NameFromString(req.Arguments[0]) + if err != nil { + return err + } + + err = api.Routing().Put(req.Context, req.Arguments[0], data, opts...) + if err != nil { + if err == iface.ErrOffline { + err = errAllowOffline + } + return err + } + + return res.Emit(routing.QueryEvent{ + Type: routing.Value, + ID: ipnsName.Peer(), + }) }, Encoders: cmds.EncoderMap{ cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *routing.QueryEvent) error { @@ -521,7 +479,7 @@ identified by QmFoo. return nil }, routing.Value: func(obj *routing.QueryEvent, out io.Writer, verbose bool) error { - fmt.Fprintf(out, "%s\n", obj.ID.Pretty()) + fmt.Fprintf(out, "%s\n", obj.ID) return nil }, } @@ -534,8 +492,10 @@ identified by QmFoo. Type: routing.QueryEvent{}, } -type printFunc func(obj *routing.QueryEvent, out io.Writer, verbose bool) error -type pfuncMap map[routing.QueryEventType]printFunc +type ( + printFunc func(obj *routing.QueryEvent, out io.Writer, verbose bool) error + pfuncMap map[routing.QueryEventType]printFunc +) func printEvent(obj *routing.QueryEvent, out io.Writer, verbose bool, override pfuncMap) error { if verbose { @@ -589,7 +549,7 @@ func printEvent(obj *routing.QueryEvent, out io.Writer, verbose bool, override p } func escapeDhtKey(s string) (string, error) { - parts := path.SplitList(s) + parts := strings.Split(s, "/") if len(parts) != 3 || parts[0] != "" || !(parts[1] == "ipns" || parts[1] == "pk") { @@ -600,5 +560,6 @@ func escapeDhtKey(s string) (string, error) { if err != nil { return "", err } - return path.Join(append(parts[:2], string(k))), nil + + return strings.Join(append(parts[:2], string(k)), "/"), nil } diff --git a/core/commands/stat.go b/core/commands/stat.go index a09c70ea1..4ceb95f13 100644 --- a/core/commands/stat.go +++ b/core/commands/stat.go @@ -96,7 +96,7 @@ Example: // Must be online! if !nd.IsOnline { - return cmds.Errorf(cmds.ErrClient, ErrNotOnline.Error()) + return cmds.Errorf(cmds.ErrClient, "unable to run offline: %s", ErrNotOnline) } if nd.Reporter == nil { diff --git a/core/commands/stat_provide.go b/core/commands/stat_provide.go index e192944d6..6ee51e516 100644 --- a/core/commands/stat_provide.go +++ b/core/commands/stat_provide.go @@ -7,10 +7,10 @@ import ( "time" humanize "github.com/dustin/go-humanize" + "github.com/ipfs/boxo/provider" cmds "github.com/ipfs/go-ipfs-cmds" "github.com/ipfs/kubo/core/commands/cmdenv" - - "github.com/ipfs/go-ipfs-provider/batched" + "golang.org/x/exp/constraints" ) var statProvideCmd = &cmds.Command{ @@ -34,12 +34,7 @@ This interface is not stable and may change from release to release. return ErrNotOnline } - sys, ok := nd.Provider.(*batched.BatchProvidingSystem) - if !ok { - return fmt.Errorf("can only return stats if Experimental.AcceleratedDHTClient is enabled") - } - - stats, err := sys.Stat(req.Context) + stats, err := nd.Provider.Stat() if err != nil { return err } @@ -51,7 +46,7 @@ This interface is not stable and may change from release to release. return nil }, Encoders: cmds.EncoderMap{ - cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, s *batched.BatchedProviderStats) error { + cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, s *provider.ReproviderStats) error { wtr := tabwriter.NewWriter(w, 1, 2, 1, ' ', 0) defer wtr.Flush() @@ -62,14 +57,14 @@ This interface is not stable and may change from release to release. return nil }), }, - Type: batched.BatchedProviderStats{}, + Type: provider.ReproviderStats{}, } func humanDuration(val time.Duration) string { return val.Truncate(time.Microsecond).String() } -func humanNumber(n int) string { +func humanNumber[T constraints.Float | constraints.Integer](n T) string { nf := float64(n) str := humanSI(nf, 0) fullStr := humanFull(nf, 0) diff --git a/core/commands/swarm.go b/core/commands/swarm.go index 947abaf38..c0ff131d1 100644 --- a/core/commands/swarm.go +++ b/core/commands/swarm.go @@ -1,18 +1,19 @@ package commands import ( - "bytes" "context" + "encoding/base64" "encoding/json" "errors" "fmt" "io" "path" "sort" + "strconv" "sync" + "text/tabwriter" "time" - files "github.com/ipfs/go-ipfs-files" "github.com/ipfs/kubo/commands" "github.com/ipfs/kubo/config" "github.com/ipfs/kubo/core/commands/cmdenv" @@ -21,8 +22,10 @@ import ( "github.com/ipfs/kubo/repo/fsrepo" cmds "github.com/ipfs/go-ipfs-cmds" + ic "github.com/libp2p/go-libp2p/core/crypto" inet "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/peer" + pstore "github.com/libp2p/go-libp2p/core/peerstore" rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager" ma "github.com/multiformats/go-multiaddr" madns "github.com/multiformats/go-multiaddr-dns" @@ -57,8 +60,8 @@ ipfs peers in the internet. "filters": swarmFiltersCmd, "peers": swarmPeersCmd, "peering": swarmPeeringCmd, - "stats": swarmStatsCmd, // libp2p Network Resource Manager - "limit": swarmLimitCmd, // libp2p Network Resource Manager + "resources": swarmResourcesCmd, // libp2p Network Resource Manager + }, } @@ -70,6 +73,7 @@ const ( swarmResetLimitsOptionName = "reset" swarmSaveOptionName = "save" swarmUsedResourcesPercentageName = "min-used-limit-perc" + swarmIdentifyOptionName = "identify" ) type peeringResult struct { @@ -81,8 +85,8 @@ var swarmPeeringCmd = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "Modify the peering subsystem.", ShortDescription: ` -'ipfs swarm peering' manages the peering subsystem. -Peers in the peering subsystem are maintained to be connected, reconnected +'ipfs swarm peering' manages the peering subsystem. +Peers in the peering subsystem are maintained to be connected, reconnected on disconnect with a back-off. The changes are not saved to the config. `, @@ -278,17 +282,18 @@ var swarmPeersCmd = &cmds.Command{ cmds.BoolOption(swarmStreamsOptionName, "Also list information about open streams for each peer"), cmds.BoolOption(swarmLatencyOptionName, "Also list information about latency to each peer"), cmds.BoolOption(swarmDirectionOptionName, "Also list information about the direction of connection"), + cmds.BoolOption(swarmIdentifyOptionName, "Also list information about peers identify"), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { api, err := cmdenv.GetApi(env, req) if err != nil { return err } - verbose, _ := req.Options[swarmVerboseOptionName].(bool) latency, _ := req.Options[swarmLatencyOptionName].(bool) streams, _ := req.Options[swarmStreamsOptionName].(bool) direction, _ := req.Options[swarmDirectionOptionName].(bool) + identify, _ := req.Options[swarmIdentifyOptionName].(bool) conns, err := api.Swarm().Peers(req.Context) if err != nil { @@ -299,7 +304,7 @@ var swarmPeersCmd = &cmds.Command{ for _, c := range conns { ci := connInfo{ Addr: c.Address().String(), - Peer: c.ID().Pretty(), + Peer: c.ID().String(), } if verbose || direction { @@ -329,6 +334,15 @@ var swarmPeersCmd = &cmds.Command{ ci.Streams = append(ci.Streams, streamInfo{Protocol: string(s)}) } } + + if verbose || identify { + n, err := cmdenv.GetNode(env) + if err != nil { + return err + } + identifyResult, _ := ci.identifyPeer(n.Peerstore, c.ID()) + ci.Identify = identifyResult + } sort.Sort(&ci) out.Peers = append(out.Peers, ci) } @@ -365,29 +379,15 @@ var swarmPeersCmd = &cmds.Command{ Type: connInfos{}, } -var swarmStatsCmd = &cmds.Command{ +var swarmResourcesCmd = &cmds.Command{ Status: cmds.Experimental, Helptext: cmds.HelpText{ - Tagline: "Report resource usage for a scope.", - LongDescription: `Report resource usage for a scope. -The scope can be one of the following: -- system -- reports the system aggregate resource usage. -- transient -- reports the transient resource usage. -- svc: -- reports the resource usage of a specific service. -- proto: -- reports the resource usage of a specific protocol. -- peer: -- reports the resource usage of a specific peer. -- all -- reports the resource usage for all currently active scopes. - -The output of this command is JSON. - -To see all resources that are close to hitting their respective limit, one can do something like: - ipfs swarm stats --min-used-limit-perc=90 all -`}, - Arguments: []cmds.Argument{ - cmds.StringArg("scope", true, false, "scope of the stat report"), - }, - Options: []cmds.Option{ - cmds.IntOption(swarmUsedResourcesPercentageName, "Only display resources that are using above the specified percentage of their respective limit"), + Tagline: "Get a summary of all resources accounted for by the libp2p Resource Manager.", + LongDescription: ` +Get a summary of all resources accounted for by the libp2p Resource Manager. +This includes the limits and the usage against those limits. +This can output a human readable table and JSON encoding. +`, }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { node, err := cmdenv.GetNode(env) @@ -399,125 +399,68 @@ To see all resources that are close to hitting their respective limit, one can d return libp2p.ErrNoResourceMgr } - if len(req.Arguments) != 1 { - return fmt.Errorf("must specify exactly one scope") - } - - percentage, _ := req.Options[swarmUsedResourcesPercentageName].(int) - scope := req.Arguments[0] - - if percentage != 0 && scope != "all" { - return fmt.Errorf("%q can only be used when scope is %q", swarmUsedResourcesPercentageName, "all") - } - - result, err := libp2p.NetStat(node.ResourceManager, scope, percentage) + cfg, err := node.Repo.Config() if err != nil { return err } - b := new(bytes.Buffer) - enc := json.NewEncoder(b) - err = enc.Encode(result) - if err != nil { - return err - } - return cmds.EmitOnce(res, b) - }, - Encoders: cmds.EncoderMap{ - cmds.Text: HumanJSONEncoder, - }, -} - -var swarmLimitCmd = &cmds.Command{ - Status: cmds.Experimental, - Helptext: cmds.HelpText{ - Tagline: "Get or set resource limits for a scope.", - LongDescription: `Get or set resource limits for a scope. -The scope can be one of the following: -- all -- all limits actually being applied. -- system -- limits for the system aggregate resource usage. -- transient -- limits for the transient resource usage. -- svc: -- limits for the resource usage of a specific service. -- proto: -- limits for the resource usage of a specific protocol. -- peer: -- limits for the resource usage of a specific peer. - -The output of this command is JSON. - -It is possible to use this command to inspect and tweak limits at runtime: - - $ ipfs swarm limit system > limit.json - $ vi limit.json - $ ipfs swarm limit system limit.json - -Changes made via command line are persisted in the Swarm.ResourceMgr.Limits field of the $IPFS_PATH/config file. -`}, - Arguments: []cmds.Argument{ - cmds.StringArg("scope", true, false, "scope of the limit"), - cmds.FileArg("limit.json", false, false, "limits to be set").EnableStdin(), - }, - Options: []cmds.Option{ - cmds.BoolOption(swarmResetLimitsOptionName, "reset limit to default"), - }, - Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { - node, err := cmdenv.GetNode(env) + userResourceOverrides, err := node.Repo.UserResourceOverrides() if err != nil { return err } - if node.ResourceManager == nil { + // FIXME: we shouldn't recompute limits, either save them or load them from libp2p (https://github.com/libp2p/go-libp2p/issues/2166) + limitConfig, _, err := libp2p.LimitConfig(cfg.Swarm, userResourceOverrides) + if err != nil { + return err + } + + rapi, ok := node.ResourceManager.(rcmgr.ResourceManagerState) + if !ok { // NullResourceManager return libp2p.ErrNoResourceMgr } - scope := req.Arguments[0] - - // set scope limit to new values (when limit.json is passed as a second arg) - if req.Files != nil { - var newLimit rcmgr.BaseLimit - it := req.Files.Entries() - if it.Next() { - file := files.FileFromEntry(it) - if file == nil { - return errors.New("expected a JSON file") - } - - r := io.LimitReader(file, 32*1024*1024) // 32MiB - - if err := json.NewDecoder(r).Decode(&newLimit); err != nil { - return fmt.Errorf("decoding JSON as ResourceMgrScopeConfig: %w", err) - } - return libp2p.NetSetLimit(node.ResourceManager, node.Repo, scope, newLimit) - } - if err := it.Err(); err != nil { - return fmt.Errorf("error opening limit JSON file: %w", err) - } - } - - var result interface{} - _, reset := req.Options[swarmResetLimitsOptionName] - if reset { - result, err = libp2p.NetResetLimit(node.ResourceManager, node.Repo, scope) - } else if scope == "all" { - result, err = libp2p.NetLimitAll(node.ResourceManager) - } else { - // get scope limit - result, err = libp2p.NetLimit(node.ResourceManager, scope) - } - - if err != nil { - return err - } - - b := new(bytes.Buffer) - enc := json.NewEncoder(b) - err = enc.Encode(result) - if err != nil { - return err - } - return cmds.EmitOnce(res, b) + return cmds.EmitOnce(res, libp2p.MergeLimitsAndStatsIntoLimitsConfigAndUsage(limitConfig, rapi.Stat())) }, Encoders: cmds.EncoderMap{ - cmds.Text: HumanJSONEncoder, + cmds.JSON: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, limitsAndUsage libp2p.LimitsConfigAndUsage) error { + return json.NewEncoder(w).Encode(limitsAndUsage) + }), + cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, limitsAndUsage libp2p.LimitsConfigAndUsage) error { + tw := tabwriter.NewWriter(w, 20, 8, 0, '\t', 0) + defer tw.Flush() + + fmt.Fprintf(tw, "%s\t%s\t%s\t%s\t%s\t\n", "Scope", "Limit Name", "Limit Value", "Limit Usage Amount", "Limit Usage Percent") + for _, ri := range libp2p.LimitConfigsToInfo(limitsAndUsage) { + var limit, percentage string + switch ri.LimitValue { + case rcmgr.Unlimited64: + limit = "unlimited" + percentage = "n/a" + case rcmgr.BlockAllLimit64: + limit = "blockAll" + percentage = "n/a" + default: + limit = strconv.FormatInt(int64(ri.LimitValue), 10) + if ri.CurrentUsage == 0 { + percentage = "0%" + } else { + percentage = strconv.FormatFloat(float64(ri.CurrentUsage)/float64(ri.LimitValue)*100, 'f', 1, 64) + "%" + } + } + fmt.Fprintf(tw, "%s\t%s\t%s\t%d\t%s\t\n", + ri.ScopeName, + ri.LimitName, + limit, + ri.CurrentUsage, + percentage, + ) + } + + return nil + }), }, + Type: libp2p.LimitsConfigAndUsage{}, } type streamInfo struct { @@ -525,12 +468,13 @@ type streamInfo struct { } type connInfo struct { - Addr string - Peer string - Latency string - Muxer string - Direction inet.Direction - Streams []streamInfo + Addr string `json:",omitempty"` + Peer string `json:",omitempty"` + Latency string `json:",omitempty"` + Muxer string `json:",omitempty"` + Direction inet.Direction `json:",omitempty"` + Streams []streamInfo `json:",omitempty"` + Identify IdOutput `json:",omitempty"` } func (ci *connInfo) Less(i, j int) bool { @@ -561,6 +505,43 @@ func (ci connInfos) Swap(i, j int) { ci.Peers[i], ci.Peers[j] = ci.Peers[j], ci.Peers[i] } +func (ci *connInfo) identifyPeer(ps pstore.Peerstore, p peer.ID) (IdOutput, error) { + var info IdOutput + info.ID = p.String() + + if pk := ps.PubKey(p); pk != nil { + pkb, err := ic.MarshalPublicKey(pk) + if err != nil { + return IdOutput{}, err + } + info.PublicKey = base64.StdEncoding.EncodeToString(pkb) + } + + addrInfo := ps.PeerInfo(p) + addrs, err := peer.AddrInfoToP2pAddrs(&addrInfo) + if err != nil { + return IdOutput{}, err + } + + for _, a := range addrs { + info.Addresses = append(info.Addresses, a.String()) + } + sort.Strings(info.Addresses) + + if protocols, err := ps.GetProtocols(p); err == nil { + info.Protocols = append(info.Protocols, protocols...) + sort.Slice(info.Protocols, func(i, j int) bool { return info.Protocols[i] < info.Protocols[j] }) + } + + if v, err := ps.Get(p, "AgentVersion"); err == nil { + if vs, ok := v.(string); ok { + info.AgentVersion = vs + } + } + + return info, nil +} + // directionString transfers to string func directionString(d inet.Direction) string { switch d { @@ -597,7 +578,7 @@ var swarmAddrsCmd = &cmds.Command{ out := make(map[string][]string) for p, paddrs := range addrs { - s := p.Pretty() + s := p.String() for _, a := range paddrs { out[s] = append(out[s], a.String()) } @@ -618,7 +599,7 @@ var swarmAddrsCmd = &cmds.Command{ paddrs := am.Addrs[p] fmt.Fprintf(w, "%s (%d)\n", p, len(paddrs)) for _, addr := range paddrs { - fmt.Fprintf(w, "\t"+addr+"\n") + fmt.Fprintf(w, "\t%s\n", addr) } } @@ -660,7 +641,7 @@ var swarmAddrsLocalCmd = &cmds.Command{ for _, addr := range maddrs { saddr := addr.String() if showid { - saddr = path.Join(saddr, p2pProtocolName, self.ID().Pretty()) + saddr = path.Join(saddr, p2pProtocolName, self.ID().String()) } addrs = append(addrs, saddr) } @@ -707,11 +688,13 @@ var swarmAddrsListenCmd = &cmds.Command{ var swarmConnectCmd = &cmds.Command{ Helptext: cmds.HelpText{ - Tagline: "Open connection to a given address.", + Tagline: "Open connection to a given peer.", ShortDescription: ` -'ipfs swarm connect' opens a new direct connection to a peer address. +'ipfs swarm connect' attempts to ensure a connection to a given peer. -The address format is an IPFS multiaddr: +Multiaddresses given are advisory, for example the node may already be aware of other addresses for a given peer or may already have an established connection to the peer. + +The address format is a libp2p multiaddr: ipfs swarm connect /ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ `, @@ -739,7 +722,7 @@ ipfs swarm connect /ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N output := make([]string, len(pis)) for i, pi := range pis { - output[i] = "connect " + pi.ID.Pretty() + output[i] = "connect " + pi.ID.String() err := api.Swarm().Connect(req.Context, pi) if err != nil { @@ -804,7 +787,7 @@ it will reconnect. // a good backwards compat solution. Right now, I'm just // preserving the current behavior. for _, addr := range maddrs { - msg := "disconnect " + ainfo.ID.Pretty() + msg := "disconnect " + ainfo.ID.String() if err := api.Swarm().Disconnect(req.Context, addr); err != nil { msg += " failure: " + err.Error() } else { diff --git a/core/commands/tar.go b/core/commands/tar.go deleted file mode 100644 index eaec9ab68..000000000 --- a/core/commands/tar.go +++ /dev/null @@ -1,113 +0,0 @@ -package commands - -import ( - "fmt" - "io" - - cmds "github.com/ipfs/go-ipfs-cmds" - "github.com/ipfs/kubo/core/commands/cmdenv" - tar "github.com/ipfs/kubo/tar" - - dag "github.com/ipfs/go-merkledag" - path "github.com/ipfs/interface-go-ipfs-core/path" -) - -var TarCmd = &cmds.Command{ - Status: cmds.Deprecated, // https://github.com/ipfs/kubo/issues/7951 - Helptext: cmds.HelpText{ - Tagline: "Utility functions for tar files in ipfs.", - }, - - Subcommands: map[string]*cmds.Command{ - "add": tarAddCmd, - "cat": tarCatCmd, - }, -} - -var tarAddCmd = &cmds.Command{ - Status: cmds.Deprecated, // https://github.com/ipfs/kubo/issues/7951 - Helptext: cmds.HelpText{ - Tagline: "Import a tar file into IPFS.", - ShortDescription: ` -'ipfs tar add' will parse a tar file and create a merkledag structure to -represent it. -`, - }, - - Arguments: []cmds.Argument{ - cmds.FileArg("file", true, false, "Tar file to add.").EnableStdin(), - }, - Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { - api, err := cmdenv.GetApi(env, req) - if err != nil { - return err - } - - enc, err := cmdenv.GetCidEncoder(req) - if err != nil { - return err - } - - it := req.Files.Entries() - file, err := cmdenv.GetFileArg(it) - if err != nil { - return err - } - - node, err := tar.ImportTar(req.Context, file, api.Dag()) - if err != nil { - return err - } - - c := node.Cid() - - return cmds.EmitOnce(res, &AddEvent{ - Name: it.Name(), - Hash: enc.Encode(c), - }) - }, - Type: AddEvent{}, - Encoders: cmds.EncoderMap{ - cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *AddEvent) error { - fmt.Fprintln(w, out.Hash) - return nil - }), - }, -} - -var tarCatCmd = &cmds.Command{ - Status: cmds.Deprecated, // https://github.com/ipfs/kubo/issues/7951 - Helptext: cmds.HelpText{ - Tagline: "Export a tar file from IPFS.", - ShortDescription: ` -'ipfs tar cat' will export a tar file from a previously imported one in IPFS. -`, - }, - - Arguments: []cmds.Argument{ - cmds.StringArg("path", true, false, "ipfs path of archive to export.").EnableStdin(), - }, - Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { - api, err := cmdenv.GetApi(env, req) - if err != nil { - return err - } - - root, err := api.ResolveNode(req.Context, path.New(req.Arguments[0])) - if err != nil { - return err - } - - rootpb, ok := root.(*dag.ProtoNode) - if !ok { - return dag.ErrNotProtobuf - } - - r, err := tar.ExportTar(req.Context, rootpb, api.Dag()) - if err != nil { - return err - } - - return res.Emit(r) - }, -} diff --git a/core/commands/unixfs/ls.go b/core/commands/unixfs/ls.go deleted file mode 100644 index dc4221eb6..000000000 --- a/core/commands/unixfs/ls.go +++ /dev/null @@ -1,231 +0,0 @@ -package unixfs - -import ( - "fmt" - "io" - "sort" - "text/tabwriter" - - cmdenv "github.com/ipfs/kubo/core/commands/cmdenv" - - cmds "github.com/ipfs/go-ipfs-cmds" - merkledag "github.com/ipfs/go-merkledag" - unixfs "github.com/ipfs/go-unixfs" - path "github.com/ipfs/interface-go-ipfs-core/path" -) - -type LsLink struct { - Name, Hash string - Size uint64 - Type string -} - -type LsObject struct { - Hash string - Size uint64 - Type string - Links []LsLink -} - -type LsOutput struct { - Arguments map[string]string - Objects map[string]*LsObject -} - -var LsCmd = &cmds.Command{ - Status: cmds.Deprecated, // https://github.com/ipfs/kubo/pull/7755 - Helptext: cmds.HelpText{ - Tagline: "List directory contents for Unix filesystem objects. Deprecated: Use 'ipfs ls' and 'ipfs files ls' instead.", - ShortDescription: ` -Displays the contents of an IPFS or IPNS object(s) at the given path. - -The JSON output contains size information. For files, the child size -is the total size of the file contents. For directories, the child -size is the IPFS link size. - -This functionality is deprecated, and will be removed in future versions as it duplicates the functionality of 'ipfs ls'. -If possible, please use 'ipfs ls' instead. -`, - LongDescription: ` -Displays the contents of an IPFS or IPNS object(s) at the given path. - -The JSON output contains size information. For files, the child size -is the total size of the file contents. For directories, the child -size is the IPFS link size. - -The path can be a prefixless ref; in this case, we assume it to be an -/ipfs ref and not /ipns. - -Example: - - > ipfs file ls QmW2WQi7j6c7UgJTarActp7tDNikE4B2qXtFCfLPdsgaTQ - cat.jpg - > ipfs file ls /ipfs/QmW2WQi7j6c7UgJTarActp7tDNikE4B2qXtFCfLPdsgaTQ - cat.jpg - -This functionality is deprecated, and will be removed in future versions as it duplicates the functionality of 'ipfs ls'. -If possible, please use 'ipfs ls' instead. -`, - }, - - Arguments: []cmds.Argument{ - cmds.StringArg("ipfs-path", true, true, "The path to the IPFS object(s) to list links from.").EnableStdin(), - }, - Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { - nd, err := cmdenv.GetNode(env) - if err != nil { - return err - } - - api, err := cmdenv.GetApi(env, req) - if err != nil { - return err - } - - if err := req.ParseBodyArgs(); err != nil { - return err - } - - paths := req.Arguments - - output := LsOutput{ - Arguments: map[string]string{}, - Objects: map[string]*LsObject{}, - } - - for _, p := range paths { - ctx := req.Context - - merkleNode, err := api.ResolveNode(ctx, path.New(p)) - if err != nil { - return err - } - - c := merkleNode.Cid() - - hash := c.String() - output.Arguments[p] = hash - - if _, ok := output.Objects[hash]; ok { - // duplicate argument for an already-listed node - continue - } - - ndpb, ok := merkleNode.(*merkledag.ProtoNode) - if !ok { - return merkledag.ErrNotProtobuf - } - - unixFSNode, err := unixfs.FSNodeFromBytes(ndpb.Data()) - if err != nil { - return err - } - - t := unixFSNode.Type() - - output.Objects[hash] = &LsObject{ - Hash: c.String(), - Type: t.String(), - Size: unixFSNode.FileSize(), - } - - switch t { - case unixfs.TFile: - break - case unixfs.THAMTShard: - // We need a streaming ls API for this. - return fmt.Errorf("cannot list large directories yet") - case unixfs.TDirectory: - links := make([]LsLink, len(merkleNode.Links())) - output.Objects[hash].Links = links - for i, link := range merkleNode.Links() { - linkNode, err := link.GetNode(ctx, nd.DAG) - if err != nil { - return err - } - lnpb, ok := linkNode.(*merkledag.ProtoNode) - if !ok { - return merkledag.ErrNotProtobuf - } - - d, err := unixfs.FSNodeFromBytes(lnpb.Data()) - if err != nil { - return err - } - t := d.Type() - lsLink := LsLink{ - Name: link.Name, - Hash: link.Cid.String(), - Type: t.String(), - } - if t == unixfs.TFile { - lsLink.Size = d.FileSize() - } else { - lsLink.Size = link.Size - } - links[i] = lsLink - } - case unixfs.TSymlink: - return fmt.Errorf("cannot list symlinks yet") - default: - return fmt.Errorf("unrecognized type: %s", t) - } - } - - return cmds.EmitOnce(res, &output) - }, - Encoders: cmds.EncoderMap{ - cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *LsOutput) error { - tw := tabwriter.NewWriter(w, 1, 2, 1, ' ', 0) - - nonDirectories := []string{} - directories := []string{} - for argument, hash := range out.Arguments { - object, ok := out.Objects[hash] - if !ok { - return fmt.Errorf("unresolved hash: %s", hash) - } - - if object.Type == "Directory" { - directories = append(directories, argument) - } else { - nonDirectories = append(nonDirectories, argument) - } - } - sort.Strings(nonDirectories) - sort.Strings(directories) - - for _, argument := range nonDirectories { - fmt.Fprintf(tw, "%s\n", argument) - } - - seen := map[string]bool{} - for i, argument := range directories { - hash := out.Arguments[argument] - if _, ok := seen[hash]; ok { - continue - } - seen[hash] = true - - object := out.Objects[hash] - if i > 0 || len(nonDirectories) > 0 { - fmt.Fprintln(tw) - } - if len(out.Arguments) > 1 { - for _, arg := range directories[i:] { - if out.Arguments[arg] == hash { - fmt.Fprintf(tw, "%s:\n", cmdenv.EscNonPrint(arg)) - } - } - } - for _, link := range object.Links { - fmt.Fprintf(tw, "%s\n", cmdenv.EscNonPrint(link.Name)) - } - } - tw.Flush() - - return nil - }), - }, - Type: LsOutput{}, -} diff --git a/core/commands/unixfs/unixfs.go b/core/commands/unixfs/unixfs.go deleted file mode 100644 index b50d08d92..000000000 --- a/core/commands/unixfs/unixfs.go +++ /dev/null @@ -1,20 +0,0 @@ -package unixfs - -import ( - cmds "github.com/ipfs/go-ipfs-cmds" -) - -var UnixFSCmd = &cmds.Command{ - Status: cmds.Deprecated, // https://github.com/ipfs/kubo/pull/7755 - Helptext: cmds.HelpText{ - Tagline: "Interact with IPFS objects representing Unix filesystems.", - ShortDescription: ` -Old interface to file systems represented by UnixFS. -Superseded by modern alternatives: 'ipfs ls' and 'ipfs files' -`, - }, - - Subcommands: map[string]*cmds.Command{ - "ls": LsCmd, - }, -} diff --git a/core/commands/urlstore.go b/core/commands/urlstore.go deleted file mode 100644 index bd67c56ff..000000000 --- a/core/commands/urlstore.go +++ /dev/null @@ -1,105 +0,0 @@ -package commands - -import ( - "fmt" - "io" - "net/url" - - filestore "github.com/ipfs/go-filestore" - cmdenv "github.com/ipfs/kubo/core/commands/cmdenv" - - cmds "github.com/ipfs/go-ipfs-cmds" - files "github.com/ipfs/go-ipfs-files" - "github.com/ipfs/interface-go-ipfs-core/options" -) - -var urlStoreCmd = &cmds.Command{ - Helptext: cmds.HelpText{ - Tagline: "Interact with urlstore.", - }, - Subcommands: map[string]*cmds.Command{ - "add": urlAdd, - }, -} - -var urlAdd = &cmds.Command{ - Status: cmds.Deprecated, - Helptext: cmds.HelpText{ - Tagline: "Add URL via urlstore.", - LongDescription: ` -DEPRECATED: Use 'ipfs add --nocopy --cid-version=1 URL'. - -Add URLs to ipfs without storing the data locally. - -The URL provided must be stable and ideally on a web server under your -control. - -The file is added using raw-leaves but otherwise using the default -settings for 'ipfs add'. -`, - }, - Options: []cmds.Option{ - cmds.BoolOption(trickleOptionName, "t", "Use trickle-dag format for dag generation."), - cmds.BoolOption(pinOptionName, "Pin this object when adding.").WithDefault(true), - }, - Arguments: []cmds.Argument{ - cmds.StringArg("url", true, false, "URL to add to IPFS"), - }, - Type: &BlockStat{}, - - Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { - log.Error("The 'ipfs urlstore' command is deprecated, please use 'ipfs add --nocopy --cid-version=1") - - urlString := req.Arguments[0] - if !filestore.IsURL(req.Arguments[0]) { - return fmt.Errorf("unsupported url syntax: %s", urlString) - } - - url, err := url.Parse(urlString) - if err != nil { - return err - } - - enc, err := cmdenv.GetCidEncoder(req) - if err != nil { - return err - } - - api, err := cmdenv.GetApi(env, req) - if err != nil { - return err - } - - useTrickledag, _ := req.Options[trickleOptionName].(bool) - dopin, _ := req.Options[pinOptionName].(bool) - - opts := []options.UnixfsAddOption{ - options.Unixfs.Pin(dopin), - options.Unixfs.CidVersion(1), - options.Unixfs.RawLeaves(true), - options.Unixfs.Nocopy(true), - } - - if useTrickledag { - opts = append(opts, options.Unixfs.Layout(options.TrickleLayout)) - } - - file := files.NewWebFile(url) - - path, err := api.Unixfs().Add(req.Context, file, opts...) - if err != nil { - return err - } - size, _ := file.Size() - return cmds.EmitOnce(res, &BlockStat{ - Key: enc.Encode(path.Cid()), - Size: int(size), - }) - }, - Encoders: cmds.EncoderMap{ - cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, bs *BlockStat) error { - _, err := fmt.Fprintln(w, bs.Key) - return err - }), - }, -} diff --git a/core/commands/version.go b/core/commands/version.go index e404074fe..221726881 100644 --- a/core/commands/version.go +++ b/core/commands/version.go @@ -5,17 +5,25 @@ import ( "fmt" "io" "runtime/debug" + "strings" - version "github.com/ipfs/kubo" - + versioncmp "github.com/hashicorp/go-version" cmds "github.com/ipfs/go-ipfs-cmds" + version "github.com/ipfs/kubo" + "github.com/ipfs/kubo/config" + "github.com/ipfs/kubo/core" + "github.com/ipfs/kubo/core/commands/cmdenv" + "github.com/libp2p/go-libp2p-kad-dht/fullrt" + peer "github.com/libp2p/go-libp2p/core/peer" + pstore "github.com/libp2p/go-libp2p/core/peerstore" ) const ( - versionNumberOptionName = "number" - versionCommitOptionName = "commit" - versionRepoOptionName = "repo" - versionAllOptionName = "all" + versionNumberOptionName = "number" + versionCommitOptionName = "commit" + versionRepoOptionName = "repo" + versionAllOptionName = "all" + versionCheckThresholdOptionName = "min-percent" ) var VersionCmd = &cmds.Command{ @@ -24,7 +32,8 @@ var VersionCmd = &cmds.Command{ ShortDescription: "Returns the current version of IPFS and exits.", }, Subcommands: map[string]*cmds.Command{ - "deps": depsVersionCommand, + "deps": depsVersionCommand, + "check": checkVersionCommand, }, Options: []cmds.Option{ @@ -130,3 +139,161 @@ Print out all dependencies and their versions.`, }), }, } + +const DefaultMinimalVersionFraction = 0.05 // 5% + +type VersionCheckOutput struct { + UpdateAvailable bool + RunningVersion string + GreatestVersion string + PeersSampled int + WithGreaterVersion int +} + +var checkVersionCommand = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "Checks Kubo version against connected peers.", + ShortDescription: ` +This command uses the libp2p identify protocol to check the 'AgentVersion' +of connected peers and see if the Kubo version we're running is outdated. + +Peers with an AgentVersion that doesn't start with 'kubo/' are ignored. +'UpdateAvailable' is set to true only if the 'min-fraction' criteria are met. + +The 'ipfs daemon' does the same check regularly and logs when a new version +is available. You can stop these regular checks by setting +Version.SwarmCheckEnabled:false in the config. +`, + }, + Options: []cmds.Option{ + cmds.IntOption(versionCheckThresholdOptionName, "t", "Percentage (1-100) of sampled peers with the new Kubo version needed to trigger an update warning.").WithDefault(config.DefaultSwarmCheckPercentThreshold), + }, + Type: VersionCheckOutput{}, + + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + nd, err := cmdenv.GetNode(env) + if err != nil { + return err + } + + if !nd.IsOnline { + return ErrNotOnline + } + + minPercent, _ := req.Options[versionCheckThresholdOptionName].(int64) + output, err := DetectNewKuboVersion(nd, minPercent) + if err != nil { + return err + } + + if err := cmds.EmitOnce(res, output); err != nil { + return err + } + return nil + }, +} + +// DetectNewKuboVersion observers kubo version reported by other peers via +// libp2p identify protocol and notifies when threshold fraction of seen swarm +// is running updated Kubo. It is used by RPC and CLI at 'ipfs version check' +// and also periodically when 'ipfs daemon' is running. +func DetectNewKuboVersion(nd *core.IpfsNode, minPercent int64) (VersionCheckOutput, error) { + ourVersion, err := versioncmp.NewVersion(version.CurrentVersionNumber) + if err != nil { + return VersionCheckOutput{}, fmt.Errorf("could not parse our own version %q: %w", + version.CurrentVersionNumber, err) + } + // MAJOR.MINOR.PATCH without any suffix + ourVersion = ourVersion.Core() + + greatestVersionSeen := ourVersion + totalPeersSampled := 1 // Us (and to avoid division-by-zero edge case) + withGreaterVersion := 0 + + recordPeerVersion := func(agentVersion string) { + // We process the version as is it assembled in GetUserAgentVersion + segments := strings.Split(agentVersion, "/") + if len(segments) < 2 { + return + } + if segments[0] != "kubo" { + return + } + versionNumber := segments[1] // As in our CurrentVersionNumber + + peerVersion, err := versioncmp.NewVersion(versionNumber) + if err != nil { + // Do not error on invalid remote versions, just ignore + return + } + + // Ignore prerelases and development releases (-dev, -rcX) + if peerVersion.Metadata() != "" || peerVersion.Prerelease() != "" { + return + } + + // MAJOR.MINOR.PATCH without any suffix + peerVersion = peerVersion.Core() + + // Valid peer version number + totalPeersSampled += 1 + if ourVersion.LessThan(peerVersion) { + withGreaterVersion += 1 + } + if peerVersion.GreaterThan(greatestVersionSeen) { + greatestVersionSeen = peerVersion + } + } + + processPeerstoreEntry := func(id peer.ID) { + if v, err := nd.Peerstore.Get(id, "AgentVersion"); err == nil { + recordPeerVersion(v.(string)) + } else if errors.Is(err, pstore.ErrNotFound) { // ignore noop + } else { // a bug, usually. + log.Errorw("failed to get agent version from peerstore", "error", err) + } + } + + // Amino DHT client keeps information about previously seen peers + if nd.DHTClient != nd.DHT && nd.DHTClient != nil { + client, ok := nd.DHTClient.(*fullrt.FullRT) + if !ok { + return VersionCheckOutput{}, errors.New("could not perform version check due to missing or incompatible DHT configuration") + } + for _, p := range client.Stat() { + processPeerstoreEntry(p) + } + } else if nd.DHT != nil && nd.DHT.WAN != nil { + for _, pi := range nd.DHT.WAN.RoutingTable().GetPeerInfos() { + processPeerstoreEntry(pi.Id) + } + } else if nd.DHT != nil && nd.DHT.LAN != nil { + for _, pi := range nd.DHT.LAN.RoutingTable().GetPeerInfos() { + processPeerstoreEntry(pi.Id) + } + } else { + return VersionCheckOutput{}, errors.New("could not perform version check due to missing or incompatible DHT configuration") + } + + if minPercent < 1 || minPercent > 100 { + if minPercent == 0 { + minPercent = config.DefaultSwarmCheckPercentThreshold + } else { + return VersionCheckOutput{}, errors.New("Version.SwarmCheckPercentThreshold must be between 1 and 100") + } + } + + minFraction := float64(minPercent) / 100.0 + + // UpdateAvailable flag is set only if minFraction was reached + greaterFraction := float64(withGreaterVersion) / float64(totalPeersSampled) + + // Gathered metric are returned every time + return VersionCheckOutput{ + UpdateAvailable: (greaterFraction >= minFraction), + RunningVersion: ourVersion.String(), + GreatestVersion: greatestVersionSeen.String(), + PeersSampled: totalPeersSampled, + WithGreaterVersion: withGreaterVersion, + }, nil +} diff --git a/core/core.go b/core/core.go index 9e515b816..0c9333e06 100644 --- a/core/core.go +++ b/core/core.go @@ -11,20 +11,23 @@ package core import ( "context" + "encoding/json" "io" + "time" - "github.com/ipfs/go-filestore" - pin "github.com/ipfs/go-ipfs-pinner" + "github.com/ipfs/boxo/filestore" + pin "github.com/ipfs/boxo/pinning/pinner" + "github.com/ipfs/go-datastore" - bserv "github.com/ipfs/go-blockservice" - "github.com/ipfs/go-fetcher" - "github.com/ipfs/go-graphsync" - bstore "github.com/ipfs/go-ipfs-blockstore" - exchange "github.com/ipfs/go-ipfs-exchange-interface" - provider "github.com/ipfs/go-ipfs-provider" + bserv "github.com/ipfs/boxo/blockservice" + bstore "github.com/ipfs/boxo/blockstore" + exchange "github.com/ipfs/boxo/exchange" + "github.com/ipfs/boxo/fetcher" + mfs "github.com/ipfs/boxo/mfs" + pathresolver "github.com/ipfs/boxo/path/resolver" + provider "github.com/ipfs/boxo/provider" ipld "github.com/ipfs/go-ipld-format" logging "github.com/ipfs/go-log" - mfs "github.com/ipfs/go-mfs" goprocess "github.com/jbenet/goprocess" ddht "github.com/libp2p/go-libp2p-kad-dht/dual" pubsub "github.com/libp2p/go-libp2p-pubsub" @@ -43,14 +46,15 @@ import ( ma "github.com/multiformats/go-multiaddr" madns "github.com/multiformats/go-multiaddr-dns" - "github.com/ipfs/go-namesys" - ipnsrp "github.com/ipfs/go-namesys/republisher" - "github.com/ipfs/kubo/core/bootstrap" + "github.com/ipfs/boxo/bootstrap" + "github.com/ipfs/boxo/namesys" + ipnsrp "github.com/ipfs/boxo/namesys/republisher" + "github.com/ipfs/boxo/peering" + "github.com/ipfs/kubo/config" "github.com/ipfs/kubo/core/node" "github.com/ipfs/kubo/core/node/libp2p" "github.com/ipfs/kubo/fuse/mount" "github.com/ipfs/kubo/p2p" - "github.com/ipfs/kubo/peering" "github.com/ipfs/kubo/repo" irouting "github.com/ipfs/kubo/routing" ) @@ -59,7 +63,6 @@ var log = logging.Logger("core") // IpfsNode is IPFS Core module. It represents an IPFS instance. type IpfsNode struct { - // Self Identity peer.ID // the local node's identity @@ -72,33 +75,38 @@ type IpfsNode struct { PNetFingerprint libp2p.PNetFingerprint `optional:"true"` // fingerprint of private network // Services - Peerstore pstore.Peerstore `optional:"true"` // storage for other Peer instances - Blockstore bstore.GCBlockstore // the block store (lower level) - Filestore *filestore.Filestore `optional:"true"` // the filestore blockstore - BaseBlocks node.BaseBlocks // the raw blockstore, no filestore wrapping - GCLocker bstore.GCLocker // the locker used to protect the blockstore during gc - Blocks bserv.BlockService // the block service, get/add blocks. - DAG ipld.DAGService // the merkle dag service, get/add objects. - IPLDFetcherFactory fetcher.Factory `name:"ipldFetcher"` // fetcher that paths over the IPLD data model - UnixFSFetcherFactory fetcher.Factory `name:"unixfsFetcher"` // fetcher that interprets UnixFS data - Reporter *metrics.BandwidthCounter `optional:"true"` - Discovery mdns.Service `optional:"true"` - FilesRoot *mfs.Root - RecordValidator record.Validator + Peerstore pstore.Peerstore `optional:"true"` // storage for other Peer instances + Blockstore bstore.GCBlockstore // the block store (lower level) + Filestore *filestore.Filestore `optional:"true"` // the filestore blockstore + BaseBlocks node.BaseBlocks // the raw blockstore, no filestore wrapping + GCLocker bstore.GCLocker // the locker used to protect the blockstore during gc + Blocks bserv.BlockService // the block service, get/add blocks. + DAG ipld.DAGService // the merkle dag service, get/add objects. + IPLDFetcherFactory fetcher.Factory `name:"ipldFetcher"` // fetcher that paths over the IPLD data model + UnixFSFetcherFactory fetcher.Factory `name:"unixfsFetcher"` // fetcher that interprets UnixFS data + OfflineIPLDFetcherFactory fetcher.Factory `name:"offlineIpldFetcher"` // fetcher that paths over the IPLD data model without fetching new blocks + OfflineUnixFSFetcherFactory fetcher.Factory `name:"offlineUnixfsFetcher"` // fetcher that interprets UnixFS data without fetching new blocks + Reporter *metrics.BandwidthCounter `optional:"true"` + Discovery mdns.Service `optional:"true"` + FilesRoot *mfs.Root + RecordValidator record.Validator // Online - PeerHost p2phost.Host `optional:"true"` // the network host (server+client) - Peering *peering.PeeringService `optional:"true"` - Filters *ma.Filters `optional:"true"` - Bootstrapper io.Closer `optional:"true"` // the periodic bootstrapper - Routing irouting.ProvideManyRouter `optional:"true"` // the routing system. recommend ipfs-dht - DNSResolver *madns.Resolver // the DNS resolver - Exchange exchange.Interface // the block exchange + strategy (bitswap) - Namesys namesys.NameSystem // the name system, resolves paths to hashes - Provider provider.System // the value provider system - IpnsRepub *ipnsrp.Republisher `optional:"true"` - GraphExchange graphsync.GraphExchange `optional:"true"` - ResourceManager network.ResourceManager `optional:"true"` + PeerHost p2phost.Host `optional:"true"` // the network host (server+client) + Peering *peering.PeeringService `optional:"true"` + Filters *ma.Filters `optional:"true"` + Bootstrapper io.Closer `optional:"true"` // the periodic bootstrapper + Routing irouting.ProvideManyRouter `optional:"true"` // the routing system. recommend ipfs-dht + DNSResolver *madns.Resolver // the DNS resolver + IPLDPathResolver pathresolver.Resolver `name:"ipldPathResolver"` // The IPLD path resolver + UnixFSPathResolver pathresolver.Resolver `name:"unixFSPathResolver"` // The UnixFS path resolver + OfflineIPLDPathResolver pathresolver.Resolver `name:"offlineIpldPathResolver"` // The IPLD path resolver that uses only locally available blocks + OfflineUnixFSPathResolver pathresolver.Resolver `name:"offlineUnixFSPathResolver"` // The UnixFS path resolver that uses only locally available blocks + Exchange exchange.Interface // the block exchange + strategy (bitswap) + Namesys namesys.NameSystem // the name system, resolves paths to hashes + Provider provider.System // the value provider system + IpnsRepub *ipnsrp.Republisher `optional:"true"` + ResourceManager network.ResourceManager `optional:"true"` PubSub *pubsub.PubSub `optional:"true"` PSRouter *psrouter.PubsubValueStore `optional:"true"` @@ -162,12 +170,39 @@ func (n *IpfsNode) Bootstrap(cfg bootstrap.BootstrapConfig) error { return ps } } + if load, _ := cfg.BackupPeers(); load == nil { + save := func(ctx context.Context, peerList []peer.AddrInfo) { + err := n.saveTempBootstrapPeers(ctx, peerList) + if err != nil { + log.Warnf("saveTempBootstrapPeers failed: %s", err) + return + } + } + load = func(ctx context.Context) []peer.AddrInfo { + peerList, err := n.loadTempBootstrapPeers(ctx) + if err != nil { + log.Warnf("loadTempBootstrapPeers failed: %s", err) + return nil + } + return peerList + } + cfg.SetBackupPeers(load, save) + } + + repoConf, err := n.Repo.Config() + if err != nil { + return err + } + if repoConf.Internal.BackupBootstrapInterval != nil { + cfg.BackupBootstrapInterval = repoConf.Internal.BackupBootstrapInterval.WithDefault(time.Hour) + } - var err error n.Bootstrapper, err = bootstrap.Bootstrap(n.Identity, n.PeerHost, n.Routing, cfg) return err } +var TempBootstrapPeersKey = datastore.NewKey("/local/temp_bootstrap_peers") + func (n *IpfsNode) loadBootstrapPeers() ([]peer.AddrInfo, error) { cfg, err := n.Repo.Config() if err != nil { @@ -177,6 +212,33 @@ func (n *IpfsNode) loadBootstrapPeers() ([]peer.AddrInfo, error) { return cfg.BootstrapPeers() } +func (n *IpfsNode) saveTempBootstrapPeers(ctx context.Context, peerList []peer.AddrInfo) error { + ds := n.Repo.Datastore() + bytes, err := json.Marshal(config.BootstrapPeerStrings(peerList)) + if err != nil { + return err + } + + if err := ds.Put(ctx, TempBootstrapPeersKey, bytes); err != nil { + return err + } + return ds.Sync(ctx, TempBootstrapPeersKey) +} + +func (n *IpfsNode) loadTempBootstrapPeers(ctx context.Context) ([]peer.AddrInfo, error) { + ds := n.Repo.Datastore() + bytes, err := ds.Get(ctx, TempBootstrapPeersKey) + if err != nil { + return nil, err + } + + var addrs []string + if err := json.Unmarshal(bytes, &addrs); err != nil { + return nil, err + } + return config.ParseBootstrapPeers(addrs) +} + type ConstructPeerHostOpts struct { AddrsFactory p2pbhost.AddrsFactory DisableNatPortMap bool diff --git a/core/core_test.go b/core/core_test.go index 1d0703de0..5d004937a 100644 --- a/core/core_test.go +++ b/core/core_test.go @@ -1,28 +1,14 @@ package core import ( - "crypto/rand" - "errors" - "fmt" - "net/http/httptest" - "path" "testing" - "time" context "context" - "github.com/ipfs/go-cid" - "github.com/ipfs/go-delegated-routing/client" - "github.com/ipfs/go-ipns" - "github.com/ipfs/kubo/core/node/libp2p" "github.com/ipfs/kubo/repo" - "github.com/libp2p/go-libp2p/core/crypto" - peer "github.com/libp2p/go-libp2p/core/peer" - "github.com/stretchr/testify/require" datastore "github.com/ipfs/go-datastore" syncds "github.com/ipfs/go-datastore/sync" - drs "github.com/ipfs/go-delegated-routing/server" config "github.com/ipfs/kubo/config" ) @@ -34,7 +20,7 @@ func TestInitialization(t *testing.T) { { Identity: id, Addresses: config.Addresses{ - Swarm: []string{"/ip4/0.0.0.0/tcp/4001", "/ip4/0.0.0.0/udp/4001/quic"}, + Swarm: []string{"/ip4/0.0.0.0/tcp/4001", "/ip4/0.0.0.0/udp/4001/quic-v1"}, API: []string{"/ip4/127.0.0.1/tcp/8000"}, }, }, @@ -42,7 +28,7 @@ func TestInitialization(t *testing.T) { { Identity: id, Addresses: config.Addresses{ - Swarm: []string{"/ip4/0.0.0.0/tcp/4001", "/ip4/0.0.0.0/udp/4001/quic"}, + Swarm: []string{"/ip4/0.0.0.0/tcp/4001", "/ip4/0.0.0.0/udp/4001/quic-v1"}, API: []string{"/ip4/127.0.0.1/tcp/8000"}, }, }, @@ -79,285 +65,3 @@ var testIdentity = config.Identity{ PeerID: "QmNgdzLieYi8tgfo2WfTUzNVH5hQK9oAYGVf6dxN12NrHt", PrivKey: "CAASrRIwggkpAgEAAoICAQCwt67GTUQ8nlJhks6CgbLKOx7F5tl1r9zF4m3TUrG3Pe8h64vi+ILDRFd7QJxaJ/n8ux9RUDoxLjzftL4uTdtv5UXl2vaufCc/C0bhCRvDhuWPhVsD75/DZPbwLsepxocwVWTyq7/ZHsCfuWdoh/KNczfy+Gn33gVQbHCnip/uhTVxT7ARTiv8Qa3d7qmmxsR+1zdL/IRO0mic/iojcb3Oc/PRnYBTiAZFbZdUEit/99tnfSjMDg02wRayZaT5ikxa6gBTMZ16Yvienq7RwSELzMQq2jFA4i/TdiGhS9uKywltiN2LrNDBcQJSN02pK12DKoiIy+wuOCRgs2NTQEhU2sXCk091v7giTTOpFX2ij9ghmiRfoSiBFPJA5RGwiH6ansCHtWKY1K8BS5UORM0o3dYk87mTnKbCsdz4bYnGtOWafujYwzueGx8r+IWiys80IPQKDeehnLW6RgoyjszKgL/2XTyP54xMLSW+Qb3BPgDcPaPO0hmop1hW9upStxKsefW2A2d46Ds4HEpJEry7PkS5M4gKL/zCKHuxuXVk14+fZQ1rstMuvKjrekpAC2aVIKMI9VRA3awtnje8HImQMdj+r+bPmv0N8rTTr3eS4J8Yl7k12i95LLfK+fWnmUh22oTNzkRlaiERQrUDyE4XNCtJc0xs1oe1yXGqazCIAQIDAQABAoICAQCk1N/ftahlRmOfAXk//8wNl7FvdJD3le6+YSKBj0uWmN1ZbUSQk64chr12iGCOM2WY180xYjy1LOS44PTXaeW5bEiTSnb3b3SH+HPHaWCNM2EiSogHltYVQjKW+3tfH39vlOdQ9uQ+l9Gh6iTLOqsCRyszpYPqIBwi1NMLY2Ej8PpVU7ftnFWouHZ9YKS7nAEiMoowhTu/7cCIVwZlAy3AySTuKxPMVj9LORqC32PVvBHZaMPJ+X1Xyijqg6aq39WyoztkXg3+Xxx5j5eOrK6vO/Lp6ZUxaQilHDXoJkKEJjgIBDZpluss08UPfOgiWAGkW+L4fgUxY0qDLDAEMhyEBAn6KOKVL1JhGTX6GjhWziI94bddSpHKYOEIDzUy4H8BXnKhtnyQV6ELS65C2hj9D0IMBTj7edCF1poJy0QfdK0cuXgMvxHLeUO5uc2YWfbNosvKxqygB9rToy4b22YvNwsZUXsTY6Jt+p9V2OgXSKfB5VPeRbjTJL6xqvvUJpQytmII/C9JmSDUtCbYceHj6X9jgigLk20VV6nWHqCTj3utXD6NPAjoycVpLKDlnWEgfVELDIk0gobxUqqSm3jTPEKRPJgxkgPxbwxYumtw++1UY2y35w3WRDc2xYPaWKBCQeZy+mL6ByXp9bWlNvxS3Knb6oZp36/ovGnf2pGvdQKCAQEAyKpipz2lIUySDyE0avVWAmQb2tWGKXALPohzj7AwkcfEg2GuwoC6GyVE2sTJD1HRazIjOKn3yQORg2uOPeG7sx7EKHxSxCKDrbPawkvLCq8JYSy9TLvhqKUVVGYPqMBzu2POSLEA81QXas+aYjKOFWA2Zrjq26zV9ey3+6Lc6WULePgRQybU8+RHJc6fdjUCCfUxgOrUO2IQOuTJ+FsDpVnrMUGlokmWn23OjL4qTL9wGDnWGUs2pjSzNbj3qA0d8iqaiMUyHX/D/VS0wpeT1osNBSm8suvSibYBn+7wbIApbwXUxZaxMv2OHGz3empae4ckvNZs7r8wsI9UwFt8mwKCAQEA4XK6gZkv9t+3YCcSPw2ensLvL/xU7i2bkC9tfTGdjnQfzZXIf5KNdVuj/SerOl2S1s45NMs3ysJbADwRb4ahElD/V71nGzV8fpFTitC20ro9fuX4J0+twmBolHqeH9pmeGTjAeL1rvt6vxs4FkeG/yNft7GdXpXTtEGaObn8Mt0tPY+aB3UnKrnCQoQAlPyGHFrVRX0UEcp6wyyNGhJCNKeNOvqCHTFObhbhO+KWpWSN0MkVHnqaIBnIn1Te8FtvP/iTwXGnKc0YXJUG6+LM6LmOguW6tg8ZqiQeYyyR+e9eCFH4csLzkrTl1GxCxwEsoSLIMm7UDcjttW6tYEghkwKCAQEAmeCO5lCPYImnN5Lu71ZTLmI2OgmjaANTnBBnDbi+hgv61gUCToUIMejSdDCTPfwv61P3TmyIZs0luPGxkiKYHTNqmOE9Vspgz8Mr7fLRMNApESuNvloVIY32XVImj/GEzh4rAfM6F15U1sN8T/EUo6+0B/Glp+9R49QzAfRSE2g48/rGwgf1JVHYfVWFUtAzUA+GdqWdOixo5cCsYJbqpNHfWVZN/bUQnBFIYwUwysnC29D+LUdQEQQ4qOm+gFAOtrWU62zMkXJ4iLt8Ify6kbrvsRXgbhQIzzGS7WH9XDarj0eZciuslr15TLMC1Azadf+cXHLR9gMHA13mT9vYIQKCAQA/DjGv8cKCkAvf7s2hqROGYAs6Jp8yhrsN1tYOwAPLRhtnCs+rLrg17M2vDptLlcRuI/vIElamdTmylRpjUQpX7yObzLO73nfVhpwRJVMdGU394iBIDncQ+JoHfUwgqJskbUM40dvZdyjbrqc/Q/4z+hbZb+oN/GXb8sVKBATPzSDMKQ/xqgisYIw+wmDPStnPsHAaIWOtni47zIgilJzD0WEk78/YjmPbUrboYvWziK5JiRRJFA1rkQqV1c0M+OXixIm+/yS8AksgCeaHr0WUieGcJtjT9uE8vyFop5ykhRiNxy9wGaq6i7IEecsrkd6DqxDHWkwhFuO1bSE83q/VAoIBAEA+RX1i/SUi08p71ggUi9WFMqXmzELp1L3hiEjOc2AklHk2rPxsaTh9+G95BvjhP7fRa/Yga+yDtYuyjO99nedStdNNSg03aPXILl9gs3r2dPiQKUEXZJ3FrH6tkils/8BlpOIRfbkszrdZIKTO9GCdLWQ30dQITDACs8zV/1GFGrHFrqnnMe/NpIFHWNZJ0/WZMi8wgWO6Ik8jHEpQtVXRiXLqy7U6hk170pa4GHOzvftfPElOZZjy9qn7KjdAQqy6spIrAE94OEL+fBgbHQZGLpuTlj6w6YGbMtPU8uo7sXKoc6WOCb68JWft3tejGLDa1946HAWqVM9B/UcneNc=", } - -var errNotSupported = errors.New("method not supported") - -func TestDelegatedRoutingSingle(t *testing.T) { - require := require.New(t) - - pID1, priv1, err := GeneratePeerID() - require.NoError(err) - - pID2, _, err := GeneratePeerID() - require.NoError(err) - - theID := path.Join("/ipns", string(pID1)) - theErrorID := path.Join("/ipns", string(pID2)) - - d := &delegatedRoutingService{ - goodPeerID: pID1, - badPeerID: pID2, - pk1: priv1, - } - - url := StartRoutingServer(t, d) - n := GetNode(t, url) - - ctx := context.Background() - - v, err := n.Routing.GetValue(ctx, theID) - require.NoError(err) - require.NotNil(v) - require.Contains(string(v), "RECORD FROM SERVICE 0") - - v, err = n.Routing.GetValue(ctx, theErrorID) - require.Nil(v) - require.Error(err) - - err = n.Routing.PutValue(ctx, theID, v) - require.NoError(err) - -} - -func TestDelegatedRoutingMulti(t *testing.T) { - require := require.New(t) - - pID1, priv1, err := GeneratePeerID() - require.NoError(err) - - pID2, priv2, err := GeneratePeerID() - require.NoError(err) - - theID1 := path.Join("/ipns", string(pID1)) - theID2 := path.Join("/ipns", string(pID2)) - - d1 := &delegatedRoutingService{ - goodPeerID: pID1, - badPeerID: pID2, - pk1: priv1, - serviceID: 1, - } - - url1 := StartRoutingServer(t, d1) - - d2 := &delegatedRoutingService{ - goodPeerID: pID2, - badPeerID: pID1, - pk1: priv2, - serviceID: 2, - } - - url2 := StartRoutingServer(t, d2) - - n := GetNode(t, url1, url2) - - ctx := context.Background() - - v, err := n.Routing.GetValue(ctx, theID1) - require.NoError(err) - require.NotNil(v) - require.Contains(string(v), "RECORD FROM SERVICE 1") - - v, err = n.Routing.GetValue(ctx, theID2) - require.NoError(err) - require.NotNil(v) - require.Contains(string(v), "RECORD FROM SERVICE 2") -} - -func StartRoutingServer(t *testing.T, d drs.DelegatedRoutingService) string { - t.Helper() - - f := drs.DelegatedRoutingAsyncHandler(d) - svr := httptest.NewServer(f) - t.Cleanup(func() { - svr.Close() - }) - - return svr.URL -} - -func GetNode(t *testing.T, reframeURLs ...string) *IpfsNode { - t.Helper() - - routers := make(config.Routers) - var routerNames []string - for i, ru := range reframeURLs { - rn := fmt.Sprintf("reframe-%d", i) - routerNames = append(routerNames, rn) - routers[rn] = - config.RouterParser{ - Router: config.Router{ - Type: config.RouterTypeReframe, - Parameters: &config.ReframeRouterParams{ - Endpoint: ru, - }, - }, - } - } - - var crs []config.ConfigRouter - for _, rn := range routerNames { - crs = append(crs, config.ConfigRouter{ - RouterName: rn, - IgnoreErrors: true, - Timeout: config.Duration{Duration: time.Minute}, - }) - } - - const parallelRouterName = "parallel-router" - - routers[parallelRouterName] = config.RouterParser{ - Router: config.Router{ - Type: config.RouterTypeParallel, - Parameters: &config.ComposableRouterParams{ - Routers: crs, - }, - }, - } - cfg := config.Config{ - Identity: testIdentity, - Addresses: config.Addresses{ - Swarm: []string{"/ip4/0.0.0.0/tcp/0", "/ip4/0.0.0.0/udp/0/quic"}, - API: []string{"/ip4/127.0.0.1/tcp/0"}, - }, - Routing: config.Routing{ - Type: config.NewOptionalString("custom"), - Routers: routers, - Methods: config.Methods{ - config.MethodNameFindPeers: config.Method{ - RouterName: parallelRouterName, - }, - config.MethodNameFindProviders: config.Method{ - RouterName: parallelRouterName, - }, - config.MethodNameGetIPNS: config.Method{ - RouterName: parallelRouterName, - }, - config.MethodNameProvide: config.Method{ - RouterName: parallelRouterName, - }, - config.MethodNamePutIPNS: config.Method{ - RouterName: parallelRouterName, - }, - }, - }, - } - - r := &repo.Mock{ - C: cfg, - D: syncds.MutexWrap(datastore.NewMapDatastore()), - } - - n, err := NewNode(context.Background(), - &BuildCfg{ - Repo: r, - Online: true, - Routing: libp2p.ConstructDelegatedRouting( - cfg.Routing.Routers, - cfg.Routing.Methods, - cfg.Identity.PeerID, - cfg.Addresses.Swarm, - cfg.Identity.PrivKey, - ), - }, - ) - require.NoError(t, err) - - return n -} - -func GeneratePeerID() (peer.ID, crypto.PrivKey, error) { - priv, pk, err := crypto.GenerateEd25519Key(rand.Reader) - if err != nil { - return peer.ID(""), nil, err - } - - pid, err := peer.IDFromPublicKey(pk) - return pid, priv, err -} - -type delegatedRoutingService struct { - goodPeerID, badPeerID peer.ID - pk1 crypto.PrivKey - serviceID int -} - -func (drs *delegatedRoutingService) FindProviders(ctx context.Context, key cid.Cid) (<-chan client.FindProvidersAsyncResult, error) { - return nil, errNotSupported -} - -func (drs *delegatedRoutingService) Provide(ctx context.Context, req *client.ProvideRequest) (<-chan client.ProvideAsyncResult, error) { - return nil, errNotSupported -} - -func (drs *delegatedRoutingService) GetIPNS(ctx context.Context, id []byte) (<-chan client.GetIPNSAsyncResult, error) { - ctx, cancel := context.WithCancel(ctx) - ch := make(chan client.GetIPNSAsyncResult) - go func() { - defer close(ch) - defer cancel() - - var out client.GetIPNSAsyncResult - switch peer.ID(id) { - case drs.goodPeerID: - ie, err := ipns.Create(drs.pk1, []byte(fmt.Sprintf("RECORD FROM SERVICE %d", drs.serviceID)), 0, time.Now().Add(10*time.Hour), 100*time.Hour) - if err != nil { - log.Fatal(err) - } - ieb, err := ie.Marshal() - if err != nil { - log.Fatal(err) - } - - out = client.GetIPNSAsyncResult{ - Record: ieb, - Err: nil, - } - case drs.badPeerID: - out = client.GetIPNSAsyncResult{ - Record: nil, - Err: errors.New("THE ERROR"), - } - default: - return - } - - select { - case <-ctx.Done(): - return - case ch <- out: - } - }() - - return ch, nil - -} - -func (drs *delegatedRoutingService) PutIPNS(ctx context.Context, id []byte, record []byte) (<-chan client.PutIPNSAsyncResult, error) { - ctx, cancel := context.WithCancel(ctx) - ch := make(chan client.PutIPNSAsyncResult) - go func() { - defer close(ch) - defer cancel() - - var out client.PutIPNSAsyncResult - switch peer.ID(id) { - case drs.goodPeerID: - out = client.PutIPNSAsyncResult{} - case drs.badPeerID: - out = client.PutIPNSAsyncResult{ - Err: fmt.Errorf("THE ERROR %d", drs.serviceID), - } - default: - return - } - - select { - case <-ctx.Done(): - return - case ch <- out: - } - }() - - return ch, nil -} diff --git a/core/coreapi/block.go b/core/coreapi/block.go index 886a21bf4..b386ecd0a 100644 --- a/core/coreapi/block.go +++ b/core/coreapi/block.go @@ -6,12 +6,12 @@ import ( "errors" "io" + "github.com/ipfs/boxo/path" + pin "github.com/ipfs/boxo/pinning/pinner" blocks "github.com/ipfs/go-block-format" cid "github.com/ipfs/go-cid" - pin "github.com/ipfs/go-ipfs-pinner" - coreiface "github.com/ipfs/interface-go-ipfs-core" - caopts "github.com/ipfs/interface-go-ipfs-core/options" - path "github.com/ipfs/interface-go-ipfs-core/path" + coreiface "github.com/ipfs/kubo/core/coreiface" + caopts "github.com/ipfs/kubo/core/coreiface/options" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" @@ -22,7 +22,7 @@ import ( type BlockAPI CoreAPI type BlockStat struct { - path path.Resolved + path path.ImmutablePath size int } @@ -60,24 +60,26 @@ func (api *BlockAPI) Put(ctx context.Context, src io.Reader, opts ...caopts.Bloc } if settings.Pin { - api.pinning.PinWithMode(b.Cid(), pin.Recursive) + if err = api.pinning.PinWithMode(ctx, b.Cid(), pin.Recursive, ""); err != nil { + return nil, err + } if err := api.pinning.Flush(ctx); err != nil { return nil, err } } - return &BlockStat{path: path.IpldPath(b.Cid()), size: len(data)}, nil + return &BlockStat{path: path.FromCid(b.Cid()), size: len(data)}, nil } func (api *BlockAPI) Get(ctx context.Context, p path.Path) (io.Reader, error) { ctx, span := tracing.Span(ctx, "CoreAPI.BlockAPI", "Get", trace.WithAttributes(attribute.String("path", p.String()))) defer span.End() - rp, err := api.core().ResolvePath(ctx, p) + rp, _, err := api.core().ResolvePath(ctx, p) if err != nil { return nil, err } - b, err := api.blocks.GetBlock(ctx, rp.Cid()) + b, err := api.blocks.GetBlock(ctx, rp.RootCid()) if err != nil { return nil, err } @@ -89,7 +91,7 @@ func (api *BlockAPI) Rm(ctx context.Context, p path.Path, opts ...caopts.BlockRm ctx, span := tracing.Span(ctx, "CoreAPI.BlockAPI", "Rm", trace.WithAttributes(attribute.String("path", p.String()))) defer span.End() - rp, err := api.core().ResolvePath(ctx, p) + rp, _, err := api.core().ResolvePath(ctx, p) if err != nil { return err } @@ -98,7 +100,7 @@ func (api *BlockAPI) Rm(ctx context.Context, p path.Path, opts ...caopts.BlockRm if err != nil { return err } - cids := []cid.Cid{rp.Cid()} + cids := []cid.Cid{rp.RootCid()} o := util.RmBlocksOpts{Force: settings.Force} out, err := util.RmBlocks(ctx, api.blockstore, api.pinning, cids, o) @@ -130,18 +132,18 @@ func (api *BlockAPI) Stat(ctx context.Context, p path.Path) (coreiface.BlockStat ctx, span := tracing.Span(ctx, "CoreAPI.BlockAPI", "Stat", trace.WithAttributes(attribute.String("path", p.String()))) defer span.End() - rp, err := api.core().ResolvePath(ctx, p) + rp, _, err := api.core().ResolvePath(ctx, p) if err != nil { return nil, err } - b, err := api.blocks.GetBlock(ctx, rp.Cid()) + b, err := api.blocks.GetBlock(ctx, rp.RootCid()) if err != nil { return nil, err } return &BlockStat{ - path: path.IpldPath(b.Cid()), + path: path.FromCid(b.Cid()), size: len(b.RawData()), }, nil } @@ -150,7 +152,7 @@ func (bs *BlockStat) Size() int { return bs.size } -func (bs *BlockStat) Path() path.Resolved { +func (bs *BlockStat) Path() path.ImmutablePath { return bs.path } diff --git a/core/coreapi/coreapi.go b/core/coreapi/coreapi.go index fb549171a..b757929a2 100644 --- a/core/coreapi/coreapi.go +++ b/core/coreapi/coreapi.go @@ -1,15 +1,12 @@ /* +**NOTE: this package is experimental.** + Package coreapi provides direct access to the core commands in IPFS. If you are embedding IPFS directly in your Go program, this package is the public interface you should use to read and write files or otherwise control IPFS. -If you are running IPFS as a separate process, you should use `go-ipfs-api` to -work with it via HTTP. As we finalize the interfaces here, `go-ipfs-api` will -transparently adopt them so you can use the same code with either package. - -**NOTE: this package is experimental.** `go-ipfs` has mainly been developed -as a standalone application and library-style use of this package is still new. -Interfaces here aren't yet completely stable. +If you are running IPFS as a separate process, you should use `client/rpc` to +work with it via HTTP. */ package coreapi @@ -18,18 +15,20 @@ import ( "errors" "fmt" - bserv "github.com/ipfs/go-blockservice" - "github.com/ipfs/go-fetcher" - blockstore "github.com/ipfs/go-ipfs-blockstore" - exchange "github.com/ipfs/go-ipfs-exchange-interface" - offlinexch "github.com/ipfs/go-ipfs-exchange-offline" - pin "github.com/ipfs/go-ipfs-pinner" - provider "github.com/ipfs/go-ipfs-provider" - offlineroute "github.com/ipfs/go-ipfs-routing/offline" + bserv "github.com/ipfs/boxo/blockservice" + blockstore "github.com/ipfs/boxo/blockstore" + exchange "github.com/ipfs/boxo/exchange" + offlinexch "github.com/ipfs/boxo/exchange/offline" + "github.com/ipfs/boxo/fetcher" + dag "github.com/ipfs/boxo/ipld/merkledag" + pathresolver "github.com/ipfs/boxo/path/resolver" + pin "github.com/ipfs/boxo/pinning/pinner" + provider "github.com/ipfs/boxo/provider" + offlineroute "github.com/ipfs/boxo/routing/offline" ipld "github.com/ipfs/go-ipld-format" - dag "github.com/ipfs/go-merkledag" - coreiface "github.com/ipfs/interface-go-ipfs-core" - "github.com/ipfs/interface-go-ipfs-core/options" + "github.com/ipfs/kubo/config" + coreiface "github.com/ipfs/kubo/core/coreiface" + "github.com/ipfs/kubo/core/coreiface/options" pubsub "github.com/libp2p/go-libp2p-pubsub" record "github.com/libp2p/go-libp2p-record" ci "github.com/libp2p/go-libp2p/core/crypto" @@ -39,7 +38,7 @@ import ( routing "github.com/libp2p/go-libp2p/core/routing" madns "github.com/multiformats/go-multiaddr-dns" - "github.com/ipfs/go-namesys" + "github.com/ipfs/boxo/namesys" "github.com/ipfs/kubo/core" "github.com/ipfs/kubo/core/node" "github.com/ipfs/kubo/repo" @@ -65,9 +64,11 @@ type CoreAPI struct { recordValidator record.Validator exchange exchange.Interface - namesys namesys.NameSystem - routing routing.Routing - dnsResolver *madns.Resolver + namesys namesys.NameSystem + routing routing.Routing + dnsResolver *madns.Resolver + ipldPathResolver pathresolver.Resolver + unixFSPathResolver pathresolver.Resolver provider provider.System @@ -129,11 +130,6 @@ func (api *CoreAPI) Pin() coreiface.PinAPI { return (*PinAPI)(api) } -// Dht returns the DhtAPI interface implementation backed by the go-ipfs node -func (api *CoreAPI) Dht() coreiface.DhtAPI { - return (*DhtAPI)(api) -} - // Swarm returns the SwarmAPI interface implementation backed by the go-ipfs node func (api *CoreAPI) Swarm() coreiface.SwarmAPI { return (*SwarmAPI)(api) @@ -144,6 +140,11 @@ func (api *CoreAPI) PubSub() coreiface.PubSubAPI { return (*PubSubAPI)(api) } +// Routing returns the RoutingAPI interface implementation backed by the kubo node +func (api *CoreAPI) Routing() coreiface.RoutingAPI { + return (*RoutingAPI)(api) +} + // WithOptions returns api with global options applied func (api *CoreAPI) WithOptions(opts ...options.ApiOption) (coreiface.CoreAPI, error) { settings := api.parentOpts // make sure to copy @@ -174,13 +175,15 @@ func (api *CoreAPI) WithOptions(opts ...options.ApiOption) (coreiface.CoreAPI, e ipldFetcherFactory: n.IPLDFetcherFactory, unixFSFetcherFactory: n.UnixFSFetcherFactory, - peerstore: n.Peerstore, - peerHost: n.PeerHost, - namesys: n.Namesys, - recordValidator: n.RecordValidator, - exchange: n.Exchange, - routing: n.Routing, - dnsResolver: n.DNSResolver, + peerstore: n.Peerstore, + peerHost: n.PeerHost, + namesys: n.Namesys, + recordValidator: n.RecordValidator, + exchange: n.Exchange, + routing: n.Routing, + dnsResolver: n.DNSResolver, + ipldPathResolver: n.IPLDPathResolver, + unixFSPathResolver: n.UnixFSPathResolver, provider: n.Provider, @@ -218,17 +221,21 @@ func (api *CoreAPI) WithOptions(opts ...options.ApiOption) (coreiface.CoreAPI, e return nil, fmt.Errorf("cannot specify negative resolve cache size") } - subAPI.routing = offlineroute.NewOfflineRouter(subAPI.repo.Datastore(), subAPI.recordValidator) - - subAPI.namesys, err = namesys.NewNameSystem(subAPI.routing, + nsOptions := []namesys.Option{ namesys.WithDatastore(subAPI.repo.Datastore()), namesys.WithDNSResolver(subAPI.dnsResolver), - namesys.WithCache(cs)) + namesys.WithCache(cs), + namesys.WithMaxCacheTTL(cfg.Ipns.MaxCacheTTL.WithDefault(config.DefaultIpnsMaxCacheTTL)), + } + + subAPI.routing = offlineroute.NewOfflineRouter(subAPI.repo.Datastore(), subAPI.recordValidator) + + subAPI.namesys, err = namesys.NewNameSystem(subAPI.routing, nsOptions...) if err != nil { return nil, fmt.Errorf("error constructing namesys: %w", err) } - subAPI.provider = provider.NewOfflineProvider() + subAPI.provider = provider.NewNoopProvider() subAPI.peerstore = nil subAPI.peerHost = nil diff --git a/core/coreapi/dag.go b/core/coreapi/dag.go index 0f38fc4d5..70686f62e 100644 --- a/core/coreapi/dag.go +++ b/core/coreapi/dag.go @@ -3,13 +3,14 @@ package coreapi import ( "context" + dag "github.com/ipfs/boxo/ipld/merkledag" + pin "github.com/ipfs/boxo/pinning/pinner" cid "github.com/ipfs/go-cid" - pin "github.com/ipfs/go-ipfs-pinner" ipld "github.com/ipfs/go-ipld-format" - dag "github.com/ipfs/go-merkledag" - "github.com/ipfs/kubo/tracing" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" + + "github.com/ipfs/kubo/tracing" ) type dagAPI struct { @@ -29,7 +30,9 @@ func (adder *pinningAdder) Add(ctx context.Context, nd ipld.Node) error { return err } - adder.pinning.PinWithMode(nd.Cid(), pin.Recursive) + if err := adder.pinning.PinWithMode(ctx, nd.Cid(), pin.Recursive, ""); err != nil { + return err + } return adder.pinning.Flush(ctx) } @@ -48,7 +51,9 @@ func (adder *pinningAdder) AddMany(ctx context.Context, nds []ipld.Node) error { for _, nd := range nds { c := nd.Cid() if cids.Visit(c) { - adder.pinning.PinWithMode(c, pin.Recursive) + if err := adder.pinning.PinWithMode(ctx, c, pin.Recursive, ""); err != nil { + return err + } } } diff --git a/core/coreapi/key.go b/core/coreapi/key.go index 903659bb9..a6101dae8 100644 --- a/core/coreapi/key.go +++ b/core/coreapi/key.go @@ -7,10 +7,10 @@ import ( "fmt" "sort" - ipfspath "github.com/ipfs/go-path" - coreiface "github.com/ipfs/interface-go-ipfs-core" - caopts "github.com/ipfs/interface-go-ipfs-core/options" - path "github.com/ipfs/interface-go-ipfs-core/path" + "github.com/ipfs/boxo/ipns" + "github.com/ipfs/boxo/path" + coreiface "github.com/ipfs/kubo/core/coreiface" + caopts "github.com/ipfs/kubo/core/coreiface/options" "github.com/ipfs/kubo/tracing" crypto "github.com/libp2p/go-libp2p/core/crypto" peer "github.com/libp2p/go-libp2p/core/peer" @@ -23,6 +23,19 @@ type KeyAPI CoreAPI type key struct { name string peerID peer.ID + path path.Path +} + +func newKey(name string, pid peer.ID) (*key, error) { + p, err := path.NewPath("/ipns/" + ipns.NameFromPeer(pid).String()) + if err != nil { + return nil, err + } + return &key{ + name: name, + peerID: pid, + path: p, + }, nil } // Name returns the key name @@ -32,7 +45,7 @@ func (k *key) Name() string { // Path returns the path of the key. func (k *key) Path() path.Path { - return path.New(ipfspath.Join([]string{"/ipns", coreiface.FormatKeyID(k.peerID)})) + return k.path } // ID returns key PeerID @@ -98,7 +111,7 @@ func (api *KeyAPI) Generate(ctx context.Context, name string, opts ...caopts.Key return nil, err } - return &key{name, pid}, nil + return newKey(name, pid) } // List returns a list keys stored in keystore. @@ -114,7 +127,10 @@ func (api *KeyAPI) List(ctx context.Context) ([]coreiface.Key, error) { sort.Strings(keys) out := make([]coreiface.Key, len(keys)+1) - out[0] = &key{"self", api.identity} + out[0], err = newKey("self", api.identity) + if err != nil { + return nil, err + } for n, k := range keys { privKey, err := api.repo.Keystore().Get(k) @@ -129,7 +145,10 @@ func (api *KeyAPI) List(ctx context.Context) ([]coreiface.Key, error) { return nil, err } - out[n+1] = &key{k, pid} + out[n+1], err = newKey(k, pid) + if err != nil { + return nil, err + } } return out, nil } @@ -171,7 +190,8 @@ func (api *KeyAPI) Rename(ctx context.Context, oldName string, newName string, o // This is important, because future code will delete key `oldName` // even if it is the same as newName. if newName == oldName { - return &key{oldName, pid}, false, nil + k, err := newKey(oldName, pid) + return k, false, err } overwrite := false @@ -195,7 +215,13 @@ func (api *KeyAPI) Rename(ctx context.Context, oldName string, newName string, o return nil, false, err } - return &key{newName, pid}, overwrite, ks.Delete(oldName) + err = ks.Delete(oldName) + if err != nil { + return nil, false, err + } + + k, err := newKey(newName, pid) + return k, overwrite, err } // Remove removes keys from keystore. Returns ipns path of the removed key. @@ -226,7 +252,7 @@ func (api *KeyAPI) Remove(ctx context.Context, name string) (coreiface.Key, erro return nil, err } - return &key{"", pid}, nil + return newKey("", pid) } func (api *KeyAPI) Self(ctx context.Context) (coreiface.Key, error) { @@ -234,5 +260,85 @@ func (api *KeyAPI) Self(ctx context.Context) (coreiface.Key, error) { return nil, errors.New("identity not loaded") } - return &key{"self", api.identity}, nil + return newKey("self", api.identity) +} + +const signedMessagePrefix = "libp2p-key signed message:" + +func (api *KeyAPI) Sign(ctx context.Context, name string, data []byte) (coreiface.Key, []byte, error) { + var ( + sk crypto.PrivKey + err error + ) + if name == "" || name == "self" { + name = "self" + sk = api.privateKey + } else { + sk, err = api.repo.Keystore().Get(name) + } + if err != nil { + return nil, nil, err + } + + pid, err := peer.IDFromPrivateKey(sk) + if err != nil { + return nil, nil, err + } + + key, err := newKey(name, pid) + if err != nil { + return nil, nil, err + } + + data = append([]byte(signedMessagePrefix), data...) + + sig, err := sk.Sign(data) + if err != nil { + return nil, nil, err + } + + return key, sig, nil +} + +func (api *KeyAPI) Verify(ctx context.Context, keyOrName string, signature, data []byte) (coreiface.Key, bool, error) { + var ( + name string + pk crypto.PubKey + err error + ) + if keyOrName == "" || keyOrName == "self" { + name = "self" + pk = api.privateKey.GetPublic() + } else if sk, err := api.repo.Keystore().Get(keyOrName); err == nil { + name = keyOrName + pk = sk.GetPublic() + } else if ipnsName, err := ipns.NameFromString(keyOrName); err == nil { + // This works for both IPNS names and Peer IDs. + name = "" + pk, err = ipnsName.Peer().ExtractPublicKey() + if err != nil { + return nil, false, err + } + } else { + return nil, false, fmt.Errorf("'%q' is not a known key, an IPNS Name, or a valid PeerID", keyOrName) + } + + pid, err := peer.IDFromPublicKey(pk) + if err != nil { + return nil, false, err + } + + key, err := newKey(name, pid) + if err != nil { + return nil, false, err + } + + data = append([]byte(signedMessagePrefix), data...) + + valid, err := pk.Verify(data, signature) + if err != nil { + return nil, false, err + } + + return key, valid, nil } diff --git a/core/coreapi/name.go b/core/coreapi/name.go index 69dc1137b..3c4145ed5 100644 --- a/core/coreapi/name.go +++ b/core/coreapi/name.go @@ -6,51 +6,34 @@ import ( "strings" "time" - keystore "github.com/ipfs/go-ipfs-keystore" - "github.com/ipfs/go-namesys" + "github.com/ipfs/boxo/ipns" + keystore "github.com/ipfs/boxo/keystore" + "github.com/ipfs/boxo/namesys" "github.com/ipfs/kubo/tracing" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" - ipath "github.com/ipfs/go-path" - coreiface "github.com/ipfs/interface-go-ipfs-core" - caopts "github.com/ipfs/interface-go-ipfs-core/options" - path "github.com/ipfs/interface-go-ipfs-core/path" + "github.com/ipfs/boxo/path" + coreiface "github.com/ipfs/kubo/core/coreiface" + caopts "github.com/ipfs/kubo/core/coreiface/options" ci "github.com/libp2p/go-libp2p/core/crypto" peer "github.com/libp2p/go-libp2p/core/peer" ) type NameAPI CoreAPI -type ipnsEntry struct { - name string - value path.Path -} - -// Name returns the ipnsEntry name. -func (e *ipnsEntry) Name() string { - return e.name -} - -// Value returns the ipnsEntry value. -func (e *ipnsEntry) Value() path.Path { - return e.value -} - -type requestContextKey string - // Publish announces new IPNS name and returns the new IPNS entry. -func (api *NameAPI) Publish(ctx context.Context, p path.Path, opts ...caopts.NamePublishOption) (coreiface.IpnsEntry, error) { +func (api *NameAPI) Publish(ctx context.Context, p path.Path, opts ...caopts.NamePublishOption) (ipns.Name, error) { ctx, span := tracing.Span(ctx, "CoreAPI.NameAPI", "Publish", trace.WithAttributes(attribute.String("path", p.String()))) defer span.End() if err := api.checkPublishAllowed(); err != nil { - return nil, err + return ipns.Name{}, err } options, err := caopts.NamePublishOptions(opts...) if err != nil { - return nil, err + return ipns.Name{}, err } span.SetAttributes( attribute.Bool("allowoffline", options.AllowOffline), @@ -63,39 +46,36 @@ func (api *NameAPI) Publish(ctx context.Context, p path.Path, opts ...caopts.Nam err = api.checkOnline(options.AllowOffline) if err != nil { - return nil, err - } - - pth, err := ipath.ParsePath(p.String()) - if err != nil { - return nil, err + return ipns.Name{}, err } k, err := keylookup(api.privateKey, api.repo.Keystore(), options.Key) if err != nil { - return nil, err - } - - if options.TTL != nil { - // nolint: staticcheck // non-backward compatible change - ctx = context.WithValue(ctx, requestContextKey("ipns-publish-ttl"), *options.TTL) + return ipns.Name{}, err } eol := time.Now().Add(options.ValidTime) - err = api.namesys.PublishWithEOL(ctx, k, pth, eol) + + publishOptions := []namesys.PublishOption{ + namesys.PublishWithEOL(eol), + namesys.PublishWithIPNSOption(ipns.WithV1Compatibility(options.CompatibleWithV1)), + } + + if options.TTL != nil { + publishOptions = append(publishOptions, namesys.PublishWithTTL(*options.TTL)) + } + + err = api.namesys.Publish(ctx, k, p, publishOptions...) if err != nil { - return nil, err + return ipns.Name{}, err } pid, err := peer.IDFromPrivateKey(k) if err != nil { - return nil, err + return ipns.Name{}, err } - return &ipnsEntry{ - name: coreiface.FormatKeyID(pid), - value: p, - }, nil + return ipns.NameFromPeer(pid), nil } func (api *NameAPI) Search(ctx context.Context, name string, opts ...caopts.NameResolveOption) (<-chan coreiface.IpnsResult, error) { @@ -128,12 +108,17 @@ func (api *NameAPI) Search(ctx context.Context, name string, opts ...caopts.Name name = "/ipns/" + name } + p, err := path.NewPath(name) + if err != nil { + return nil, err + } + out := make(chan coreiface.IpnsResult) go func() { defer close(out) - for res := range resolver.ResolveAsync(ctx, name, options.ResolveOpts...) { + for res := range resolver.ResolveAsync(ctx, p, options.ResolveOpts...) { select { - case out <- coreiface.IpnsResult{Path: path.New(res.Path.String()), Err: res.Err}: + case out <- coreiface.IpnsResult{Path: res.Path, Err: res.Err}: case <-ctx.Done(): return } diff --git a/core/coreapi/object.go b/core/coreapi/object.go index 28dd0df08..0f6c2747a 100644 --- a/core/coreapi/object.go +++ b/core/coreapi/object.go @@ -1,30 +1,19 @@ package coreapi import ( - "bytes" "context" - "encoding/base64" - "encoding/json" - "encoding/xml" - "errors" - "fmt" - "io" - cid "github.com/ipfs/go-cid" - pin "github.com/ipfs/go-ipfs-pinner" - ipld "github.com/ipfs/go-ipld-format" - dag "github.com/ipfs/go-merkledag" - "github.com/ipfs/go-merkledag/dagutils" - ft "github.com/ipfs/go-unixfs" - coreiface "github.com/ipfs/interface-go-ipfs-core" - caopts "github.com/ipfs/interface-go-ipfs-core/options" - ipath "github.com/ipfs/interface-go-ipfs-core/path" - "github.com/ipfs/kubo/tracing" + dag "github.com/ipfs/boxo/ipld/merkledag" + "github.com/ipfs/boxo/ipld/merkledag/dagutils" + ft "github.com/ipfs/boxo/ipld/unixfs" + "github.com/ipfs/boxo/path" + coreiface "github.com/ipfs/kubo/core/coreiface" + caopts "github.com/ipfs/kubo/core/coreiface/options" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" -) -const inputLimit = 2 << 20 + "github.com/ipfs/kubo/tracing" +) type ObjectAPI CoreAPI @@ -38,178 +27,7 @@ type Node struct { Data string } -func (api *ObjectAPI) New(ctx context.Context, opts ...caopts.ObjectNewOption) (ipld.Node, error) { - ctx, span := tracing.Span(ctx, "CoreAPI.ObjectAPI", "New") - defer span.End() - - options, err := caopts.ObjectNewOptions(opts...) - if err != nil { - return nil, err - } - - var n ipld.Node - switch options.Type { - case "empty": - n = new(dag.ProtoNode) - case "unixfs-dir": - n = ft.EmptyDirNode() - default: - return nil, fmt.Errorf("unknown node type: %s", options.Type) - } - - err = api.dag.Add(ctx, n) - if err != nil { - return nil, err - } - return n, nil -} - -func (api *ObjectAPI) Put(ctx context.Context, src io.Reader, opts ...caopts.ObjectPutOption) (ipath.Resolved, error) { - ctx, span := tracing.Span(ctx, "CoreAPI.ObjectAPI", "Put") - defer span.End() - - options, err := caopts.ObjectPutOptions(opts...) - if err != nil { - return nil, err - } - span.SetAttributes( - attribute.Bool("pin", options.Pin), - attribute.String("datatype", options.DataType), - attribute.String("inputenc", options.InputEnc), - ) - - data, err := io.ReadAll(io.LimitReader(src, inputLimit+10)) - if err != nil { - return nil, err - } - - var dagnode *dag.ProtoNode - switch options.InputEnc { - case "json": - node := new(Node) - decoder := json.NewDecoder(bytes.NewReader(data)) - decoder.DisallowUnknownFields() - err = decoder.Decode(node) - if err != nil { - return nil, err - } - - dagnode, err = deserializeNode(node, options.DataType) - if err != nil { - return nil, err - } - - case "protobuf": - dagnode, err = dag.DecodeProtobuf(data) - - case "xml": - node := new(Node) - err = xml.Unmarshal(data, node) - if err != nil { - return nil, err - } - - dagnode, err = deserializeNode(node, options.DataType) - if err != nil { - return nil, err - } - - default: - return nil, errors.New("unknown object encoding") - } - - if err != nil { - return nil, err - } - - if options.Pin { - defer api.blockstore.PinLock(ctx).Unlock(ctx) - } - - err = api.dag.Add(ctx, dagnode) - if err != nil { - return nil, err - } - - if options.Pin { - api.pinning.PinWithMode(dagnode.Cid(), pin.Recursive) - err = api.pinning.Flush(ctx) - if err != nil { - return nil, err - } - } - - return ipath.IpfsPath(dagnode.Cid()), nil -} - -func (api *ObjectAPI) Get(ctx context.Context, path ipath.Path) (ipld.Node, error) { - ctx, span := tracing.Span(ctx, "CoreAPI.ObjectAPI", "Get", trace.WithAttributes(attribute.String("path", path.String()))) - defer span.End() - return api.core().ResolveNode(ctx, path) -} - -func (api *ObjectAPI) Data(ctx context.Context, path ipath.Path) (io.Reader, error) { - ctx, span := tracing.Span(ctx, "CoreAPI.ObjectAPI", "Data", trace.WithAttributes(attribute.String("path", path.String()))) - defer span.End() - - nd, err := api.core().ResolveNode(ctx, path) - if err != nil { - return nil, err - } - - pbnd, ok := nd.(*dag.ProtoNode) - if !ok { - return nil, dag.ErrNotProtobuf - } - - return bytes.NewReader(pbnd.Data()), nil -} - -func (api *ObjectAPI) Links(ctx context.Context, path ipath.Path) ([]*ipld.Link, error) { - ctx, span := tracing.Span(ctx, "CoreAPI.ObjectAPI", "Links", trace.WithAttributes(attribute.String("path", path.String()))) - defer span.End() - - nd, err := api.core().ResolveNode(ctx, path) - if err != nil { - return nil, err - } - - links := nd.Links() - out := make([]*ipld.Link, len(links)) - for n, l := range links { - out[n] = (*ipld.Link)(l) - } - - return out, nil -} - -func (api *ObjectAPI) Stat(ctx context.Context, path ipath.Path) (*coreiface.ObjectStat, error) { - ctx, span := tracing.Span(ctx, "CoreAPI.ObjectAPI", "Stat", trace.WithAttributes(attribute.String("path", path.String()))) - defer span.End() - - nd, err := api.core().ResolveNode(ctx, path) - if err != nil { - return nil, err - } - - stat, err := nd.Stat() - if err != nil { - return nil, err - } - - out := &coreiface.ObjectStat{ - Cid: nd.Cid(), - NumLinks: stat.NumLinks, - BlockSize: stat.BlockSize, - LinksSize: stat.LinksSize, - DataSize: stat.DataSize, - CumulativeSize: stat.CumulativeSize, - } - - return out, nil -} - -func (api *ObjectAPI) AddLink(ctx context.Context, base ipath.Path, name string, child ipath.Path, opts ...caopts.ObjectAddLinkOption) (ipath.Resolved, error) { +func (api *ObjectAPI) AddLink(ctx context.Context, base path.Path, name string, child path.Path, opts ...caopts.ObjectAddLinkOption) (path.ImmutablePath, error) { ctx, span := tracing.Span(ctx, "CoreAPI.ObjectAPI", "AddLink", trace.WithAttributes( attribute.String("base", base.String()), attribute.String("name", name), @@ -219,23 +37,23 @@ func (api *ObjectAPI) AddLink(ctx context.Context, base ipath.Path, name string, options, err := caopts.ObjectAddLinkOptions(opts...) if err != nil { - return nil, err + return path.ImmutablePath{}, err } span.SetAttributes(attribute.Bool("create", options.Create)) baseNd, err := api.core().ResolveNode(ctx, base) if err != nil { - return nil, err + return path.ImmutablePath{}, err } childNd, err := api.core().ResolveNode(ctx, child) if err != nil { - return nil, err + return path.ImmutablePath{}, err } basePb, ok := baseNd.(*dag.ProtoNode) if !ok { - return nil, dag.ErrNotProtobuf + return path.ImmutablePath{}, dag.ErrNotProtobuf } var createfunc func() *dag.ProtoNode @@ -247,18 +65,18 @@ func (api *ObjectAPI) AddLink(ctx context.Context, base ipath.Path, name string, err = e.InsertNodeAtPath(ctx, name, childNd, createfunc) if err != nil { - return nil, err + return path.ImmutablePath{}, err } nnode, err := e.Finalize(ctx, api.dag) if err != nil { - return nil, err + return path.ImmutablePath{}, err } - return ipath.IpfsPath(nnode.Cid()), nil + return path.FromCid(nnode.Cid()), nil } -func (api *ObjectAPI) RmLink(ctx context.Context, base ipath.Path, link string) (ipath.Resolved, error) { +func (api *ObjectAPI) RmLink(ctx context.Context, base path.Path, link string) (path.ImmutablePath, error) { ctx, span := tracing.Span(ctx, "CoreAPI.ObjectAPI", "RmLink", trace.WithAttributes( attribute.String("base", base.String()), attribute.String("link", link)), @@ -267,73 +85,30 @@ func (api *ObjectAPI) RmLink(ctx context.Context, base ipath.Path, link string) baseNd, err := api.core().ResolveNode(ctx, base) if err != nil { - return nil, err + return path.ImmutablePath{}, err } basePb, ok := baseNd.(*dag.ProtoNode) if !ok { - return nil, dag.ErrNotProtobuf + return path.ImmutablePath{}, dag.ErrNotProtobuf } e := dagutils.NewDagEditor(basePb, api.dag) err = e.RmLink(ctx, link) if err != nil { - return nil, err + return path.ImmutablePath{}, err } nnode, err := e.Finalize(ctx, api.dag) if err != nil { - return nil, err + return path.ImmutablePath{}, err } - return ipath.IpfsPath(nnode.Cid()), nil + return path.FromCid(nnode.Cid()), nil } -func (api *ObjectAPI) AppendData(ctx context.Context, path ipath.Path, r io.Reader) (ipath.Resolved, error) { - ctx, span := tracing.Span(ctx, "CoreAPI.ObjectAPI", "AppendData", trace.WithAttributes(attribute.String("path", path.String()))) - defer span.End() - - return api.patchData(ctx, path, r, true) -} - -func (api *ObjectAPI) SetData(ctx context.Context, path ipath.Path, r io.Reader) (ipath.Resolved, error) { - ctx, span := tracing.Span(ctx, "CoreAPI.ObjectAPI", "SetData", trace.WithAttributes(attribute.String("path", path.String()))) - defer span.End() - - return api.patchData(ctx, path, r, false) -} - -func (api *ObjectAPI) patchData(ctx context.Context, path ipath.Path, r io.Reader, appendData bool) (ipath.Resolved, error) { - nd, err := api.core().ResolveNode(ctx, path) - if err != nil { - return nil, err - } - - pbnd, ok := nd.(*dag.ProtoNode) - if !ok { - return nil, dag.ErrNotProtobuf - } - - data, err := io.ReadAll(r) - if err != nil { - return nil, err - } - - if appendData { - data = append(pbnd.Data(), data...) - } - pbnd.SetData(data) - - err = api.dag.Add(ctx, pbnd) - if err != nil { - return nil, err - } - - return ipath.IpfsPath(pbnd.Cid()), nil -} - -func (api *ObjectAPI) Diff(ctx context.Context, before ipath.Path, after ipath.Path) ([]coreiface.ObjectChange, error) { +func (api *ObjectAPI) Diff(ctx context.Context, before path.Path, after path.Path) ([]coreiface.ObjectChange, error) { ctx, span := tracing.Span(ctx, "CoreAPI.ObjectAPI", "Diff", trace.WithAttributes( attribute.String("before", before.String()), attribute.String("after", after.String()), @@ -363,11 +138,11 @@ func (api *ObjectAPI) Diff(ctx context.Context, before ipath.Path, after ipath.P } if change.Before.Defined() { - out[i].Before = ipath.IpfsPath(change.Before) + out[i].Before = path.FromCid(change.Before) } if change.After.Defined() { - out[i].After = ipath.IpfsPath(change.After) + out[i].After = path.FromCid(change.After) } } @@ -377,37 +152,3 @@ func (api *ObjectAPI) Diff(ctx context.Context, before ipath.Path, after ipath.P func (api *ObjectAPI) core() coreiface.CoreAPI { return (*CoreAPI)(api) } - -func deserializeNode(nd *Node, dataFieldEncoding string) (*dag.ProtoNode, error) { - dagnode := new(dag.ProtoNode) - switch dataFieldEncoding { - case "text": - dagnode.SetData([]byte(nd.Data)) - case "base64": - data, err := base64.StdEncoding.DecodeString(nd.Data) - if err != nil { - return nil, err - } - dagnode.SetData(data) - default: - return nil, fmt.Errorf("unknown data field encoding") - } - - links := make([]*ipld.Link, len(nd.Links)) - for i, link := range nd.Links { - c, err := cid.Decode(link.Hash) - if err != nil { - return nil, err - } - links[i] = &ipld.Link{ - Name: link.Name, - Size: link.Size, - Cid: c, - } - } - if err := dagnode.SetLinks(links); err != nil { - return nil, err - } - - return dagnode, nil -} diff --git a/core/coreapi/path.go b/core/coreapi/path.go index c890ade6e..1eb1f7181 100644 --- a/core/coreapi/path.go +++ b/core/coreapi/path.go @@ -2,22 +2,19 @@ package coreapi import ( "context" + "errors" "fmt" - gopath "path" - "github.com/ipfs/go-namesys/resolve" + "github.com/ipfs/boxo/namesys" "github.com/ipfs/kubo/tracing" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" - "github.com/ipfs/go-cid" - "github.com/ipfs/go-fetcher" + "github.com/ipfs/boxo/path" + ipfspathresolver "github.com/ipfs/boxo/path/resolver" ipld "github.com/ipfs/go-ipld-format" - ipfspath "github.com/ipfs/go-path" - ipfspathresolver "github.com/ipfs/go-path/resolver" - coreiface "github.com/ipfs/interface-go-ipfs-core" - path "github.com/ipfs/interface-go-ipfs-core/path" + coreiface "github.com/ipfs/kubo/core/coreiface" ) // ResolveNode resolves the path `p` using Unixfs resolver, gets and returns the @@ -26,12 +23,12 @@ func (api *CoreAPI) ResolveNode(ctx context.Context, p path.Path) (ipld.Node, er ctx, span := tracing.Span(ctx, "CoreAPI", "ResolveNode", trace.WithAttributes(attribute.String("path", p.String()))) defer span.End() - rp, err := api.ResolvePath(ctx, p) + rp, _, err := api.ResolvePath(ctx, p) if err != nil { return nil, err } - node, err := api.dag.Get(ctx, rp.Cid()) + node, err := api.dag.Get(ctx, rp.RootCid()) if err != nil { return nil, err } @@ -40,46 +37,50 @@ func (api *CoreAPI) ResolveNode(ctx context.Context, p path.Path) (ipld.Node, er // ResolvePath resolves the path `p` using Unixfs resolver, returns the // resolved path. -func (api *CoreAPI) ResolvePath(ctx context.Context, p path.Path) (path.Resolved, error) { +func (api *CoreAPI) ResolvePath(ctx context.Context, p path.Path) (path.ImmutablePath, []string, error) { ctx, span := tracing.Span(ctx, "CoreAPI", "ResolvePath", trace.WithAttributes(attribute.String("path", p.String()))) defer span.End() - if _, ok := p.(path.Resolved); ok { - return p.(path.Resolved), nil - } - if err := p.IsValid(); err != nil { - return nil, err - } - - ipath := ipfspath.Path(p.String()) - ipath, err := resolve.ResolveIPNS(ctx, api.namesys, ipath) - if err == resolve.ErrNoNamesys { - return nil, coreiface.ErrOffline + res, err := namesys.Resolve(ctx, api.namesys, p) + if errors.Is(err, namesys.ErrNoNamesys) { + return path.ImmutablePath{}, nil, coreiface.ErrOffline } else if err != nil { - return nil, err + return path.ImmutablePath{}, nil, err + } + p = res.Path + + var resolver ipfspathresolver.Resolver + switch p.Namespace() { + case path.IPLDNamespace: + resolver = api.ipldPathResolver + case path.IPFSNamespace: + resolver = api.unixFSPathResolver + default: + return path.ImmutablePath{}, nil, fmt.Errorf("unsupported path namespace: %s", p.Namespace()) } - if ipath.Segments()[0] != "ipfs" && ipath.Segments()[0] != "ipld" { - return nil, fmt.Errorf("unsupported path namespace: %s", p.Namespace()) - } - - var dataFetcher fetcher.Factory - if ipath.Segments()[0] == "ipld" { - dataFetcher = api.ipldFetcherFactory - } else { - dataFetcher = api.unixFSFetcherFactory - } - resolver := ipfspathresolver.NewBasicResolver(dataFetcher) - - node, rest, err := resolver.ResolveToLastNode(ctx, ipath) + imPath, err := path.NewImmutablePath(p) if err != nil { - return nil, err + return path.ImmutablePath{}, nil, err } - root, err := cid.Parse(ipath.Segments()[1]) + node, remainder, err := resolver.ResolveToLastNode(ctx, imPath) if err != nil { - return nil, err + return path.ImmutablePath{}, nil, err } - return path.NewResolvedPath(ipath, node, root, gopath.Join(rest...)), nil + segments := []string{p.Namespace(), node.String()} + segments = append(segments, remainder...) + + p, err = path.NewPathFromSegments(segments...) + if err != nil { + return path.ImmutablePath{}, nil, err + } + + imPath, err = path.NewImmutablePath(p) + if err != nil { + return path.ImmutablePath{}, nil, err + } + + return imPath, remainder, nil } diff --git a/core/coreapi/pin.go b/core/coreapi/pin.go index 41327c8c3..22b3aa25c 100644 --- a/core/coreapi/pin.go +++ b/core/coreapi/pin.go @@ -3,18 +3,20 @@ package coreapi import ( "context" "fmt" + "strings" - bserv "github.com/ipfs/go-blockservice" + bserv "github.com/ipfs/boxo/blockservice" + offline "github.com/ipfs/boxo/exchange/offline" + "github.com/ipfs/boxo/ipld/merkledag" + "github.com/ipfs/boxo/path" + pin "github.com/ipfs/boxo/pinning/pinner" "github.com/ipfs/go-cid" - offline "github.com/ipfs/go-ipfs-exchange-offline" - pin "github.com/ipfs/go-ipfs-pinner" - "github.com/ipfs/go-merkledag" - coreiface "github.com/ipfs/interface-go-ipfs-core" - caopts "github.com/ipfs/interface-go-ipfs-core/options" - "github.com/ipfs/interface-go-ipfs-core/path" - "github.com/ipfs/kubo/tracing" + coreiface "github.com/ipfs/kubo/core/coreiface" + caopts "github.com/ipfs/kubo/core/coreiface/options" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" + + "github.com/ipfs/kubo/tracing" ) type PinAPI CoreAPI @@ -37,7 +39,7 @@ func (api *PinAPI) Add(ctx context.Context, p path.Path, opts ...caopts.PinAddOp defer api.blockstore.PinLock(ctx).Unlock(ctx) - err = api.pinning.Pin(ctx, dagNode, settings.Recursive) + err = api.pinning.Pin(ctx, dagNode, settings.Recursive, settings.Name) if err != nil { return fmt.Errorf("pin: %s", err) } @@ -66,14 +68,14 @@ func (api *PinAPI) Ls(ctx context.Context, opts ...caopts.PinLsOption) (<-chan c return nil, fmt.Errorf("invalid type '%s', must be one of {direct, indirect, recursive, all}", settings.Type) } - return api.pinLsAll(ctx, settings.Type), nil + return api.pinLsAll(ctx, settings.Type, settings.Detailed, settings.Name), nil } func (api *PinAPI) IsPinned(ctx context.Context, p path.Path, opts ...caopts.PinIsPinnedOption) (string, bool, error) { ctx, span := tracing.Span(ctx, "CoreAPI.PinAPI", "IsPinned", trace.WithAttributes(attribute.String("path", p.String()))) defer span.End() - resolved, err := api.core().ResolvePath(ctx, p) + resolved, _, err := api.core().ResolvePath(ctx, p) if err != nil { return "", false, fmt.Errorf("error resolving path: %s", err) } @@ -90,7 +92,7 @@ func (api *PinAPI) IsPinned(ctx context.Context, p path.Path, opts ...caopts.Pin return "", false, fmt.Errorf("invalid type '%s', must be one of {direct, indirect, recursive, all}", settings.WithType) } - return api.pinning.IsPinnedWithType(ctx, resolved.Cid(), mode) + return api.pinning.IsPinnedWithType(ctx, resolved.RootCid(), mode) } // Rm pin rm api @@ -98,7 +100,7 @@ func (api *PinAPI) Rm(ctx context.Context, p path.Path, opts ...caopts.PinRmOpti ctx, span := tracing.Span(ctx, "CoreAPI.PinAPI", "Rm", trace.WithAttributes(attribute.String("path", p.String()))) defer span.End() - rp, err := api.core().ResolvePath(ctx, p) + rp, _, err := api.core().ResolvePath(ctx, p) if err != nil { return err } @@ -114,7 +116,7 @@ func (api *PinAPI) Rm(ctx context.Context, p path.Path, opts ...caopts.PinRmOpti // to take a lock to prevent a concurrent garbage collection defer api.blockstore.PinLock(ctx).Unlock(ctx) - if err = api.pinning.Unpin(ctx, rp.Cid(), settings.Recursive); err != nil { + if err = api.pinning.Unpin(ctx, rp.RootCid(), settings.Recursive); err != nil { return err } @@ -135,19 +137,19 @@ func (api *PinAPI) Update(ctx context.Context, from path.Path, to path.Path, opt span.SetAttributes(attribute.Bool("unpin", settings.Unpin)) - fp, err := api.core().ResolvePath(ctx, from) + fp, _, err := api.core().ResolvePath(ctx, from) if err != nil { return err } - tp, err := api.core().ResolvePath(ctx, to) + tp, _, err := api.core().ResolvePath(ctx, to) if err != nil { return err } defer api.blockstore.PinLock(ctx).Unlock(ctx) - err = api.pinning.Update(ctx, fp.Cid(), tp.Cid(), settings.Unpin) + err = api.pinning.Update(ctx, fp.RootCid(), tp.RootCid(), settings.Unpin) if err != nil { return err } @@ -156,6 +158,7 @@ func (api *PinAPI) Update(ctx context.Context, from path.Path, to path.Path, opt } type pinStatus struct { + err error cid cid.Cid ok bool badNodes []coreiface.BadPinNode @@ -163,7 +166,7 @@ type pinStatus struct { // BadNode is used in PinVerifyRes type badNode struct { - path path.Resolved + path path.ImmutablePath err error } @@ -175,7 +178,11 @@ func (s *pinStatus) BadNodes() []coreiface.BadPinNode { return s.badNodes } -func (n *badNode) Path() path.Resolved { +func (s *pinStatus) Err() error { + return s.err +} + +func (n *badNode) Path() path.ImmutablePath { return n.path } @@ -191,10 +198,6 @@ func (api *PinAPI) Verify(ctx context.Context) (<-chan coreiface.PinStatus, erro bs := api.blockstore DAG := merkledag.NewDAGService(bserv.New(bs, offline.Exchange(bs))) getLinks := merkledag.GetLinksWithDAG(DAG) - recPins, err := api.pinning.RecursiveKeys(ctx) - if err != nil { - return nil, err - } var checkPin func(root cid.Cid) *pinStatus checkPin = func(root cid.Cid) *pinStatus { @@ -208,7 +211,7 @@ func (api *PinAPI) Verify(ctx context.Context) (<-chan coreiface.PinStatus, erro links, err := getLinks(ctx, root) if err != nil { status := &pinStatus{ok: false, cid: root} - status.badNodes = []coreiface.BadPinNode{&badNode{path: path.IpldPath(root), err: err}} + status.badNodes = []coreiface.BadPinNode{&badNode{path: path.FromCid(root), err: err}} visited[root] = status return status } @@ -229,8 +232,18 @@ func (api *PinAPI) Verify(ctx context.Context) (<-chan coreiface.PinStatus, erro out := make(chan coreiface.PinStatus) go func() { defer close(out) - for _, c := range recPins { - out <- checkPin(c) + for p := range api.pinning.RecursiveKeys(ctx, false) { + var res *pinStatus + if p.Err != nil { + res = &pinStatus{err: p.Err} + } else { + res = checkPin(p.Pin.Key) + } + select { + case <-ctx.Done(): + return + case out <- res: + } } }() @@ -239,11 +252,12 @@ func (api *PinAPI) Verify(ctx context.Context) (<-chan coreiface.PinStatus, erro type pinInfo struct { pinType string - path path.Resolved + path path.ImmutablePath + name string err error } -func (p *pinInfo) Path() path.Resolved { +func (p *pinInfo) Path() path.ImmutablePath { return p.path } @@ -251,6 +265,10 @@ func (p *pinInfo) Type() string { return p.pinType } +func (p *pinInfo) Name() string { + return p.name +} + func (p *pinInfo) Err() error { return p.err } @@ -259,101 +277,96 @@ func (p *pinInfo) Err() error { // // The caller must keep reading results until the channel is closed to prevent // leaking the goroutine that is fetching pins. -func (api *PinAPI) pinLsAll(ctx context.Context, typeStr string) <-chan coreiface.Pin { +func (api *PinAPI) pinLsAll(ctx context.Context, typeStr string, detailed bool, name string) <-chan coreiface.Pin { out := make(chan coreiface.Pin, 1) - keys := cid.NewSet() + emittedSet := cid.NewSet() - AddToResultKeys := func(keyList []cid.Cid, typeStr string) error { - for _, c := range keyList { - if keys.Visit(c) { - select { - case out <- &pinInfo{ - pinType: typeStr, - path: path.IpldPath(c), - }: - case <-ctx.Done(): - return ctx.Err() - } + AddToResultKeys := func(c cid.Cid, pinName, typeStr string) error { + if emittedSet.Visit(c) && (name == "" || strings.Contains(pinName, name)) { + select { + case out <- &pinInfo{ + pinType: typeStr, + name: pinName, + path: path.FromCid(c), + }: + case <-ctx.Done(): + return ctx.Err() } } return nil } - VisitKeys := func(keyList []cid.Cid) { - for _, c := range keyList { - keys.Visit(c) - } - } - go func() { defer close(out) - var dkeys, rkeys []cid.Cid + var rkeys []cid.Cid var err error if typeStr == "recursive" || typeStr == "all" { - rkeys, err = api.pinning.RecursiveKeys(ctx) - if err != nil { - out <- &pinInfo{err: err} - return - } - if err = AddToResultKeys(rkeys, "recursive"); err != nil { - out <- &pinInfo{err: err} - return - } - } - if typeStr == "direct" || typeStr == "all" { - dkeys, err = api.pinning.DirectKeys(ctx) - if err != nil { - out <- &pinInfo{err: err} - return - } - if err = AddToResultKeys(dkeys, "direct"); err != nil { - out <- &pinInfo{err: err} - return - } - } - if typeStr == "all" { - set := cid.NewSet() - for _, k := range rkeys { - err = merkledag.Walk( - ctx, merkledag.GetLinksWithDAG(api.dag), k, - set.Visit, - merkledag.SkipRoot(), merkledag.Concurrent(), - ) - if err != nil { + for streamedCid := range api.pinning.RecursiveKeys(ctx, detailed) { + if streamedCid.Err != nil { + out <- &pinInfo{err: streamedCid.Err} + return + } + if err = AddToResultKeys(streamedCid.Pin.Key, streamedCid.Pin.Name, "recursive"); err != nil { out <- &pinInfo{err: err} return } + rkeys = append(rkeys, streamedCid.Pin.Key) } - if err = AddToResultKeys(set.Keys(), "indirect"); err != nil { - out <- &pinInfo{err: err} - return + } + if typeStr == "direct" || typeStr == "all" { + for streamedCid := range api.pinning.DirectKeys(ctx, detailed) { + if streamedCid.Err != nil { + out <- &pinInfo{err: streamedCid.Err} + return + } + if err = AddToResultKeys(streamedCid.Pin.Key, streamedCid.Pin.Name, "direct"); err != nil { + out <- &pinInfo{err: err} + return + } } } if typeStr == "indirect" { // We need to first visit the direct pins that have priority // without emitting them - dkeys, err = api.pinning.DirectKeys(ctx) - if err != nil { - out <- &pinInfo{err: err} - return + for streamedCid := range api.pinning.DirectKeys(ctx, detailed) { + if streamedCid.Err != nil { + out <- &pinInfo{err: streamedCid.Err} + return + } + emittedSet.Add(streamedCid.Pin.Key) } - VisitKeys(dkeys) - rkeys, err = api.pinning.RecursiveKeys(ctx) - if err != nil { - out <- &pinInfo{err: err} - return + for streamedCid := range api.pinning.RecursiveKeys(ctx, detailed) { + if streamedCid.Err != nil { + out <- &pinInfo{err: streamedCid.Err} + return + } + emittedSet.Add(streamedCid.Pin.Key) + rkeys = append(rkeys, streamedCid.Pin.Key) } - VisitKeys(rkeys) - - set := cid.NewSet() + } + if typeStr == "indirect" || typeStr == "all" { + walkingSet := cid.NewSet() for _, k := range rkeys { err = merkledag.Walk( ctx, merkledag.GetLinksWithDAG(api.dag), k, - set.Visit, + func(c cid.Cid) bool { + if !walkingSet.Visit(c) { + return false + } + if emittedSet.Has(c) { + return true // skipped + } + err := AddToResultKeys(c, "", "indirect") + if err != nil { + out <- &pinInfo{err: err} + return false + } + return true + }, merkledag.SkipRoot(), merkledag.Concurrent(), ) if err != nil { @@ -361,10 +374,6 @@ func (api *PinAPI) pinLsAll(ctx context.Context, typeStr string) <-chan coreifac return } } - if err = AddToResultKeys(set.Keys(), "indirect"); err != nil { - out <- &pinInfo{err: err} - return - } } }() diff --git a/core/coreapi/pubsub.go b/core/coreapi/pubsub.go index af29afecc..27c6813fe 100644 --- a/core/coreapi/pubsub.go +++ b/core/coreapi/pubsub.go @@ -4,8 +4,8 @@ import ( "context" "errors" - coreiface "github.com/ipfs/interface-go-ipfs-core" - caopts "github.com/ipfs/interface-go-ipfs-core/options" + coreiface "github.com/ipfs/kubo/core/coreiface" + caopts "github.com/ipfs/kubo/core/coreiface/options" "github.com/ipfs/kubo/tracing" pubsub "github.com/libp2p/go-libp2p-pubsub" peer "github.com/libp2p/go-libp2p/core/peer" diff --git a/core/coreapi/dht.go b/core/coreapi/routing.go similarity index 57% rename from core/coreapi/dht.go rename to core/coreapi/routing.go index 3691a16c6..fe273158e 100644 --- a/core/coreapi/dht.go +++ b/core/coreapi/routing.go @@ -2,17 +2,19 @@ package coreapi import ( "context" + "errors" "fmt" + "strings" - blockservice "github.com/ipfs/go-blockservice" + blockservice "github.com/ipfs/boxo/blockservice" + blockstore "github.com/ipfs/boxo/blockstore" + offline "github.com/ipfs/boxo/exchange/offline" + dag "github.com/ipfs/boxo/ipld/merkledag" + "github.com/ipfs/boxo/path" cid "github.com/ipfs/go-cid" cidutil "github.com/ipfs/go-cidutil" - blockstore "github.com/ipfs/go-ipfs-blockstore" - offline "github.com/ipfs/go-ipfs-exchange-offline" - dag "github.com/ipfs/go-merkledag" - coreiface "github.com/ipfs/interface-go-ipfs-core" - caopts "github.com/ipfs/interface-go-ipfs-core/options" - path "github.com/ipfs/interface-go-ipfs-core/path" + coreiface "github.com/ipfs/kubo/core/coreiface" + caopts "github.com/ipfs/kubo/core/coreiface/options" "github.com/ipfs/kubo/tracing" peer "github.com/libp2p/go-libp2p/core/peer" routing "github.com/libp2p/go-libp2p/core/routing" @@ -20,9 +22,56 @@ import ( "go.opentelemetry.io/otel/trace" ) -type DhtAPI CoreAPI +type RoutingAPI CoreAPI -func (api *DhtAPI) FindPeer(ctx context.Context, p peer.ID) (peer.AddrInfo, error) { +func (api *RoutingAPI) Get(ctx context.Context, key string) ([]byte, error) { + if !api.nd.IsOnline { + return nil, coreiface.ErrOffline + } + + dhtKey, err := normalizeKey(key) + if err != nil { + return nil, err + } + + return api.routing.GetValue(ctx, dhtKey) +} + +func (api *RoutingAPI) Put(ctx context.Context, key string, value []byte, opts ...caopts.RoutingPutOption) error { + options, err := caopts.RoutingPutOptions(opts...) + if err != nil { + return err + } + + err = api.checkOnline(options.AllowOffline) + if err != nil { + return err + } + + dhtKey, err := normalizeKey(key) + if err != nil { + return err + } + + return api.routing.PutValue(ctx, dhtKey, value) +} + +func normalizeKey(s string) (string, error) { + parts := strings.Split(s, "/") + if len(parts) != 3 || + parts[0] != "" || + !(parts[1] == "ipns" || parts[1] == "pk") { + return "", errors.New("invalid key") + } + + k, err := peer.Decode(parts[2]) + if err != nil { + return "", err + } + return strings.Join(append(parts[:2], string(k)), "/"), nil +} + +func (api *RoutingAPI) FindPeer(ctx context.Context, p peer.ID) (peer.AddrInfo, error) { ctx, span := tracing.Span(ctx, "CoreAPI.DhtAPI", "FindPeer", trace.WithAttributes(attribute.String("peer", p.String()))) defer span.End() err := api.checkOnline(false) @@ -38,11 +87,11 @@ func (api *DhtAPI) FindPeer(ctx context.Context, p peer.ID) (peer.AddrInfo, erro return pi, nil } -func (api *DhtAPI) FindProviders(ctx context.Context, p path.Path, opts ...caopts.DhtFindProvidersOption) (<-chan peer.AddrInfo, error) { +func (api *RoutingAPI) FindProviders(ctx context.Context, p path.Path, opts ...caopts.RoutingFindProvidersOption) (<-chan peer.AddrInfo, error) { ctx, span := tracing.Span(ctx, "CoreAPI.DhtAPI", "FindProviders", trace.WithAttributes(attribute.String("path", p.String()))) defer span.End() - settings, err := caopts.DhtFindProvidersOptions(opts...) + settings, err := caopts.RoutingFindProvidersOptions(opts...) if err != nil { return nil, err } @@ -53,7 +102,7 @@ func (api *DhtAPI) FindProviders(ctx context.Context, p path.Path, opts ...caopt return nil, err } - rp, err := api.core().ResolvePath(ctx, p) + rp, _, err := api.core().ResolvePath(ctx, p) if err != nil { return nil, err } @@ -63,15 +112,15 @@ func (api *DhtAPI) FindProviders(ctx context.Context, p path.Path, opts ...caopt return nil, fmt.Errorf("number of providers must be greater than 0") } - pchan := api.routing.FindProvidersAsync(ctx, rp.Cid(), numProviders) + pchan := api.routing.FindProvidersAsync(ctx, rp.RootCid(), numProviders) return pchan, nil } -func (api *DhtAPI) Provide(ctx context.Context, path path.Path, opts ...caopts.DhtProvideOption) error { +func (api *RoutingAPI) Provide(ctx context.Context, path path.Path, opts ...caopts.RoutingProvideOption) error { ctx, span := tracing.Span(ctx, "CoreAPI.DhtAPI", "Provide", trace.WithAttributes(attribute.String("path", path.String()))) defer span.End() - settings, err := caopts.DhtProvideOptions(opts...) + settings, err := caopts.RoutingProvideOptions(opts...) if err != nil { return err } @@ -82,12 +131,12 @@ func (api *DhtAPI) Provide(ctx context.Context, path path.Path, opts ...caopts.D return err } - rp, err := api.core().ResolvePath(ctx, path) + rp, _, err := api.core().ResolvePath(ctx, path) if err != nil { return err } - c := rp.Cid() + c := rp.RootCid() has, err := api.blockstore.Has(ctx, c) if err != nil { @@ -149,6 +198,6 @@ func provideKeysRec(ctx context.Context, r routing.Routing, bs blockstore.Blocks } } -func (api *DhtAPI) core() coreiface.CoreAPI { +func (api *RoutingAPI) core() coreiface.CoreAPI { return (*CoreAPI)(api) } diff --git a/core/coreapi/swarm.go b/core/coreapi/swarm.go index 2faa912d9..e5332a217 100644 --- a/core/coreapi/swarm.go +++ b/core/coreapi/swarm.go @@ -5,7 +5,7 @@ import ( "sort" "time" - coreiface "github.com/ipfs/interface-go-ipfs-core" + coreiface "github.com/ipfs/kubo/core/coreiface" "github.com/ipfs/kubo/tracing" inet "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/peer" @@ -29,8 +29,10 @@ type connInfo struct { } // tag used in the connection manager when explicitly connecting to a peer. -const connectionManagerTag = "user-connect" -const connectionManagerWeight = 100 +const ( + connectionManagerTag = "user-connect" + connectionManagerWeight = 100 +) func (api *SwarmAPI) Connect(ctx context.Context, pi peer.AddrInfo) error { ctx, span := tracing.Span(ctx, "CoreAPI.SwarmAPI", "Connect", trace.WithAttributes(attribute.String("peerid", pi.ID.String()))) diff --git a/core/coreapi/test/api_test.go b/core/coreapi/test/api_test.go index 012aa8f8e..d647a32c8 100644 --- a/core/coreapi/test/api_test.go +++ b/core/coreapi/test/api_test.go @@ -8,10 +8,10 @@ import ( "path/filepath" "testing" - "github.com/ipfs/go-filestore" - keystore "github.com/ipfs/go-ipfs-keystore" + "github.com/ipfs/boxo/bootstrap" + "github.com/ipfs/boxo/filestore" + keystore "github.com/ipfs/boxo/keystore" "github.com/ipfs/kubo/core" - "github.com/ipfs/kubo/core/bootstrap" "github.com/ipfs/kubo/core/coreapi" mock "github.com/ipfs/kubo/core/mock" "github.com/ipfs/kubo/core/node/libp2p" @@ -19,9 +19,9 @@ import ( "github.com/ipfs/go-datastore" syncds "github.com/ipfs/go-datastore/sync" - coreiface "github.com/ipfs/interface-go-ipfs-core" - "github.com/ipfs/interface-go-ipfs-core/tests" "github.com/ipfs/kubo/config" + coreiface "github.com/ipfs/kubo/core/coreiface" + "github.com/ipfs/kubo/core/coreiface/tests" "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/peer" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" @@ -31,7 +31,7 @@ const testPeerID = "QmTFauExutTsy4XP6JbMFcw2Wa9645HJt2bTqL6qYDCKfe" type NodeProvider struct{} -func (NodeProvider) MakeAPISwarm(ctx context.Context, fullIdentity bool, n int) ([]coreiface.CoreAPI, error) { +func (NodeProvider) MakeAPISwarm(t *testing.T, ctx context.Context, fullIdentity bool, online bool, n int) ([]coreiface.CoreAPI, error) { mn := mocknet.New() nodes := make([]*core.IpfsNode, n) @@ -56,7 +56,7 @@ func (NodeProvider) MakeAPISwarm(ctx context.Context, fullIdentity bool, n int) } ident = config.Identity{ - PeerID: id.Pretty(), + PeerID: id.String(), PrivKey: base64.StdEncoding.EncodeToString(kbytes), } } else { @@ -82,7 +82,7 @@ func (NodeProvider) MakeAPISwarm(ctx context.Context, fullIdentity bool, n int) Routing: libp2p.DHTServerOption, Repo: r, Host: mock.MockHostOption(mn), - Online: fullIdentity, + Online: online, ExtraOpts: map[string]bool{ "pubsub": true, }, @@ -102,15 +102,17 @@ func (NodeProvider) MakeAPISwarm(ctx context.Context, fullIdentity bool, n int) return nil, err } - bsinf := bootstrap.BootstrapConfigWithPeers( - []peer.AddrInfo{ - nodes[0].Peerstore.PeerInfo(nodes[0].Identity), - }, - ) + if online { + bsinf := bootstrap.BootstrapConfigWithPeers( + []peer.AddrInfo{ + nodes[0].Peerstore.PeerInfo(nodes[0].Identity), + }, + ) - for _, n := range nodes[1:] { - if err := n.Bootstrap(bsinf); err != nil { - return nil, err + for _, n := range nodes[1:] { + if err := n.Bootstrap(bsinf); err != nil { + return nil, err + } } } @@ -118,5 +120,5 @@ func (NodeProvider) MakeAPISwarm(ctx context.Context, fullIdentity bool, n int) } func TestIface(t *testing.T) { - tests.TestApi(&NodeProvider{})(t) + tests.TestApi(NodeProvider{})(t) } diff --git a/core/coreapi/test/path_test.go b/core/coreapi/test/path_test.go index bf26b39ce..692853a9a 100644 --- a/core/coreapi/test/path_test.go +++ b/core/coreapi/test/path_test.go @@ -6,11 +6,11 @@ import ( "testing" "time" - files "github.com/ipfs/go-ipfs-files" - "github.com/ipfs/go-merkledag" - uio "github.com/ipfs/go-unixfs/io" - "github.com/ipfs/interface-go-ipfs-core/options" - "github.com/ipfs/interface-go-ipfs-core/path" + "github.com/ipfs/boxo/files" + "github.com/ipfs/boxo/ipld/merkledag" + uio "github.com/ipfs/boxo/ipld/unixfs/io" + "github.com/ipfs/boxo/path" + "github.com/ipfs/kubo/core/coreiface/options" "github.com/ipld/go-ipld-prime" ) @@ -19,7 +19,7 @@ func TestPathUnixFSHAMTPartial(t *testing.T) { defer cancel() // Create a node - apis, err := NodeProvider{}.MakeAPISwarm(ctx, true, 1) + apis, err := NodeProvider{}.MakeAPISwarm(t, ctx, true, true, 1) if err != nil { t.Fatal(err) } @@ -45,7 +45,7 @@ func TestPathUnixFSHAMTPartial(t *testing.T) { } // Get the root of the directory - nd, err := a.Dag().Get(ctx, r.Cid()) + nd, err := a.Dag().Get(ctx, r.RootCid()) if err != nil { t.Fatal(err) } @@ -55,7 +55,7 @@ func TestPathUnixFSHAMTPartial(t *testing.T) { pbNode := nd.(*merkledag.ProtoNode) // Remove one of the sharded directory blocks - if err := a.Block().Rm(ctx, path.IpfsPath(pbNode.Links()[0].Cid)); err != nil { + if err := a.Block().Rm(ctx, path.FromCid(pbNode.Links()[0].Cid)); err != nil { t.Fatal(err) } @@ -67,7 +67,12 @@ func TestPathUnixFSHAMTPartial(t *testing.T) { // The node will go out to the (non-existent) network looking for the missing block. Make sure we're erroring // because we exceeded the timeout on our query timeoutCtx, timeoutCancel := context.WithTimeout(ctx, time.Second*1) - _, err := a.ResolveNode(timeoutCtx, path.Join(r, k)) + newPath, err := path.Join(r, k) + if err != nil { + t.Fatal(err) + } + + _, err = a.ResolveNode(timeoutCtx, newPath) if err != nil { if timeoutCtx.Err() == nil { t.Fatal(err) diff --git a/core/coreapi/unixfs.go b/core/coreapi/unixfs.go index 753a3d3a0..e175488f3 100644 --- a/core/coreapi/unixfs.go +++ b/core/coreapi/unixfs.go @@ -3,66 +3,42 @@ package coreapi import ( "context" "fmt" - "sync" - "github.com/ipfs/kubo/core" + blockservice "github.com/ipfs/boxo/blockservice" + bstore "github.com/ipfs/boxo/blockstore" + "github.com/ipfs/boxo/files" + filestore "github.com/ipfs/boxo/filestore" + merkledag "github.com/ipfs/boxo/ipld/merkledag" + dagtest "github.com/ipfs/boxo/ipld/merkledag/test" + ft "github.com/ipfs/boxo/ipld/unixfs" + unixfile "github.com/ipfs/boxo/ipld/unixfs/file" + uio "github.com/ipfs/boxo/ipld/unixfs/io" + "github.com/ipfs/boxo/mfs" + "github.com/ipfs/boxo/path" + cid "github.com/ipfs/go-cid" + cidutil "github.com/ipfs/go-cidutil" + ds "github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-datastore/sync" + ipld "github.com/ipfs/go-ipld-format" + coreiface "github.com/ipfs/kubo/core/coreiface" + options "github.com/ipfs/kubo/core/coreiface/options" + "github.com/ipfs/kubo/core/coreunix" "github.com/ipfs/kubo/tracing" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" - - "github.com/ipfs/kubo/core/coreunix" - - blockservice "github.com/ipfs/go-blockservice" - cid "github.com/ipfs/go-cid" - cidutil "github.com/ipfs/go-cidutil" - filestore "github.com/ipfs/go-filestore" - bstore "github.com/ipfs/go-ipfs-blockstore" - files "github.com/ipfs/go-ipfs-files" - ipld "github.com/ipfs/go-ipld-format" - merkledag "github.com/ipfs/go-merkledag" - dagtest "github.com/ipfs/go-merkledag/test" - mfs "github.com/ipfs/go-mfs" - ft "github.com/ipfs/go-unixfs" - unixfile "github.com/ipfs/go-unixfs/file" - uio "github.com/ipfs/go-unixfs/io" - coreiface "github.com/ipfs/interface-go-ipfs-core" - options "github.com/ipfs/interface-go-ipfs-core/options" - path "github.com/ipfs/interface-go-ipfs-core/path" ) type UnixfsAPI CoreAPI -var nilNode *core.IpfsNode -var once sync.Once - -func getOrCreateNilNode() (*core.IpfsNode, error) { - once.Do(func() { - if nilNode != nil { - return - } - node, err := core.NewNode(context.Background(), &core.BuildCfg{ - //TODO: need this to be true or all files - // hashed will be stored in memory! - NilRepo: true, - }) - if err != nil { - panic(err) - } - nilNode = node - }) - - return nilNode, nil -} - // Add builds a merkledag node from a reader, adds it to the blockstore, // and returns the key representing that node. -func (api *UnixfsAPI) Add(ctx context.Context, files files.Node, opts ...options.UnixfsAddOption) (path.Resolved, error) { +func (api *UnixfsAPI) Add(ctx context.Context, files files.Node, opts ...options.UnixfsAddOption) (path.ImmutablePath, error) { ctx, span := tracing.Span(ctx, "CoreAPI.UnixfsAPI", "Add") defer span.End() settings, prefix, err := options.UnixfsAddOptions(opts...) if err != nil { - return nil, err + return path.ImmutablePath{}, err } span.SetAttributes( @@ -83,7 +59,7 @@ func (api *UnixfsAPI) Add(ctx context.Context, files files.Node, opts ...options cfg, err := api.repo.Config() if err != nil { - return nil, err + return path.ImmutablePath{}, err } // check if repo will exceed storage limit if added @@ -95,7 +71,7 @@ func (api *UnixfsAPI) Add(ctx context.Context, files files.Node, opts ...options //} if settings.NoCopy && !(cfg.Experimental.FilestoreEnabled || cfg.Experimental.UrlstoreEnabled) { - return nil, fmt.Errorf("either the filestore or the urlstore must be enabled to use nocopy, see: https://github.com/ipfs/kubo/blob/master/docs/experimental-features.md#ipfs-filestore") + return path.ImmutablePath{}, fmt.Errorf("either the filestore or the urlstore must be enabled to use nocopy, see: https://github.com/ipfs/kubo/blob/master/docs/experimental-features.md#ipfs-filestore") } addblockstore := api.blockstore @@ -106,13 +82,12 @@ func (api *UnixfsAPI) Add(ctx context.Context, files files.Node, opts ...options pinning := api.pinning if settings.OnlyHash { - node, err := getOrCreateNilNode() - if err != nil { - return nil, err - } - addblockstore = node.Blockstore - exch = node.Exchange - pinning = node.Pinning + // setup a /dev/null pipeline to simulate adding the data + dstore := dssync.MutexWrap(ds.NewNullDatastore()) + bs := bstore.NewBlockstore(dstore, bstore.WriteThrough()) + addblockstore = bstore.NewGCBlockstore(bs, nil) // gclocker will never be used + exch = nil // exchange will never be used + pinning = nil // pinner will never be used } bserv := blockservice.New(addblockstore, exch) // hash security 001 @@ -131,18 +106,18 @@ func (api *UnixfsAPI) Add(ctx context.Context, files files.Node, opts ...options syncDserv = &syncDagService{ DAGService: dserv, syncFn: func() error { - ds := api.repo.Datastore() - if err := ds.Sync(ctx, bstore.BlockPrefix); err != nil { + rds := api.repo.Datastore() + if err := rds.Sync(ctx, bstore.BlockPrefix); err != nil { return err } - return ds.Sync(ctx, filestore.FilestorePrefix) + return rds.Sync(ctx, filestore.FilestorePrefix) }, } } fileAdder, err := coreunix.NewAdder(ctx, pinning, addblockstore, syncDserv) if err != nil { - return nil, err + return path.ImmutablePath{}, err } fileAdder.Chunker = settings.Chunker @@ -155,6 +130,10 @@ func (api *UnixfsAPI) Add(ctx context.Context, files files.Node, opts ...options fileAdder.RawLeaves = settings.RawLeaves fileAdder.NoCopy = settings.NoCopy fileAdder.CidBuilder = prefix + fileAdder.PreserveMode = settings.PreserveMode + fileAdder.PreserveMtime = settings.PreserveMtime + fileAdder.FileMode = settings.Mode + fileAdder.FileMtime = settings.Mtime switch settings.Layout { case options.BalancedLayout: @@ -162,7 +141,7 @@ func (api *UnixfsAPI) Add(ctx context.Context, files files.Node, opts ...options case options.TrickleLayout: fileAdder.Trickle = true default: - return nil, fmt.Errorf("unknown layout: %d", settings.Layout) + return path.ImmutablePath{}, fmt.Errorf("unknown layout: %d", settings.Layout) } if settings.Inline { @@ -178,11 +157,11 @@ func (api *UnixfsAPI) Add(ctx context.Context, files files.Node, opts ...options // Use the same prefix for the "empty" MFS root as for the file adder. err := emptyDirNode.SetCidBuilder(fileAdder.CidBuilder) if err != nil { - return nil, err + return path.ImmutablePath{}, err } mr, err := mfs.NewRoot(ctx, md, emptyDirNode, nil) if err != nil { - return nil, err + return path.ImmutablePath{}, err } fileAdder.SetMfsRoot(mr) @@ -190,16 +169,16 @@ func (api *UnixfsAPI) Add(ctx context.Context, files files.Node, opts ...options nd, err := fileAdder.AddAllAndPin(ctx, files) if err != nil { - return nil, err + return path.ImmutablePath{}, err } if !settings.OnlyHash { if err := api.provider.Provide(nd.Cid()); err != nil { - return nil, err + return path.ImmutablePath{}, err } } - return path.IpfsPath(nd.Cid()), nil + return path.FromCid(nd.Cid()), nil } func (api *UnixfsAPI) Get(ctx context.Context, p path.Path) (files.Node, error) { @@ -253,7 +232,6 @@ func (api *UnixfsAPI) processLink(ctx context.Context, linkres ft.LinkResult, se defer span.End() if linkres.Link != nil { span.SetAttributes(attribute.String("linkname", linkres.Link.Name), attribute.String("cid", linkres.Link.Cid.String())) - } if linkres.Err != nil { @@ -296,6 +274,8 @@ func (api *UnixfsAPI) processLink(ctx context.Context, linkres ft.LinkResult, se if !settings.UseCumulativeSize { lnk.Size = d.FileSize() } + lnk.Mode = d.Mode() + lnk.ModTime = d.ModTime() } } @@ -314,7 +294,7 @@ func (api *UnixfsAPI) lsFromLinksAsync(ctx context.Context, dir uio.Directory, s defer close(out) for l := range dir.EnumLinksAsync(ctx) { select { - case out <- api.processLink(ctx, l, settings): //TODO: perf: processing can be done in background and in parallel + case out <- api.processLink(ctx, l, settings): // TODO: perf: processing can be done in background and in parallel case <-ctx.Done(): return } @@ -329,7 +309,7 @@ func (api *UnixfsAPI) lsFromLinks(ctx context.Context, ndlinks []*ipld.Link, set for _, l := range ndlinks { lr := ft.LinkResult{Link: &ipld.Link{Name: l.Name, Size: l.Size, Cid: l.Cid}} - links <- api.processLink(ctx, lr, settings) //TODO: can be parallel if settings.Async + links <- api.processLink(ctx, lr, settings) // TODO: can be parallel if settings.Async } close(links) return links, nil diff --git a/core/corehttp/commands.go b/core/corehttp/commands.go index c0ebf5506..14de87d08 100644 --- a/core/corehttp/commands.go +++ b/core/corehttp/commands.go @@ -9,23 +9,21 @@ import ( "strconv" "strings" - version "github.com/ipfs/kubo" - oldcmds "github.com/ipfs/kubo/commands" - "github.com/ipfs/kubo/core" - corecommands "github.com/ipfs/kubo/core/commands" - cmds "github.com/ipfs/go-ipfs-cmds" cmdsHttp "github.com/ipfs/go-ipfs-cmds/http" - path "github.com/ipfs/go-path" + version "github.com/ipfs/kubo" + oldcmds "github.com/ipfs/kubo/commands" config "github.com/ipfs/kubo/config" + "github.com/ipfs/kubo/core" + corecommands "github.com/ipfs/kubo/core/commands" + "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" ) -var ( - errAPIVersionMismatch = errors.New("api version mismatch") -) +var errAPIVersionMismatch = errors.New("api version mismatch") -const originEnvKey = "API_ORIGIN" -const originEnvKeyDeprecate = `You are using the ` + originEnvKey + `ENV Variable. +const ( + originEnvKey = "API_ORIGIN" + originEnvKeyDeprecate = `You are using the ` + originEnvKey + `ENV Variable. This functionality is deprecated, and will be removed in future versions. Instead, try either adding headers to the config, or passing them via cli arguments: @@ -33,6 +31,7 @@ cli arguments: ipfs config API.HTTPHeaders --json '{"Access-Control-Allow-Origin": ["*"]}' ipfs daemon ` +) // APIPath is the path at which the API is mounted. const APIPath = "/api/v0" @@ -100,7 +99,6 @@ func addCORSDefaults(c *cmdsHttp.ServerConfig) { } func patchCORSVars(c *cmdsHttp.ServerConfig, addr net.Addr) { - // we have to grab the port from an addr, which may be an ip6 addr. // TODO: this should take multiaddrs and derive port from there. port := "" @@ -123,17 +121,13 @@ func patchCORSVars(c *cmdsHttp.ServerConfig, addr net.Addr) { c.SetAllowedOrigins(newOrigins...) } -func commandsOption(cctx oldcmds.Context, command *cmds.Command, allowGet bool) ServeOption { +func commandsOption(cctx oldcmds.Context, command *cmds.Command) ServeOption { return func(n *core.IpfsNode, l net.Listener, mux *http.ServeMux) (*http.ServeMux, error) { - cfg := cmdsHttp.NewServerConfig() - cfg.AllowGet = allowGet - corsAllowedMethods := []string{http.MethodPost} - if allowGet { - corsAllowedMethods = append(corsAllowedMethods, http.MethodGet) - } - cfg.SetAllowedMethods(corsAllowedMethods...) + cfg.AddAllowedHeaders("Origin", "Accept", "Content-Type", "X-Requested-With") + cfg.SetAllowedMethods(http.MethodPost) + cfg.APIPath = APIPath rcfg, err := n.Repo.Config() if err != nil { @@ -146,33 +140,79 @@ func commandsOption(cctx oldcmds.Context, command *cmds.Command, allowGet bool) patchCORSVars(cfg, l.Addr()) cmdHandler := cmdsHttp.NewHandler(&cctx, command, cfg) + + if len(rcfg.API.Authorizations) > 0 { + authorizations := convertAuthorizationsMap(rcfg.API.Authorizations) + cmdHandler = withAuthSecrets(authorizations, cmdHandler) + } + + cmdHandler = otelhttp.NewHandler(cmdHandler, "corehttp.cmdsHandler") mux.Handle(APIPath+"/", cmdHandler) return mux, nil } } +type rpcAuthScopeWithUser struct { + config.RPCAuthScope + User string +} + +func convertAuthorizationsMap(authScopes map[string]*config.RPCAuthScope) map[string]rpcAuthScopeWithUser { + // authorizations is a map where we can just check for the header value to match. + authorizations := map[string]rpcAuthScopeWithUser{} + for user, authScope := range authScopes { + expectedHeader := config.ConvertAuthSecret(authScope.AuthSecret) + if expectedHeader != "" { + authorizations[expectedHeader] = rpcAuthScopeWithUser{ + RPCAuthScope: *authScopes[user], + User: user, + } + } + } + + return authorizations +} + +func withAuthSecrets(authorizations map[string]rpcAuthScopeWithUser, next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + authorizationHeader := r.Header.Get("Authorization") + auth, ok := authorizations[authorizationHeader] + + if ok { + // version check is implicitly allowed + if r.URL.Path == "/api/v0/version" { + next.ServeHTTP(w, r) + return + } + // everything else has to be safelisted via AllowedPaths + for _, prefix := range auth.AllowedPaths { + if strings.HasPrefix(r.URL.Path, prefix) { + next.ServeHTTP(w, r) + return + } + } + } + + http.Error(w, "Kubo RPC Access Denied: Please provide a valid authorization token as defined in the API.Authorizations configuration.", http.StatusForbidden) + }) +} + // CommandsOption constructs a ServerOption for hooking the commands into the // HTTP server. It will NOT allow GET requests. func CommandsOption(cctx oldcmds.Context) ServeOption { - return commandsOption(cctx, corecommands.Root, false) -} - -// CommandsROOption constructs a ServerOption for hooking the read-only commands -// into the HTTP server. It will allow GET requests. -func CommandsROOption(cctx oldcmds.Context) ServeOption { - return commandsOption(cctx, corecommands.RootRO, true) + return commandsOption(cctx, corecommands.Root) } // CheckVersionOption returns a ServeOption that checks whether the client ipfs version matches. Does nothing when the user agent string does not contain `/kubo/` or `/go-ipfs/` func CheckVersionOption() ServeOption { daemonVersion := version.ApiVersion - return ServeOption(func(n *core.IpfsNode, l net.Listener, parent *http.ServeMux) (*http.ServeMux, error) { + return func(n *core.IpfsNode, l net.Listener, parent *http.ServeMux) (*http.ServeMux, error) { mux := http.NewServeMux() parent.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { if strings.HasPrefix(r.URL.Path, APIPath) { cmdqry := r.URL.Path[len(APIPath):] - pth := path.SplitList(cmdqry) + pth := strings.Split(cmdqry, "/") // backwards compatibility to previous version check if len(pth) >= 2 && pth[1] != "version" { @@ -189,5 +229,5 @@ func CheckVersionOption() ServeOption { }) return mux, nil - }) + } } diff --git a/core/corehttp/corehttp.go b/core/corehttp/corehttp.go index fe9f1b1db..6a9f43b51 100644 --- a/core/corehttp/corehttp.go +++ b/core/corehttp/corehttp.go @@ -31,9 +31,9 @@ const shutdownTimeout = 30 * time.Second // initially passed in if not. type ServeOption func(*core.IpfsNode, net.Listener, *http.ServeMux) (*http.ServeMux, error) -// makeHandler turns a list of ServeOptions into a http.Handler that implements +// MakeHandler turns a list of ServeOptions into a http.Handler that implements // all of the given options, in order. -func makeHandler(n *core.IpfsNode, l net.Listener, options ...ServeOption) (http.Handler, error) { +func MakeHandler(n *core.IpfsNode, l net.Listener, options ...ServeOption) (http.Handler, error) { topMux := http.NewServeMux() mux := topMux for _, option := range options { @@ -75,7 +75,7 @@ func ListenAndServe(n *core.IpfsNode, listeningMultiAddr string, options ...Serv // we might have listened to /tcp/0 - let's see what we are listing on addr = list.Multiaddr() - fmt.Printf("API server listening on %s\n", addr) + fmt.Printf("RPC API server listening on %s\n", addr) return Serve(n, manet.NetListener(list), options...) } @@ -86,7 +86,7 @@ func Serve(node *core.IpfsNode, lis net.Listener, options ...ServeOption) error // make sure we close this no matter what. defer lis.Close() - handler, err := makeHandler(node, lis, options...) + handler, err := MakeHandler(node, lis, options...) if err != nil { return err } diff --git a/core/corehttp/gateway.go b/core/corehttp/gateway.go index 334000b5a..6ac381885 100644 --- a/core/corehttp/gateway.go +++ b/core/corehttp/gateway.go @@ -2,141 +2,76 @@ package corehttp import ( "context" + "errors" "fmt" + "io" "net" "net/http" - "sort" + "time" - coreiface "github.com/ipfs/interface-go-ipfs-core" - options "github.com/ipfs/interface-go-ipfs-core/options" - path "github.com/ipfs/interface-go-ipfs-core/path" + "github.com/ipfs/boxo/blockservice" + "github.com/ipfs/boxo/exchange/offline" + "github.com/ipfs/boxo/files" + "github.com/ipfs/boxo/gateway" + "github.com/ipfs/boxo/namesys" + "github.com/ipfs/boxo/path" + offlineroute "github.com/ipfs/boxo/routing/offline" + "github.com/ipfs/go-cid" version "github.com/ipfs/kubo" - core "github.com/ipfs/kubo/core" - coreapi "github.com/ipfs/kubo/core/coreapi" - id "github.com/libp2p/go-libp2p/p2p/protocol/identify" + "github.com/ipfs/kubo/config" + "github.com/ipfs/kubo/core" + iface "github.com/ipfs/kubo/core/coreiface" + "github.com/ipfs/kubo/core/node" + "github.com/libp2p/go-libp2p/core/routing" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" ) -type GatewayConfig struct { - Headers map[string][]string - Writable bool -} - -// NodeAPI defines the minimal set of API services required by a gateway handler -type NodeAPI interface { - // Unixfs returns an implementation of Unixfs API - Unixfs() coreiface.UnixfsAPI - - // Block returns an implementation of Block API - Block() coreiface.BlockAPI - - // Dag returns an implementation of Dag API - Dag() coreiface.APIDagService - - // ResolvePath resolves the path using Unixfs resolver - ResolvePath(context.Context, path.Path) (path.Resolved, error) -} - -// A helper function to clean up a set of headers: -// 1. Canonicalizes. -// 2. Deduplicates. -// 3. Sorts. -func cleanHeaderSet(headers []string) []string { - // Deduplicate and canonicalize. - m := make(map[string]struct{}, len(headers)) - for _, h := range headers { - m[http.CanonicalHeaderKey(h)] = struct{}{} - } - result := make([]string, 0, len(m)) - for k := range m { - result = append(result, k) - } - - // Sort - sort.Strings(result) - return result -} - -func GatewayOption(writable bool, paths ...string) ServeOption { +func GatewayOption(paths ...string) ServeOption { return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) { - cfg, err := n.Repo.Config() + config, headers, err := getGatewayConfig(n) if err != nil { return nil, err } - api, err := coreapi.NewCoreAPI(n, options.Api.FetchBlocks(!cfg.Gateway.NoFetch)) + backend, err := newGatewayBackend(n) if err != nil { return nil, err } - headers := make(map[string][]string, len(cfg.Gateway.HTTPHeaders)) - for h, v := range cfg.Gateway.HTTPHeaders { - headers[http.CanonicalHeaderKey(h)] = v - } - - AddAccessControlHeaders(headers) - - offlineAPI, err := api.WithOptions(options.Api.Offline(true)) - if err != nil { - return nil, err - } - - gateway := NewGatewayHandler(GatewayConfig{ - Headers: headers, - Writable: writable, - }, api, offlineAPI) - - gateway = otelhttp.NewHandler(gateway, "Gateway.Request") + handler := gateway.NewHandler(config, backend) + handler = gateway.NewHeaders(headers).ApplyCors().Wrap(handler) + handler = otelhttp.NewHandler(handler, "Gateway") for _, p := range paths { - mux.Handle(p+"/", gateway) + mux.Handle(p+"/", handler) } + return mux, nil } } -// AddAccessControlHeaders adds default headers used for controlling -// cross-origin requests. This function adds several values to the -// Access-Control-Allow-Headers and Access-Control-Expose-Headers entries. -// If the Access-Control-Allow-Origin entry is missing a value of '*' is -// added, indicating that browsers should allow requesting code from any -// origin to access the resource. -// If the Access-Control-Allow-Methods entry is missing a value of 'GET' is -// added, indicating that browsers may use the GET method when issuing cross -// origin requests. -func AddAccessControlHeaders(headers map[string][]string) { - // Hard-coded headers. - const ACAHeadersName = "Access-Control-Allow-Headers" - const ACEHeadersName = "Access-Control-Expose-Headers" - const ACAOriginName = "Access-Control-Allow-Origin" - const ACAMethodsName = "Access-Control-Allow-Methods" +func HostnameOption() ServeOption { + return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) { + config, headers, err := getGatewayConfig(n) + if err != nil { + return nil, err + } - if _, ok := headers[ACAOriginName]; !ok { - // Default to *all* - headers[ACAOriginName] = []string{"*"} + backend, err := newGatewayBackend(n) + if err != nil { + return nil, err + } + + childMux := http.NewServeMux() + + var handler http.Handler + handler = gateway.NewHostnameHandler(config, backend, childMux) + handler = gateway.NewHeaders(headers).ApplyCors().Wrap(handler) + handler = otelhttp.NewHandler(handler, "HostnameGateway") + + mux.Handle("/", handler) + return childMux, nil } - if _, ok := headers[ACAMethodsName]; !ok { - // Default to GET - headers[ACAMethodsName] = []string{http.MethodGet} - } - - headers[ACAHeadersName] = cleanHeaderSet( - append([]string{ - "Content-Type", - "User-Agent", - "Range", - "X-Requested-With", - }, headers[ACAHeadersName]...)) - - headers[ACEHeadersName] = cleanHeaderSet( - append([]string{ - "Content-Length", - "Content-Range", - "X-Chunked-Output", - "X-Stream-Output", - "X-Ipfs-Path", - "X-Ipfs-Roots", - }, headers[ACEHeadersName]...)) } func VersionOption() ServeOption { @@ -144,8 +79,209 @@ func VersionOption() ServeOption { mux.HandleFunc("/version", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Commit: %s\n", version.CurrentCommit) fmt.Fprintf(w, "Client Version: %s\n", version.GetUserAgentVersion()) - fmt.Fprintf(w, "Protocol Version: %s\n", id.DefaultProtocolVersion) }) return mux, nil } } + +func Libp2pGatewayOption() ServeOption { + return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) { + bserv := blockservice.New(n.Blocks.Blockstore(), offline.Exchange(n.Blocks.Blockstore())) + + backend, err := gateway.NewBlocksBackend(bserv, + // GatewayOverLibp2p only returns things that are in local blockstore + // (same as Gateway.NoFetch=true), we have to pass offline path resolver + gateway.WithResolver(n.OfflineUnixFSPathResolver), + ) + if err != nil { + return nil, err + } + + gwConfig := gateway.Config{ + DeserializedResponses: false, + NoDNSLink: true, + PublicGateways: nil, + Menu: nil, + } + + handler := gateway.NewHandler(gwConfig, &offlineGatewayErrWrapper{gwimpl: backend}) + handler = otelhttp.NewHandler(handler, "Libp2p-Gateway") + + mux.Handle("/ipfs/", handler) + + return mux, nil + } +} + +func newGatewayBackend(n *core.IpfsNode) (gateway.IPFSBackend, error) { + cfg, err := n.Repo.Config() + if err != nil { + return nil, err + } + + bserv := n.Blocks + var vsRouting routing.ValueStore = n.Routing + nsys := n.Namesys + pathResolver := n.UnixFSPathResolver + + if cfg.Gateway.NoFetch { + bserv = blockservice.New(bserv.Blockstore(), offline.Exchange(bserv.Blockstore())) + + cs := cfg.Ipns.ResolveCacheSize + if cs == 0 { + cs = node.DefaultIpnsCacheSize + } + if cs < 0 { + return nil, fmt.Errorf("cannot specify negative resolve cache size") + } + + nsOptions := []namesys.Option{ + namesys.WithDatastore(n.Repo.Datastore()), + namesys.WithDNSResolver(n.DNSResolver), + namesys.WithCache(cs), + namesys.WithMaxCacheTTL(cfg.Ipns.MaxCacheTTL.WithDefault(config.DefaultIpnsMaxCacheTTL)), + } + + vsRouting = offlineroute.NewOfflineRouter(n.Repo.Datastore(), n.RecordValidator) + nsys, err = namesys.NewNameSystem(vsRouting, nsOptions...) + if err != nil { + return nil, fmt.Errorf("error constructing namesys: %w", err) + } + + // Gateway.NoFetch=true requires offline path resolver + // to avoid fetching missing blocks during path traversal + pathResolver = n.OfflineUnixFSPathResolver + } + + backend, err := gateway.NewBlocksBackend(bserv, + gateway.WithValueStore(vsRouting), + gateway.WithNameSystem(nsys), + gateway.WithResolver(pathResolver), + ) + if err != nil { + return nil, err + } + return &offlineGatewayErrWrapper{gwimpl: backend}, nil +} + +type offlineGatewayErrWrapper struct { + gwimpl gateway.IPFSBackend +} + +func offlineErrWrap(err error) error { + if errors.Is(err, iface.ErrOffline) { + return fmt.Errorf("%s : %w", err.Error(), gateway.ErrServiceUnavailable) + } + return err +} + +func (o *offlineGatewayErrWrapper) Get(ctx context.Context, path path.ImmutablePath, ranges ...gateway.ByteRange) (gateway.ContentPathMetadata, *gateway.GetResponse, error) { + md, n, err := o.gwimpl.Get(ctx, path, ranges...) + err = offlineErrWrap(err) + return md, n, err +} + +func (o *offlineGatewayErrWrapper) GetAll(ctx context.Context, path path.ImmutablePath) (gateway.ContentPathMetadata, files.Node, error) { + md, n, err := o.gwimpl.GetAll(ctx, path) + err = offlineErrWrap(err) + return md, n, err +} + +func (o *offlineGatewayErrWrapper) GetBlock(ctx context.Context, path path.ImmutablePath) (gateway.ContentPathMetadata, files.File, error) { + md, n, err := o.gwimpl.GetBlock(ctx, path) + err = offlineErrWrap(err) + return md, n, err +} + +func (o *offlineGatewayErrWrapper) Head(ctx context.Context, path path.ImmutablePath) (gateway.ContentPathMetadata, *gateway.HeadResponse, error) { + md, n, err := o.gwimpl.Head(ctx, path) + err = offlineErrWrap(err) + return md, n, err +} + +func (o *offlineGatewayErrWrapper) ResolvePath(ctx context.Context, path path.ImmutablePath) (gateway.ContentPathMetadata, error) { + md, err := o.gwimpl.ResolvePath(ctx, path) + err = offlineErrWrap(err) + return md, err +} + +func (o *offlineGatewayErrWrapper) GetCAR(ctx context.Context, path path.ImmutablePath, params gateway.CarParams) (gateway.ContentPathMetadata, io.ReadCloser, error) { + md, data, err := o.gwimpl.GetCAR(ctx, path, params) + err = offlineErrWrap(err) + return md, data, err +} + +func (o *offlineGatewayErrWrapper) IsCached(ctx context.Context, path path.Path) bool { + return o.gwimpl.IsCached(ctx, path) +} + +func (o *offlineGatewayErrWrapper) GetIPNSRecord(ctx context.Context, c cid.Cid) ([]byte, error) { + rec, err := o.gwimpl.GetIPNSRecord(ctx, c) + err = offlineErrWrap(err) + return rec, err +} + +func (o *offlineGatewayErrWrapper) ResolveMutable(ctx context.Context, path path.Path) (path.ImmutablePath, time.Duration, time.Time, error) { + imPath, ttl, lastMod, err := o.gwimpl.ResolveMutable(ctx, path) + err = offlineErrWrap(err) + return imPath, ttl, lastMod, err +} + +func (o *offlineGatewayErrWrapper) GetDNSLinkRecord(ctx context.Context, s string) (path.Path, error) { + p, err := o.gwimpl.GetDNSLinkRecord(ctx, s) + err = offlineErrWrap(err) + return p, err +} + +var _ gateway.IPFSBackend = (*offlineGatewayErrWrapper)(nil) + +var defaultPaths = []string{"/ipfs/", "/ipns/", "/p2p/"} + +var subdomainGatewaySpec = &gateway.PublicGateway{ + Paths: defaultPaths, + UseSubdomains: true, +} + +var defaultKnownGateways = map[string]*gateway.PublicGateway{ + "localhost": subdomainGatewaySpec, +} + +func getGatewayConfig(n *core.IpfsNode) (gateway.Config, map[string][]string, error) { + cfg, err := n.Repo.Config() + if err != nil { + return gateway.Config{}, nil, err + } + + // Initialize gateway configuration, with empty PublicGateways, handled after. + gwCfg := gateway.Config{ + DeserializedResponses: cfg.Gateway.DeserializedResponses.WithDefault(config.DefaultDeserializedResponses), + DisableHTMLErrors: cfg.Gateway.DisableHTMLErrors.WithDefault(config.DefaultDisableHTMLErrors), + NoDNSLink: cfg.Gateway.NoDNSLink, + PublicGateways: map[string]*gateway.PublicGateway{}, + } + + // Add default implicit known gateways, such as subdomain gateway on localhost. + for hostname, gw := range defaultKnownGateways { + gwCfg.PublicGateways[hostname] = gw + } + + // Apply values from cfg.Gateway.PublicGateways if they exist. + for hostname, gw := range cfg.Gateway.PublicGateways { + if gw == nil { + // Remove any implicit defaults, if present. This is useful when one + // wants to disable subdomain gateway on localhost, etc. + delete(gwCfg.PublicGateways, hostname) + continue + } + + gwCfg.PublicGateways[hostname] = &gateway.PublicGateway{ + Paths: gw.Paths, + NoDNSLink: gw.NoDNSLink, + UseSubdomains: gw.UseSubdomains, + InlineDNSLink: gw.InlineDNSLink.WithDefault(config.DefaultInlineDNSLink), + DeserializedResponses: gw.DeserializedResponses.WithDefault(gwCfg.DeserializedResponses), + } + } + + return gwCfg, cfg.Gateway.HTTPHeaders, nil +} diff --git a/core/corehttp/gateway_handler.go b/core/corehttp/gateway_handler.go deleted file mode 100644 index 1222b17bc..000000000 --- a/core/corehttp/gateway_handler.go +++ /dev/null @@ -1,1106 +0,0 @@ -package corehttp - -import ( - "context" - "fmt" - "html/template" - "io" - "mime" - "net/http" - "net/textproto" - "net/url" - "os" - gopath "path" - "regexp" - "runtime/debug" - "strings" - "time" - - cid "github.com/ipfs/go-cid" - files "github.com/ipfs/go-ipfs-files" - ipld "github.com/ipfs/go-ipld-format" - dag "github.com/ipfs/go-merkledag" - mfs "github.com/ipfs/go-mfs" - path "github.com/ipfs/go-path" - "github.com/ipfs/go-path/resolver" - coreiface "github.com/ipfs/interface-go-ipfs-core" - ipath "github.com/ipfs/interface-go-ipfs-core/path" - routing "github.com/libp2p/go-libp2p/core/routing" - mc "github.com/multiformats/go-multicodec" - prometheus "github.com/prometheus/client_golang/prometheus" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/trace" - "go.uber.org/zap" -) - -const ( - ipfsPathPrefix = "/ipfs/" - ipnsPathPrefix = "/ipns/" - immutableCacheControl = "public, max-age=29030400, immutable" -) - -var ( - onlyASCII = regexp.MustCompile("[[:^ascii:]]") - noModtime = time.Unix(0, 0) // disables Last-Modified header if passed as modtime -) - -// HTML-based redirect for errors which can be recovered from, but we want -// to provide hint to people that they should fix things on their end. -var redirectTemplate = template.Must(template.New("redirect").Parse(` - - - - - - - -
{{.ErrorMsg}}
(if a redirect does not happen in 10 seconds, use "{{.SuggestedPath}}" instead)
- -`)) - -type redirectTemplateData struct { - RedirectURL string - SuggestedPath string - ErrorMsg string -} - -// gatewayHandler is a HTTP handler that serves IPFS objects (accessible by default at /ipfs/) -// (it serves requests like GET /ipfs/QmVRzPKPzNtSrEzBFm2UZfxmPAgnaLke4DMcerbsGGSaFe/link) -type gatewayHandler struct { - config GatewayConfig - api NodeAPI - offlineAPI NodeAPI - - // generic metrics - firstContentBlockGetMetric *prometheus.HistogramVec - unixfsGetMetric *prometheus.SummaryVec // deprecated, use firstContentBlockGetMetric - - // response type metrics - unixfsFileGetMetric *prometheus.HistogramVec - unixfsGenDirGetMetric *prometheus.HistogramVec - carStreamGetMetric *prometheus.HistogramVec - rawBlockGetMetric *prometheus.HistogramVec -} - -// StatusResponseWriter enables us to override HTTP Status Code passed to -// WriteHeader function inside of http.ServeContent. Decision is based on -// presence of HTTP Headers such as Location. -type statusResponseWriter struct { - http.ResponseWriter -} - -// Custom type for collecting error details to be handled by `webRequestError` -type requestError struct { - Message string - StatusCode int - Err error -} - -func (r *requestError) Error() string { - return r.Err.Error() -} - -func newRequestError(message string, err error, statusCode int) *requestError { - return &requestError{ - Message: message, - Err: err, - StatusCode: statusCode, - } -} - -func (sw *statusResponseWriter) WriteHeader(code int) { - // Check if we need to adjust Status Code to account for scheduled redirect - // This enables us to return payload along with HTTP 301 - // for subdomain redirect in web browsers while also returning body for cli - // tools which do not follow redirects by default (curl, wget). - redirect := sw.ResponseWriter.Header().Get("Location") - if redirect != "" && code == http.StatusOK { - code = http.StatusMovedPermanently - log.Debugw("subdomain redirect", "location", redirect, "status", code) - } - sw.ResponseWriter.WriteHeader(code) -} - -// ServeContent replies to the request using the content in the provided ReadSeeker -// and returns the status code written and any error encountered during a write. -// It wraps http.ServeContent which takes care of If-None-Match+Etag, -// Content-Length and range requests. -func ServeContent(w http.ResponseWriter, req *http.Request, name string, modtime time.Time, content io.ReadSeeker) (int, bool, error) { - ew := &errRecordingResponseWriter{ResponseWriter: w} - http.ServeContent(ew, req, name, modtime, content) - - // When we calculate some metrics we want a flag that lets us to ignore - // errors and 304 Not Modified, and only care when requested data - // was sent in full. - dataSent := ew.code/100 == 2 && ew.err == nil - - return ew.code, dataSent, ew.err -} - -// errRecordingResponseWriter wraps a ResponseWriter to record the status code and any write error. -type errRecordingResponseWriter struct { - http.ResponseWriter - code int - err error -} - -func (w *errRecordingResponseWriter) WriteHeader(code int) { - if w.code == 0 { - w.code = code - } - w.ResponseWriter.WriteHeader(code) -} - -func (w *errRecordingResponseWriter) Write(p []byte) (int, error) { - n, err := w.ResponseWriter.Write(p) - if err != nil && w.err == nil { - w.err = err - } - return n, err -} - -// ReadFrom exposes errRecordingResponseWriter's underlying ResponseWriter to io.Copy -// to allow optimized methods to be taken advantage of. -func (w *errRecordingResponseWriter) ReadFrom(r io.Reader) (n int64, err error) { - n, err = io.Copy(w.ResponseWriter, r) - if err != nil && w.err == nil { - w.err = err - } - return n, err -} - -func newGatewaySummaryMetric(name string, help string) *prometheus.SummaryVec { - summaryMetric := prometheus.NewSummaryVec( - prometheus.SummaryOpts{ - Namespace: "ipfs", - Subsystem: "http", - Name: name, - Help: help, - }, - []string{"gateway"}, - ) - if err := prometheus.Register(summaryMetric); err != nil { - if are, ok := err.(prometheus.AlreadyRegisteredError); ok { - summaryMetric = are.ExistingCollector.(*prometheus.SummaryVec) - } else { - log.Errorf("failed to register ipfs_http_%s: %v", name, err) - } - } - return summaryMetric -} - -func newGatewayHistogramMetric(name string, help string) *prometheus.HistogramVec { - // We can add buckets as a parameter in the future, but for now using static defaults - // suggested in https://github.com/ipfs/kubo/issues/8441 - defaultBuckets := []float64{0.05, 0.1, 0.25, 0.5, 1, 2, 5, 10, 30, 60} - histogramMetric := prometheus.NewHistogramVec( - prometheus.HistogramOpts{ - Namespace: "ipfs", - Subsystem: "http", - Name: name, - Help: help, - Buckets: defaultBuckets, - }, - []string{"gateway"}, - ) - if err := prometheus.Register(histogramMetric); err != nil { - if are, ok := err.(prometheus.AlreadyRegisteredError); ok { - histogramMetric = are.ExistingCollector.(*prometheus.HistogramVec) - } else { - log.Errorf("failed to register ipfs_http_%s: %v", name, err) - } - } - return histogramMetric -} - -// NewGatewayHandler returns an http.Handler that can act as a gateway to IPFS content -// offlineApi is a version of the API that should not make network requests for missing data -func NewGatewayHandler(c GatewayConfig, api NodeAPI, offlineAPI NodeAPI) http.Handler { - return newGatewayHandler(c, api, offlineAPI) -} - -func newGatewayHandler(c GatewayConfig, api NodeAPI, offlineAPI NodeAPI) *gatewayHandler { - i := &gatewayHandler{ - config: c, - api: api, - offlineAPI: offlineAPI, - // Improved Metrics - // ---------------------------- - // Time till the first content block (bar in /ipfs/cid/foo/bar) - // (format-agnostic, across all response types) - firstContentBlockGetMetric: newGatewayHistogramMetric( - "gw_first_content_block_get_latency_seconds", - "The time till the first content block is received on GET from the gateway.", - ), - - // Response-type specific metrics - // ---------------------------- - // UnixFS: time it takes to return a file - unixfsFileGetMetric: newGatewayHistogramMetric( - "gw_unixfs_file_get_duration_seconds", - "The time to serve an entire UnixFS file from the gateway.", - ), - // UnixFS: time it takes to generate static HTML with directory listing - unixfsGenDirGetMetric: newGatewayHistogramMetric( - "gw_unixfs_gen_dir_listing_get_duration_seconds", - "The time to serve a generated UnixFS HTML directory listing from the gateway.", - ), - // CAR: time it takes to return requested CAR stream - carStreamGetMetric: newGatewayHistogramMetric( - "gw_car_stream_get_duration_seconds", - "The time to GET an entire CAR stream from the gateway.", - ), - // Block: time it takes to return requested Block - rawBlockGetMetric: newGatewayHistogramMetric( - "gw_raw_block_get_duration_seconds", - "The time to GET an entire raw Block from the gateway.", - ), - - // Legacy Metrics - // ---------------------------- - unixfsGetMetric: newGatewaySummaryMetric( // TODO: remove? - // (deprecated, use firstContentBlockGetMetric instead) - "unixfs_get_latency_seconds", - "The time to receive the first UnixFS node on a GET from the gateway.", - ), - } - return i -} - -func parseIpfsPath(p string) (cid.Cid, string, error) { - rootPath, err := path.ParsePath(p) - if err != nil { - return cid.Cid{}, "", err - } - - // Check the path. - rsegs := rootPath.Segments() - if rsegs[0] != "ipfs" { - return cid.Cid{}, "", fmt.Errorf("WritableGateway: only ipfs paths supported") - } - - rootCid, err := cid.Decode(rsegs[1]) - if err != nil { - return cid.Cid{}, "", err - } - - return rootCid, path.Join(rsegs[2:]), nil -} - -func (i *gatewayHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - // the hour is a hard fallback, we don't expect it to happen, but just in case - ctx, cancel := context.WithTimeout(r.Context(), time.Hour) - defer cancel() - r = r.WithContext(ctx) - - defer func() { - if r := recover(); r != nil { - log.Error("A panic occurred in the gateway handler!") - log.Error(r) - debug.PrintStack() - } - }() - - if i.config.Writable { - switch r.Method { - case http.MethodPost: - i.postHandler(w, r) - return - case http.MethodPut: - i.putHandler(w, r) - return - case http.MethodDelete: - i.deleteHandler(w, r) - return - } - } - - switch r.Method { - case http.MethodGet, http.MethodHead: - i.getOrHeadHandler(w, r) - return - case http.MethodOptions: - i.optionsHandler(w, r) - return - } - - errmsg := "Method " + r.Method + " not allowed: " - var status int - if !i.config.Writable { - status = http.StatusMethodNotAllowed - errmsg = errmsg + "read only access" - w.Header().Add("Allow", http.MethodGet) - w.Header().Add("Allow", http.MethodHead) - w.Header().Add("Allow", http.MethodOptions) - } else { - status = http.StatusBadRequest - errmsg = errmsg + "bad request for " + r.URL.Path - } - http.Error(w, errmsg, status) -} - -func (i *gatewayHandler) optionsHandler(w http.ResponseWriter, r *http.Request) { - /* - OPTIONS is a noop request that is used by the browsers to check - if server accepts cross-site XMLHttpRequest (indicated by the presence of CORS headers) - https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Preflighted_requests - */ - i.addUserHeaders(w) // return all custom headers (including CORS ones, if set) -} - -func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request) { - begin := time.Now() - - logger := log.With("from", r.RequestURI) - logger.Debug("http request received") - - if err := handleUnsupportedHeaders(r); err != nil { - webRequestError(w, err) - return - } - - if requestHandled := handleProtocolHandlerRedirect(w, r, logger); requestHandled { - return - } - - if err := handleServiceWorkerRegistration(r); err != nil { - webRequestError(w, err) - return - } - - contentPath := ipath.New(r.URL.Path) - - if requestHandled := i.handleOnlyIfCached(w, r, contentPath, logger); requestHandled { - return - } - - if requestHandled := handleSuperfluousNamespace(w, r, contentPath); requestHandled { - return - } - - // Detect when explicit Accept header or ?format parameter are present - responseFormat, formatParams, err := customResponseFormat(r) - if err != nil { - webError(w, "error while processing the Accept header", err, http.StatusBadRequest) - return - } - trace.SpanFromContext(r.Context()).SetAttributes(attribute.String("ResponseFormat", responseFormat)) - - resolvedPath, contentPath, ok := i.handlePathResolution(w, r, responseFormat, contentPath, logger) - if !ok { - return - } - trace.SpanFromContext(r.Context()).SetAttributes(attribute.String("ResolvedPath", resolvedPath.String())) - - // Detect when If-None-Match HTTP header allows returning HTTP 304 Not Modified - if inm := r.Header.Get("If-None-Match"); inm != "" { - pathCid := resolvedPath.Cid() - // need to check against both File and Dir Etag variants - // because this inexpensive check happens before we do any I/O - cidEtag := getEtag(r, pathCid) - dirEtag := getDirListingEtag(pathCid) - if etagMatch(inm, cidEtag, dirEtag) { - // Finish early if client already has a matching Etag - w.WriteHeader(http.StatusNotModified) - return - } - } - - if err := i.handleGettingFirstBlock(r, begin, contentPath, resolvedPath); err != nil { - webRequestError(w, err) - return - } - - if err := i.setCommonHeaders(w, r, contentPath); err != nil { - webRequestError(w, err) - return - } - - // Support custom response formats passed via ?format or Accept HTTP header - switch responseFormat { - case "": - switch resolvedPath.Cid().Prefix().Codec { - case uint64(mc.Json), uint64(mc.DagJson), uint64(mc.Cbor), uint64(mc.DagCbor): - logger.Debugw("serving codec", "path", contentPath) - i.serveCodec(r.Context(), w, r, resolvedPath, contentPath, begin, responseFormat) - default: - logger.Debugw("serving unixfs", "path", contentPath) - i.serveUnixFS(r.Context(), w, r, resolvedPath, contentPath, begin, logger) - } - return - case "application/vnd.ipld.raw": - logger.Debugw("serving raw block", "path", contentPath) - i.serveRawBlock(r.Context(), w, r, resolvedPath, contentPath, begin) - return - case "application/vnd.ipld.car": - logger.Debugw("serving car stream", "path", contentPath) - carVersion := formatParams["version"] - i.serveCAR(r.Context(), w, r, resolvedPath, contentPath, carVersion, begin) - return - case "application/x-tar": - logger.Debugw("serving tar file", "path", contentPath) - i.serveTAR(r.Context(), w, r, resolvedPath, contentPath, begin, logger) - return - case "application/json", "application/vnd.ipld.dag-json", - "application/cbor", "application/vnd.ipld.dag-cbor": - logger.Debugw("serving codec", "path", contentPath) - i.serveCodec(r.Context(), w, r, resolvedPath, contentPath, begin, responseFormat) - return - default: // catch-all for unsuported application/vnd.* - err := fmt.Errorf("unsupported format %q", responseFormat) - webError(w, "failed respond with requested content type", err, http.StatusBadRequest) - return - } -} - -func (i *gatewayHandler) postHandler(w http.ResponseWriter, r *http.Request) { - p, err := i.api.Unixfs().Add(r.Context(), files.NewReaderFile(r.Body)) - if err != nil { - internalWebError(w, err) - return - } - - i.addUserHeaders(w) // ok, _now_ write user's headers. - w.Header().Set("IPFS-Hash", p.Cid().String()) - log.Debugw("CID created, http redirect", "from", r.URL, "to", p, "status", http.StatusCreated) - http.Redirect(w, r, p.String(), http.StatusCreated) -} - -func (i *gatewayHandler) putHandler(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - ds := i.api.Dag() - - // Parse the path - rootCid, newPath, err := parseIpfsPath(r.URL.Path) - if err != nil { - webError(w, "WritableGateway: failed to parse the path", err, http.StatusBadRequest) - return - } - if newPath == "" || newPath == "/" { - http.Error(w, "WritableGateway: empty path", http.StatusBadRequest) - return - } - newDirectory, newFileName := gopath.Split(newPath) - - // Resolve the old root. - - rnode, err := ds.Get(ctx, rootCid) - if err != nil { - webError(w, "WritableGateway: Could not create DAG from request", err, http.StatusInternalServerError) - return - } - - pbnd, ok := rnode.(*dag.ProtoNode) - if !ok { - webError(w, "Cannot read non protobuf nodes through gateway", dag.ErrNotProtobuf, http.StatusBadRequest) - return - } - - // Create the new file. - newFilePath, err := i.api.Unixfs().Add(ctx, files.NewReaderFile(r.Body)) - if err != nil { - webError(w, "WritableGateway: could not create DAG from request", err, http.StatusInternalServerError) - return - } - - newFile, err := ds.Get(ctx, newFilePath.Cid()) - if err != nil { - webError(w, "WritableGateway: failed to resolve new file", err, http.StatusInternalServerError) - return - } - - // Patch the new file into the old root. - - root, err := mfs.NewRoot(ctx, ds, pbnd, nil) - if err != nil { - webError(w, "WritableGateway: failed to create MFS root", err, http.StatusBadRequest) - return - } - - if newDirectory != "" { - err := mfs.Mkdir(root, newDirectory, mfs.MkdirOpts{Mkparents: true, Flush: false}) - if err != nil { - webError(w, "WritableGateway: failed to create MFS directory", err, http.StatusInternalServerError) - return - } - } - dirNode, err := mfs.Lookup(root, newDirectory) - if err != nil { - webError(w, "WritableGateway: failed to lookup directory", err, http.StatusInternalServerError) - return - } - dir, ok := dirNode.(*mfs.Directory) - if !ok { - http.Error(w, "WritableGateway: target directory is not a directory", http.StatusBadRequest) - return - } - err = dir.Unlink(newFileName) - switch err { - case os.ErrNotExist, nil: - default: - webError(w, "WritableGateway: failed to replace existing file", err, http.StatusBadRequest) - return - } - err = dir.AddChild(newFileName, newFile) - if err != nil { - webError(w, "WritableGateway: failed to link file into directory", err, http.StatusInternalServerError) - return - } - nnode, err := root.GetDirectory().GetNode() - if err != nil { - webError(w, "WritableGateway: failed to finalize", err, http.StatusInternalServerError) - return - } - newcid := nnode.Cid() - - i.addUserHeaders(w) // ok, _now_ write user's headers. - w.Header().Set("IPFS-Hash", newcid.String()) - - redirectURL := gopath.Join(ipfsPathPrefix, newcid.String(), newPath) - log.Debugw("CID replaced, redirect", "from", r.URL, "to", redirectURL, "status", http.StatusCreated) - http.Redirect(w, r, redirectURL, http.StatusCreated) -} - -func (i *gatewayHandler) deleteHandler(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - - // parse the path - - rootCid, newPath, err := parseIpfsPath(r.URL.Path) - if err != nil { - webError(w, "WritableGateway: failed to parse the path", err, http.StatusBadRequest) - return - } - if newPath == "" || newPath == "/" { - http.Error(w, "WritableGateway: empty path", http.StatusBadRequest) - return - } - directory, filename := gopath.Split(newPath) - - // lookup the root - - rootNodeIPLD, err := i.api.Dag().Get(ctx, rootCid) - if err != nil { - webError(w, "WritableGateway: failed to resolve root CID", err, http.StatusInternalServerError) - return - } - rootNode, ok := rootNodeIPLD.(*dag.ProtoNode) - if !ok { - http.Error(w, "WritableGateway: empty path", http.StatusInternalServerError) - return - } - - // construct the mfs root - - root, err := mfs.NewRoot(ctx, i.api.Dag(), rootNode, nil) - if err != nil { - webError(w, "WritableGateway: failed to construct the MFS root", err, http.StatusBadRequest) - return - } - - // lookup the parent directory - - parentNode, err := mfs.Lookup(root, directory) - if err != nil { - webError(w, "WritableGateway: failed to look up parent", err, http.StatusInternalServerError) - return - } - - parent, ok := parentNode.(*mfs.Directory) - if !ok { - http.Error(w, "WritableGateway: parent is not a directory", http.StatusInternalServerError) - return - } - - // delete the file - - switch parent.Unlink(filename) { - case nil, os.ErrNotExist: - default: - webError(w, "WritableGateway: failed to remove file", err, http.StatusInternalServerError) - return - } - - nnode, err := root.GetDirectory().GetNode() - if err != nil { - webError(w, "WritableGateway: failed to finalize", err, http.StatusInternalServerError) - return - } - ncid := nnode.Cid() - - i.addUserHeaders(w) // ok, _now_ write user's headers. - w.Header().Set("IPFS-Hash", ncid.String()) - - redirectURL := gopath.Join(ipfsPathPrefix+ncid.String(), directory) - // note: StatusCreated is technically correct here as we created a new resource. - log.Debugw("CID deleted, redirect", "from", r.RequestURI, "to", redirectURL, "status", http.StatusCreated) - http.Redirect(w, r, redirectURL, http.StatusCreated) -} - -func (i *gatewayHandler) addUserHeaders(w http.ResponseWriter) { - for k, v := range i.config.Headers { - w.Header()[k] = v - } -} - -func addCacheControlHeaders(w http.ResponseWriter, r *http.Request, contentPath ipath.Path, fileCid cid.Cid) (modtime time.Time) { - // Set Etag to based on CID (override whatever was set before) - w.Header().Set("Etag", getEtag(r, fileCid)) - - // Set Cache-Control and Last-Modified based on contentPath properties - if contentPath.Mutable() { - // mutable namespaces such as /ipns/ can't be cached forever - - /* For now we set Last-Modified to Now() to leverage caching heuristics built into modern browsers: - * https://github.com/ipfs/kubo/pull/8074#pullrequestreview-645196768 - * but we should not set it to fake values and use Cache-Control based on TTL instead */ - modtime = time.Now() - - // TODO: set Cache-Control based on TTL of IPNS/DNSLink: https://github.com/ipfs/kubo/issues/1818#issuecomment-1015849462 - // TODO: set Last-Modified based on /ipns/ publishing timestamp? - } else { - // immutable! CACHE ALL THE THINGS, FOREVER! wolololol - w.Header().Set("Cache-Control", immutableCacheControl) - - // Set modtime to 'zero time' to disable Last-Modified header (superseded by Cache-Control) - modtime = noModtime - - // TODO: set Last-Modified? - TBD - /ipfs/ modification metadata is present in unixfs 1.5 https://github.com/ipfs/kubo/issues/6920? - } - - return modtime -} - -// Set Content-Disposition if filename URL query param is present, return preferred filename -func addContentDispositionHeader(w http.ResponseWriter, r *http.Request, contentPath ipath.Path) string { - /* This logic enables: - * - creation of HTML links that trigger "Save As.." dialog instead of being rendered by the browser - * - overriding the filename used when saving subresource assets on HTML page - * - providing a default filename for HTTP clients when downloading direct /ipfs/CID without any subpath - */ - - // URL param ?filename=cat.jpg triggers Content-Disposition: [..] filename - // which impacts default name used in "Save As.." dialog - name := getFilename(contentPath) - urlFilename := r.URL.Query().Get("filename") - if urlFilename != "" { - disposition := "inline" - // URL param ?download=true triggers Content-Disposition: [..] attachment - // which skips rendering and forces "Save As.." dialog in browsers - if r.URL.Query().Get("download") == "true" { - disposition = "attachment" - } - setContentDispositionHeader(w, urlFilename, disposition) - name = urlFilename - } - return name -} - -// Set Content-Disposition to arbitrary filename and disposition -func setContentDispositionHeader(w http.ResponseWriter, filename string, disposition string) { - utf8Name := url.PathEscape(filename) - asciiName := url.PathEscape(onlyASCII.ReplaceAllLiteralString(filename, "_")) - w.Header().Set("Content-Disposition", fmt.Sprintf("%s; filename=\"%s\"; filename*=UTF-8''%s", disposition, asciiName, utf8Name)) -} - -// Set X-Ipfs-Roots with logical CID array for efficient HTTP cache invalidation. -func (i *gatewayHandler) buildIpfsRootsHeader(contentPath string, r *http.Request) (string, error) { - /* - These are logical roots where each CID represent one path segment - and resolves to either a directory or the root block of a file. - The main purpose of this header is allow HTTP caches to do smarter decisions - around cache invalidation (eg. keep specific subdirectory/file if it did not change) - - A good example is Wikipedia, which is HAMT-sharded, but we only care about - logical roots that represent each segment of the human-readable content - path: - - Given contentPath = /ipns/en.wikipedia-on-ipfs.org/wiki/Block_of_Wikipedia_in_Turkey - rootCidList is a generated by doing `ipfs resolve -r` on each sub path: - /ipns/en.wikipedia-on-ipfs.org → bafybeiaysi4s6lnjev27ln5icwm6tueaw2vdykrtjkwiphwekaywqhcjze - /ipns/en.wikipedia-on-ipfs.org/wiki/ → bafybeihn2f7lhumh4grizksi2fl233cyszqadkn424ptjajfenykpsaiw4 - /ipns/en.wikipedia-on-ipfs.org/wiki/Block_of_Wikipedia_in_Turkey → bafkreibn6euazfvoghepcm4efzqx5l3hieof2frhp254hio5y7n3hv5rma - - The result is an ordered array of values: - X-Ipfs-Roots: bafybeiaysi4s6lnjev27ln5icwm6tueaw2vdykrtjkwiphwekaywqhcjze,bafybeihn2f7lhumh4grizksi2fl233cyszqadkn424ptjajfenykpsaiw4,bafkreibn6euazfvoghepcm4efzqx5l3hieof2frhp254hio5y7n3hv5rma - - Note that while the top one will change every time any article is changed, - the last root (responsible for specific article) may not change at all. - */ - var sp strings.Builder - var pathRoots []string - pathSegments := strings.Split(contentPath[6:], "/") - sp.WriteString(contentPath[:5]) // /ipfs or /ipns - for _, root := range pathSegments { - if root == "" { - continue - } - sp.WriteString("/") - sp.WriteString(root) - resolvedSubPath, err := i.api.ResolvePath(r.Context(), ipath.New(sp.String())) - if err != nil { - return "", err - } - pathRoots = append(pathRoots, resolvedSubPath.Cid().String()) - } - rootCidList := strings.Join(pathRoots, ",") // convention from rfc2616#sec4.2 - return rootCidList, nil -} - -func webRequestError(w http.ResponseWriter, err *requestError) { - webError(w, err.Message, err.Err, err.StatusCode) -} - -func webError(w http.ResponseWriter, message string, err error, defaultCode int) { - if _, ok := err.(resolver.ErrNoLink); ok { - webErrorWithCode(w, message, err, http.StatusNotFound) - } else if err == routing.ErrNotFound { - webErrorWithCode(w, message, err, http.StatusNotFound) - } else if ipld.IsNotFound(err) { - webErrorWithCode(w, message, err, http.StatusNotFound) - } else if err == context.DeadlineExceeded { - webErrorWithCode(w, message, err, http.StatusRequestTimeout) - } else { - webErrorWithCode(w, message, err, defaultCode) - } -} - -func webErrorWithCode(w http.ResponseWriter, message string, err error, code int) { - http.Error(w, fmt.Sprintf("%s: %s", message, err), code) - if code >= 500 { - log.Warnf("server error: %s: %s", message, err) - } -} - -// return a 500 error and log -func internalWebError(w http.ResponseWriter, err error) { - webErrorWithCode(w, "internalWebError", err, http.StatusInternalServerError) -} - -func getFilename(contentPath ipath.Path) string { - s := contentPath.String() - if (strings.HasPrefix(s, ipfsPathPrefix) || strings.HasPrefix(s, ipnsPathPrefix)) && strings.Count(gopath.Clean(s), "/") <= 2 { - // Don't want to treat ipfs.io in /ipns/ipfs.io as a filename. - return "" - } - return gopath.Base(s) -} - -// etagMatch evaluates if we can respond with HTTP 304 Not Modified -// It supports multiple weak and strong etags passed in If-None-Matc stringh -// including the wildcard one. -func etagMatch(ifNoneMatchHeader string, cidEtag string, dirEtag string) bool { - buf := ifNoneMatchHeader - for { - buf = textproto.TrimString(buf) - if len(buf) == 0 { - break - } - if buf[0] == ',' { - buf = buf[1:] - continue - } - // If-None-Match: * should match against any etag - if buf[0] == '*' { - return true - } - etag, remain := scanETag(buf) - if etag == "" { - break - } - // Check for match both strong and weak etags - if etagWeakMatch(etag, cidEtag) || etagWeakMatch(etag, dirEtag) { - return true - } - buf = remain - } - return false -} - -// scanETag determines if a syntactically valid ETag is present at s. If so, -// the ETag and remaining text after consuming ETag is returned. Otherwise, -// it returns "", "". -// (This is the same logic as one executed inside of http.ServeContent) -func scanETag(s string) (etag string, remain string) { - s = textproto.TrimString(s) - start := 0 - if strings.HasPrefix(s, "W/") { - start = 2 - } - if len(s[start:]) < 2 || s[start] != '"' { - return "", "" - } - // ETag is either W/"text" or "text". - // See RFC 7232 2.3. - for i := start + 1; i < len(s); i++ { - c := s[i] - switch { - // Character values allowed in ETags. - case c == 0x21 || c >= 0x23 && c <= 0x7E || c >= 0x80: - case c == '"': - return s[:i+1], s[i+1:] - default: - return "", "" - } - } - return "", "" -} - -// etagWeakMatch reports whether a and b match using weak ETag comparison. -func etagWeakMatch(a, b string) bool { - return strings.TrimPrefix(a, "W/") == strings.TrimPrefix(b, "W/") -} - -// generate Etag value based on HTTP request and CID -func getEtag(r *http.Request, cid cid.Cid) string { - prefix := `"` - suffix := `"` - responseFormat, _, err := customResponseFormat(r) - if err == nil && responseFormat != "" { - // application/vnd.ipld.foo → foo - // application/x-bar → x-bar - shortFormat := responseFormat[strings.LastIndexAny(responseFormat, "/.")+1:] - // Etag: "cid.shortFmt" (gives us nice compression together with Content-Disposition in block (raw) and car responses) - suffix = `.` + shortFormat + suffix - } - // TODO: include selector suffix when https://github.com/ipfs/kubo/issues/8769 lands - return prefix + cid.String() + suffix -} - -// return explicit response format if specified in request as query parameter or via Accept HTTP header -func customResponseFormat(r *http.Request) (mediaType string, params map[string]string, err error) { - if formatParam := r.URL.Query().Get("format"); formatParam != "" { - // translate query param to a content type - switch formatParam { - case "raw": - return "application/vnd.ipld.raw", nil, nil - case "car": - return "application/vnd.ipld.car", nil, nil - case "tar": - return "application/x-tar", nil, nil - case "dag-json": - return "application/vnd.ipld.dag-json", nil, nil - case "json": - return "application/json", nil, nil - case "dag-cbor": - return "application/vnd.ipld.dag-cbor", nil, nil - case "cbor": - return "application/cbor", nil, nil - } - } - // Browsers and other user agents will send Accept header with generic types like: - // Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 - // We only care about explicit, vendor-specific content-types. - for _, accept := range r.Header.Values("Accept") { - // respond to the very first ipld content type - if strings.HasPrefix(accept, "application/vnd.ipld") || - strings.HasPrefix(accept, "application/x-tar") || - strings.HasPrefix(accept, "application/json") || - strings.HasPrefix(accept, "application/cbor") { - mediatype, params, err := mime.ParseMediaType(accept) - if err != nil { - return "", nil, err - } - return mediatype, params, nil - } - } - return "", nil, nil -} - -// returns unquoted path with all special characters revealed as \u codes -func debugStr(path string) string { - q := fmt.Sprintf("%+q", path) - if len(q) >= 3 { - q = q[1 : len(q)-1] - } - return q -} - -// Resolve the provided contentPath including any special handling related to -// the requested responseFormat. Returned ok flag indicates if gateway handler -// should continue processing the request. -func (i *gatewayHandler) handlePathResolution(w http.ResponseWriter, r *http.Request, responseFormat string, contentPath ipath.Path, logger *zap.SugaredLogger) (resolvedPath ipath.Resolved, newContentPath ipath.Path, ok bool) { - // Attempt to resolve the provided path. - resolvedPath, err := i.api.ResolvePath(r.Context(), contentPath) - - switch err { - case nil: - return resolvedPath, contentPath, true - case coreiface.ErrOffline: - webError(w, "ipfs resolve -r "+debugStr(contentPath.String()), err, http.StatusServiceUnavailable) - return nil, nil, false - default: - // The path can't be resolved. - if isUnixfsResponseFormat(responseFormat) { - // If we have origin isolation (subdomain gw, DNSLink website), - // and response type is UnixFS (default for website hosting) - // check for presence of _redirects file and apply rules defined there. - // See: https://github.com/ipfs/specs/pull/290 - if hasOriginIsolation(r) { - resolvedPath, newContentPath, ok, hadMatchingRule := i.serveRedirectsIfPresent(w, r, resolvedPath, contentPath, logger) - if hadMatchingRule { - logger.Debugw("applied a rule from _redirects file") - return resolvedPath, newContentPath, ok - } - } - - // if Accept is text/html, see if ipfs-404.html is present - // This logic isn't documented and will likely be removed at some point. - // Any 404 logic in _redirects above will have already run by this time, so it's really an extra fall back - if i.serveLegacy404IfPresent(w, r, contentPath) { - logger.Debugw("served legacy 404") - return nil, nil, false - } - } - - // Note: webError will replace http.StatusBadRequest with StatusNotFound if necessary - webError(w, "ipfs resolve -r "+debugStr(contentPath.String()), err, http.StatusBadRequest) - return nil, nil, false - } -} - -// Detect 'Cache-Control: only-if-cached' in request and return data if it is already in the local datastore. -// https://github.com/ipfs/specs/blob/main/http-gateways/PATH_GATEWAY.md#cache-control-request-header -func (i *gatewayHandler) handleOnlyIfCached(w http.ResponseWriter, r *http.Request, contentPath ipath.Path, logger *zap.SugaredLogger) (requestHandled bool) { - if r.Header.Get("Cache-Control") == "only-if-cached" { - _, err := i.offlineAPI.Block().Stat(r.Context(), contentPath) - if err != nil { - if r.Method == http.MethodHead { - w.WriteHeader(http.StatusPreconditionFailed) - return true - } - errMsg := fmt.Sprintf("%q not in local datastore", contentPath.String()) - http.Error(w, errMsg, http.StatusPreconditionFailed) - return true - } - if r.Method == http.MethodHead { - w.WriteHeader(http.StatusOK) - return true - } - } - return false -} - -func handleUnsupportedHeaders(r *http.Request) (err *requestError) { - // X-Ipfs-Gateway-Prefix was removed (https://github.com/ipfs/kubo/issues/7702) - // TODO: remove this after go-ipfs 0.13 ships - if prfx := r.Header.Get("X-Ipfs-Gateway-Prefix"); prfx != "" { - err := fmt.Errorf("X-Ipfs-Gateway-Prefix support was removed: https://github.com/ipfs/kubo/issues/7702") - return newRequestError("unsupported HTTP header", err, http.StatusBadRequest) - } - return nil -} - -// ?uri query param support for requests produced by web browsers -// via navigator.registerProtocolHandler Web API -// https://developer.mozilla.org/en-US/docs/Web/API/Navigator/registerProtocolHandler -// TLDR: redirect /ipfs/?uri=ipfs%3A%2F%2Fcid%3Fquery%3Dval to /ipfs/cid?query=val -func handleProtocolHandlerRedirect(w http.ResponseWriter, r *http.Request, logger *zap.SugaredLogger) (requestHandled bool) { - if uriParam := r.URL.Query().Get("uri"); uriParam != "" { - u, err := url.Parse(uriParam) - if err != nil { - webError(w, "failed to parse uri query parameter", err, http.StatusBadRequest) - return true - } - if u.Scheme != "ipfs" && u.Scheme != "ipns" { - webError(w, "uri query parameter scheme must be ipfs or ipns", err, http.StatusBadRequest) - return true - } - path := u.Path - if u.RawQuery != "" { // preserve query if present - path = path + "?" + u.RawQuery - } - - redirectURL := gopath.Join("/", u.Scheme, u.Host, path) - logger.Debugw("uri param, redirect", "to", redirectURL, "status", http.StatusMovedPermanently) - http.Redirect(w, r, redirectURL, http.StatusMovedPermanently) - return true - } - - return false -} - -// Disallow Service Worker registration on namespace roots -// https://github.com/ipfs/kubo/issues/4025 -func handleServiceWorkerRegistration(r *http.Request) (err *requestError) { - if r.Header.Get("Service-Worker") == "script" { - matched, _ := regexp.MatchString(`^/ip[fn]s/[^/]+$`, r.URL.Path) - if matched { - err := fmt.Errorf("registration is not allowed for this scope") - return newRequestError("navigator.serviceWorker", err, http.StatusBadRequest) - } - } - - return nil -} - -// Attempt to fix redundant /ipfs/ namespace as long as resulting -// 'intended' path is valid. This is in case gremlins were tickled -// wrong way and user ended up at /ipfs/ipfs/{cid} or /ipfs/ipns/{id} -// like in bafybeien3m7mdn6imm425vc2s22erzyhbvk5n3ofzgikkhmdkh5cuqbpbq :^)) -func handleSuperfluousNamespace(w http.ResponseWriter, r *http.Request, contentPath ipath.Path) (requestHandled bool) { - // If the path is valid, there's nothing to do - if pathErr := contentPath.IsValid(); pathErr == nil { - return false - } - - // If there's no superflous namespace, there's nothing to do - if !(strings.HasPrefix(r.URL.Path, "/ipfs/ipfs/") || strings.HasPrefix(r.URL.Path, "/ipfs/ipns/")) { - return false - } - - // Attempt to fix the superflous namespace - intendedPath := ipath.New(strings.TrimPrefix(r.URL.Path, "/ipfs")) - if err := intendedPath.IsValid(); err != nil { - webError(w, "invalid ipfs path", err, http.StatusBadRequest) - return true - } - intendedURL := intendedPath.String() - if r.URL.RawQuery != "" { - // we render HTML, so ensure query entries are properly escaped - q, _ := url.ParseQuery(r.URL.RawQuery) - intendedURL = intendedURL + "?" + q.Encode() - } - // return HTTP 400 (Bad Request) with HTML error page that: - // - points at correct canonical path via header - // - displays human-readable error - // - redirects to intendedURL after a short delay - - w.WriteHeader(http.StatusBadRequest) - if err := redirectTemplate.Execute(w, redirectTemplateData{ - RedirectURL: intendedURL, - SuggestedPath: intendedPath.String(), - ErrorMsg: fmt.Sprintf("invalid path: %q should be %q", r.URL.Path, intendedPath.String()), - }); err != nil { - webError(w, "failed to redirect when fixing superfluous namespace", err, http.StatusBadRequest) - } - - return true -} - -func (i *gatewayHandler) handleGettingFirstBlock(r *http.Request, begin time.Time, contentPath ipath.Path, resolvedPath ipath.Resolved) *requestError { - // Update the global metric of the time it takes to read the final root block of the requested resource - // NOTE: for legacy reasons this happens before we go into content-type specific code paths - _, err := i.api.Block().Get(r.Context(), resolvedPath) - if err != nil { - return newRequestError("ipfs block get "+resolvedPath.Cid().String(), err, http.StatusInternalServerError) - } - ns := contentPath.Namespace() - timeToGetFirstContentBlock := time.Since(begin).Seconds() - i.unixfsGetMetric.WithLabelValues(ns).Observe(timeToGetFirstContentBlock) // deprecated, use firstContentBlockGetMetric instead - i.firstContentBlockGetMetric.WithLabelValues(ns).Observe(timeToGetFirstContentBlock) - return nil -} - -func (i *gatewayHandler) setCommonHeaders(w http.ResponseWriter, r *http.Request, contentPath ipath.Path) *requestError { - i.addUserHeaders(w) // ok, _now_ write user's headers. - w.Header().Set("X-Ipfs-Path", contentPath.String()) - - if rootCids, err := i.buildIpfsRootsHeader(contentPath.String(), r); err == nil { - w.Header().Set("X-Ipfs-Roots", rootCids) - } else { // this should never happen, as we resolved the contentPath already - return newRequestError("error while resolving X-Ipfs-Roots", err, http.StatusInternalServerError) - } - - return nil -} diff --git a/core/corehttp/gateway_handler_block.go b/core/corehttp/gateway_handler_block.go deleted file mode 100644 index 3bf7c76be..000000000 --- a/core/corehttp/gateway_handler_block.go +++ /dev/null @@ -1,55 +0,0 @@ -package corehttp - -import ( - "bytes" - "context" - "io" - "net/http" - "time" - - ipath "github.com/ipfs/interface-go-ipfs-core/path" - "github.com/ipfs/kubo/tracing" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/trace" -) - -// serveRawBlock returns bytes behind a raw block -func (i *gatewayHandler) serveRawBlock(ctx context.Context, w http.ResponseWriter, r *http.Request, resolvedPath ipath.Resolved, contentPath ipath.Path, begin time.Time) { - ctx, span := tracing.Span(ctx, "Gateway", "ServeRawBlock", trace.WithAttributes(attribute.String("path", resolvedPath.String()))) - defer span.End() - blockCid := resolvedPath.Cid() - blockReader, err := i.api.Block().Get(ctx, resolvedPath) - if err != nil { - webError(w, "ipfs block get "+blockCid.String(), err, http.StatusInternalServerError) - return - } - block, err := io.ReadAll(blockReader) - if err != nil { - webError(w, "ipfs block get "+blockCid.String(), err, http.StatusInternalServerError) - return - } - content := bytes.NewReader(block) - - // Set Content-Disposition - var name string - if urlFilename := r.URL.Query().Get("filename"); urlFilename != "" { - name = urlFilename - } else { - name = blockCid.String() + ".bin" - } - setContentDispositionHeader(w, name, "attachment") - - // Set remaining headers - modtime := addCacheControlHeaders(w, r, contentPath, blockCid) - w.Header().Set("Content-Type", "application/vnd.ipld.raw") - w.Header().Set("X-Content-Type-Options", "nosniff") // no funny business in the browsers :^) - - // ServeContent will take care of - // If-None-Match+Etag, Content-Length and range requests - _, dataSent, _ := ServeContent(w, r, name, modtime, content) - - if dataSent { - // Update metrics - i.rawBlockGetMetric.WithLabelValues(contentPath.Namespace()).Observe(time.Since(begin).Seconds()) - } -} diff --git a/core/corehttp/gateway_handler_car.go b/core/corehttp/gateway_handler_car.go deleted file mode 100644 index 3363c5199..000000000 --- a/core/corehttp/gateway_handler_car.go +++ /dev/null @@ -1,99 +0,0 @@ -package corehttp - -import ( - "context" - "fmt" - "net/http" - "time" - - blocks "github.com/ipfs/go-block-format" - cid "github.com/ipfs/go-cid" - coreiface "github.com/ipfs/interface-go-ipfs-core" - ipath "github.com/ipfs/interface-go-ipfs-core/path" - "github.com/ipfs/kubo/tracing" - gocar "github.com/ipld/go-car" - selectorparse "github.com/ipld/go-ipld-prime/traversal/selector/parse" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/trace" -) - -// serveCAR returns a CAR stream for specific DAG+selector -func (i *gatewayHandler) serveCAR(ctx context.Context, w http.ResponseWriter, r *http.Request, resolvedPath ipath.Resolved, contentPath ipath.Path, carVersion string, begin time.Time) { - ctx, span := tracing.Span(ctx, "Gateway", "ServeCAR", trace.WithAttributes(attribute.String("path", resolvedPath.String()))) - defer span.End() - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - switch carVersion { - case "": // noop, client does not care about version - case "1": // noop, we support this - default: - err := fmt.Errorf("only version=1 is supported") - webError(w, "unsupported CAR version", err, http.StatusBadRequest) - return - } - rootCid := resolvedPath.Cid() - - // Set Content-Disposition - var name string - if urlFilename := r.URL.Query().Get("filename"); urlFilename != "" { - name = urlFilename - } else { - name = rootCid.String() + ".car" - } - setContentDispositionHeader(w, name, "attachment") - - // Set Cache-Control (same logic as for a regular files) - addCacheControlHeaders(w, r, contentPath, rootCid) - - // Weak Etag W/ because we can't guarantee byte-for-byte identical - // responses, but still want to benefit from HTTP Caching. Two CAR - // responses for the same CID and selector will be logically equivalent, - // but when CAR is streamed, then in theory, blocks may arrive from - // datastore in non-deterministic order. - etag := `W/` + getEtag(r, rootCid) - w.Header().Set("Etag", etag) - - // Finish early if Etag match - if r.Header.Get("If-None-Match") == etag { - w.WriteHeader(http.StatusNotModified) - return - } - - // Make it clear we don't support range-requests over a car stream - // Partial downloads and resumes should be handled using requests for - // sub-DAGs and IPLD selectors: https://github.com/ipfs/go-ipfs/issues/8769 - w.Header().Set("Accept-Ranges", "none") - - w.Header().Set("Content-Type", "application/vnd.ipld.car; version=1") - w.Header().Set("X-Content-Type-Options", "nosniff") // no funny business in the browsers :^) - - // Same go-car settings as dag.export command - store := dagStore{dag: i.api.Dag(), ctx: ctx} - - // TODO: support selectors passed as request param: https://github.com/ipfs/kubo/issues/8769 - dag := gocar.Dag{Root: rootCid, Selector: selectorparse.CommonSelector_ExploreAllRecursively} - car := gocar.NewSelectiveCar(ctx, store, []gocar.Dag{dag}, gocar.TraverseLinksOnlyOnce()) - - if err := car.Write(w); err != nil { - // We return error as a trailer, however it is not something browsers can access - // (https://github.com/mdn/browser-compat-data/issues/14703) - // Due to this, we suggest client always verify that - // the received CAR stream response is matching requested DAG selector - w.Header().Set("X-Stream-Error", err.Error()) - return - } - - // Update metrics - i.carStreamGetMetric.WithLabelValues(contentPath.Namespace()).Observe(time.Since(begin).Seconds()) -} - -// FIXME(@Jorropo): https://github.com/ipld/go-car/issues/315 -type dagStore struct { - dag coreiface.APIDagService - ctx context.Context -} - -func (ds dagStore) Get(_ context.Context, c cid.Cid) (blocks.Block, error) { - return ds.dag.Get(ds.ctx, c) -} diff --git a/core/corehttp/gateway_handler_codec.go b/core/corehttp/gateway_handler_codec.go deleted file mode 100644 index 95a151c79..000000000 --- a/core/corehttp/gateway_handler_codec.go +++ /dev/null @@ -1,258 +0,0 @@ -package corehttp - -import ( - "bytes" - "context" - "fmt" - "html" - "io" - "net/http" - "strings" - "time" - - cid "github.com/ipfs/go-cid" - ipldlegacy "github.com/ipfs/go-ipld-legacy" - ipath "github.com/ipfs/interface-go-ipfs-core/path" - "github.com/ipfs/kubo/assets" - dih "github.com/ipfs/kubo/assets/dag-index-html" - "github.com/ipfs/kubo/tracing" - "github.com/ipld/go-ipld-prime" - "github.com/ipld/go-ipld-prime/multicodec" - mc "github.com/multiformats/go-multicodec" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/trace" -) - -// codecToContentType maps the supported IPLD codecs to the HTTP Content -// Type they should have. -var codecToContentType = map[uint64]string{ - uint64(mc.Json): "application/json", - uint64(mc.Cbor): "application/cbor", - uint64(mc.DagJson): "application/vnd.ipld.dag-json", - uint64(mc.DagCbor): "application/vnd.ipld.dag-cbor", -} - -// contentTypeToCodecs maps the HTTP Content Type to the respective -// possible codecs. If the original data is in one of those codecs, -// we stream the raw bytes. Otherwise, we encode in the last codec -// of the list. -var contentTypeToCodecs = map[string][]uint64{ - "application/json": {uint64(mc.Json), uint64(mc.DagJson)}, - "application/vnd.ipld.dag-json": {uint64(mc.DagJson)}, - "application/cbor": {uint64(mc.Cbor), uint64(mc.DagCbor)}, - "application/vnd.ipld.dag-cbor": {uint64(mc.DagCbor)}, -} - -// contentTypeToExtension maps the HTTP Content Type to the respective file -// extension, used in Content-Disposition header when downloading the file. -var contentTypeToExtension = map[string]string{ - "application/json": ".json", - "application/vnd.ipld.dag-json": ".json", - "application/cbor": ".cbor", - "application/vnd.ipld.dag-cbor": ".cbor", -} - -func (i *gatewayHandler) serveCodec(ctx context.Context, w http.ResponseWriter, r *http.Request, resolvedPath ipath.Resolved, contentPath ipath.Path, begin time.Time, requestedContentType string) { - ctx, span := tracing.Span(ctx, "Gateway", "ServeCodec", trace.WithAttributes(attribute.String("path", resolvedPath.String()), attribute.String("requestedContentType", requestedContentType))) - defer span.End() - - cidCodec := resolvedPath.Cid().Prefix().Codec - responseContentType := requestedContentType - - // If the resolved path still has some remainder, return error for now. - // TODO: handle this when we have IPLD Patch (https://ipld.io/specs/patch/) via HTTP PUT - // TODO: (depends on https://github.com/ipfs/kubo/issues/4801 and https://github.com/ipfs/kubo/issues/4782) - if resolvedPath.Remainder() != "" { - path := strings.TrimSuffix(resolvedPath.String(), resolvedPath.Remainder()) - err := fmt.Errorf("%q of %q could not be returned: reading IPLD Kinds other than Links (CBOR Tag 42) is not implemented: try reading %q instead", resolvedPath.Remainder(), resolvedPath.String(), path) - webError(w, "unsupported pathing", err, http.StatusNotImplemented) - return - } - - // If no explicit content type was requested, the response will have one based on the codec from the CID - if requestedContentType == "" { - cidContentType, ok := codecToContentType[cidCodec] - if !ok { - // Should not happen unless function is called with wrong parameters. - err := fmt.Errorf("content type not found for codec: %v", cidCodec) - webError(w, "internal error", err, http.StatusInternalServerError) - return - } - responseContentType = cidContentType - } - - // Set HTTP headers (for caching etc) - modtime := addCacheControlHeaders(w, r, contentPath, resolvedPath.Cid()) - name := setCodecContentDisposition(w, r, resolvedPath, responseContentType) - w.Header().Set("Content-Type", responseContentType) - w.Header().Set("X-Content-Type-Options", "nosniff") - - // No content type is specified by the user (via Accept, or format=). However, - // we support this format. Let's handle it. - if requestedContentType == "" { - isDAG := cidCodec == uint64(mc.DagJson) || cidCodec == uint64(mc.DagCbor) - acceptsHTML := strings.Contains(r.Header.Get("Accept"), "text/html") - download := r.URL.Query().Get("download") == "true" - - if isDAG && acceptsHTML && !download { - i.serveCodecHTML(ctx, w, r, resolvedPath, contentPath) - } else { - i.serveCodecRaw(ctx, w, r, resolvedPath, contentPath, name, modtime) - } - - return - } - - // Otherwise, the user has requested a specific content type. Let's first get - // the codecs that can be used with this content type. - codecs, ok := contentTypeToCodecs[requestedContentType] - if !ok { - // This is never supposed to happen unless function is called with wrong parameters. - err := fmt.Errorf("unsupported content type: %s", requestedContentType) - webError(w, err.Error(), err, http.StatusInternalServerError) - return - } - - // If we need to convert, use the last codec (strict dag- variant) - toCodec := codecs[len(codecs)-1] - - // If the requested content type has "dag-", ALWAYS go through the encoding - // process in order to validate the content. - if strings.Contains(requestedContentType, "dag-") { - i.serveCodecConverted(ctx, w, r, resolvedPath, contentPath, toCodec, modtime) - return - } - - // Otherwise, check if the data is encoded with the requested content type. - // If so, we can directly stream the raw data. serveRawBlock cannot be directly - // used here as it sets different headers. - for _, codec := range codecs { - if resolvedPath.Cid().Prefix().Codec == codec { - i.serveCodecRaw(ctx, w, r, resolvedPath, contentPath, name, modtime) - return - } - } - - // Finally, if nothing of the above is true, we have to actually convert the codec. - i.serveCodecConverted(ctx, w, r, resolvedPath, contentPath, toCodec, modtime) -} - -func (i *gatewayHandler) serveCodecHTML(ctx context.Context, w http.ResponseWriter, r *http.Request, resolvedPath ipath.Resolved, contentPath ipath.Path) { - // A HTML directory index will be presented, be sure to set the correct - // type instead of relying on autodetection (which may fail). - w.Header().Set("Content-Type", "text/html") - - // Clear Content-Disposition -- we want HTML to be rendered inline - w.Header().Del("Content-Disposition") - - // Generated index requires custom Etag (output may change between Kubo versions) - dagEtag := getDagIndexEtag(resolvedPath.Cid()) - w.Header().Set("Etag", dagEtag) - - // Remove Cache-Control for now to match UnixFS dir-index-html responses - // (we don't want browser to cache HTML forever) - // TODO: if we ever change behavior for UnixFS dir listings, same changes should be applied here - w.Header().Del("Cache-Control") - - cidCodec := mc.Code(resolvedPath.Cid().Prefix().Codec) - if err := dih.DagIndexTemplate.Execute(w, dih.DagIndexTemplateData{ - Path: contentPath.String(), - CID: resolvedPath.Cid().String(), - CodecName: cidCodec.String(), - CodecHex: fmt.Sprintf("0x%x", uint64(cidCodec)), - }); err != nil { - webError(w, "failed to generate HTML listing for this DAG: try fetching raw block with ?format=raw", err, http.StatusInternalServerError) - } -} - -func (i *gatewayHandler) serveCodecRaw(ctx context.Context, w http.ResponseWriter, r *http.Request, resolvedPath ipath.Resolved, contentPath ipath.Path, name string, modtime time.Time) { - blockCid := resolvedPath.Cid() - blockReader, err := i.api.Block().Get(ctx, resolvedPath) - if err != nil { - webError(w, "ipfs block get "+blockCid.String(), err, http.StatusInternalServerError) - return - } - block, err := io.ReadAll(blockReader) - if err != nil { - webError(w, "ipfs block get "+blockCid.String(), err, http.StatusInternalServerError) - return - } - content := bytes.NewReader(block) - - // ServeContent will take care of - // If-None-Match+Etag, Content-Length and range requests - _, _, _ = ServeContent(w, r, name, modtime, content) -} - -func (i *gatewayHandler) serveCodecConverted(ctx context.Context, w http.ResponseWriter, r *http.Request, resolvedPath ipath.Resolved, contentPath ipath.Path, toCodec uint64, modtime time.Time) { - obj, err := i.api.Dag().Get(ctx, resolvedPath.Cid()) - if err != nil { - webError(w, "ipfs dag get "+html.EscapeString(resolvedPath.String()), err, http.StatusInternalServerError) - return - } - - universal, ok := obj.(ipldlegacy.UniversalNode) - if !ok { - err = fmt.Errorf("%T is not a valid IPLD node", obj) - webError(w, err.Error(), err, http.StatusInternalServerError) - return - } - finalNode := universal.(ipld.Node) - - encoder, err := multicodec.LookupEncoder(toCodec) - if err != nil { - webError(w, err.Error(), err, http.StatusInternalServerError) - return - } - - // Ensure IPLD node conforms to the codec specification. - var buf bytes.Buffer - err = encoder(finalNode, &buf) - if err != nil { - webError(w, err.Error(), err, http.StatusInternalServerError) - return - } - - // Sets correct Last-Modified header. This code is borrowed from the standard - // library (net/http/server.go) as we cannot use serveFile. - if !(modtime.IsZero() || modtime.Equal(unixEpochTime)) { - w.Header().Set("Last-Modified", modtime.UTC().Format(http.TimeFormat)) - } - - _, _ = w.Write(buf.Bytes()) -} - -func setCodecContentDisposition(w http.ResponseWriter, r *http.Request, resolvedPath ipath.Resolved, contentType string) string { - var dispType, name string - - ext, ok := contentTypeToExtension[contentType] - if !ok { - // Should never happen. - ext = ".bin" - } - - if urlFilename := r.URL.Query().Get("filename"); urlFilename != "" { - name = urlFilename - } else { - name = resolvedPath.Cid().String() + ext - } - - // JSON should be inlined, but ?download=true should still override - if r.URL.Query().Get("download") == "true" { - dispType = "attachment" - } else { - switch ext { - case ".json": // codecs that serialize to JSON can be rendered by browsers - dispType = "inline" - default: // everything else is assumed binary / opaque bytes - dispType = "attachment" - } - } - - setContentDispositionHeader(w, name, dispType) - return name -} - -func getDagIndexEtag(dagCid cid.Cid) string { - return `"DagIndex-` + assets.AssetHash + `_CID-` + dagCid.String() + `"` -} diff --git a/core/corehttp/gateway_handler_tar.go b/core/corehttp/gateway_handler_tar.go deleted file mode 100644 index 532d88757..000000000 --- a/core/corehttp/gateway_handler_tar.go +++ /dev/null @@ -1,92 +0,0 @@ -package corehttp - -import ( - "context" - "html" - "net/http" - "time" - - files "github.com/ipfs/go-ipfs-files" - ipath "github.com/ipfs/interface-go-ipfs-core/path" - "github.com/ipfs/kubo/tracing" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/trace" - "go.uber.org/zap" -) - -var unixEpochTime = time.Unix(0, 0) - -func (i *gatewayHandler) serveTAR(ctx context.Context, w http.ResponseWriter, r *http.Request, resolvedPath ipath.Resolved, contentPath ipath.Path, begin time.Time, logger *zap.SugaredLogger) { - ctx, span := tracing.Span(ctx, "Gateway", "ServeTAR", trace.WithAttributes(attribute.String("path", resolvedPath.String()))) - defer span.End() - - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - // Get Unixfs file - file, err := i.api.Unixfs().Get(ctx, resolvedPath) - if err != nil { - webError(w, "ipfs cat "+html.EscapeString(contentPath.String()), err, http.StatusBadRequest) - return - } - defer file.Close() - - rootCid := resolvedPath.Cid() - - // Set Cache-Control and read optional Last-Modified time - modtime := addCacheControlHeaders(w, r, contentPath, rootCid) - - // Weak Etag W/ because we can't guarantee byte-for-byte identical - // responses, but still want to benefit from HTTP Caching. Two TAR - // responses for the same CID will be logically equivalent, - // but when TAR is streamed, then in theory, files and directories - // may arrive in different order (depends on TAR lib and filesystem/inodes). - etag := `W/` + getEtag(r, rootCid) - w.Header().Set("Etag", etag) - - // Finish early if Etag match - if r.Header.Get("If-None-Match") == etag { - w.WriteHeader(http.StatusNotModified) - return - } - - // Set Content-Disposition - var name string - if urlFilename := r.URL.Query().Get("filename"); urlFilename != "" { - name = urlFilename - } else { - name = rootCid.String() + ".tar" - } - setContentDispositionHeader(w, name, "attachment") - - // Construct the TAR writer - tarw, err := files.NewTarWriter(w) - if err != nil { - webError(w, "could not build tar writer", err, http.StatusInternalServerError) - return - } - defer tarw.Close() - - // Sets correct Last-Modified header. This code is borrowed from the standard - // library (net/http/server.go) as we cannot use serveFile without throwing the entire - // TAR into the memory first. - if !(modtime.IsZero() || modtime.Equal(unixEpochTime)) { - w.Header().Set("Last-Modified", modtime.UTC().Format(http.TimeFormat)) - } - - w.Header().Set("Content-Type", "application/x-tar") - w.Header().Set("X-Content-Type-Options", "nosniff") // no funny business in the browsers :^) - - // The TAR has a top-level directory (or file) named by the CID. - if err := tarw.WriteFile(file, rootCid.String()); err != nil { - w.Header().Set("X-Stream-Error", err.Error()) - // Trailer headers do not work in web browsers - // (see https://github.com/mdn/browser-compat-data/issues/14703) - // and we have limited options around error handling in browser contexts. - // To improve UX/DX, we finish response stream with error message, allowing client to - // (1) detect error by having corrupted TAR - // (2) be able to reason what went wrong by instecting the tail of TAR stream - _, _ = w.Write([]byte(err.Error())) - return - } -} diff --git a/core/corehttp/gateway_handler_unixfs.go b/core/corehttp/gateway_handler_unixfs.go deleted file mode 100644 index 75d51d93a..000000000 --- a/core/corehttp/gateway_handler_unixfs.go +++ /dev/null @@ -1,46 +0,0 @@ -package corehttp - -import ( - "context" - "fmt" - "html" - "net/http" - "time" - - files "github.com/ipfs/go-ipfs-files" - ipath "github.com/ipfs/interface-go-ipfs-core/path" - "github.com/ipfs/kubo/tracing" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/trace" - "go.uber.org/zap" -) - -func (i *gatewayHandler) serveUnixFS(ctx context.Context, w http.ResponseWriter, r *http.Request, resolvedPath ipath.Resolved, contentPath ipath.Path, begin time.Time, logger *zap.SugaredLogger) { - ctx, span := tracing.Span(ctx, "Gateway", "ServeUnixFS", trace.WithAttributes(attribute.String("path", resolvedPath.String()))) - defer span.End() - - // Handling UnixFS - dr, err := i.api.Unixfs().Get(ctx, resolvedPath) - if err != nil { - webError(w, "ipfs cat "+html.EscapeString(contentPath.String()), err, http.StatusBadRequest) - return - } - defer dr.Close() - - // Handling Unixfs file - if f, ok := dr.(files.File); ok { - logger.Debugw("serving unixfs file", "path", contentPath) - i.serveFile(ctx, w, r, resolvedPath, contentPath, f, begin) - return - } - - // Handling Unixfs directory - dir, ok := dr.(files.Directory) - if !ok { - internalWebError(w, fmt.Errorf("unsupported UnixFS type")) - return - } - - logger.Debugw("serving unixfs directory", "path", contentPath) - i.serveDirectory(ctx, w, r, resolvedPath, contentPath, dir, begin, logger) -} diff --git a/core/corehttp/gateway_handler_unixfs__redirects.go b/core/corehttp/gateway_handler_unixfs__redirects.go deleted file mode 100644 index 81cf05731..000000000 --- a/core/corehttp/gateway_handler_unixfs__redirects.go +++ /dev/null @@ -1,287 +0,0 @@ -package corehttp - -import ( - "fmt" - "io" - "net/http" - gopath "path" - "strconv" - "strings" - - files "github.com/ipfs/go-ipfs-files" - redirects "github.com/ipfs/go-ipfs-redirects-file" - ipath "github.com/ipfs/interface-go-ipfs-core/path" - "go.uber.org/zap" -) - -// Resolving a UnixFS path involves determining if the provided `path.Path` exists and returning the `path.Resolved` -// corresponding to that path. For UnixFS, path resolution is more involved. -// -// When a path under requested CID does not exist, Gateway will check if a `_redirects` file exists -// underneath the root CID of the path, and apply rules defined there. -// See sepcification introduced in: https://github.com/ipfs/specs/pull/290 -// -// Scenario 1: -// If a path exists, we always return the `path.Resolved` corresponding to that path, regardless of the existence of a `_redirects` file. -// -// Scenario 2: -// If a path does not exist, usually we should return a `nil` resolution path and an error indicating that the path -// doesn't exist. However, a `_redirects` file may exist and contain a redirect rule that redirects that path to a different path. -// We need to evaluate the rule and perform the redirect if present. -// -// Scenario 3: -// Another possibility is that the path corresponds to a rewrite rule (i.e. a rule with a status of 200). -// In this case, we don't perform a redirect, but do need to return a `path.Resolved` and `path.Path` corresponding to -// the rewrite destination path. -// -// Note that for security reasons, redirect rules are only processed when the request has origin isolation. -// See https://github.com/ipfs/specs/pull/290 for more information. -func (i *gatewayHandler) serveRedirectsIfPresent(w http.ResponseWriter, r *http.Request, resolvedPath ipath.Resolved, contentPath ipath.Path, logger *zap.SugaredLogger) (newResolvedPath ipath.Resolved, newContentPath ipath.Path, continueProcessing bool, hadMatchingRule bool) { - redirectsFile := i.getRedirectsFile(r, contentPath, logger) - if redirectsFile != nil { - redirectRules, err := i.getRedirectRules(r, redirectsFile) - if err != nil { - internalWebError(w, err) - return nil, nil, false, true - } - - redirected, newPath, err := i.handleRedirectsFileRules(w, r, contentPath, redirectRules) - if err != nil { - err = fmt.Errorf("trouble processing _redirects file at %q: %w", redirectsFile.String(), err) - internalWebError(w, err) - return nil, nil, false, true - } - - if redirected { - return nil, nil, false, true - } - - // 200 is treated as a rewrite, so update the path and continue - if newPath != "" { - // Reassign contentPath and resolvedPath since the URL was rewritten - contentPath = ipath.New(newPath) - resolvedPath, err = i.api.ResolvePath(r.Context(), contentPath) - if err != nil { - internalWebError(w, err) - return nil, nil, false, true - } - - return resolvedPath, contentPath, true, true - } - } - // No matching rule, paths remain the same, continue regular processing - return resolvedPath, contentPath, true, false -} - -func (i *gatewayHandler) handleRedirectsFileRules(w http.ResponseWriter, r *http.Request, contentPath ipath.Path, redirectRules []redirects.Rule) (redirected bool, newContentPath string, err error) { - // Attempt to match a rule to the URL path, and perform the corresponding redirect or rewrite - pathParts := strings.Split(contentPath.String(), "/") - if len(pathParts) > 3 { - // All paths should start with /ipfs/cid/, so get the path after that - urlPath := "/" + strings.Join(pathParts[3:], "/") - rootPath := strings.Join(pathParts[:3], "/") - // Trim off the trailing / - urlPath = strings.TrimSuffix(urlPath, "/") - - for _, rule := range redirectRules { - // Error right away if the rule is invalid - if !rule.MatchAndExpandPlaceholders(urlPath) { - continue - } - - // We have a match! - - // Rewrite - if rule.Status == 200 { - // Prepend the rootPath - toPath := rootPath + rule.To - return false, toPath, nil - } - - // Or 4xx - if rule.Status == 404 || rule.Status == 410 || rule.Status == 451 { - toPath := rootPath + rule.To - content4xxPath := ipath.New(toPath) - err := i.serve4xx(w, r, content4xxPath, rule.Status) - return true, toPath, err - } - - // Or redirect - if rule.Status >= 301 && rule.Status <= 308 { - http.Redirect(w, r, rule.To, rule.Status) - return true, "", nil - } - } - } - - // No redirects matched - return false, "", nil -} - -func (i *gatewayHandler) getRedirectRules(r *http.Request, redirectsFilePath ipath.Resolved) ([]redirects.Rule, error) { - // Convert the path into a file node - node, err := i.api.Unixfs().Get(r.Context(), redirectsFilePath) - if err != nil { - return nil, fmt.Errorf("could not get _redirects: %w", err) - } - defer node.Close() - - // Convert the node into a file - f, ok := node.(files.File) - if !ok { - return nil, fmt.Errorf("could not parse _redirects: %w", err) - } - - // Parse redirect rules from file - redirectRules, err := redirects.Parse(f) - if err != nil { - return nil, fmt.Errorf("could not parse _redirects: %w", err) - } - - return redirectRules, nil -} - -// Returns a resolved path to the _redirects file located in the root CID path of the requested path -func (i *gatewayHandler) getRedirectsFile(r *http.Request, contentPath ipath.Path, logger *zap.SugaredLogger) ipath.Resolved { - // contentPath is the full ipfs path to the requested resource, - // regardless of whether path or subdomain resolution is used. - rootPath := getRootPath(contentPath) - - // Check for _redirects file. - // Any path resolution failures are ignored and we just assume there's no _redirects file. - // Note that ignoring these errors also ensures that the use of the empty CID (bafkqaaa) in tests doesn't fail. - path := ipath.Join(rootPath, "_redirects") - resolvedPath, err := i.api.ResolvePath(r.Context(), path) - if err != nil { - return nil - } - return resolvedPath -} - -// Returns the root CID Path for the given path -func getRootPath(path ipath.Path) ipath.Path { - parts := strings.Split(path.String(), "/") - return ipath.New(gopath.Join("/", path.Namespace(), parts[2])) -} - -func (i *gatewayHandler) serve4xx(w http.ResponseWriter, r *http.Request, content4xxPath ipath.Path, status int) error { - resolved4xxPath, err := i.api.ResolvePath(r.Context(), content4xxPath) - if err != nil { - return err - } - - node, err := i.api.Unixfs().Get(r.Context(), resolved4xxPath) - if err != nil { - return err - } - defer node.Close() - - f, ok := node.(files.File) - if !ok { - return fmt.Errorf("could not convert node for %d page to file", status) - } - - size, err := f.Size() - if err != nil { - return fmt.Errorf("could not get size of %d page", status) - } - - log.Debugf("using _redirects: custom %d file at %q", status, content4xxPath) - w.Header().Set("Content-Type", "text/html") - w.Header().Set("Content-Length", strconv.FormatInt(size, 10)) - addCacheControlHeaders(w, r, content4xxPath, resolved4xxPath.Cid()) - w.WriteHeader(status) - _, err = io.CopyN(w, f, size) - return err -} - -func hasOriginIsolation(r *http.Request) bool { - _, gw := r.Context().Value(requestContextKey("gw-hostname")).(string) - _, dnslink := r.Context().Value("dnslink-hostname").(string) - - if gw || dnslink { - return true - } - - return false -} - -func isUnixfsResponseFormat(responseFormat string) bool { - // The implicit response format is UnixFS - return responseFormat == "" -} - -// Deprecated: legacy ipfs-404.html files are superseded by _redirects file -// This is provided only for backward-compatibility, until websites migrate -// to 404s managed via _redirects file (https://github.com/ipfs/specs/pull/290) -func (i *gatewayHandler) serveLegacy404IfPresent(w http.ResponseWriter, r *http.Request, contentPath ipath.Path) bool { - resolved404Path, ctype, err := i.searchUpTreeFor404(r, contentPath) - if err != nil { - return false - } - - dr, err := i.api.Unixfs().Get(r.Context(), resolved404Path) - if err != nil { - return false - } - defer dr.Close() - - f, ok := dr.(files.File) - if !ok { - return false - } - - size, err := f.Size() - if err != nil { - return false - } - - log.Debugw("using pretty 404 file", "path", contentPath) - w.Header().Set("Content-Type", ctype) - w.Header().Set("Content-Length", strconv.FormatInt(size, 10)) - w.WriteHeader(http.StatusNotFound) - _, err = io.CopyN(w, f, size) - return err == nil -} - -func (i *gatewayHandler) searchUpTreeFor404(r *http.Request, contentPath ipath.Path) (ipath.Resolved, string, error) { - filename404, ctype, err := preferred404Filename(r.Header.Values("Accept")) - if err != nil { - return nil, "", err - } - - pathComponents := strings.Split(contentPath.String(), "/") - - for idx := len(pathComponents); idx >= 3; idx-- { - pretty404 := gopath.Join(append(pathComponents[0:idx], filename404)...) - parsed404Path := ipath.New("/" + pretty404) - if parsed404Path.IsValid() != nil { - break - } - resolvedPath, err := i.api.ResolvePath(r.Context(), parsed404Path) - if err != nil { - continue - } - return resolvedPath, ctype, nil - } - - return nil, "", fmt.Errorf("no pretty 404 in any parent folder") -} - -func preferred404Filename(acceptHeaders []string) (string, string, error) { - // If we ever want to offer a 404 file for a different content type - // then this function will need to parse q weightings, but for now - // the presence of anything matching HTML is enough. - for _, acceptHeader := range acceptHeaders { - accepted := strings.Split(acceptHeader, ",") - for _, spec := range accepted { - contentType := strings.SplitN(spec, ";", 1)[0] - switch contentType { - case "*/*", "text/*", "text/html": - return "ipfs-404.html", "text/html", nil - } - } - } - - return "", "", fmt.Errorf("there is no 404 file for the requested content types") -} diff --git a/core/corehttp/gateway_handler_unixfs_dir.go b/core/corehttp/gateway_handler_unixfs_dir.go deleted file mode 100644 index 5e90a8a79..000000000 --- a/core/corehttp/gateway_handler_unixfs_dir.go +++ /dev/null @@ -1,210 +0,0 @@ -package corehttp - -import ( - "context" - "net/http" - "net/url" - gopath "path" - "strings" - "time" - - "github.com/dustin/go-humanize" - cid "github.com/ipfs/go-cid" - files "github.com/ipfs/go-ipfs-files" - path "github.com/ipfs/go-path" - "github.com/ipfs/go-path/resolver" - options "github.com/ipfs/interface-go-ipfs-core/options" - ipath "github.com/ipfs/interface-go-ipfs-core/path" - "github.com/ipfs/kubo/assets" - "github.com/ipfs/kubo/tracing" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/trace" - "go.uber.org/zap" -) - -// serveDirectory returns the best representation of UnixFS directory -// -// It will return index.html if present, or generate directory listing otherwise. -func (i *gatewayHandler) serveDirectory(ctx context.Context, w http.ResponseWriter, r *http.Request, resolvedPath ipath.Resolved, contentPath ipath.Path, dir files.Directory, begin time.Time, logger *zap.SugaredLogger) { - ctx, span := tracing.Span(ctx, "Gateway", "ServeDirectory", trace.WithAttributes(attribute.String("path", resolvedPath.String()))) - defer span.End() - - // HostnameOption might have constructed an IPNS/IPFS path using the Host header. - // In this case, we need the original path for constructing redirects - // and links that match the requested URL. - // For example, http://example.net would become /ipns/example.net, and - // the redirects and links would end up as http://example.net/ipns/example.net - requestURI, err := url.ParseRequestURI(r.RequestURI) - if err != nil { - webError(w, "failed to parse request path", err, http.StatusInternalServerError) - return - } - originalURLPath := requestURI.Path - - // Ensure directory paths end with '/' - if originalURLPath[len(originalURLPath)-1] != '/' { - // don't redirect to trailing slash if it's go get - // https://github.com/ipfs/kubo/pull/3963 - goget := r.URL.Query().Get("go-get") == "1" - if !goget { - suffix := "/" - // preserve query parameters - if r.URL.RawQuery != "" { - suffix = suffix + "?" + r.URL.RawQuery - } - // /ipfs/cid/foo?bar must be redirected to /ipfs/cid/foo/?bar - redirectURL := originalURLPath + suffix - logger.Debugw("directory location moved permanently", "status", http.StatusMovedPermanently) - http.Redirect(w, r, redirectURL, http.StatusMovedPermanently) - return - } - } - - // Check if directory has index.html, if so, serveFile - idxPath := ipath.Join(contentPath, "index.html") - idx, err := i.api.Unixfs().Get(ctx, idxPath) - switch err.(type) { - case nil: - f, ok := idx.(files.File) - if !ok { - internalWebError(w, files.ErrNotReader) - return - } - - logger.Debugw("serving index.html file", "path", idxPath) - // write to request - i.serveFile(ctx, w, r, resolvedPath, idxPath, f, begin) - return - case resolver.ErrNoLink: - logger.Debugw("no index.html; noop", "path", idxPath) - default: - internalWebError(w, err) - return - } - - // See statusResponseWriter.WriteHeader - // and https://github.com/ipfs/kubo/issues/7164 - // Note: this needs to occur before listingTemplate.Execute otherwise we get - // superfluous response.WriteHeader call from prometheus/client_golang - if w.Header().Get("Location") != "" { - logger.Debugw("location moved permanently", "status", http.StatusMovedPermanently) - w.WriteHeader(http.StatusMovedPermanently) - return - } - - // A HTML directory index will be presented, be sure to set the correct - // type instead of relying on autodetection (which may fail). - w.Header().Set("Content-Type", "text/html") - - // Generated dir index requires custom Etag (output may change between go-ipfs versions) - dirEtag := getDirListingEtag(resolvedPath.Cid()) - w.Header().Set("Etag", dirEtag) - - if r.Method == http.MethodHead { - logger.Debug("return as request's HTTP method is HEAD") - return - } - - // Optimization: use Unixfs.Ls without resolving children, but using the - // cumulative DAG size as the file size. This allows for a fast listing - // while keeping a good enough Size field. - results, err := i.api.Unixfs().Ls(ctx, - resolvedPath, - options.Unixfs.ResolveChildren(false), - options.Unixfs.UseCumulativeSize(true), - ) - if err != nil { - internalWebError(w, err) - return - } - - dirListing := make([]directoryItem, 0, len(results)) - for link := range results { - if link.Err != nil { - internalWebError(w, err) - return - } - - hash := link.Cid.String() - di := directoryItem{ - Size: humanize.Bytes(uint64(link.Size)), - Name: link.Name, - Path: gopath.Join(originalURLPath, link.Name), - Hash: hash, - ShortHash: shortHash(hash), - } - dirListing = append(dirListing, di) - } - - // construct the correct back link - // https://github.com/ipfs/kubo/issues/1365 - backLink := originalURLPath - - // don't go further up than /ipfs/$hash/ - pathSplit := path.SplitList(contentPath.String()) - switch { - // skip backlink when listing a content root - case len(pathSplit) == 3: // url: /ipfs/$hash - backLink = "" - - // skip backlink when listing a content root - case len(pathSplit) == 4 && pathSplit[3] == "": // url: /ipfs/$hash/ - backLink = "" - - // add the correct link depending on whether the path ends with a slash - default: - if strings.HasSuffix(backLink, "/") { - backLink += ".." - } else { - backLink += "/.." - } - } - - size := "?" - if s, err := dir.Size(); err == nil { - // Size may not be defined/supported. Continue anyways. - size = humanize.Bytes(uint64(s)) - } - - hash := resolvedPath.Cid().String() - - // Gateway root URL to be used when linking to other rootIDs. - // This will be blank unless subdomain or DNSLink resolution is being used - // for this request. - var gwURL string - - // Get gateway hostname and build gateway URL. - if h, ok := r.Context().Value(requestContextKey("gw-hostname")).(string); ok { - gwURL = "//" + h - } else { - gwURL = "" - } - - dnslink := hasDNSLinkOrigin(gwURL, contentPath.String()) - - // See comment above where originalUrlPath is declared. - tplData := listingTemplateData{ - GatewayURL: gwURL, - DNSLink: dnslink, - Listing: dirListing, - Size: size, - Path: contentPath.String(), - Breadcrumbs: breadcrumbs(contentPath.String(), dnslink), - BackLink: backLink, - Hash: hash, - } - - logger.Debugw("request processed", "tplDataDNSLink", dnslink, "tplDataSize", size, "tplDataBackLink", backLink, "tplDataHash", hash) - - if err := listingTemplate.Execute(w, tplData); err != nil { - internalWebError(w, err) - return - } - - // Update metrics - i.unixfsGenDirGetMetric.WithLabelValues(contentPath.Namespace()).Observe(time.Since(begin).Seconds()) -} - -func getDirListingEtag(dirCid cid.Cid) string { - return `"DirIndex-` + assets.AssetHash + `_CID-` + dirCid.String() + `"` -} diff --git a/core/corehttp/gateway_handler_unixfs_file.go b/core/corehttp/gateway_handler_unixfs_file.go deleted file mode 100644 index 9463be1ac..000000000 --- a/core/corehttp/gateway_handler_unixfs_file.go +++ /dev/null @@ -1,104 +0,0 @@ -package corehttp - -import ( - "context" - "fmt" - "io" - "mime" - "net/http" - gopath "path" - "strings" - "time" - - "github.com/gabriel-vasile/mimetype" - files "github.com/ipfs/go-ipfs-files" - ipath "github.com/ipfs/interface-go-ipfs-core/path" - "github.com/ipfs/kubo/tracing" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/trace" -) - -// serveFile returns data behind a file along with HTTP headers based on -// the file itself, its CID and the contentPath used for accessing it. -func (i *gatewayHandler) serveFile(ctx context.Context, w http.ResponseWriter, r *http.Request, resolvedPath ipath.Resolved, contentPath ipath.Path, file files.File, begin time.Time) { - _, span := tracing.Span(ctx, "Gateway", "ServeFile", trace.WithAttributes(attribute.String("path", resolvedPath.String()))) - defer span.End() - - // Set Cache-Control and read optional Last-Modified time - modtime := addCacheControlHeaders(w, r, contentPath, resolvedPath.Cid()) - - // Set Content-Disposition - name := addContentDispositionHeader(w, r, contentPath) - - // Prepare size value for Content-Length HTTP header (set inside of http.ServeContent) - size, err := file.Size() - if err != nil { - http.Error(w, "cannot serve files with unknown sizes", http.StatusBadGateway) - return - } - - if size == 0 { - // We override null files to 200 to avoid issues with fragment caching reverse proxies. - // Also whatever you are asking for, it's cheaper to just give you the complete file (nothing). - // TODO: remove this if clause once https://github.com/golang/go/issues/54794 is fixed in two latest releases of go - w.Header().Set("Content-Type", "text/plain") - w.WriteHeader(http.StatusOK) - return - } - - // Lazy seeker enables efficient range-requests and HTTP HEAD responses - content := &lazySeeker{ - size: size, - reader: file, - } - - // Calculate deterministic value for Content-Type HTTP header - // (we prefer to do it here, rather than using implicit sniffing in http.ServeContent) - var ctype string - if _, isSymlink := file.(*files.Symlink); isSymlink { - // We should be smarter about resolving symlinks but this is the - // "most correct" we can be without doing that. - ctype = "inode/symlink" - } else { - ctype = mime.TypeByExtension(gopath.Ext(name)) - if ctype == "" { - // uses https://github.com/gabriel-vasile/mimetype library to determine the content type. - // Fixes https://github.com/ipfs/kubo/issues/7252 - mimeType, err := mimetype.DetectReader(content) - if err != nil { - http.Error(w, fmt.Sprintf("cannot detect content-type: %s", err.Error()), http.StatusInternalServerError) - return - } - - ctype = mimeType.String() - _, err = content.Seek(0, io.SeekStart) - if err != nil { - http.Error(w, "seeker can't seek", http.StatusInternalServerError) - return - } - } - // Strip the encoding from the HTML Content-Type header and let the - // browser figure it out. - // - // Fixes https://github.com/ipfs/kubo/issues/2203 - if strings.HasPrefix(ctype, "text/html;") { - ctype = "text/html" - } - } - // Setting explicit Content-Type to avoid mime-type sniffing on the client - // (unifies behavior across gateways and web browsers) - w.Header().Set("Content-Type", ctype) - - // special fixup around redirects - w = &statusResponseWriter{w} - - // ServeContent will take care of - // If-None-Match+Etag, Content-Length and range requests - _, dataSent, _ := ServeContent(w, r, name, modtime, content) - - // Was response successful? - if dataSent { - // Update metrics - i.unixfsFileGetMetric.WithLabelValues(contentPath.Namespace()).Observe(time.Since(begin).Seconds()) - } -} diff --git a/core/corehttp/gateway_indexPage.go b/core/corehttp/gateway_indexPage.go deleted file mode 100644 index b0db8ac1a..000000000 --- a/core/corehttp/gateway_indexPage.go +++ /dev/null @@ -1,133 +0,0 @@ -package corehttp - -import ( - "html/template" - "net/url" - "path" - "strings" - - ipfspath "github.com/ipfs/go-path" - "github.com/ipfs/kubo/assets" -) - -// structs for directory listing -type listingTemplateData struct { - GatewayURL string - DNSLink bool - Listing []directoryItem - Size string - Path string - Breadcrumbs []breadcrumb - BackLink string - Hash string -} - -type directoryItem struct { - Size string - Name string - Path string - Hash string - ShortHash string -} - -type breadcrumb struct { - Name string - Path string -} - -func breadcrumbs(urlPath string, dnslinkOrigin bool) []breadcrumb { - var ret []breadcrumb - - p, err := ipfspath.ParsePath(urlPath) - if err != nil { - // No breadcrumbs, fallback to bare Path in template - return ret - } - segs := p.Segments() - contentRoot := segs[1] - for i, seg := range segs { - if i == 0 { - ret = append(ret, breadcrumb{Name: seg}) - } else { - ret = append(ret, breadcrumb{ - Name: seg, - Path: "/" + strings.Join(segs[0:i+1], "/"), - }) - } - } - - // Drop the /ipns/ prefix from breadcrumb Paths when directory - // listing on a DNSLink website (loaded due to Host header in HTTP - // request). Necessary because the hostname most likely won't have a - // public gateway mounted. - if dnslinkOrigin { - prefix := "/ipns/" + contentRoot - for i, crumb := range ret { - if strings.HasPrefix(crumb.Path, prefix) { - ret[i].Path = strings.Replace(crumb.Path, prefix, "", 1) - } - } - // Make contentRoot breadcrumb link to the website root - ret[1].Path = "/" - } - - return ret -} - -func shortHash(hash string) string { - if len(hash) <= 8 { - return hash - } - return (hash[0:4] + "\u2026" + hash[len(hash)-4:]) -} - -// helper to detect DNSLink website context -// (when hostname from gwURL is matching /ipns/ in path) -func hasDNSLinkOrigin(gwURL string, path string) bool { - if gwURL != "" { - fqdn := stripPort(strings.TrimPrefix(gwURL, "//")) - return strings.HasPrefix(path, "/ipns/"+fqdn) - } - return false -} - -var listingTemplate *template.Template - -func init() { - knownIconsBytes, err := assets.Asset.ReadFile("dir-index-html/knownIcons.txt") - if err != nil { - panic(err) - } - knownIcons := make(map[string]struct{}) - for _, ext := range strings.Split(strings.TrimSuffix(string(knownIconsBytes), "\n"), "\n") { - knownIcons[ext] = struct{}{} - } - - // helper to guess the type/icon for it by the extension name - iconFromExt := func(name string) string { - ext := path.Ext(name) - _, ok := knownIcons[ext] - if !ok { - // default blank icon - return "ipfs-_blank" - } - return "ipfs-" + ext[1:] // slice of the first dot - } - - // custom template-escaping function to escape a full path, including '#' and '?' - urlEscape := func(rawUrl string) string { - pathURL := url.URL{Path: rawUrl} - return pathURL.String() - } - - // Directory listing template - dirIndexBytes, err := assets.Asset.ReadFile("dir-index-html/dir-index.html") - if err != nil { - panic(err) - } - - listingTemplate = template.Must(template.New("dir").Funcs(template.FuncMap{ - "iconFromExt": iconFromExt, - "urlEscape": urlEscape, - }).Parse(string(dirIndexBytes))) -} diff --git a/core/corehttp/gateway_test.go b/core/corehttp/gateway_test.go index 0d2f07dbe..df307ef73 100644 --- a/core/corehttp/gateway_test.go +++ b/core/corehttp/gateway_test.go @@ -6,73 +6,67 @@ import ( "io" "net/http" "net/http/httptest" - "regexp" "strings" "testing" - "time" - namesys "github.com/ipfs/go-namesys" + "github.com/ipfs/boxo/namesys" version "github.com/ipfs/kubo" - core "github.com/ipfs/kubo/core" + "github.com/ipfs/kubo/core" "github.com/ipfs/kubo/core/coreapi" - repo "github.com/ipfs/kubo/repo" + "github.com/ipfs/kubo/repo" + "github.com/stretchr/testify/assert" - datastore "github.com/ipfs/go-datastore" + "github.com/ipfs/boxo/path" + "github.com/ipfs/go-datastore" syncds "github.com/ipfs/go-datastore/sync" - files "github.com/ipfs/go-ipfs-files" - path "github.com/ipfs/go-path" - iface "github.com/ipfs/interface-go-ipfs-core" - nsopts "github.com/ipfs/interface-go-ipfs-core/options/namesys" - ipath "github.com/ipfs/interface-go-ipfs-core/path" - config "github.com/ipfs/kubo/config" + "github.com/ipfs/kubo/config" + iface "github.com/ipfs/kubo/core/coreiface" ci "github.com/libp2p/go-libp2p/core/crypto" - id "github.com/libp2p/go-libp2p/p2p/protocol/identify" ) -// `ipfs object new unixfs-dir` -var emptyDir = "/ipfs/QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn" - type mockNamesys map[string]path.Path -func (m mockNamesys) Resolve(ctx context.Context, name string, opts ...nsopts.ResolveOpt) (value path.Path, err error) { - cfg := nsopts.DefaultResolveOpts() +func (m mockNamesys) Resolve(ctx context.Context, p path.Path, opts ...namesys.ResolveOption) (namesys.Result, error) { + cfg := namesys.DefaultResolveOptions() for _, o := range opts { o(&cfg) } depth := cfg.Depth - if depth == nsopts.UnlimitedDepth { + if depth == namesys.UnlimitedDepth { // max uint depth = ^uint(0) } + var ( + value path.Path + ) + name := path.SegmentsToString(p.Segments()[:2]...) for strings.HasPrefix(name, "/ipns/") { if depth == 0 { - return value, namesys.ErrResolveRecursion + return namesys.Result{Path: value}, namesys.ErrResolveRecursion } depth-- - var ok bool - value, ok = m[name] + v, ok := m[name] if !ok { - return "", namesys.ErrResolveFailed + return namesys.Result{}, namesys.ErrResolveFailed } + value = v name = value.String() } - return value, nil + + value, err := path.Join(value, p.Segments()[2:]...) + return namesys.Result{Path: value}, err } -func (m mockNamesys) ResolveAsync(ctx context.Context, name string, opts ...nsopts.ResolveOpt) <-chan namesys.Result { - out := make(chan namesys.Result, 1) - v, err := m.Resolve(ctx, name, opts...) - out <- namesys.Result{Path: v, Err: err} +func (m mockNamesys) ResolveAsync(ctx context.Context, p path.Path, opts ...namesys.ResolveOption) <-chan namesys.AsyncResult { + out := make(chan namesys.AsyncResult, 1) + res, err := m.Resolve(ctx, p, opts...) + out <- namesys.AsyncResult{Path: res.Path, TTL: res.TTL, LastMod: res.LastMod, Err: err} close(out) return out } -func (m mockNamesys) Publish(ctx context.Context, name ci.PrivKey, value path.Path) error { - return errors.New("not implemented for mockNamesys") -} - -func (m mockNamesys) PublishWithEOL(ctx context.Context, name ci.PrivKey, value path.Path, _ time.Time) error { +func (m mockNamesys) Publish(ctx context.Context, name ci.PrivKey, value path.Path, opts ...namesys.PublishOption) error { return errors.New("not implemented for mockNamesys") } @@ -132,10 +126,10 @@ func newTestServerAndNode(t *testing.T, ns mockNamesys) (*httptest.Server, iface ts := httptest.NewServer(dh) t.Cleanup(func() { ts.Close() }) - dh.Handler, err = makeHandler(n, + dh.Handler, err = MakeHandler(n, ts.Listener, HostnameOption(), - GatewayOption(false, "/ipfs", "/ipns"), + GatewayOption("/ipfs", "/ipns"), VersionOption(), ) if err != nil { @@ -150,478 +144,6 @@ func newTestServerAndNode(t *testing.T, ns mockNamesys) (*httptest.Server, iface return ts, api, n.Context() } -func matchPathOrBreadcrumbs(s string, expected string) bool { - matched, _ := regexp.MatchString("Index of\n[\t ]*"+regexp.QuoteMeta(expected), s) - return matched -} - -func TestUriQueryRedirect(t *testing.T) { - ts, _, _ := newTestServerAndNode(t, mockNamesys{}) - - cid := "QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR" - for i, test := range []struct { - path string - status int - location string - }{ - // - Browsers will send original URI in URL-escaped form - // - We expect query parameters to be persisted - // - We drop fragments, as those should not be sent by a browser - {"/ipfs/?uri=ipfs%3A%2F%2FQmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco%2Fwiki%2FFoo_%C4%85%C4%99.html%3Ffilename%3Dtest-%C4%99.html%23header-%C4%85", http.StatusMovedPermanently, "/ipfs/QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco/wiki/Foo_%c4%85%c4%99.html?filename=test-%c4%99.html"}, - {"/ipfs/?uri=ipns%3A%2F%2Fexample.com%2Fwiki%2FFoo_%C4%85%C4%99.html%3Ffilename%3Dtest-%C4%99.html", http.StatusMovedPermanently, "/ipns/example.com/wiki/Foo_%c4%85%c4%99.html?filename=test-%c4%99.html"}, - {"/ipfs/?uri=ipfs://" + cid, http.StatusMovedPermanently, "/ipfs/" + cid}, - {"/ipfs?uri=ipfs://" + cid, http.StatusMovedPermanently, "/ipfs/?uri=ipfs://" + cid}, - {"/ipfs/?uri=ipns://" + cid, http.StatusMovedPermanently, "/ipns/" + cid}, - {"/ipns/?uri=ipfs%3A%2F%2FQmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco%2Fwiki%2FFoo_%C4%85%C4%99.html%3Ffilename%3Dtest-%C4%99.html%23header-%C4%85", http.StatusMovedPermanently, "/ipfs/QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco/wiki/Foo_%c4%85%c4%99.html?filename=test-%c4%99.html"}, - {"/ipns/?uri=ipns%3A%2F%2Fexample.com%2Fwiki%2FFoo_%C4%85%C4%99.html%3Ffilename%3Dtest-%C4%99.html", http.StatusMovedPermanently, "/ipns/example.com/wiki/Foo_%c4%85%c4%99.html?filename=test-%c4%99.html"}, - {"/ipns?uri=ipns://" + cid, http.StatusMovedPermanently, "/ipns/?uri=ipns://" + cid}, - {"/ipns/?uri=ipns://" + cid, http.StatusMovedPermanently, "/ipns/" + cid}, - {"/ipns/?uri=ipfs://" + cid, http.StatusMovedPermanently, "/ipfs/" + cid}, - {"/ipfs/?uri=unsupported://" + cid, http.StatusBadRequest, ""}, - {"/ipfs/?uri=invaliduri", http.StatusBadRequest, ""}, - {"/ipfs/?uri=" + cid, http.StatusBadRequest, ""}, - } { - - r, err := http.NewRequest(http.MethodGet, ts.URL+test.path, nil) - if err != nil { - t.Fatal(err) - } - resp, err := doWithoutRedirect(r) - if err != nil { - t.Fatal(err) - } - defer resp.Body.Close() - - if resp.StatusCode != test.status { - t.Errorf("(%d) got %d, expected %d from %s", i, resp.StatusCode, test.status, ts.URL+test.path) - } - - locHdr := resp.Header.Get("Location") - if locHdr != test.location { - t.Errorf("(%d) location header got %s, expected %s from %s", i, locHdr, test.location, ts.URL+test.path) - } - } -} - -func TestGatewayGet(t *testing.T) { - ns := mockNamesys{} - ts, api, ctx := newTestServerAndNode(t, ns) - - k, err := api.Unixfs().Add(ctx, files.NewBytesFile([]byte("fnord"))) - if err != nil { - t.Fatal(err) - } - ns["/ipns/example.com"] = path.FromString(k.String()) - ns["/ipns/working.example.com"] = path.FromString(k.String()) - ns["/ipns/double.example.com"] = path.FromString("/ipns/working.example.com") - ns["/ipns/triple.example.com"] = path.FromString("/ipns/double.example.com") - ns["/ipns/broken.example.com"] = path.FromString("/ipns/" + k.Cid().String()) - // We picked .man because: - // 1. It's a valid TLD. - // 2. Go treats it as the file extension for "man" files (even though - // nobody actually *uses* this extension, AFAIK). - // - // Unfortunately, this may not work on all platforms as file type - // detection is platform dependent. - ns["/ipns/example.man"] = path.FromString(k.String()) - - t.Log(ts.URL) - for i, test := range []struct { - host string - path string - status int - text string - }{ - {"127.0.0.1:8080", "/", http.StatusNotFound, "404 page not found\n"}, - {"127.0.0.1:8080", "/" + k.Cid().String(), http.StatusNotFound, "404 page not found\n"}, - {"127.0.0.1:8080", k.String(), http.StatusOK, "fnord"}, - {"127.0.0.1:8080", "/ipns/nxdomain.example.com", http.StatusBadRequest, "ipfs resolve -r /ipns/nxdomain.example.com: " + namesys.ErrResolveFailed.Error() + "\n"}, - {"127.0.0.1:8080", "/ipns/%0D%0A%0D%0Ahello", http.StatusBadRequest, "ipfs resolve -r /ipns/\\r\\n\\r\\nhello: " + namesys.ErrResolveFailed.Error() + "\n"}, - {"127.0.0.1:8080", "/ipns/example.com", http.StatusOK, "fnord"}, - {"example.com", "/", http.StatusOK, "fnord"}, - - {"working.example.com", "/", http.StatusOK, "fnord"}, - {"double.example.com", "/", http.StatusOK, "fnord"}, - {"triple.example.com", "/", http.StatusOK, "fnord"}, - {"working.example.com", k.String(), http.StatusNotFound, "ipfs resolve -r /ipns/working.example.com" + k.String() + ": no link named \"ipfs\" under " + k.Cid().String() + "\n"}, - {"broken.example.com", "/", http.StatusBadRequest, "ipfs resolve -r /ipns/broken.example.com/: " + namesys.ErrResolveFailed.Error() + "\n"}, - {"broken.example.com", k.String(), http.StatusBadRequest, "ipfs resolve -r /ipns/broken.example.com" + k.String() + ": " + namesys.ErrResolveFailed.Error() + "\n"}, - // This test case ensures we don't treat the TLD as a file extension. - {"example.man", "/", http.StatusOK, "fnord"}, - } { - var c http.Client - r, err := http.NewRequest(http.MethodGet, ts.URL+test.path, nil) - if err != nil { - t.Fatal(err) - } - r.Host = test.host - resp, err := c.Do(r) - - urlstr := "http://" + test.host + test.path - if err != nil { - t.Errorf("error requesting %s: %s", urlstr, err) - continue - } - defer resp.Body.Close() - contentType := resp.Header.Get("Content-Type") - if contentType != "text/plain; charset=utf-8" { - t.Errorf("expected content type to be text/plain, got %s", contentType) - } - body, err := io.ReadAll(resp.Body) - if resp.StatusCode != test.status { - t.Errorf("(%d) got %d, expected %d from %s", i, resp.StatusCode, test.status, urlstr) - t.Errorf("Body: %s", body) - continue - } - if err != nil { - t.Fatalf("error reading response from %s: %s", urlstr, err) - } - if string(body) != test.text { - t.Errorf("unexpected response body from %s: expected %q; got %q", urlstr, test.text, body) - continue - } - } -} - -func TestPretty404(t *testing.T) { - ns := mockNamesys{} - ts, api, ctx := newTestServerAndNode(t, ns) - - f1 := files.NewMapDirectory(map[string]files.Node{ - "ipfs-404.html": files.NewBytesFile([]byte("Custom 404")), - "deeper": files.NewMapDirectory(map[string]files.Node{ - "ipfs-404.html": files.NewBytesFile([]byte("Deep custom 404")), - }), - }) - - k, err := api.Unixfs().Add(ctx, f1) - if err != nil { - t.Fatal(err) - } - - host := "example.net" - ns["/ipns/"+host] = path.FromString(k.String()) - - for _, test := range []struct { - path string - accept string - status int - text string - }{ - {"/ipfs-404.html", "text/html", http.StatusOK, "Custom 404"}, - {"/nope", "text/html", http.StatusNotFound, "Custom 404"}, - {"/nope", "text/*", http.StatusNotFound, "Custom 404"}, - {"/nope", "*/*", http.StatusNotFound, "Custom 404"}, - {"/nope", "application/json", http.StatusNotFound, "ipfs resolve -r /ipns/example.net/nope: no link named \"nope\" under QmcmnF7XG5G34RdqYErYDwCKNFQ6jb8oKVR21WAJgubiaj\n"}, - {"/deeper/nope", "text/html", http.StatusNotFound, "Deep custom 404"}, - {"/deeper/", "text/html", http.StatusOK, ""}, - {"/deeper", "text/html", http.StatusOK, ""}, - {"/nope/nope", "text/html", http.StatusNotFound, "Custom 404"}, - } { - var c http.Client - req, err := http.NewRequest("GET", ts.URL+test.path, nil) - if err != nil { - t.Fatal(err) - } - req.Header.Add("Accept", test.accept) - req.Host = host - resp, err := c.Do(req) - - if err != nil { - t.Fatalf("error requesting %s: %s", test.path, err) - } - - defer resp.Body.Close() - if resp.StatusCode != test.status { - t.Fatalf("got %d, expected %d, from %s", resp.StatusCode, test.status, test.path) - } - body, err := io.ReadAll(resp.Body) - if err != nil { - t.Fatalf("error reading response from %s: %s", test.path, err) - } - - if test.text != "" && string(body) != test.text { - t.Fatalf("unexpected response body from %s: got %q, expected %q", test.path, body, test.text) - } - } -} - -func TestIPNSHostnameRedirect(t *testing.T) { - ns := mockNamesys{} - ts, api, ctx := newTestServerAndNode(t, ns) - t.Logf("test server url: %s", ts.URL) - - // create /ipns/example.net/foo/index.html - - f1 := files.NewMapDirectory(map[string]files.Node{ - "_": files.NewBytesFile([]byte("_")), - "foo": files.NewMapDirectory(map[string]files.Node{ - "index.html": files.NewBytesFile([]byte("_")), - }), - }) - - k, err := api.Unixfs().Add(ctx, f1) - if err != nil { - t.Fatal(err) - } - - t.Logf("k: %s\n", k) - ns["/ipns/example.net"] = path.FromString(k.String()) - - // make request to directory containing index.html - req, err := http.NewRequest(http.MethodGet, ts.URL+"/foo", nil) - if err != nil { - t.Fatal(err) - } - req.Host = "example.net" - - res, err := doWithoutRedirect(req) - if err != nil { - t.Fatal(err) - } - - // expect 301 redirect to same path, but with trailing slash - if res.StatusCode != 301 { - t.Errorf("status is %d, expected 301", res.StatusCode) - } - hdr := res.Header["Location"] - if len(hdr) < 1 { - t.Errorf("location header not present") - } else if hdr[0] != "/foo/" { - t.Errorf("location header is %v, expected /foo/", hdr[0]) - } - - // make request with prefix to directory containing index.html - req, err = http.NewRequest(http.MethodGet, ts.URL+"/foo", nil) - if err != nil { - t.Fatal(err) - } - req.Host = "example.net" - - res, err = doWithoutRedirect(req) - if err != nil { - t.Fatal(err) - } - - // expect 301 redirect to same path, but with prefix and trailing slash - if res.StatusCode != 301 { - t.Errorf("status is %d, expected 301", res.StatusCode) - } - hdr = res.Header["Location"] - if len(hdr) < 1 { - t.Errorf("location header not present") - } else if hdr[0] != "/foo/" { - t.Errorf("location header is %v, expected /foo/", hdr[0]) - } - - // make sure /version isn't exposed - req, err = http.NewRequest(http.MethodGet, ts.URL+"/version", nil) - if err != nil { - t.Fatal(err) - } - req.Host = "example.net" - - res, err = doWithoutRedirect(req) - if err != nil { - t.Fatal(err) - } - - if res.StatusCode != 404 { - t.Fatalf("expected a 404 error, got: %s", res.Status) - } -} - -// Test directory listing on DNSLink website -// (scenario when Host header is the same as URL hostname) -// This is basic regression test: additional end-to-end tests -// can be found in test/sharness/t0115-gateway-dir-listing.sh -func TestIPNSHostnameBacklinks(t *testing.T) { - ns := mockNamesys{} - ts, api, ctx := newTestServerAndNode(t, ns) - t.Logf("test server url: %s", ts.URL) - - f1 := files.NewMapDirectory(map[string]files.Node{ - "file.txt": files.NewBytesFile([]byte("1")), - "foo? #<'": files.NewMapDirectory(map[string]files.Node{ - "file.txt": files.NewBytesFile([]byte("2")), - "bar": files.NewMapDirectory(map[string]files.Node{ - "file.txt": files.NewBytesFile([]byte("3")), - }), - }), - }) - - // create /ipns/example.net/foo/ - k, err := api.Unixfs().Add(ctx, f1) - if err != nil { - t.Fatal(err) - } - - k2, err := api.ResolvePath(ctx, ipath.Join(k, "foo? #<'")) - if err != nil { - t.Fatal(err) - } - - k3, err := api.ResolvePath(ctx, ipath.Join(k, "foo? #<'/bar")) - if err != nil { - t.Fatal(err) - } - - t.Logf("k: %s\n", k) - ns["/ipns/example.net"] = path.FromString(k.String()) - - // make request to directory listing - req, err := http.NewRequest(http.MethodGet, ts.URL+"/foo%3F%20%23%3C%27/", nil) - if err != nil { - t.Fatal(err) - } - req.Host = "example.net" - - res, err := doWithoutRedirect(req) - if err != nil { - t.Fatal(err) - } - - // expect correct links - body, err := io.ReadAll(res.Body) - if err != nil { - t.Fatalf("error reading response: %s", err) - } - s := string(body) - t.Logf("body: %s\n", string(body)) - - if !matchPathOrBreadcrumbs(s, "/ipns/example.net/foo? #<'") { - t.Fatalf("expected a path in directory listing") - } - if !strings.Contains(s, "") { - t.Fatalf("expected backlink in directory listing") - } - if !strings.Contains(s, "") { - t.Fatalf("expected file in directory listing") - } - if !strings.Contains(s, "") { - t.Fatalf("expected no backlink in directory listing of the root CID") - } - if !strings.Contains(s, "") { - t.Fatalf("expected file in directory listing") - } - if !strings.Contains(s, "example.net/foo? #<'/bar") { - t.Fatalf("expected a path in directory listing") - } - if !strings.Contains(s, "") { - t.Fatalf("expected backlink in directory listing") - } - if !strings.Contains(s, "") { - t.Fatalf("expected file in directory listing") - } - if !strings.Contains(s, k3.Cid().String()) { - t.Fatalf("expected hash in directory listing") - } -} - -func TestCacheControlImmutable(t *testing.T) { - ts, _, _ := newTestServerAndNode(t, nil) - t.Logf("test server url: %s", ts.URL) - - req, err := http.NewRequest(http.MethodGet, ts.URL+emptyDir+"/", nil) - if err != nil { - t.Fatal(err) - } - - res, err := doWithoutRedirect(req) - if err != nil { - t.Fatal(err) - } - - // check the immutable tag isn't set - hdrs, ok := res.Header["Cache-Control"] - if ok { - for _, hdr := range hdrs { - if strings.Contains(hdr, "immutable") { - t.Fatalf("unexpected Cache-Control: immutable on directory listing: %s", hdr) - } - } - } -} - -func TestGoGetSupport(t *testing.T) { - ts, _, _ := newTestServerAndNode(t, nil) - t.Logf("test server url: %s", ts.URL) - - // mimic go-get - req, err := http.NewRequest(http.MethodGet, ts.URL+emptyDir+"?go-get=1", nil) - if err != nil { - t.Fatal(err) - } - - res, err := doWithoutRedirect(req) - if err != nil { - t.Fatal(err) - } - - if res.StatusCode != 200 { - t.Errorf("status is %d, expected 200", res.StatusCode) - } -} - func TestVersion(t *testing.T) { version.CurrentCommit = "theshortcommithash" @@ -651,33 +173,43 @@ func TestVersion(t *testing.T) { if !strings.Contains(s, "Client Version: "+version.GetUserAgentVersion()) { t.Fatalf("response doesn't contain client version:\n%s", s) } - - if !strings.Contains(s, "Protocol Version: "+id.DefaultProtocolVersion) { - t.Fatalf("response doesn't contain protocol version:\n%s", s) - } } -func TestEtagMatch(t *testing.T) { - for _, test := range []struct { - header string // value in If-None-Match HTTP header - cidEtag string - dirEtag string - expected bool // expected result of etagMatch(header, cidEtag, dirEtag) +func TestDeserializedResponsesInheritance(t *testing.T) { + for _, testCase := range []struct { + globalSetting config.Flag + gatewaySetting config.Flag + expectedGatewaySetting bool }{ - {"", `"etag"`, "", false}, // no If-None-Match - {"", "", `"etag"`, false}, // no If-None-Match - {`"etag"`, `"etag"`, "", true}, // file etag match - {`W/"etag"`, `"etag"`, "", true}, // file etag match - {`"foo", W/"bar", W/"etag"`, `"etag"`, "", true}, // file etag match (array) - {`"foo",W/"bar",W/"etag"`, `"etag"`, "", true}, // file etag match (compact array) - {`"etag"`, "", `W/"etag"`, true}, // dir etag match - {`"etag"`, "", `W/"etag"`, true}, // dir etag match - {`W/"etag"`, "", `W/"etag"`, true}, // dir etag match - {`*`, `"etag"`, "", true}, // wildcard etag match + {config.True, config.Default, true}, + {config.False, config.Default, false}, + {config.False, config.True, true}, + {config.True, config.False, false}, } { - result := etagMatch(test.header, test.cidEtag, test.dirEtag) - if result != test.expected { - t.Fatalf("unexpected result of etagMatch(%q, %q, %q), got %t, expected %t", test.header, test.cidEtag, test.dirEtag, result, test.expected) + c := config.Config{ + Identity: config.Identity{ + PeerID: "QmTFauExutTsy4XP6JbMFcw2Wa9645HJt2bTqL6qYDCKfe", // required by offline node + }, + Gateway: config.Gateway{ + DeserializedResponses: testCase.globalSetting, + PublicGateways: map[string]*config.GatewaySpec{ + "example.com": { + DeserializedResponses: testCase.gatewaySetting, + }, + }, + }, } + r := &repo.Mock{ + C: c, + D: syncds.MutexWrap(datastore.NewMapDatastore()), + } + n, err := core.NewNode(context.Background(), &core.BuildCfg{Repo: r}) + assert.NoError(t, err) + + gwCfg, _, err := getGatewayConfig(n) + assert.NoError(t, err) + + assert.Contains(t, gwCfg.PublicGateways, "example.com") + assert.Equal(t, testCase.expectedGatewaySetting, gwCfg.PublicGateways["example.com"].DeserializedResponses) } } diff --git a/core/corehttp/hostname.go b/core/corehttp/hostname.go deleted file mode 100644 index 39e857aad..000000000 --- a/core/corehttp/hostname.go +++ /dev/null @@ -1,603 +0,0 @@ -package corehttp - -import ( - "context" - "fmt" - "net" - "net/http" - "net/url" - "regexp" - "strings" - - cid "github.com/ipfs/go-cid" - namesys "github.com/ipfs/go-namesys" - core "github.com/ipfs/kubo/core" - coreapi "github.com/ipfs/kubo/core/coreapi" - "github.com/libp2p/go-libp2p/core/peer" - dns "github.com/miekg/dns" - - mbase "github.com/multiformats/go-multibase" - - iface "github.com/ipfs/interface-go-ipfs-core" - options "github.com/ipfs/interface-go-ipfs-core/options" - nsopts "github.com/ipfs/interface-go-ipfs-core/options/namesys" - config "github.com/ipfs/kubo/config" -) - -var defaultPaths = []string{"/ipfs/", "/ipns/", "/api/", "/p2p/"} - -var subdomainGatewaySpec = &config.GatewaySpec{ - Paths: defaultPaths, - UseSubdomains: true, -} - -var defaultKnownGateways = map[string]*config.GatewaySpec{ - "localhost": subdomainGatewaySpec, -} - -// Label's max length in DNS (https://tools.ietf.org/html/rfc1034#page-7) -const dnsLabelMaxLength int = 63 - -// HostnameOption rewrites an incoming request based on the Host header. -func HostnameOption() ServeOption { - return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) { - childMux := http.NewServeMux() - - coreAPI, err := coreapi.NewCoreAPI(n) - if err != nil { - return nil, err - } - - cfg, err := n.Repo.Config() - if err != nil { - return nil, err - } - - knownGateways := prepareKnownGateways(cfg.Gateway.PublicGateways) - - mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - // Unfortunately, many (well, ipfs.io) gateways use - // DNSLink so if we blindly rewrite with DNSLink, we'll - // break /ipfs links. - // - // We fix this by maintaining a list of known gateways - // and the paths that they serve "gateway" content on. - // That way, we can use DNSLink for everything else. - - // Support X-Forwarded-Host if added by a reverse proxy - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Host - host := r.Host - if xHost := r.Header.Get("X-Forwarded-Host"); xHost != "" { - host = xHost - } - - // HTTP Host & Path check: is this one of our "known gateways"? - if gw, ok := isKnownHostname(host, knownGateways); ok { - // This is a known gateway but request is not using - // the subdomain feature. - - // Does this gateway _handle_ this path? - if hasPrefix(r.URL.Path, gw.Paths...) { - // It does. - - // Should this gateway use subdomains instead of paths? - if gw.UseSubdomains { - // Yes, redirect if applicable - // Example: dweb.link/ipfs/{cid} → {cid}.ipfs.dweb.link - useInlinedDNSLink := gw.InlineDNSLink.WithDefault(config.DefaultInlineDNSLink) - newURL, err := toSubdomainURL(host, r.URL.Path, r, useInlinedDNSLink, coreAPI) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - if newURL != "" { - // Set "Location" header with redirect destination. - // It is ignored by curl in default mode, but will - // be respected by user agents that follow - // redirects by default, namely web browsers - w.Header().Set("Location", newURL) - - // Note: we continue regular gateway processing: - // HTTP Status Code http.StatusMovedPermanently - // will be set later, in statusResponseWriter - } - } - - // Not a subdomain resource, continue with path processing - // Example: 127.0.0.1:8080/ipfs/{CID}, ipfs.io/ipfs/{CID} etc - childMux.ServeHTTP(w, r) - return - } - // Not a whitelisted path - - // Try DNSLink, if it was not explicitly disabled for the hostname - if !gw.NoDNSLink && isDNSLinkName(r.Context(), coreAPI, host) { - // rewrite path and handle as DNSLink - r.URL.Path = "/ipns/" + stripPort(host) + r.URL.Path - childMux.ServeHTTP(w, withHostnameContext(r, host)) - return - } - - // If not, resource does not exist on the hostname, return 404 - http.NotFound(w, r) - return - } - - // HTTP Host check: is this one of our subdomain-based "known gateways"? - // IPFS details extracted from the host: {rootID}.{ns}.{gwHostname} - // /ipfs/ example: {cid}.ipfs.localhost:8080, {cid}.ipfs.dweb.link - // /ipns/ example: {libp2p-key}.ipns.localhost:8080, {inlined-dnslink-fqdn}.ipns.dweb.link - if gw, gwHostname, ns, rootID, ok := knownSubdomainDetails(host, knownGateways); ok { - // Looks like we're using a known gateway in subdomain mode. - - // Assemble original path prefix. - pathPrefix := "/" + ns + "/" + rootID - - // Retrieve whether or not we should inline DNSLink. - useInlinedDNSLink := gw.InlineDNSLink.WithDefault(config.DefaultInlineDNSLink) - - // Does this gateway _handle_ subdomains AND this path? - if !(gw.UseSubdomains && hasPrefix(pathPrefix, gw.Paths...)) { - // If not, resource does not exist, return 404 - http.NotFound(w, r) - return - } - - // Check if rootID is a valid CID - if rootCID, err := cid.Decode(rootID); err == nil { - // Do we need to redirect root CID to a canonical DNS representation? - dnsCID, err := toDNSLabel(rootID, rootCID) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - if !strings.HasPrefix(r.Host, dnsCID) { - dnsPrefix := "/" + ns + "/" + dnsCID - newURL, err := toSubdomainURL(gwHostname, dnsPrefix+r.URL.Path, r, useInlinedDNSLink, coreAPI) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - if newURL != "" { - // Redirect to deterministic CID to ensure CID - // always gets the same Origin on the web - http.Redirect(w, r, newURL, http.StatusMovedPermanently) - return - } - } - - // Do we need to fix multicodec in PeerID represented as CIDv1? - if isPeerIDNamespace(ns) { - if rootCID.Type() != cid.Libp2pKey { - newURL, err := toSubdomainURL(gwHostname, pathPrefix+r.URL.Path, r, useInlinedDNSLink, coreAPI) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - if newURL != "" { - // Redirect to CID fixed inside of toSubdomainURL() - http.Redirect(w, r, newURL, http.StatusMovedPermanently) - return - } - } - } - } else { // rootID is not a CID.. - - // Check if rootID is a single DNS label with an inlined - // DNSLink FQDN a single DNS label. We support this so - // loading DNSLink names over TLS "just works" on public - // HTTP gateways. - // - // Rationale for doing this can be found under "Option C" - // at: https://github.com/ipfs/in-web-browsers/issues/169 - // - // TLDR is: - // https://dweb.link/ipns/my.v-long.example.com - // can be loaded from a subdomain gateway with a wildcard - // TLS cert if represented as a single DNS label: - // https://my-v--long-example-com.ipns.dweb.link - if ns == "ipns" && !strings.Contains(rootID, ".") { - // if there is no TXT recordfor rootID - if !isDNSLinkName(r.Context(), coreAPI, rootID) { - // my-v--long-example-com → my.v-long.example.com - dnslinkFQDN := toDNSLinkFQDN(rootID) - if isDNSLinkName(r.Context(), coreAPI, dnslinkFQDN) { - // update path prefix to use real FQDN with DNSLink - pathPrefix = "/ipns/" + dnslinkFQDN - } - } - } - } - - // Rewrite the path to not use subdomains - r.URL.Path = pathPrefix + r.URL.Path - - // Serve path request - childMux.ServeHTTP(w, withHostnameContext(r, gwHostname)) - return - } - // We don't have a known gateway. Fallback on DNSLink lookup - - // Wildcard HTTP Host check: - // 1. is wildcard DNSLink enabled (Gateway.NoDNSLink=false)? - // 2. does Host header include a fully qualified domain name (FQDN)? - // 3. does DNSLink record exist in DNS? - if !cfg.Gateway.NoDNSLink && isDNSLinkName(r.Context(), coreAPI, host) { - // rewrite path and handle as DNSLink - r.URL.Path = "/ipns/" + stripPort(host) + r.URL.Path - ctx := context.WithValue(r.Context(), requestContextKey("dnslink-hostname"), host) - childMux.ServeHTTP(w, withHostnameContext(r.WithContext(ctx), host)) - return - } - - // else, treat it as an old school gateway, I guess. - childMux.ServeHTTP(w, r) - }) - return childMux, nil - } -} - -type gatewayHosts struct { - exact map[string]*config.GatewaySpec - wildcard []wildcardHost -} - -type wildcardHost struct { - re *regexp.Regexp - spec *config.GatewaySpec -} - -type requestContextKey string - -// Extends request context to include hostname of a canonical gateway root -// (subdomain root or dnslink fqdn) -func withHostnameContext(r *http.Request, hostname string) *http.Request { - // This is required for links on directory listing pages to work correctly - // on subdomain and dnslink gateways. While DNSlink could read value from - // Host header, subdomain gateways have more comples rules (knownSubdomainDetails) - // More: https://github.com/ipfs/dir-index-html/issues/42 - // nolint: staticcheck // non-backward compatible change - ctx := context.WithValue(r.Context(), requestContextKey("gw-hostname"), hostname) - return r.WithContext(ctx) -} - -func prepareKnownGateways(publicGateways map[string]*config.GatewaySpec) gatewayHosts { - var hosts gatewayHosts - - hosts.exact = make(map[string]*config.GatewaySpec, len(publicGateways)+len(defaultKnownGateways)) - - // First, implicit defaults such as subdomain gateway on localhost - for hostname, gw := range defaultKnownGateways { - hosts.exact[hostname] = gw - } - - // Then apply values from Gateway.PublicGateways, if present in the config - for hostname, gw := range publicGateways { - if gw == nil { - // Remove any implicit defaults, if present. This is useful when one - // wants to disable subdomain gateway on localhost etc. - delete(hosts.exact, hostname) - continue - } - if strings.Contains(hostname, "*") { - // from *.domain.tld, construct a regexp that match any direct subdomain - // of .domain.tld. - // - // Regexp will be in the form of ^[^.]+\.domain.tld(?::\d+)?$ - - escaped := strings.ReplaceAll(hostname, ".", `\.`) - regexed := strings.ReplaceAll(escaped, "*", "[^.]+") - - re, err := regexp.Compile(fmt.Sprintf(`^%s(?::\d+)?$`, regexed)) - if err != nil { - log.Warn("invalid wildcard gateway hostname \"%s\"", hostname) - } - - hosts.wildcard = append(hosts.wildcard, wildcardHost{re: re, spec: gw}) - } else { - hosts.exact[hostname] = gw - } - } - - return hosts -} - -// isKnownHostname checks Gateway.PublicGateways and returns matching -// GatewaySpec with graceful fallback to version without port -func isKnownHostname(hostname string, knownGateways gatewayHosts) (gw *config.GatewaySpec, ok bool) { - // Try hostname (host+optional port - value from Host header as-is) - if gw, ok := knownGateways.exact[hostname]; ok { - return gw, ok - } - // Also test without port - if gw, ok = knownGateways.exact[stripPort(hostname)]; ok { - return gw, ok - } - - // Wildcard support. Test both with and without port. - for _, host := range knownGateways.wildcard { - if host.re.MatchString(hostname) { - return host.spec, true - } - } - - return nil, false -} - -// Parses Host header and looks for a known gateway matching subdomain host. -// If found, returns GatewaySpec and subdomain components extracted from Host -// header: {rootID}.{ns}.{gwHostname} -// Note: hostname is host + optional port -func knownSubdomainDetails(hostname string, knownGateways gatewayHosts) (gw *config.GatewaySpec, gwHostname, ns, rootID string, ok bool) { - labels := strings.Split(hostname, ".") - // Look for FQDN of a known gateway hostname. - // Example: given "dist.ipfs.tech.ipns.dweb.link": - // 1. Lookup "link" TLD in knownGateways: negative - // 2. Lookup "dweb.link" in knownGateways: positive - // - // Stops when we have 2 or fewer labels left as we need at least a - // rootId and a namespace. - for i := len(labels) - 1; i >= 2; i-- { - fqdn := strings.Join(labels[i:], ".") - gw, ok := isKnownHostname(fqdn, knownGateways) - if !ok { - continue - } - - ns := labels[i-1] - if !isSubdomainNamespace(ns) { - continue - } - - // Merge remaining labels (could be a FQDN with DNSLink) - rootID := strings.Join(labels[:i-1], ".") - return gw, fqdn, ns, rootID, true - } - // no match - return nil, "", "", "", false -} - -// isDomainNameAndNotPeerID returns bool if string looks like a valid DNS name AND is not a PeerID -func isDomainNameAndNotPeerID(hostname string) bool { - if len(hostname) == 0 { - return false - } - if _, err := peer.Decode(hostname); err == nil { - return false - } - _, ok := dns.IsDomainName(hostname) - return ok -} - -// isDNSLinkName returns bool if a valid DNS TXT record exist for provided host -func isDNSLinkName(ctx context.Context, ipfs iface.CoreAPI, host string) bool { - dnslinkName := stripPort(host) - - if !isDomainNameAndNotPeerID(dnslinkName) { - return false - } - - name := "/ipns/" + dnslinkName - // check if DNSLink exists - depth := options.Name.ResolveOption(nsopts.Depth(1)) - _, err := ipfs.Name().Resolve(ctx, name, depth) - return err == nil || err == namesys.ErrResolveRecursion -} - -func isSubdomainNamespace(ns string) bool { - switch ns { - case "ipfs", "ipns", "p2p", "ipld": - return true - default: - return false - } -} - -func isPeerIDNamespace(ns string) bool { - switch ns { - case "ipns", "p2p": - return true - default: - return false - } -} - -// Converts a CID to DNS-safe representation that fits in 63 characters -func toDNSLabel(rootID string, rootCID cid.Cid) (dnsCID string, err error) { - // Return as-is if things fit - if len(rootID) <= dnsLabelMaxLength { - return rootID, nil - } - - // Convert to Base36 and see if that helped - rootID, err = cid.NewCidV1(rootCID.Type(), rootCID.Hash()).StringOfBase(mbase.Base36) - if err != nil { - return "", err - } - if len(rootID) <= dnsLabelMaxLength { - return rootID, nil - } - - // Can't win with DNS at this point, return error - return "", fmt.Errorf("CID incompatible with DNS label length limit of 63: %s", rootID) -} - -// Returns true if HTTP request involves TLS certificate. -// See https://github.com/ipfs/in-web-browsers/issues/169 to understand how it -// impacts DNSLink websites on public gateways. -func isHTTPSRequest(r *http.Request) bool { - // X-Forwarded-Proto if added by a reverse proxy - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto - xproto := r.Header.Get("X-Forwarded-Proto") - // Is request a native TLS (not used atm, but future-proofing) - // or a proxied HTTPS (eg. go-ipfs behind nginx at a public gw)? - return r.URL.Scheme == "https" || xproto == "https" -} - -// Converts a FQDN to DNS-safe representation that fits in 63 characters: -// my.v-long.example.com → my-v--long-example-com -func toDNSLinkDNSLabel(fqdn string) (dnsLabel string, err error) { - dnsLabel = strings.ReplaceAll(fqdn, "-", "--") - dnsLabel = strings.ReplaceAll(dnsLabel, ".", "-") - if len(dnsLabel) > dnsLabelMaxLength { - return "", fmt.Errorf("DNSLink representation incompatible with DNS label length limit of 63: %s", dnsLabel) - } - return dnsLabel, nil -} - -// Converts a DNS-safe representation of DNSLink FQDN to real FQDN: -// my-v--long-example-com → my.v-long.example.com -func toDNSLinkFQDN(dnsLabel string) (fqdn string) { - fqdn = strings.ReplaceAll(dnsLabel, "--", "@") // @ placeholder is unused in DNS labels - fqdn = strings.ReplaceAll(fqdn, "-", ".") - fqdn = strings.ReplaceAll(fqdn, "@", "-") - return fqdn -} - -// Converts a hostname/path to a subdomain-based URL, if applicable. -func toSubdomainURL(hostname, path string, r *http.Request, inlineDNSLink bool, ipfs iface.CoreAPI) (redirURL string, err error) { - var scheme, ns, rootID, rest string - - query := r.URL.RawQuery - parts := strings.SplitN(path, "/", 4) - isHTTPS := isHTTPSRequest(r) - safeRedirectURL := func(in string) (out string, err error) { - safeURI, err := url.ParseRequestURI(in) - if err != nil { - return "", err - } - return safeURI.String(), nil - } - - if isHTTPS { - scheme = "https:" - } else { - scheme = "http:" - } - - switch len(parts) { - case 4: - rest = parts[3] - fallthrough - case 3: - ns = parts[1] - rootID = parts[2] - default: - return "", nil - } - - if !isSubdomainNamespace(ns) { - return "", nil - } - - // add prefix if query is present - if query != "" { - query = "?" + query - } - - // Normalize problematic PeerIDs (eg. ed25519+identity) to CID representation - if isPeerIDNamespace(ns) && !isDomainNameAndNotPeerID(rootID) { - peerID, err := peer.Decode(rootID) - // Note: PeerID CIDv1 with protobuf multicodec will fail, but we fix it - // in the next block - if err == nil { - rootID = peer.ToCid(peerID).String() - } - } - - // If rootID is a CID, ensure it uses DNS-friendly text representation - if rootCID, err := cid.Decode(rootID); err == nil { - multicodec := rootCID.Type() - var base mbase.Encoding = mbase.Base32 - - // Normalizations specific to /ipns/{libp2p-key} - if isPeerIDNamespace(ns) { - // Using Base36 for /ipns/ for consistency - // Context: https://github.com/ipfs/kubo/pull/7441#discussion_r452372828 - base = mbase.Base36 - - // PeerIDs represented as CIDv1 are expected to have libp2p-key - // multicodec (https://github.com/libp2p/specs/pull/209). - // We ease the transition by fixing multicodec on the fly: - // https://github.com/ipfs/kubo/issues/5287#issuecomment-492163929 - if multicodec != cid.Libp2pKey { - multicodec = cid.Libp2pKey - } - } - - // Ensure CID text representation used in subdomain is compatible - // with the way DNS and URIs are implemented in user agents. - // - // 1. Switch to CIDv1 and enable case-insensitive Base encoding - // to avoid issues when user agent force-lowercases the hostname - // before making the request - // (https://github.com/ipfs/in-web-browsers/issues/89) - rootCID = cid.NewCidV1(multicodec, rootCID.Hash()) - rootID, err = rootCID.StringOfBase(base) - if err != nil { - return "", err - } - // 2. Make sure CID fits in a DNS label, adjust encoding if needed - // (https://github.com/ipfs/kubo/issues/7318) - rootID, err = toDNSLabel(rootID, rootCID) - if err != nil { - return "", err - } - } else { // rootID is not a CID - - // Check if rootID is a FQDN with DNSLink and convert it to TLS-safe - // representation that fits in a single DNS label. We support this so - // loading DNSLink names over TLS "just works" on public HTTP gateways - // that pass 'https' in X-Forwarded-Proto to go-ipfs. - // - // Rationale can be found under "Option C" - // at: https://github.com/ipfs/in-web-browsers/issues/169 - // - // TLDR is: - // /ipns/my.v-long.example.com - // can be loaded from a subdomain gateway with a wildcard TLS cert if - // represented as a single DNS label: - // https://my-v--long-example-com.ipns.dweb.link - if (inlineDNSLink || isHTTPS) && ns == "ipns" && strings.Contains(rootID, ".") { - if isDNSLinkName(r.Context(), ipfs, rootID) { - // my.v-long.example.com → my-v--long-example-com - dnsLabel, err := toDNSLinkDNSLabel(rootID) - if err != nil { - return "", err - } - // update path prefix to use real FQDN with DNSLink - rootID = dnsLabel - } - } - } - - return safeRedirectURL(fmt.Sprintf( - "%s//%s.%s.%s/%s%s", - scheme, - rootID, - ns, - hostname, - rest, - query, - )) -} - -func hasPrefix(path string, prefixes ...string) bool { - for _, prefix := range prefixes { - // Assume people are creative with trailing slashes in Gateway config - p := strings.TrimSuffix(prefix, "/") - // Support for both /version and /ipfs/$cid - if p == path || strings.HasPrefix(path, p+"/") { - return true - } - } - return false -} - -func stripPort(hostname string) string { - host, _, err := net.SplitHostPort(hostname) - if err == nil { - return host - } - return hostname -} diff --git a/core/corehttp/hostname_test.go b/core/corehttp/hostname_test.go deleted file mode 100644 index 6f0713528..000000000 --- a/core/corehttp/hostname_test.go +++ /dev/null @@ -1,307 +0,0 @@ -package corehttp - -import ( - "errors" - "net/http" - "net/http/httptest" - "testing" - - cid "github.com/ipfs/go-cid" - files "github.com/ipfs/go-ipfs-files" - path "github.com/ipfs/go-path" - config "github.com/ipfs/kubo/config" - coreapi "github.com/ipfs/kubo/core/coreapi" -) - -func TestToSubdomainURL(t *testing.T) { - ns := mockNamesys{} - n, err := newNodeWithMockNamesys(ns) - if err != nil { - t.Fatal(err) - } - coreAPI, err := coreapi.NewCoreAPI(n) - if err != nil { - t.Fatal(err) - } - testCID, err := coreAPI.Unixfs().Add(n.Context(), files.NewBytesFile([]byte("fnord"))) - if err != nil { - t.Fatal(err) - } - ns["/ipns/dnslink.long-name.example.com"] = path.FromString(testCID.String()) - ns["/ipns/dnslink.too-long.f1siqrebi3vir8sab33hu5vcy008djegvay6atmz91ojesyjs8lx350b7y7i1nvyw2haytfukfyu2f2x4tocdrfa0zgij6p4zpl4u5o.example.com"] = path.FromString(testCID.String()) - httpRequest := httptest.NewRequest("GET", "http://127.0.0.1:8080", nil) - httpsRequest := httptest.NewRequest("GET", "https://https-request-stub.example.com", nil) - httpsProxiedRequest := httptest.NewRequest("GET", "http://proxied-https-request-stub.example.com", nil) - httpsProxiedRequest.Header.Set("X-Forwarded-Proto", "https") - - for _, test := range []struct { - // in: - request *http.Request - gwHostname string - inlineDNSLink bool - path string - // out: - url string - err error - }{ - // DNSLink - {httpRequest, "localhost", false, "/ipns/dnslink.io", "http://dnslink.io.ipns.localhost/", nil}, - // Hostname with port - {httpRequest, "localhost:8080", false, "/ipns/dnslink.io", "http://dnslink.io.ipns.localhost:8080/", nil}, - // CIDv0 → CIDv1base32 - {httpRequest, "localhost", false, "/ipfs/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", "http://bafybeif7a7gdklt6hodwdrmwmxnhksctcuav6lfxlcyfz4khzl3qfmvcgu.ipfs.localhost/", nil}, - // CIDv1 with long sha512 - {httpRequest, "localhost", false, "/ipfs/bafkrgqe3ohjcjplc6n4f3fwunlj6upltggn7xqujbsvnvyw764srszz4u4rshq6ztos4chl4plgg4ffyyxnayrtdi5oc4xb2332g645433aeg", "", errors.New("CID incompatible with DNS label length limit of 63: kf1siqrebi3vir8sab33hu5vcy008djegvay6atmz91ojesyjs8lx350b7y7i1nvyw2haytfukfyu2f2x4tocdrfa0zgij6p4zpl4u5oj")}, - // PeerID as CIDv1 needs to have libp2p-key multicodec - {httpRequest, "localhost", false, "/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", "http://k2k4r8n0flx3ra0y5dr8fmyvwbzy3eiztmtq6th694k5a3rznayp3e4o.ipns.localhost/", nil}, - {httpRequest, "localhost", false, "/ipns/bafybeickencdqw37dpz3ha36ewrh4undfjt2do52chtcky4rxkj447qhdm", "http://k2k4r8l9ja7hkzynavdqup76ou46tnvuaqegbd04a4o1mpbsey0meucb.ipns.localhost/", nil}, - // PeerID: ed25519+identity multihash → CIDv1Base36 - {httpRequest, "localhost", false, "/ipns/12D3KooWFB51PRY9BxcXSH6khFXw1BZeszeLDy7C8GciskqCTZn5", "http://k51qzi5uqu5di608geewp3nqkg0bpujoasmka7ftkyxgcm3fh1aroup0gsdrna.ipns.localhost/", nil}, - {httpRequest, "sub.localhost", false, "/ipfs/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", "http://bafybeif7a7gdklt6hodwdrmwmxnhksctcuav6lfxlcyfz4khzl3qfmvcgu.ipfs.sub.localhost/", nil}, - // HTTPS requires DNSLink name to fit in a single DNS label – see "Option C" from https://github.com/ipfs/in-web-browsers/issues/169 - {httpRequest, "dweb.link", false, "/ipns/dnslink.long-name.example.com", "http://dnslink.long-name.example.com.ipns.dweb.link/", nil}, - {httpsRequest, "dweb.link", false, "/ipns/dnslink.long-name.example.com", "https://dnslink-long--name-example-com.ipns.dweb.link/", nil}, - {httpsProxiedRequest, "dweb.link", false, "/ipns/dnslink.long-name.example.com", "https://dnslink-long--name-example-com.ipns.dweb.link/", nil}, - // HTTP requests can also be converted to fit into a single DNS label - https://github.com/ipfs/kubo/issues/9243 - {httpRequest, "localhost", true, "/ipns/dnslink.long-name.example.com", "http://dnslink-long--name-example-com.ipns.localhost/", nil}, - {httpRequest, "dweb.link", true, "/ipns/dnslink.long-name.example.com", "http://dnslink-long--name-example-com.ipns.dweb.link/", nil}, - } { - url, err := toSubdomainURL(test.gwHostname, test.path, test.request, test.inlineDNSLink, coreAPI) - if url != test.url || !equalError(err, test.err) { - t.Errorf("(%s, %v, %s) returned (%s, %v), expected (%s, %v)", test.gwHostname, test.inlineDNSLink, test.path, url, err, test.url, test.err) - } - } -} - -func TestToDNSLinkDNSLabel(t *testing.T) { - for _, test := range []struct { - in string - out string - err error - }{ - {"dnslink.long-name.example.com", "dnslink-long--name-example-com", nil}, - {"dnslink.too-long.f1siqrebi3vir8sab33hu5vcy008djegvay6atmz91ojesyjs8lx350b7y7i1nvyw2haytfukfyu2f2x4tocdrfa0zgij6p4zpl4u5o.example.com", "", errors.New("DNSLink representation incompatible with DNS label length limit of 63: dnslink-too--long-f1siqrebi3vir8sab33hu5vcy008djegvay6atmz91ojesyjs8lx350b7y7i1nvyw2haytfukfyu2f2x4tocdrfa0zgij6p4zpl4u5o-example-com")}, - } { - out, err := toDNSLinkDNSLabel(test.in) - if out != test.out || !equalError(err, test.err) { - t.Errorf("(%s) returned (%s, %v), expected (%s, %v)", test.in, out, err, test.out, test.err) - } - } -} - -func TestToDNSLinkFQDN(t *testing.T) { - for _, test := range []struct { - in string - out string - }{ - {"singlelabel", "singlelabel"}, - {"docs-ipfs-tech", "docs.ipfs.tech"}, - {"dnslink-long--name-example-com", "dnslink.long-name.example.com"}, - } { - out := toDNSLinkFQDN(test.in) - if out != test.out { - t.Errorf("(%s) returned (%s), expected (%s)", test.in, out, test.out) - } - } -} - -func TestIsHTTPSRequest(t *testing.T) { - httpRequest := httptest.NewRequest("GET", "http://127.0.0.1:8080", nil) - httpsRequest := httptest.NewRequest("GET", "https://https-request-stub.example.com", nil) - httpsProxiedRequest := httptest.NewRequest("GET", "http://proxied-https-request-stub.example.com", nil) - httpsProxiedRequest.Header.Set("X-Forwarded-Proto", "https") - httpProxiedRequest := httptest.NewRequest("GET", "http://proxied-http-request-stub.example.com", nil) - httpProxiedRequest.Header.Set("X-Forwarded-Proto", "http") - oddballRequest := httptest.NewRequest("GET", "foo://127.0.0.1:8080", nil) - for _, test := range []struct { - in *http.Request - out bool - }{ - {httpRequest, false}, - {httpsRequest, true}, - {httpsProxiedRequest, true}, - {httpProxiedRequest, false}, - {oddballRequest, false}, - } { - out := isHTTPSRequest(test.in) - if out != test.out { - t.Errorf("(%+v): returned %t, expected %t", test.in, out, test.out) - } - } -} - -func TestHasPrefix(t *testing.T) { - for _, test := range []struct { - prefixes []string - path string - out bool - }{ - {[]string{"/ipfs"}, "/ipfs/cid", true}, - {[]string{"/ipfs/"}, "/ipfs/cid", true}, - {[]string{"/version/"}, "/version", true}, - {[]string{"/version"}, "/version", true}, - } { - out := hasPrefix(test.path, test.prefixes...) - if out != test.out { - t.Errorf("(%+v, %s) returned '%t', expected '%t'", test.prefixes, test.path, out, test.out) - } - } -} - -func TestIsDomainNameAndNotPeerID(t *testing.T) { - for _, test := range []struct { - hostname string - out bool - }{ - {"", false}, - {"example.com", true}, - {"non-icann.something", true}, - {"..", false}, - {"12D3KooWFB51PRY9BxcXSH6khFXw1BZeszeLDy7C8GciskqCTZn5", false}, // valid peerid - {"k51qzi5uqu5di608geewp3nqkg0bpujoasmka7ftkyxgcm3fh1aroup0gsdrna", false}, // valid peerid - } { - out := isDomainNameAndNotPeerID(test.hostname) - if out != test.out { - t.Errorf("(%s) returned '%t', expected '%t'", test.hostname, out, test.out) - } - } -} - -func TestPortStripping(t *testing.T) { - for _, test := range []struct { - in string - out string - }{ - {"localhost:8080", "localhost"}, - {"bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.ipfs.localhost:8080", "bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.ipfs.localhost"}, - {"example.com:443", "example.com"}, - {"example.com", "example.com"}, - {"foo-dweb.ipfs.pvt.k12.ma.us:8080", "foo-dweb.ipfs.pvt.k12.ma.us"}, - {"localhost", "localhost"}, - {"[::1]:8080", "::1"}, - } { - out := stripPort(test.in) - if out != test.out { - t.Errorf("(%s): returned '%s', expected '%s'", test.in, out, test.out) - } - } -} - -func TestToDNSLabel(t *testing.T) { - for _, test := range []struct { - in string - out string - err error - }{ - // <= 63 - {"QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", "QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", nil}, - {"bafybeickencdqw37dpz3ha36ewrh4undfjt2do52chtcky4rxkj447qhdm", "bafybeickencdqw37dpz3ha36ewrh4undfjt2do52chtcky4rxkj447qhdm", nil}, - // > 63 - // PeerID: ed25519+identity multihash → CIDv1Base36 - {"bafzaajaiaejca4syrpdu6gdx4wsdnokxkprgzxf4wrstuc34gxw5k5jrag2so5gk", "k51qzi5uqu5dj16qyiq0tajolkojyl9qdkr254920wxv7ghtuwcz593tp69z9m", nil}, - // CIDv1 with long sha512 → error - {"bafkrgqe3ohjcjplc6n4f3fwunlj6upltggn7xqujbsvnvyw764srszz4u4rshq6ztos4chl4plgg4ffyyxnayrtdi5oc4xb2332g645433aeg", "", errors.New("CID incompatible with DNS label length limit of 63: kf1siqrebi3vir8sab33hu5vcy008djegvay6atmz91ojesyjs8lx350b7y7i1nvyw2haytfukfyu2f2x4tocdrfa0zgij6p4zpl4u5oj")}, - } { - inCID, _ := cid.Decode(test.in) - out, err := toDNSLabel(test.in, inCID) - if out != test.out || !equalError(err, test.err) { - t.Errorf("(%s): returned (%s, %v) expected (%s, %v)", test.in, out, err, test.out, test.err) - } - } - -} - -func TestKnownSubdomainDetails(t *testing.T) { - gwLocalhost := &config.GatewaySpec{Paths: []string{"/ipfs", "/ipns", "/api"}, UseSubdomains: true} - gwDweb := &config.GatewaySpec{Paths: []string{"/ipfs", "/ipns", "/api"}, UseSubdomains: true} - gwLong := &config.GatewaySpec{Paths: []string{"/ipfs", "/ipns", "/api"}, UseSubdomains: true} - gwWildcard1 := &config.GatewaySpec{Paths: []string{"/ipfs", "/ipns", "/api"}, UseSubdomains: true} - gwWildcard2 := &config.GatewaySpec{Paths: []string{"/ipfs", "/ipns", "/api"}, UseSubdomains: true} - - knownGateways := prepareKnownGateways(map[string]*config.GatewaySpec{ - "localhost": gwLocalhost, - "dweb.link": gwDweb, - "devgateway.dweb.link": gwDweb, - "dweb.ipfs.pvt.k12.ma.us": gwLong, // note the sneaky ".ipfs." ;-) - "*.wildcard1.tld": gwWildcard1, - "*.*.wildcard2.tld": gwWildcard2, - }) - - for _, test := range []struct { - // in: - hostHeader string - // out: - gw *config.GatewaySpec - hostname string - ns string - rootID string - ok bool - }{ - // no subdomain - {"127.0.0.1:8080", nil, "", "", "", false}, - {"[::1]:8080", nil, "", "", "", false}, - {"hey.look.example.com", nil, "", "", "", false}, - {"dweb.link", nil, "", "", "", false}, - // malformed Host header - {".....dweb.link", nil, "", "", "", false}, - {"link", nil, "", "", "", false}, - {"8080:dweb.link", nil, "", "", "", false}, - {" ", nil, "", "", "", false}, - {"", nil, "", "", "", false}, - // unknown gateway host - {"bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.ipfs.unknown.example.com", nil, "", "", "", false}, - // cid in subdomain, known gateway - {"bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.ipfs.localhost:8080", gwLocalhost, "localhost:8080", "ipfs", "bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am", true}, - {"bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.ipfs.dweb.link", gwDweb, "dweb.link", "ipfs", "bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am", true}, - {"bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.ipfs.devgateway.dweb.link", gwDweb, "devgateway.dweb.link", "ipfs", "bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am", true}, - // capture everything before .ipfs. - {"foo.bar.boo-buzz.ipfs.dweb.link", gwDweb, "dweb.link", "ipfs", "foo.bar.boo-buzz", true}, - // ipns - {"bafzbeihe35nmjqar22thmxsnlsgxppd66pseq6tscs4mo25y55juhh6bju.ipns.localhost:8080", gwLocalhost, "localhost:8080", "ipns", "bafzbeihe35nmjqar22thmxsnlsgxppd66pseq6tscs4mo25y55juhh6bju", true}, - {"bafzbeihe35nmjqar22thmxsnlsgxppd66pseq6tscs4mo25y55juhh6bju.ipns.dweb.link", gwDweb, "dweb.link", "ipns", "bafzbeihe35nmjqar22thmxsnlsgxppd66pseq6tscs4mo25y55juhh6bju", true}, - // edge case check: public gateway under long TLD (see: https://publicsuffix.org) - {"bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.ipfs.dweb.ipfs.pvt.k12.ma.us", gwLong, "dweb.ipfs.pvt.k12.ma.us", "ipfs", "bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am", true}, - {"bafzbeihe35nmjqar22thmxsnlsgxppd66pseq6tscs4mo25y55juhh6bju.ipns.dweb.ipfs.pvt.k12.ma.us", gwLong, "dweb.ipfs.pvt.k12.ma.us", "ipns", "bafzbeihe35nmjqar22thmxsnlsgxppd66pseq6tscs4mo25y55juhh6bju", true}, - // dnslink in subdomain - {"en.wikipedia-on-ipfs.org.ipns.localhost:8080", gwLocalhost, "localhost:8080", "ipns", "en.wikipedia-on-ipfs.org", true}, - {"en.wikipedia-on-ipfs.org.ipns.localhost", gwLocalhost, "localhost", "ipns", "en.wikipedia-on-ipfs.org", true}, - {"dist.ipfs.tech.ipns.localhost:8080", gwLocalhost, "localhost:8080", "ipns", "dist.ipfs.tech", true}, - {"en.wikipedia-on-ipfs.org.ipns.dweb.link", gwDweb, "dweb.link", "ipns", "en.wikipedia-on-ipfs.org", true}, - // edge case check: public gateway under long TLD (see: https://publicsuffix.org) - {"foo.dweb.ipfs.pvt.k12.ma.us", nil, "", "", "", false}, - {"bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.ipfs.dweb.ipfs.pvt.k12.ma.us", gwLong, "dweb.ipfs.pvt.k12.ma.us", "ipfs", "bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am", true}, - {"bafzbeihe35nmjqar22thmxsnlsgxppd66pseq6tscs4mo25y55juhh6bju.ipns.dweb.ipfs.pvt.k12.ma.us", gwLong, "dweb.ipfs.pvt.k12.ma.us", "ipns", "bafzbeihe35nmjqar22thmxsnlsgxppd66pseq6tscs4mo25y55juhh6bju", true}, - // other namespaces - {"api.localhost", nil, "", "", "", false}, - {"peerid.p2p.localhost", gwLocalhost, "localhost", "p2p", "peerid", true}, - // wildcards - {"wildcard1.tld", nil, "", "", "", false}, - {".wildcard1.tld", nil, "", "", "", false}, - {"bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.ipfs.wildcard1.tld", nil, "", "", "", false}, - {"bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.ipfs.sub.wildcard1.tld", gwWildcard1, "sub.wildcard1.tld", "ipfs", "bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am", true}, - {"bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.ipfs.sub1.sub2.wildcard1.tld", nil, "", "", "", false}, - {"bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am.ipfs.sub1.sub2.wildcard2.tld", gwWildcard2, "sub1.sub2.wildcard2.tld", "ipfs", "bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am", true}, - } { - gw, hostname, ns, rootID, ok := knownSubdomainDetails(test.hostHeader, knownGateways) - if ok != test.ok { - t.Errorf("knownSubdomainDetails(%s): ok is %t, expected %t", test.hostHeader, ok, test.ok) - } - if rootID != test.rootID { - t.Errorf("knownSubdomainDetails(%s): rootID is '%s', expected '%s'", test.hostHeader, rootID, test.rootID) - } - if ns != test.ns { - t.Errorf("knownSubdomainDetails(%s): ns is '%s', expected '%s'", test.hostHeader, ns, test.ns) - } - if hostname != test.hostname { - t.Errorf("knownSubdomainDetails(%s): hostname is '%s', expected '%s'", test.hostHeader, hostname, test.hostname) - } - if gw != test.gw { - t.Errorf("knownSubdomainDetails(%s): gw is %+v, expected %+v", test.hostHeader, gw, test.gw) - } - } - -} - -func equalError(a, b error) bool { - return (a == nil && b == nil) || (a != nil && b != nil && a.Error() == b.Error()) -} diff --git a/core/corehttp/lazyseek.go b/core/corehttp/lazyseek.go deleted file mode 100644 index 2a379dc91..000000000 --- a/core/corehttp/lazyseek.go +++ /dev/null @@ -1,60 +0,0 @@ -package corehttp - -import ( - "fmt" - "io" -) - -// The HTTP server uses seek to determine the file size. Actually _seeking_ can -// be slow so we wrap the seeker in a _lazy_ seeker. -type lazySeeker struct { - reader io.ReadSeeker - - size int64 - offset int64 - realOffset int64 -} - -func (s *lazySeeker) Seek(offset int64, whence int) (int64, error) { - switch whence { - case io.SeekEnd: - return s.Seek(s.size+offset, io.SeekStart) - case io.SeekCurrent: - return s.Seek(s.offset+offset, io.SeekStart) - case io.SeekStart: - if offset < 0 { - return s.offset, fmt.Errorf("invalid seek offset") - } - s.offset = offset - return s.offset, nil - default: - return s.offset, fmt.Errorf("invalid whence: %d", whence) - } -} - -func (s *lazySeeker) Read(b []byte) (int, error) { - // If we're past the end, EOF. - if s.offset >= s.size { - return 0, io.EOF - } - - // actually seek - for s.offset != s.realOffset { - off, err := s.reader.Seek(s.offset, io.SeekStart) - if err != nil { - return 0, err - } - s.realOffset = off - } - off, err := s.reader.Read(b) - s.realOffset += int64(off) - s.offset += int64(off) - return off, err -} - -func (s *lazySeeker) Close() error { - if closer, ok := s.reader.(io.Closer); ok { - return closer.Close() - } - return nil -} diff --git a/core/corehttp/lazyseek_test.go b/core/corehttp/lazyseek_test.go deleted file mode 100644 index 49aca0a0e..000000000 --- a/core/corehttp/lazyseek_test.go +++ /dev/null @@ -1,136 +0,0 @@ -package corehttp - -import ( - "fmt" - "io" - "strings" - "testing" -) - -type badSeeker struct { - io.ReadSeeker -} - -var errBadSeek = fmt.Errorf("bad seeker") - -func (bs badSeeker) Seek(offset int64, whence int) (int64, error) { - off, err := bs.ReadSeeker.Seek(0, io.SeekCurrent) - if err != nil { - panic(err) - } - return off, errBadSeek -} - -func TestLazySeekerError(t *testing.T) { - underlyingBuffer := strings.NewReader("fubar") - s := &lazySeeker{ - reader: badSeeker{underlyingBuffer}, - size: underlyingBuffer.Size(), - } - off, err := s.Seek(0, io.SeekEnd) - if err != nil { - t.Fatal(err) - } - if off != s.size { - t.Fatal("expected to seek to the end") - } - - // shouldn't have actually seeked. - b, err := io.ReadAll(s) - if err != nil { - t.Fatal(err) - } - if len(b) != 0 { - t.Fatal("expected to read nothing") - } - - // shouldn't need to actually seek. - off, err = s.Seek(0, io.SeekStart) - if err != nil { - t.Fatal(err) - } - if off != 0 { - t.Fatal("expected to seek to the start") - } - b, err = io.ReadAll(s) - if err != nil { - t.Fatal(err) - } - if string(b) != "fubar" { - t.Fatal("expected to read string") - } - - // should fail the second time. - off, err = s.Seek(0, io.SeekStart) - if err != nil { - t.Fatal(err) - } - if off != 0 { - t.Fatal("expected to seek to the start") - } - // right here... - b, err = io.ReadAll(s) - if err == nil { - t.Fatalf("expected an error, got output %s", string(b)) - } - if err != errBadSeek { - t.Fatalf("expected a bad seek error, got %s", err) - } - if len(b) != 0 { - t.Fatalf("expected to read nothing") - } -} - -func TestLazySeeker(t *testing.T) { - underlyingBuffer := strings.NewReader("fubar") - s := &lazySeeker{ - reader: underlyingBuffer, - size: underlyingBuffer.Size(), - } - expectByte := func(b byte) { - t.Helper() - var buf [1]byte - n, err := io.ReadFull(s, buf[:]) - if err != nil { - t.Fatal(err) - } - if n != 1 { - t.Fatalf("expected to read one byte, read %d", n) - } - if buf[0] != b { - t.Fatalf("expected %b, got %b", b, buf[0]) - } - } - expectSeek := func(whence int, off, expOff int64, expErr string) { - t.Helper() - n, err := s.Seek(off, whence) - if expErr == "" { - if err != nil { - t.Fatal("unexpected seek error: ", err) - } - } else { - if err == nil || err.Error() != expErr { - t.Fatalf("expected %s, got %s", err, expErr) - } - } - if n != expOff { - t.Fatalf("expected offset %d, got, %d", expOff, n) - } - } - - expectSeek(io.SeekEnd, 0, s.size, "") - b, err := io.ReadAll(s) - if err != nil { - t.Fatal(err) - } - if len(b) != 0 { - t.Fatal("expected to read nothing") - } - expectSeek(io.SeekEnd, -1, s.size-1, "") - expectByte('r') - expectSeek(io.SeekStart, 0, 0, "") - expectByte('f') - expectSeek(io.SeekCurrent, 1, 2, "") - expectByte('b') - expectSeek(io.SeekCurrent, -100, 3, "invalid seek offset") -} diff --git a/core/corehttp/metrics.go b/core/corehttp/metrics.go index e26be1ca9..f43362ff7 100644 --- a/core/corehttp/metrics.go +++ b/core/corehttp/metrics.go @@ -151,13 +151,11 @@ func MetricsCollectionOption(handlerName string) ServeOption { } } -var ( - peersTotalMetric = prometheus.NewDesc( - prometheus.BuildFQName("ipfs", "p2p", "peers_total"), - "Number of connected peers", - []string{"transport"}, - nil, - ) +var peersTotalMetric = prometheus.NewDesc( + prometheus.BuildFQName("ipfs", "p2p", "peers_total"), + "Number of connected peers", + []string{"transport"}, + nil, ) type IpfsNodeCollector struct { diff --git a/core/corehttp/metrics_test.go b/core/corehttp/metrics_test.go index 267d4ae97..f1bb39617 100644 --- a/core/corehttp/metrics_test.go +++ b/core/corehttp/metrics_test.go @@ -49,7 +49,7 @@ func TestPeersTotal(t *testing.T) { t.Fatalf("expected at most 2 peers transport (tcp and upd/quic), got %d, transport map %v", len(peersTransport), peersTransport) } - totalPeers := peersTransport["/ip4/tcp"] + peersTransport["/ip4/udp/quic"] + totalPeers := peersTransport["/ip4/tcp"] + peersTransport["/ip4/udp/quic-v1"] if totalPeers != 3 { t.Fatalf("expected 3 peers in either tcp or upd/quic transport, got %f", totalPeers) } diff --git a/core/corehttp/option_test.go b/core/corehttp/option_test.go index b401be9d5..be4f3afaa 100644 --- a/core/corehttp/option_test.go +++ b/core/corehttp/option_test.go @@ -51,10 +51,9 @@ func TestCheckVersionOption(t *testing.T) { called = true if !tc.shouldHandle { t.Error("handler was called even though version didn't match") - } else { - if _, err := io.WriteString(w, "check!"); err != nil { - t.Error(err) - } + } + if _, err := io.WriteString(w, "check!"); err != nil { + t.Error(err) } }) diff --git a/core/corehttp/redirect.go b/core/corehttp/redirect.go index bcd536d23..106355d24 100644 --- a/core/corehttp/redirect.go +++ b/core/corehttp/redirect.go @@ -8,8 +8,14 @@ import ( ) func RedirectOption(path string, redirect string) ServeOption { - handler := &redirectHandler{redirect} return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) { + cfg, err := n.Repo.Config() + if err != nil { + return nil, err + } + + handler := &redirectHandler{redirect, cfg.API.HTTPHeaders} + if len(path) > 0 { mux.Handle("/"+path+"/", handler) } else { @@ -20,9 +26,14 @@ func RedirectOption(path string, redirect string) ServeOption { } type redirectHandler struct { - path string + path string + headers map[string][]string } func (i *redirectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + for k, v := range i.headers { + w.Header()[http.CanonicalHeaderKey(k)] = v + } + http.Redirect(w, r, i.path, http.StatusFound) } diff --git a/core/corehttp/routing.go b/core/corehttp/routing.go new file mode 100644 index 000000000..9a2591d32 --- /dev/null +++ b/core/corehttp/routing.go @@ -0,0 +1,135 @@ +package corehttp + +import ( + "context" + "net" + "net/http" + "time" + + "github.com/ipfs/boxo/gateway" + "github.com/ipfs/boxo/ipns" + "github.com/ipfs/boxo/routing/http/server" + "github.com/ipfs/boxo/routing/http/types" + "github.com/ipfs/boxo/routing/http/types/iter" + cid "github.com/ipfs/go-cid" + core "github.com/ipfs/kubo/core" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/routing" +) + +func RoutingOption() ServeOption { + return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) { + _, headers, err := getGatewayConfig(n) + if err != nil { + return nil, err + } + + handler := server.Handler(&contentRouter{n}) + handler = gateway.NewHeaders(headers).ApplyCors().Wrap(handler) + mux.Handle("/routing/v1/", handler) + return mux, nil + } +} + +type contentRouter struct { + n *core.IpfsNode +} + +func (r *contentRouter) FindProviders(ctx context.Context, key cid.Cid, limit int) (iter.ResultIter[types.Record], error) { + ctx, cancel := context.WithCancel(ctx) + ch := r.n.Routing.FindProvidersAsync(ctx, key, limit) + return iter.ToResultIter[types.Record](&peerChanIter{ + ch: ch, + cancel: cancel, + }), nil +} + +// nolint deprecated +func (r *contentRouter) ProvideBitswap(ctx context.Context, req *server.BitswapWriteProvideRequest) (time.Duration, error) { + return 0, routing.ErrNotSupported +} + +func (r *contentRouter) FindPeers(ctx context.Context, pid peer.ID, limit int) (iter.ResultIter[*types.PeerRecord], error) { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + addr, err := r.n.Routing.FindPeer(ctx, pid) + if err != nil { + return nil, err + } + + rec := &types.PeerRecord{ + Schema: types.SchemaPeer, + ID: &addr.ID, + } + + for _, addr := range addr.Addrs { + rec.Addrs = append(rec.Addrs, types.Multiaddr{Multiaddr: addr}) + } + + return iter.ToResultIter[*types.PeerRecord](iter.FromSlice[*types.PeerRecord]([]*types.PeerRecord{rec})), nil +} + +func (r *contentRouter) GetIPNS(ctx context.Context, name ipns.Name) (*ipns.Record, error) { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + raw, err := r.n.Routing.GetValue(ctx, string(name.RoutingKey())) + if err != nil { + return nil, err + } + + return ipns.UnmarshalRecord(raw) +} + +func (r *contentRouter) PutIPNS(ctx context.Context, name ipns.Name, record *ipns.Record) error { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + raw, err := ipns.MarshalRecord(record) + if err != nil { + return err + } + + // The caller guarantees that name matches the record. This is double checked + // by the internals of PutValue. + return r.n.Routing.PutValue(ctx, string(name.RoutingKey()), raw) +} + +type peerChanIter struct { + ch <-chan peer.AddrInfo + cancel context.CancelFunc + next *peer.AddrInfo +} + +func (it *peerChanIter) Next() bool { + addr, ok := <-it.ch + if ok { + it.next = &addr + return true + } + it.next = nil + return false +} + +func (it *peerChanIter) Val() types.Record { + if it.next == nil { + return nil + } + + rec := &types.PeerRecord{ + Schema: types.SchemaPeer, + ID: &it.next.ID, + } + + for _, addr := range it.next.Addrs { + rec.Addrs = append(rec.Addrs, types.Multiaddr{Multiaddr: addr}) + } + + return rec +} + +func (it *peerChanIter) Close() error { + it.cancel() + return nil +} diff --git a/core/corehttp/webui.go b/core/corehttp/webui.go index e8531459d..4f00ad712 100644 --- a/core/corehttp/webui.go +++ b/core/corehttp/webui.go @@ -1,11 +1,23 @@ package corehttp -// TODO: move to IPNS -const WebUIPath = "/ipfs/bafybeiequgo72mrvuml56j4gk7crewig5bavumrrzhkqbim6b3s2yqi7ty" // v2.21.0 +// WebUI version confirmed to work with this Kubo version +const WebUIPath = "/ipfs/bafybeibgic2ex3fvzkinhy6k6aqyv3zy2o7bkbsmrzvzka24xetv7eeadm" // v4.4.0 // WebUIPaths is a list of all past webUI paths. var WebUIPaths = []string{ WebUIPath, + "/ipfs/bafybeid4uxz7klxcu3ffsnmn64r7ihvysamlj4ohl5h2orjsffuegcpaeq", + "/ipfs/bafybeif6abowqcavbkz243biyh7pde7ick5kkwwytrh7pd2hkbtuqysjxy", + "/ipfs/bafybeihatzsgposbr3hrngo42yckdyqcc56yean2rynnwpzxstvdlphxf4", + "/ipfs/bafybeigggyffcf6yfhx5irtwzx3cgnk6n3dwylkvcpckzhqqrigsxowjwe", + "/ipfs/bafybeidf7cpkwsjkq6xs3r6fbbxghbugilx3jtezbza7gua3k5wjixpmba", + "/ipfs/bafybeiamycmd52xvg6k3nzr6z3n33de6a2teyhquhj4kspdtnvetnkrfim", + "/ipfs/bafybeieqdeoqkf7xf4aozd524qncgiloh33qgr25lyzrkusbcre4c3fxay", + "/ipfs/bafybeicyp7ssbnj3hdzehcibmapmpuc3atrsc4ch3q6acldfh4ojjdbcxe", + "/ipfs/bafybeigs6d53gpgu34553mbi5bbkb26e4ikruoaaar75jpfdywpup2r3my", + "/ipfs/bafybeic4gops3d3lyrisqku37uio33nvt6fqxvkxihrwlqsuvf76yln4fm", + "/ipfs/bafybeifeqt7mvxaniphyu2i3qhovjaf3sayooxbh5enfdqtiehxjv2ldte", + "/ipfs/bafybeiequgo72mrvuml56j4gk7crewig5bavumrrzhkqbim6b3s2yqi7ty", "/ipfs/bafybeibjbq3tmmy7wuihhhwvbladjsd3gx3kfjepxzkq6wylik6wc3whzy", "/ipfs/bafybeiavrvt53fks6u32n5p2morgblcmck4bh4ymf4rrwu7ah5zsykmqqa", "/ipfs/bafybeiageaoxg6d7npaof6eyzqbwvbubyler7bq44hayik2hvqcggg7d2y", diff --git a/core/coreiface/block.go b/core/coreiface/block.go new file mode 100644 index 000000000..bf518f7fd --- /dev/null +++ b/core/coreiface/block.go @@ -0,0 +1,37 @@ +package iface + +import ( + "context" + "io" + + "github.com/ipfs/boxo/path" + "github.com/ipfs/kubo/core/coreiface/options" +) + +// BlockStat contains information about a block +type BlockStat interface { + // Size is the size of a block + Size() int + + // Path returns path to the block + Path() path.ImmutablePath +} + +// BlockAPI specifies the interface to the block layer +type BlockAPI interface { + // Put imports raw block data, hashing it using specified settings. + Put(context.Context, io.Reader, ...options.BlockPutOption) (BlockStat, error) + + // Get attempts to resolve the path and return a reader for data in the block + Get(context.Context, path.Path) (io.Reader, error) + + // Rm removes the block specified by the path from local blockstore. + // By default an error will be returned if the block can't be found locally. + // + // NOTE: If the specified block is pinned it won't be removed and no error + // will be returned + Rm(context.Context, path.Path, ...options.BlockRmOption) error + + // Stat returns information on + Stat(context.Context, path.Path) (BlockStat, error) +} diff --git a/core/coreiface/coreapi.go b/core/coreiface/coreapi.go new file mode 100644 index 000000000..dbb08dd7e --- /dev/null +++ b/core/coreiface/coreapi.go @@ -0,0 +1,58 @@ +// Package iface defines IPFS Core API which is a set of interfaces used to +// interact with IPFS nodes. +package iface + +import ( + "context" + + "github.com/ipfs/boxo/path" + "github.com/ipfs/kubo/core/coreiface/options" + + ipld "github.com/ipfs/go-ipld-format" +) + +// CoreAPI defines an unified interface to IPFS for Go programs +type CoreAPI interface { + // Unixfs returns an implementation of Unixfs API + Unixfs() UnixfsAPI + + // Block returns an implementation of Block API + Block() BlockAPI + + // Dag returns an implementation of Dag API + Dag() APIDagService + + // Name returns an implementation of Name API + Name() NameAPI + + // Key returns an implementation of Key API + Key() KeyAPI + + // Pin returns an implementation of Pin API + Pin() PinAPI + + // Object returns an implementation of Object API + Object() ObjectAPI + + // Swarm returns an implementation of Swarm API + Swarm() SwarmAPI + + // PubSub returns an implementation of PubSub API + PubSub() PubSubAPI + + // Routing returns an implementation of Routing API + Routing() RoutingAPI + + // ResolvePath resolves the path using UnixFS resolver, and returns the resolved + // immutable path, and the remainder of the path segments that cannot be resolved + // within UnixFS. + ResolvePath(context.Context, path.Path) (path.ImmutablePath, []string, error) + + // ResolveNode resolves the path (if not resolved already) using Unixfs + // resolver, gets and returns the resolved Node + ResolveNode(context.Context, path.Path) (ipld.Node, error) + + // WithOptions creates new instance of CoreAPI based on this instance with + // a set of options applied + WithOptions(...options.ApiOption) (CoreAPI, error) +} diff --git a/core/coreiface/dag.go b/core/coreiface/dag.go new file mode 100644 index 000000000..3cc3aeb4d --- /dev/null +++ b/core/coreiface/dag.go @@ -0,0 +1,13 @@ +package iface + +import ( + ipld "github.com/ipfs/go-ipld-format" +) + +// APIDagService extends ipld.DAGService +type APIDagService interface { + ipld.DAGService + + // Pinning returns special NodeAdder which recursively pins added nodes + Pinning() ipld.NodeAdder +} diff --git a/core/coreiface/errors.go b/core/coreiface/errors.go new file mode 100644 index 000000000..e0bd7805d --- /dev/null +++ b/core/coreiface/errors.go @@ -0,0 +1,10 @@ +package iface + +import "errors" + +var ( + ErrIsDir = errors.New("this dag node is a directory") + ErrNotFile = errors.New("this dag node is not a regular file") + ErrOffline = errors.New("this action must be run in online mode, try running 'ipfs daemon' first") + ErrNotSupported = errors.New("operation not supported") +) diff --git a/core/coreiface/idfmt.go b/core/coreiface/idfmt.go new file mode 100644 index 000000000..80fd0f822 --- /dev/null +++ b/core/coreiface/idfmt.go @@ -0,0 +1,19 @@ +package iface + +import ( + "github.com/libp2p/go-libp2p/core/peer" + mbase "github.com/multiformats/go-multibase" +) + +func FormatKeyID(id peer.ID) string { + if s, err := peer.ToCid(id).StringOfBase(mbase.Base36); err != nil { + panic(err) + } else { + return s + } +} + +// FormatKey formats the given IPNS key in a canonical way. +func FormatKey(key Key) string { + return FormatKeyID(key.ID()) +} diff --git a/core/coreiface/key.go b/core/coreiface/key.go new file mode 100644 index 000000000..6125e593b --- /dev/null +++ b/core/coreiface/key.go @@ -0,0 +1,51 @@ +package iface + +import ( + "context" + + "github.com/ipfs/boxo/path" + + "github.com/ipfs/kubo/core/coreiface/options" + + "github.com/libp2p/go-libp2p/core/peer" +) + +// Key specifies the interface to Keys in KeyAPI Keystore +type Key interface { + // Key returns key name + Name() string + + // Path returns key path + Path() path.Path + + // ID returns key PeerID + ID() peer.ID +} + +// KeyAPI specifies the interface to Keystore +type KeyAPI interface { + // Generate generates new key, stores it in the keystore under the specified + // name and returns a base58 encoded multihash of it's public key + Generate(ctx context.Context, name string, opts ...options.KeyGenerateOption) (Key, error) + + // Rename renames oldName key to newName. Returns the key and whether another + // key was overwritten, or an error + Rename(ctx context.Context, oldName string, newName string, opts ...options.KeyRenameOption) (Key, bool, error) + + // List lists keys stored in keystore + List(ctx context.Context) ([]Key, error) + + // Self returns the 'main' node key + Self(ctx context.Context) (Key, error) + + // Remove removes keys from keystore. Returns ipns path of the removed key + Remove(ctx context.Context, name string) (Key, error) + + // Sign signs the given data with the key named name. Returns the key used + // for signing, the signature, and an error. + Sign(ctx context.Context, name string, data []byte) (Key, []byte, error) + + // Verify verifies if the given data and signatures match. Returns the key used + // for verification, whether signature and data match, and an error. + Verify(ctx context.Context, keyOrName string, signature, data []byte) (Key, bool, error) +} diff --git a/core/coreiface/name.go b/core/coreiface/name.go new file mode 100644 index 000000000..f7c8f634c --- /dev/null +++ b/core/coreiface/name.go @@ -0,0 +1,40 @@ +package iface + +import ( + "context" + "errors" + + "github.com/ipfs/boxo/ipns" + "github.com/ipfs/boxo/path" + "github.com/ipfs/kubo/core/coreiface/options" +) + +var ErrResolveFailed = errors.New("could not resolve name") + +type IpnsResult struct { + path.Path + Err error +} + +// NameAPI specifies the interface to IPNS. +// +// IPNS is a PKI namespace, where names are the hashes of public keys, and the +// private key enables publishing new (signed) values. In both publish and +// resolve, the default name used is the node's own PeerID, which is the hash of +// its public key. +// +// You can use .Key API to list and generate more names and their respective keys. +type NameAPI interface { + // Publish announces new IPNS name + Publish(ctx context.Context, path path.Path, opts ...options.NamePublishOption) (ipns.Name, error) + + // Resolve attempts to resolve the newest version of the specified name + Resolve(ctx context.Context, name string, opts ...options.NameResolveOption) (path.Path, error) + + // Search is a version of Resolve which outputs paths as they are discovered, + // reducing the time to first entry + // + // Note: by default, all paths read from the channel are considered unsafe, + // except the latest (last path in channel read buffer). + Search(ctx context.Context, name string, opts ...options.NameResolveOption) (<-chan IpnsResult, error) +} diff --git a/core/coreiface/object.go b/core/coreiface/object.go new file mode 100644 index 000000000..27bb88935 --- /dev/null +++ b/core/coreiface/object.go @@ -0,0 +1,58 @@ +package iface + +import ( + "context" + + "github.com/ipfs/boxo/path" + "github.com/ipfs/kubo/core/coreiface/options" +) + +// ChangeType denotes type of change in ObjectChange +type ChangeType int + +const ( + // DiffAdd is set when a link was added to the graph + DiffAdd ChangeType = iota + + // DiffRemove is set when a link was removed from the graph + DiffRemove + + // DiffMod is set when a link was changed in the graph + DiffMod +) + +// ObjectChange represents a change ia a graph +type ObjectChange struct { + // Type of the change, either: + // * DiffAdd - Added a link + // * DiffRemove - Removed a link + // * DiffMod - Modified a link + Type ChangeType + + // Path to the changed link + Path string + + // Before holds the link path before the change. Note that when a link is + // added, this will be nil. + Before path.ImmutablePath + + // After holds the link path after the change. Note that when a link is + // removed, this will be nil. + After path.ImmutablePath +} + +// ObjectAPI specifies the interface to MerkleDAG and contains useful utilities +// for manipulating MerkleDAG data structures. +type ObjectAPI interface { + // AddLink adds a link under the specified path. child path can point to a + // subdirectory within the patent which must be present (can be overridden + // with WithCreate option). + AddLink(ctx context.Context, base path.Path, name string, child path.Path, opts ...options.ObjectAddLinkOption) (path.ImmutablePath, error) + + // RmLink removes a link from the node + RmLink(ctx context.Context, base path.Path, link string) (path.ImmutablePath, error) + + // Diff returns a set of changes needed to transform the first object into the + // second. + Diff(context.Context, path.Path, path.Path) ([]ObjectChange, error) +} diff --git a/core/coreiface/options/block.go b/core/coreiface/options/block.go new file mode 100644 index 000000000..83a43702c --- /dev/null +++ b/core/coreiface/options/block.go @@ -0,0 +1,165 @@ +package options + +import ( + "fmt" + + cid "github.com/ipfs/go-cid" + mc "github.com/multiformats/go-multicodec" + mh "github.com/multiformats/go-multihash" +) + +type BlockPutSettings struct { + CidPrefix cid.Prefix + Pin bool +} + +type BlockRmSettings struct { + Force bool +} + +type ( + BlockPutOption func(*BlockPutSettings) error + BlockRmOption func(*BlockRmSettings) error +) + +func BlockPutOptions(opts ...BlockPutOption) (*BlockPutSettings, error) { + var cidPrefix cid.Prefix + + // Baseline is CIDv1 raw sha2-255-32 (can be tweaked later via opts) + cidPrefix.Version = 1 + cidPrefix.Codec = uint64(mc.Raw) + cidPrefix.MhType = mh.SHA2_256 + cidPrefix.MhLength = -1 // -1 means len is to be calculated during mh.Sum() + + options := &BlockPutSettings{ + CidPrefix: cidPrefix, + Pin: false, + } + + // Apply any overrides + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + + return options, nil +} + +func BlockRmOptions(opts ...BlockRmOption) (*BlockRmSettings, error) { + options := &BlockRmSettings{ + Force: false, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + return options, nil +} + +type blockOpts struct{} + +var Block blockOpts + +// CidCodec is the modern option for Block.Put which specifies the multicodec to use +// in the CID returned by the Block.Put operation. +// It uses correct codes from go-multicodec and replaces the old Format now with CIDv1 as the default. +func (blockOpts) CidCodec(codecName string) BlockPutOption { + return func(settings *BlockPutSettings) error { + if codecName == "" { + return nil + } + code, err := codeFromName(codecName) + if err != nil { + return err + } + settings.CidPrefix.Codec = uint64(code) + return nil + } +} + +// Map string to code from go-multicodec +func codeFromName(codecName string) (mc.Code, error) { + var cidCodec mc.Code + err := cidCodec.Set(codecName) + return cidCodec, err +} + +// Format is a legacy option for Block.Put which specifies the multicodec to +// use to serialize the object. +// Provided for backward-compatibility only. Use CidCodec instead. +func (blockOpts) Format(format string) BlockPutOption { + return func(settings *BlockPutSettings) error { + if format == "" { + return nil + } + // Opt-in CIDv0 support for backward-compatibility + if format == "v0" { + settings.CidPrefix.Version = 0 + } + + // Fixup a legacy (invalid) names for dag-pb (0x70) + if format == "v0" || format == "protobuf" { + format = "dag-pb" + } + + // Fixup invalid name for dag-cbor (0x71) + if format == "cbor" { + format = "dag-cbor" + } + + // Set code based on name passed as "format" + code, err := codeFromName(format) + if err != nil { + return err + } + settings.CidPrefix.Codec = uint64(code) + + // If CIDv0, ensure all parameters are compatible + // (in theory go-cid would validate this anyway, but we want to provide better errors) + pref := settings.CidPrefix + if pref.Version == 0 { + if pref.Codec != uint64(mc.DagPb) { + return fmt.Errorf("only dag-pb is allowed with CIDv0") + } + if pref.MhType != mh.SHA2_256 || (pref.MhLength != -1 && pref.MhLength != 32) { + return fmt.Errorf("only sha2-255-32 is allowed with CIDv0") + } + } + + return nil + } +} + +// Hash is an option for Block.Put which specifies the multihash settings to use +// when hashing the object. Default is mh.SHA2_256 (0x12). +// If mhLen is set to -1, default length for the hash will be used +func (blockOpts) Hash(mhType uint64, mhLen int) BlockPutOption { + return func(settings *BlockPutSettings) error { + settings.CidPrefix.MhType = mhType + settings.CidPrefix.MhLength = mhLen + return nil + } +} + +// Pin is an option for Block.Put which specifies whether to (recursively) pin +// added blocks +func (blockOpts) Pin(pin bool) BlockPutOption { + return func(settings *BlockPutSettings) error { + settings.Pin = pin + return nil + } +} + +// Force is an option for Block.Rm which, when set to true, will ignore +// non-existing blocks +func (blockOpts) Force(force bool) BlockRmOption { + return func(settings *BlockRmSettings) error { + settings.Force = force + return nil + } +} diff --git a/core/coreiface/options/dht.go b/core/coreiface/options/dht.go new file mode 100644 index 000000000..4a6f7f86e --- /dev/null +++ b/core/coreiface/options/dht.go @@ -0,0 +1,29 @@ +package options + +// nolint deprecated +// Deprecated: use [RoutingProvideSettings] instead. +type DhtProvideSettings = RoutingProvideSettings + +// nolint deprecated +// Deprecated: use [RoutingFindProvidersSettings] instead. +type DhtFindProvidersSettings = RoutingFindProvidersSettings + +// nolint deprecated +// Deprecated: use [RoutingProvideOption] instead. +type DhtProvideOption = RoutingProvideOption + +// nolint deprecated +// Deprecated: use [RoutingFindProvidersOption] instead. +type DhtFindProvidersOption = RoutingFindProvidersOption + +// nolint deprecated +// Deprecated: use [RoutingProvideOptions] instead. +var DhtProvideOptions = RoutingProvideOptions + +// nolint deprecated +// Deprecated: use [RoutingFindProvidersOptions] instead. +var DhtFindProvidersOptions = RoutingFindProvidersOptions + +// nolint deprecated +// Deprecated: use [Routing] instead. +var Dht = Routing diff --git a/core/coreiface/options/global.go b/core/coreiface/options/global.go new file mode 100644 index 000000000..90e2586f1 --- /dev/null +++ b/core/coreiface/options/global.go @@ -0,0 +1,47 @@ +package options + +type ApiSettings struct { + Offline bool + FetchBlocks bool +} + +type ApiOption func(*ApiSettings) error + +func ApiOptions(opts ...ApiOption) (*ApiSettings, error) { + options := &ApiSettings{ + Offline: false, + FetchBlocks: true, + } + + return ApiOptionsTo(options, opts...) +} + +func ApiOptionsTo(options *ApiSettings, opts ...ApiOption) (*ApiSettings, error) { + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + return options, nil +} + +type apiOpts struct{} + +var Api apiOpts + +func (apiOpts) Offline(offline bool) ApiOption { + return func(settings *ApiSettings) error { + settings.Offline = offline + return nil + } +} + +// FetchBlocks when set to false prevents api from fetching blocks from the +// network while allowing other services such as IPNS to still be online +func (apiOpts) FetchBlocks(fetch bool) ApiOption { + return func(settings *ApiSettings) error { + settings.FetchBlocks = fetch + return nil + } +} diff --git a/core/coreiface/options/key.go b/core/coreiface/options/key.go new file mode 100644 index 000000000..ebff6d5a7 --- /dev/null +++ b/core/coreiface/options/key.go @@ -0,0 +1,89 @@ +package options + +const ( + RSAKey = "rsa" + Ed25519Key = "ed25519" + + DefaultRSALen = 2048 +) + +type KeyGenerateSettings struct { + Algorithm string + Size int +} + +type KeyRenameSettings struct { + Force bool +} + +type ( + KeyGenerateOption func(*KeyGenerateSettings) error + KeyRenameOption func(*KeyRenameSettings) error +) + +func KeyGenerateOptions(opts ...KeyGenerateOption) (*KeyGenerateSettings, error) { + options := &KeyGenerateSettings{ + Algorithm: RSAKey, + Size: -1, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + return options, nil +} + +func KeyRenameOptions(opts ...KeyRenameOption) (*KeyRenameSettings, error) { + options := &KeyRenameSettings{ + Force: false, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + return options, nil +} + +type keyOpts struct{} + +var Key keyOpts + +// Type is an option for Key.Generate which specifies which algorithm +// should be used for the key. Default is options.RSAKey +// +// Supported key types: +// * options.RSAKey +// * options.Ed25519Key +func (keyOpts) Type(algorithm string) KeyGenerateOption { + return func(settings *KeyGenerateSettings) error { + settings.Algorithm = algorithm + return nil + } +} + +// Size is an option for Key.Generate which specifies the size of the key to +// generated. Default is -1 +// +// value of -1 means 'use default size for key type': +// - 2048 for RSA +func (keyOpts) Size(size int) KeyGenerateOption { + return func(settings *KeyGenerateSettings) error { + settings.Size = size + return nil + } +} + +// Force is an option for Key.Rename which specifies whether to allow to +// replace existing keys. +func (keyOpts) Force(force bool) KeyRenameOption { + return func(settings *KeyRenameSettings) error { + settings.Force = force + return nil + } +} diff --git a/core/coreiface/options/name.go b/core/coreiface/options/name.go new file mode 100644 index 000000000..7b4b6a8fd --- /dev/null +++ b/core/coreiface/options/name.go @@ -0,0 +1,131 @@ +package options + +import ( + "time" + + "github.com/ipfs/boxo/namesys" +) + +const ( + DefaultNameValidTime = 24 * time.Hour +) + +type NamePublishSettings struct { + ValidTime time.Duration + Key string + TTL *time.Duration + CompatibleWithV1 bool + AllowOffline bool +} + +type NameResolveSettings struct { + Cache bool + + ResolveOpts []namesys.ResolveOption +} + +type ( + NamePublishOption func(*NamePublishSettings) error + NameResolveOption func(*NameResolveSettings) error +) + +func NamePublishOptions(opts ...NamePublishOption) (*NamePublishSettings, error) { + options := &NamePublishSettings{ + ValidTime: DefaultNameValidTime, + Key: "self", + + AllowOffline: false, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + + return options, nil +} + +func NameResolveOptions(opts ...NameResolveOption) (*NameResolveSettings, error) { + options := &NameResolveSettings{ + Cache: true, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + + return options, nil +} + +type nameOpts struct{} + +var Name nameOpts + +// ValidTime is an option for Name.Publish which specifies for how long the +// entry will remain valid. Default value is 24h +func (nameOpts) ValidTime(validTime time.Duration) NamePublishOption { + return func(settings *NamePublishSettings) error { + settings.ValidTime = validTime + return nil + } +} + +// Key is an option for Name.Publish which specifies the key to use for +// publishing. Default value is "self" which is the node's own PeerID. +// The key parameter must be either PeerID or keystore key alias. +// +// You can use KeyAPI to list and generate more names and their respective keys. +func (nameOpts) Key(key string) NamePublishOption { + return func(settings *NamePublishSettings) error { + settings.Key = key + return nil + } +} + +// AllowOffline is an option for Name.Publish which specifies whether to allow +// publishing when the node is offline. Default value is false +func (nameOpts) AllowOffline(allow bool) NamePublishOption { + return func(settings *NamePublishSettings) error { + settings.AllowOffline = allow + return nil + } +} + +// TTL is an option for Name.Publish which specifies the time duration the +// published record should be cached for (caution: experimental). +func (nameOpts) TTL(ttl time.Duration) NamePublishOption { + return func(settings *NamePublishSettings) error { + settings.TTL = &ttl + return nil + } +} + +// CompatibleWithV1 is an option for [Name.Publish] which specifies if the +// created record should be backwards compatible with V1 IPNS Records. +func (nameOpts) CompatibleWithV1(compatible bool) NamePublishOption { + return func(settings *NamePublishSettings) error { + settings.CompatibleWithV1 = compatible + return nil + } +} + +// Cache is an option for Name.Resolve which specifies if cache should be used. +// Default value is true +func (nameOpts) Cache(cache bool) NameResolveOption { + return func(settings *NameResolveSettings) error { + settings.Cache = cache + return nil + } +} + +func (nameOpts) ResolveOption(opt namesys.ResolveOption) NameResolveOption { + return func(settings *NameResolveSettings) error { + settings.ResolveOpts = append(settings.ResolveOpts, opt) + return nil + } +} diff --git a/core/coreiface/options/object.go b/core/coreiface/options/object.go new file mode 100644 index 000000000..ab780ebd9 --- /dev/null +++ b/core/coreiface/options/object.go @@ -0,0 +1,36 @@ +package options + +type ObjectAddLinkSettings struct { + Create bool +} + +type ( + ObjectAddLinkOption func(*ObjectAddLinkSettings) error +) + +func ObjectAddLinkOptions(opts ...ObjectAddLinkOption) (*ObjectAddLinkSettings, error) { + options := &ObjectAddLinkSettings{ + Create: false, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + return options, nil +} + +type objectOpts struct{} + +var Object objectOpts + +// Create is an option for Object.AddLink which specifies whether create required +// directories for the child +func (objectOpts) Create(create bool) ObjectAddLinkOption { + return func(settings *ObjectAddLinkSettings) error { + settings.Create = create + return nil + } +} diff --git a/core/coreiface/options/pin.go b/core/coreiface/options/pin.go new file mode 100644 index 000000000..5b4cc9de7 --- /dev/null +++ b/core/coreiface/options/pin.go @@ -0,0 +1,310 @@ +package options + +import "fmt" + +// PinAddSettings represent the settings for PinAPI.Add +type PinAddSettings struct { + Recursive bool + Name string +} + +// PinLsSettings represent the settings for PinAPI.Ls +type PinLsSettings struct { + Type string + Detailed bool + Name string +} + +// PinIsPinnedSettings represent the settings for PinAPI.IsPinned +type PinIsPinnedSettings struct { + WithType string +} + +// PinRmSettings represents the settings for PinAPI.Rm +type PinRmSettings struct { + Recursive bool +} + +// PinUpdateSettings represent the settings for PinAPI.Update +type PinUpdateSettings struct { + Unpin bool +} + +// PinAddOption is the signature of an option for PinAPI.Add +type PinAddOption func(*PinAddSettings) error + +// PinLsOption is the signature of an option for PinAPI.Ls +type PinLsOption func(*PinLsSettings) error + +// PinIsPinnedOption is the signature of an option for PinAPI.IsPinned +type PinIsPinnedOption func(*PinIsPinnedSettings) error + +// PinRmOption is the signature of an option for PinAPI.Rm +type PinRmOption func(*PinRmSettings) error + +// PinUpdateOption is the signature of an option for PinAPI.Update +type PinUpdateOption func(*PinUpdateSettings) error + +// PinAddOptions compile a series of PinAddOption into a ready to use +// PinAddSettings and set the default values. +func PinAddOptions(opts ...PinAddOption) (*PinAddSettings, error) { + options := &PinAddSettings{ + Recursive: true, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + + return options, nil +} + +// PinLsOptions compile a series of PinLsOption into a ready to use +// PinLsSettings and set the default values. +func PinLsOptions(opts ...PinLsOption) (*PinLsSettings, error) { + options := &PinLsSettings{ + Type: "all", + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + + return options, nil +} + +// PinIsPinnedOptions compile a series of PinIsPinnedOption into a ready to use +// PinIsPinnedSettings and set the default values. +func PinIsPinnedOptions(opts ...PinIsPinnedOption) (*PinIsPinnedSettings, error) { + options := &PinIsPinnedSettings{ + WithType: "all", + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + + return options, nil +} + +// PinRmOptions compile a series of PinRmOption into a ready to use +// PinRmSettings and set the default values. +func PinRmOptions(opts ...PinRmOption) (*PinRmSettings, error) { + options := &PinRmSettings{ + Recursive: true, + } + + for _, opt := range opts { + if err := opt(options); err != nil { + return nil, err + } + } + + return options, nil +} + +// PinUpdateOptions compile a series of PinUpdateOption into a ready to use +// PinUpdateSettings and set the default values. +func PinUpdateOptions(opts ...PinUpdateOption) (*PinUpdateSettings, error) { + options := &PinUpdateSettings{ + Unpin: true, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + + return options, nil +} + +type pinOpts struct { + Ls pinLsOpts + IsPinned pinIsPinnedOpts +} + +// Pin provide an access to all the options for the Pin API. +var Pin pinOpts + +type pinLsOpts struct{} + +// All is an option for Pin.Ls which will make it return all pins. It is +// the default +func (pinLsOpts) All() PinLsOption { + return Pin.Ls.pinType("all") +} + +// Recursive is an option for Pin.Ls which will make it only return recursive +// pins +func (pinLsOpts) Recursive() PinLsOption { + return Pin.Ls.pinType("recursive") +} + +// Direct is an option for Pin.Ls which will make it only return direct (non +// recursive) pins +func (pinLsOpts) Direct() PinLsOption { + return Pin.Ls.pinType("direct") +} + +// Indirect is an option for Pin.Ls which will make it only return indirect pins +// (objects referenced by other recursively pinned objects) +func (pinLsOpts) Indirect() PinLsOption { + return Pin.Ls.pinType("indirect") +} + +// Type is an option for Pin.Ls which will make it only return pins of the given +// type. +// +// Supported values: +// - "direct" - directly pinned objects +// - "recursive" - roots of recursive pins +// - "indirect" - indirectly pinned objects (referenced by recursively pinned +// objects) +// - "all" - all pinned objects (default) +func (pinLsOpts) Type(typeStr string) (PinLsOption, error) { + switch typeStr { + case "all", "direct", "indirect", "recursive": + return Pin.Ls.pinType(typeStr), nil + default: + return nil, fmt.Errorf("invalid type '%s', must be one of {direct, indirect, recursive, all}", typeStr) + } +} + +// pinType is an option for Pin.Ls which allows to specify which pin types should +// be returned +// +// Supported values: +// - "direct" - directly pinned objects +// - "recursive" - roots of recursive pins +// - "indirect" - indirectly pinned objects (referenced by recursively pinned +// objects) +// - "all" - all pinned objects (default) +func (pinLsOpts) pinType(t string) PinLsOption { + return func(settings *PinLsSettings) error { + settings.Type = t + return nil + } +} + +// Detailed is an option for [Pin.Ls] which sets whether or not to return +// detailed information, such as pin names and modes. +func (pinLsOpts) Detailed(detailed bool) PinLsOption { + return func(settings *PinLsSettings) error { + settings.Detailed = detailed + return nil + } +} + +func (pinLsOpts) Name(name string) PinLsOption { + return func(settings *PinLsSettings) error { + settings.Name = name + return nil + } +} + +type pinIsPinnedOpts struct{} + +// All is an option for Pin.IsPinned which will make it search in all type of pins. +// It is the default +func (pinIsPinnedOpts) All() PinIsPinnedOption { + return Pin.IsPinned.pinType("all") +} + +// Recursive is an option for Pin.IsPinned which will make it only search in +// recursive pins +func (pinIsPinnedOpts) Recursive() PinIsPinnedOption { + return Pin.IsPinned.pinType("recursive") +} + +// Direct is an option for Pin.IsPinned which will make it only search in direct +// (non recursive) pins +func (pinIsPinnedOpts) Direct() PinIsPinnedOption { + return Pin.IsPinned.pinType("direct") +} + +// Indirect is an option for Pin.IsPinned which will make it only search indirect +// pins (objects referenced by other recursively pinned objects) +func (pinIsPinnedOpts) Indirect() PinIsPinnedOption { + return Pin.IsPinned.pinType("indirect") +} + +// Type is an option for Pin.IsPinned which will make it only search pins of the given +// type. +// +// Supported values: +// - "direct" - directly pinned objects +// - "recursive" - roots of recursive pins +// - "indirect" - indirectly pinned objects (referenced by recursively pinned +// objects) +// - "all" - all pinned objects (default) +func (pinIsPinnedOpts) Type(typeStr string) (PinIsPinnedOption, error) { + switch typeStr { + case "all", "direct", "indirect", "recursive": + return Pin.IsPinned.pinType(typeStr), nil + default: + return nil, fmt.Errorf("invalid type '%s', must be one of {direct, indirect, recursive, all}", typeStr) + } +} + +// pinType is an option for Pin.IsPinned which allows to specify which pin type the given +// pin is expected to be, speeding up the research. +// +// Supported values: +// - "direct" - directly pinned objects +// - "recursive" - roots of recursive pins +// - "indirect" - indirectly pinned objects (referenced by recursively pinned +// objects) +// - "all" - all pinned objects (default) +func (pinIsPinnedOpts) pinType(t string) PinIsPinnedOption { + return func(settings *PinIsPinnedSettings) error { + settings.WithType = t + return nil + } +} + +// Recursive is an option for Pin.Add which specifies whether to pin an entire +// object tree or just one object. Default: true +func (pinOpts) Recursive(recursive bool) PinAddOption { + return func(settings *PinAddSettings) error { + settings.Recursive = recursive + return nil + } +} + +// Name is an option for Pin.Add which specifies an optional name to add to the pin. +func (pinOpts) Name(name string) PinAddOption { + return func(settings *PinAddSettings) error { + settings.Name = name + return nil + } +} + +// RmRecursive is an option for Pin.Rm which specifies whether to recursively +// unpin the object linked to by the specified object(s). This does not remove +// indirect pins referenced by other recursive pins. +func (pinOpts) RmRecursive(recursive bool) PinRmOption { + return func(settings *PinRmSettings) error { + settings.Recursive = recursive + return nil + } +} + +// Unpin is an option for Pin.Update which specifies whether to remove the old pin. +// Default is true. +func (pinOpts) Unpin(unpin bool) PinUpdateOption { + return func(settings *PinUpdateSettings) error { + settings.Unpin = unpin + return nil + } +} diff --git a/core/coreiface/options/pubsub.go b/core/coreiface/options/pubsub.go new file mode 100644 index 000000000..839ef97b1 --- /dev/null +++ b/core/coreiface/options/pubsub.go @@ -0,0 +1,60 @@ +package options + +type PubSubPeersSettings struct { + Topic string +} + +type PubSubSubscribeSettings struct { + Discover bool +} + +type ( + PubSubPeersOption func(*PubSubPeersSettings) error + PubSubSubscribeOption func(*PubSubSubscribeSettings) error +) + +func PubSubPeersOptions(opts ...PubSubPeersOption) (*PubSubPeersSettings, error) { + options := &PubSubPeersSettings{ + Topic: "", + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + return options, nil +} + +func PubSubSubscribeOptions(opts ...PubSubSubscribeOption) (*PubSubSubscribeSettings, error) { + options := &PubSubSubscribeSettings{ + Discover: false, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + return options, nil +} + +type pubsubOpts struct{} + +var PubSub pubsubOpts + +func (pubsubOpts) Topic(topic string) PubSubPeersOption { + return func(settings *PubSubPeersSettings) error { + settings.Topic = topic + return nil + } +} + +func (pubsubOpts) Discover(discover bool) PubSubSubscribeOption { + return func(settings *PubSubSubscribeSettings) error { + settings.Discover = discover + return nil + } +} diff --git a/core/coreiface/options/routing.go b/core/coreiface/options/routing.go new file mode 100644 index 000000000..8da7e7a1d --- /dev/null +++ b/core/coreiface/options/routing.go @@ -0,0 +1,98 @@ +package options + +type RoutingPutSettings struct { + AllowOffline bool +} + +type RoutingPutOption func(*RoutingPutSettings) error + +func RoutingPutOptions(opts ...RoutingPutOption) (*RoutingPutSettings, error) { + options := &RoutingPutSettings{ + AllowOffline: false, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + + return options, nil +} + +// nolint deprecated +// Deprecated: use [Routing] instead. +var Put = Routing + +type RoutingProvideSettings struct { + Recursive bool +} + +type RoutingFindProvidersSettings struct { + NumProviders int +} + +type ( + RoutingProvideOption func(*DhtProvideSettings) error + RoutingFindProvidersOption func(*DhtFindProvidersSettings) error +) + +func RoutingProvideOptions(opts ...RoutingProvideOption) (*RoutingProvideSettings, error) { + options := &RoutingProvideSettings{ + Recursive: false, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + return options, nil +} + +func RoutingFindProvidersOptions(opts ...RoutingFindProvidersOption) (*RoutingFindProvidersSettings, error) { + options := &RoutingFindProvidersSettings{ + NumProviders: 20, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + return options, nil +} + +type routingOpts struct{} + +var Routing routingOpts + +// Recursive is an option for [Routing.Provide] which specifies whether to provide +// the given path recursively. +func (routingOpts) Recursive(recursive bool) RoutingProvideOption { + return func(settings *DhtProvideSettings) error { + settings.Recursive = recursive + return nil + } +} + +// NumProviders is an option for [Routing.FindProviders] which specifies the +// number of peers to look for. Default is 20. +func (routingOpts) NumProviders(numProviders int) RoutingFindProvidersOption { + return func(settings *DhtFindProvidersSettings) error { + settings.NumProviders = numProviders + return nil + } +} + +// AllowOffline is an option for [Routing.Put] which specifies whether to allow +// publishing when the node is offline. Default value is false +func (routingOpts) AllowOffline(allow bool) RoutingPutOption { + return func(settings *RoutingPutSettings) error { + settings.AllowOffline = allow + return nil + } +} diff --git a/core/coreiface/options/unixfs.go b/core/coreiface/options/unixfs.go new file mode 100644 index 000000000..c837ec1b2 --- /dev/null +++ b/core/coreiface/options/unixfs.go @@ -0,0 +1,350 @@ +package options + +import ( + "errors" + "fmt" + "os" + "time" + + dag "github.com/ipfs/boxo/ipld/merkledag" + cid "github.com/ipfs/go-cid" + mh "github.com/multiformats/go-multihash" +) + +type Layout int + +const ( + BalancedLayout Layout = iota + TrickleLayout +) + +type UnixfsAddSettings struct { + CidVersion int + MhType uint64 + + Inline bool + InlineLimit int + RawLeaves bool + RawLeavesSet bool + + Chunker string + Layout Layout + + Pin bool + OnlyHash bool + FsCache bool + NoCopy bool + + Events chan<- interface{} + Silent bool + Progress bool + + PreserveMode bool + PreserveMtime bool + Mode os.FileMode + Mtime time.Time +} + +type UnixfsLsSettings struct { + ResolveChildren bool + UseCumulativeSize bool +} + +type ( + UnixfsAddOption func(*UnixfsAddSettings) error + UnixfsLsOption func(*UnixfsLsSettings) error +) + +func UnixfsAddOptions(opts ...UnixfsAddOption) (*UnixfsAddSettings, cid.Prefix, error) { + options := &UnixfsAddSettings{ + CidVersion: -1, + MhType: mh.SHA2_256, + + Inline: false, + InlineLimit: 32, + RawLeaves: false, + RawLeavesSet: false, + + Chunker: "size-262144", + Layout: BalancedLayout, + + Pin: false, + OnlyHash: false, + FsCache: false, + NoCopy: false, + + Events: nil, + Silent: false, + Progress: false, + + PreserveMode: false, + PreserveMtime: false, + Mode: 0, + Mtime: time.Time{}, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, cid.Prefix{}, err + } + } + + // nocopy -> rawblocks + if options.NoCopy && !options.RawLeaves { + // fixed? + if options.RawLeavesSet { + return nil, cid.Prefix{}, fmt.Errorf("nocopy option requires '--raw-leaves' to be enabled as well") + } + + // No, satisfy mandatory constraint. + options.RawLeaves = true + } + + // (hash != "sha2-256") -> CIDv1 + if options.MhType != mh.SHA2_256 { + switch options.CidVersion { + case 0: + return nil, cid.Prefix{}, errors.New("CIDv0 only supports sha2-256") + case 1, -1: + options.CidVersion = 1 + default: + return nil, cid.Prefix{}, fmt.Errorf("unknown CID version: %d", options.CidVersion) + } + } else { + if options.CidVersion < 0 { + // Default to CIDv0 + options.CidVersion = 0 + } + } + + if !options.Mtime.IsZero() && options.PreserveMtime { + options.PreserveMtime = false + } + + if options.Mode != 0 && options.PreserveMode { + options.PreserveMode = false + } + + // cidV1 -> raw blocks (by default) + if options.CidVersion > 0 && !options.RawLeavesSet { + options.RawLeaves = true + } + + prefix, err := dag.PrefixForCidVersion(options.CidVersion) + if err != nil { + return nil, cid.Prefix{}, err + } + + prefix.MhType = options.MhType + prefix.MhLength = -1 + + return options, prefix, nil +} + +func UnixfsLsOptions(opts ...UnixfsLsOption) (*UnixfsLsSettings, error) { + options := &UnixfsLsSettings{ + ResolveChildren: true, + } + + for _, opt := range opts { + err := opt(options) + if err != nil { + return nil, err + } + } + + return options, nil +} + +type unixfsOpts struct{} + +var Unixfs unixfsOpts + +// CidVersion specifies which CID version to use. Defaults to 0 unless an option +// that depends on CIDv1 is passed. +func (unixfsOpts) CidVersion(version int) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.CidVersion = version + return nil + } +} + +// Hash function to use. Implies CIDv1 if not set to sha2-256 (default). +// +// Table of functions is declared in https://github.com/multiformats/go-multihash/blob/master/multihash.go +func (unixfsOpts) Hash(mhtype uint64) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.MhType = mhtype + return nil + } +} + +// RawLeaves specifies whether to use raw blocks for leaves (data nodes with no +// links) instead of wrapping them with unixfs structures. +func (unixfsOpts) RawLeaves(enable bool) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.RawLeaves = enable + settings.RawLeavesSet = true + return nil + } +} + +// Inline tells the adder to inline small blocks into CIDs +func (unixfsOpts) Inline(enable bool) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.Inline = enable + return nil + } +} + +// InlineLimit sets the amount of bytes below which blocks will be encoded +// directly into CID instead of being stored and addressed by it's hash. +// Specifying this option won't enable block inlining. For that use `Inline` +// option. Default: 32 bytes +// +// Note that while there is no hard limit on the number of bytes, it should be +// kept at a reasonably low value, such as 64; implementations may choose to +// reject anything larger. +func (unixfsOpts) InlineLimit(limit int) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.InlineLimit = limit + return nil + } +} + +// Chunker specifies settings for the chunking algorithm to use. +// +// Default: size-262144, formats: +// size-[bytes] - Simple chunker splitting data into blocks of n bytes +// rabin-[min]-[avg]-[max] - Rabin chunker +func (unixfsOpts) Chunker(chunker string) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.Chunker = chunker + return nil + } +} + +// Layout tells the adder how to balance data between leaves. +// options.BalancedLayout is the default, it's optimized for static seekable +// files. +// options.TrickleLayout is optimized for streaming data, +func (unixfsOpts) Layout(layout Layout) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.Layout = layout + return nil + } +} + +// Pin tells the adder to pin the file root recursively after adding +func (unixfsOpts) Pin(pin bool) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.Pin = pin + return nil + } +} + +// HashOnly will make the adder calculate data hash without storing it in the +// blockstore or announcing it to the network +func (unixfsOpts) HashOnly(hashOnly bool) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.OnlyHash = hashOnly + return nil + } +} + +// Events specifies channel which will be used to report events about ongoing +// Add operation. +// +// Note that if this channel blocks it may slowdown the adder +func (unixfsOpts) Events(sink chan<- interface{}) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.Events = sink + return nil + } +} + +// Silent reduces event output +func (unixfsOpts) Silent(silent bool) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.Silent = silent + return nil + } +} + +// Progress tells the adder whether to enable progress events +func (unixfsOpts) Progress(enable bool) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.Progress = enable + return nil + } +} + +// FsCache tells the adder to check the filestore for pre-existing blocks +// +// Experimental +func (unixfsOpts) FsCache(enable bool) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.FsCache = enable + return nil + } +} + +// NoCopy tells the adder to add the files using filestore. Implies RawLeaves. +// +// Experimental +func (unixfsOpts) Nocopy(enable bool) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.NoCopy = enable + return nil + } +} + +func (unixfsOpts) ResolveChildren(resolve bool) UnixfsLsOption { + return func(settings *UnixfsLsSettings) error { + settings.ResolveChildren = resolve + return nil + } +} + +func (unixfsOpts) UseCumulativeSize(use bool) UnixfsLsOption { + return func(settings *UnixfsLsSettings) error { + settings.UseCumulativeSize = use + return nil + } +} + +// PreserveMode tells the adder to store the file permissions +func (unixfsOpts) PreserveMode(enable bool) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.PreserveMode = enable + return nil + } +} + +// PreserveMtime tells the adder to store the file modification time +func (unixfsOpts) PreserveMtime(enable bool) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.PreserveMtime = enable + return nil + } +} + +// Mode represents a unix file mode +func (unixfsOpts) Mode(mode os.FileMode) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + settings.Mode = mode + return nil + } +} + +// Mtime represents a unix file mtime +func (unixfsOpts) Mtime(seconds int64, nsecs uint32) UnixfsAddOption { + return func(settings *UnixfsAddSettings) error { + if nsecs > 999999999 { + return errors.New("mtime nanoseconds must be in range [1, 999999999]") + } + settings.Mtime = time.Unix(seconds, int64(nsecs)) + return nil + } +} diff --git a/core/coreiface/pin.go b/core/coreiface/pin.go new file mode 100644 index 000000000..ed837fc9c --- /dev/null +++ b/core/coreiface/pin.go @@ -0,0 +1,69 @@ +package iface + +import ( + "context" + + "github.com/ipfs/boxo/path" + + "github.com/ipfs/kubo/core/coreiface/options" +) + +// Pin holds information about pinned resource +type Pin interface { + // Path to the pinned object + Path() path.ImmutablePath + + // Name is the name of the pin. + Name() string + + // Type of the pin + Type() string + + // if not nil, an error happened. Everything else should be ignored. + Err() error +} + +// PinStatus holds information about pin health +type PinStatus interface { + // Ok indicates whether the pin has been verified to be correct + Ok() bool + + // BadNodes returns any bad (usually missing) nodes from the pin + BadNodes() []BadPinNode + + // if not nil, an error happened. Everything else should be ignored. + Err() error +} + +// BadPinNode is a node that has been marked as bad by Pin.Verify +type BadPinNode interface { + // Path is the path of the node + Path() path.ImmutablePath + + // Err is the reason why the node has been marked as bad + Err() error +} + +// PinAPI specifies the interface to pining +type PinAPI interface { + // Add creates new pin, be default recursive - pinning the whole referenced + // tree + Add(context.Context, path.Path, ...options.PinAddOption) error + + // Ls returns list of pinned objects on this node + Ls(context.Context, ...options.PinLsOption) (<-chan Pin, error) + + // IsPinned returns whether or not the given cid is pinned + // and an explanation of why its pinned + IsPinned(context.Context, path.Path, ...options.PinIsPinnedOption) (string, bool, error) + + // Rm removes pin for object specified by the path + Rm(context.Context, path.Path, ...options.PinRmOption) error + + // Update changes one pin to another, skipping checks for matching paths in + // the old tree + Update(ctx context.Context, from path.Path, to path.Path, opts ...options.PinUpdateOption) error + + // Verify verifies the integrity of pinned objects + Verify(context.Context) (<-chan PinStatus, error) +} diff --git a/core/coreiface/pubsub.go b/core/coreiface/pubsub.go new file mode 100644 index 000000000..5cf8de54d --- /dev/null +++ b/core/coreiface/pubsub.go @@ -0,0 +1,48 @@ +package iface + +import ( + "context" + "io" + + "github.com/ipfs/kubo/core/coreiface/options" + + "github.com/libp2p/go-libp2p/core/peer" +) + +// PubSubSubscription is an active PubSub subscription +type PubSubSubscription interface { + io.Closer + + // Next return the next incoming message + Next(context.Context) (PubSubMessage, error) +} + +// PubSubMessage is a single PubSub message +type PubSubMessage interface { + // From returns id of a peer from which the message has arrived + From() peer.ID + + // Data returns the message body + Data() []byte + + // Seq returns message identifier + Seq() []byte + + // Topics returns list of topics this message was set to + Topics() []string +} + +// PubSubAPI specifies the interface to PubSub +type PubSubAPI interface { + // Ls lists subscribed topics by name + Ls(context.Context) ([]string, error) + + // Peers list peers we are currently pubsubbing with + Peers(context.Context, ...options.PubSubPeersOption) ([]peer.ID, error) + + // Publish a message to a given pubsub topic + Publish(context.Context, string, []byte) error + + // Subscribe to messages on a given topic + Subscribe(context.Context, string, ...options.PubSubSubscribeOption) (PubSubSubscription, error) +} diff --git a/core/coreiface/routing.go b/core/coreiface/routing.go new file mode 100644 index 000000000..a17dfcad9 --- /dev/null +++ b/core/coreiface/routing.go @@ -0,0 +1,29 @@ +package iface + +import ( + "context" + + "github.com/ipfs/boxo/path" + "github.com/ipfs/kubo/core/coreiface/options" + "github.com/libp2p/go-libp2p/core/peer" +) + +// RoutingAPI specifies the interface to the routing layer. +type RoutingAPI interface { + // Get retrieves the best value for a given key + Get(context.Context, string) ([]byte, error) + + // Put sets a value for a given key + Put(ctx context.Context, key string, value []byte, opts ...options.RoutingPutOption) error + + // FindPeer queries the routing system for all the multiaddresses associated + // with the given [peer.ID]. + FindPeer(context.Context, peer.ID) (peer.AddrInfo, error) + + // FindProviders finds the peers in the routing system who can provide a specific + // value given a key. + FindProviders(context.Context, path.Path, ...options.RoutingFindProvidersOption) (<-chan peer.AddrInfo, error) + + // Provide announces to the network that you are providing given values + Provide(context.Context, path.Path, ...options.RoutingProvideOption) error +} diff --git a/core/coreiface/swarm.go b/core/coreiface/swarm.go new file mode 100644 index 000000000..9aa5466ba --- /dev/null +++ b/core/coreiface/swarm.go @@ -0,0 +1,57 @@ +package iface + +import ( + "context" + "errors" + "time" + + "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/protocol" + + ma "github.com/multiformats/go-multiaddr" +) + +var ( + ErrNotConnected = errors.New("not connected") + ErrConnNotFound = errors.New("conn not found") +) + +// ConnectionInfo contains information about a peer +type ConnectionInfo interface { + // ID returns PeerID + ID() peer.ID + + // Address returns the multiaddress via which we are connected with the peer + Address() ma.Multiaddr + + // Direction returns which way the connection was established + Direction() network.Direction + + // Latency returns last known round trip time to the peer + Latency() (time.Duration, error) + + // Streams returns list of streams established with the peer + Streams() ([]protocol.ID, error) +} + +// SwarmAPI specifies the interface to libp2p swarm +type SwarmAPI interface { + // Connect to a given peer + Connect(context.Context, peer.AddrInfo) error + + // Disconnect from a given address + Disconnect(context.Context, ma.Multiaddr) error + + // Peers returns the list of peers we are connected to + Peers(context.Context) ([]ConnectionInfo, error) + + // KnownAddrs returns the list of all addresses this node is aware of + KnownAddrs(context.Context) (map[peer.ID][]ma.Multiaddr, error) + + // LocalAddrs returns the list of announced listening addresses + LocalAddrs(context.Context) ([]ma.Multiaddr, error) + + // ListenAddrs returns the list of all listening addresses + ListenAddrs(context.Context) ([]ma.Multiaddr, error) +} diff --git a/core/coreiface/tests/api.go b/core/coreiface/tests/api.go new file mode 100644 index 000000000..86ab60ae9 --- /dev/null +++ b/core/coreiface/tests/api.go @@ -0,0 +1,109 @@ +package tests + +import ( + "context" + "errors" + "testing" + "time" + + coreiface "github.com/ipfs/kubo/core/coreiface" +) + +var errAPINotImplemented = errors.New("api not implemented") + +type Provider interface { + // Make creates n nodes. fullIdentity set to false can be ignored + MakeAPISwarm(t *testing.T, ctx context.Context, fullIdentity bool, online bool, n int) ([]coreiface.CoreAPI, error) +} + +func (tp *TestSuite) makeAPISwarm(t *testing.T, ctx context.Context, fullIdentity bool, online bool, n int) ([]coreiface.CoreAPI, error) { + if tp.apis != nil { + tp.apis <- 1 + go func() { + <-ctx.Done() + tp.apis <- -1 + }() + } + + return tp.Provider.MakeAPISwarm(t, ctx, fullIdentity, online, n) +} + +func (tp *TestSuite) makeAPI(t *testing.T, ctx context.Context) (coreiface.CoreAPI, error) { + api, err := tp.makeAPISwarm(t, ctx, false, false, 1) + if err != nil { + return nil, err + } + + return api[0], nil +} + +func (tp *TestSuite) makeAPIWithIdentityAndOffline(t *testing.T, ctx context.Context) (coreiface.CoreAPI, error) { + api, err := tp.makeAPISwarm(t, ctx, true, false, 1) + if err != nil { + return nil, err + } + + return api[0], nil +} + +func (tp *TestSuite) MakeAPISwarm(t *testing.T, ctx context.Context, n int) ([]coreiface.CoreAPI, error) { + return tp.makeAPISwarm(t, ctx, true, true, n) +} + +type TestSuite struct { + Provider + + apis chan int +} + +func TestApi(p Provider) func(t *testing.T) { + running := 1 + apis := make(chan int) + zeroRunning := make(chan struct{}) + go func() { + for i := range apis { + running += i + if running < 1 { + close(zeroRunning) + return + } + } + }() + + tp := &TestSuite{Provider: p, apis: apis} + + return func(t *testing.T) { + t.Run("Block", tp.TestBlock) + t.Run("Dag", tp.TestDag) + t.Run("Key", tp.TestKey) + t.Run("Name", tp.TestName) + t.Run("Object", tp.TestObject) + t.Run("Path", tp.TestPath) + t.Run("Pin", tp.TestPin) + t.Run("PubSub", tp.TestPubSub) + t.Run("Routing", tp.TestRouting) + t.Run("Unixfs", tp.TestUnixfs) + + apis <- -1 + t.Run("TestsCancelCtx", func(t *testing.T) { + select { + case <-zeroRunning: + case <-time.After(time.Second): + t.Errorf("%d test swarms(s) not closed", running) + } + }) + } +} + +func (tp *TestSuite) hasApi(t *testing.T, tf func(coreiface.CoreAPI) error) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + if err := tf(api); err != nil { + t.Fatal(api) + } +} diff --git a/core/coreiface/tests/block.go b/core/coreiface/tests/block.go new file mode 100644 index 000000000..3b4ca0bc0 --- /dev/null +++ b/core/coreiface/tests/block.go @@ -0,0 +1,353 @@ +package tests + +import ( + "bytes" + "context" + "io" + "strings" + "testing" + + "github.com/ipfs/boxo/path" + ipld "github.com/ipfs/go-ipld-format" + coreiface "github.com/ipfs/kubo/core/coreiface" + opt "github.com/ipfs/kubo/core/coreiface/options" + mh "github.com/multiformats/go-multihash" +) + +var ( + pbCidV0 = "QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN" // dag-pb + pbCid = "bafybeiffndsajwhk3lwjewwdxqntmjm4b5wxaaanokonsggenkbw6slwk4" // dag-pb + rawCid = "bafkreiffndsajwhk3lwjewwdxqntmjm4b5wxaaanokonsggenkbw6slwk4" // raw bytes + cborCid = "bafyreicnga62zhxnmnlt6ymq5hcbsg7gdhqdu6z4ehu3wpjhvqnflfy6nm" // dag-cbor + cborKCid = "bafyr2qgsohbwdlk7ajmmbb4lhoytmest4wdbe5xnexfvtxeatuyqqmwv3fgxp3pmhpc27gwey2cct56gloqefoqwcf3yqiqzsaqb7p4jefhcw" // dag-cbor keccak-512 +) + +// dag-pb +func pbBlock() io.Reader { + return bytes.NewReader([]byte{10, 12, 8, 2, 18, 6, 104, 101, 108, 108, 111, 10, 24, 6}) +} + +// dag-cbor +func cborBlock() io.Reader { + return bytes.NewReader([]byte{101, 72, 101, 108, 108, 111}) +} + +func (tp *TestSuite) TestBlock(t *testing.T) { + tp.hasApi(t, func(api coreiface.CoreAPI) error { + if api.Block() == nil { + return errAPINotImplemented + } + return nil + }) + + t.Run("TestBlockPut (get raw CIDv1)", tp.TestBlockPut) + t.Run("TestBlockPutCidCodec: dag-pb", tp.TestBlockPutCidCodecDagPb) + t.Run("TestBlockPutCidCodec: dag-cbor", tp.TestBlockPutCidCodecDagCbor) + t.Run("TestBlockPutFormat (legacy): cbor → dag-cbor", tp.TestBlockPutFormatDagCbor) + t.Run("TestBlockPutFormat (legacy): protobuf → dag-pb", tp.TestBlockPutFormatDagPb) + t.Run("TestBlockPutFormat (legacy): v0 → CIDv0", tp.TestBlockPutFormatV0) + t.Run("TestBlockPutHash", tp.TestBlockPutHash) + t.Run("TestBlockGet", tp.TestBlockGet) + t.Run("TestBlockRm", tp.TestBlockRm) + t.Run("TestBlockStat", tp.TestBlockStat) + t.Run("TestBlockPin", tp.TestBlockPin) +} + +// when no opts are passed, produced CID has 'raw' codec +func (tp *TestSuite) TestBlockPut(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + res, err := api.Block().Put(ctx, pbBlock()) + if err != nil { + t.Fatal(err) + } + + if res.Path().RootCid().String() != rawCid { + t.Errorf("got wrong cid: %s", res.Path().RootCid().String()) + } +} + +// Format is deprecated, it used invalid codec names. +// Confirm 'cbor' gets fixed to 'dag-cbor' +func (tp *TestSuite) TestBlockPutFormatDagCbor(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + res, err := api.Block().Put(ctx, cborBlock(), opt.Block.Format("cbor")) + if err != nil { + t.Fatal(err) + } + + if res.Path().RootCid().String() != cborCid { + t.Errorf("got wrong cid: %s", res.Path().RootCid().String()) + } +} + +// Format is deprecated, it used invalid codec names. +// Confirm 'protobuf' got fixed to 'dag-pb' +func (tp *TestSuite) TestBlockPutFormatDagPb(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + res, err := api.Block().Put(ctx, pbBlock(), opt.Block.Format("protobuf")) + if err != nil { + t.Fatal(err) + } + + if res.Path().RootCid().String() != pbCid { + t.Errorf("got wrong cid: %s", res.Path().RootCid().String()) + } +} + +// Format is deprecated, it used invalid codec names. +// Confirm fake codec 'v0' got fixed to CIDv0 (with implicit dag-pb codec) +func (tp *TestSuite) TestBlockPutFormatV0(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + res, err := api.Block().Put(ctx, pbBlock(), opt.Block.Format("v0")) + if err != nil { + t.Fatal(err) + } + + if res.Path().RootCid().String() != pbCidV0 { + t.Errorf("got wrong cid: %s", res.Path().RootCid().String()) + } +} + +func (tp *TestSuite) TestBlockPutCidCodecDagCbor(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + res, err := api.Block().Put(ctx, cborBlock(), opt.Block.CidCodec("dag-cbor")) + if err != nil { + t.Fatal(err) + } + + if res.Path().RootCid().String() != cborCid { + t.Errorf("got wrong cid: %s", res.Path().RootCid().String()) + } +} + +func (tp *TestSuite) TestBlockPutCidCodecDagPb(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + res, err := api.Block().Put(ctx, pbBlock(), opt.Block.CidCodec("dag-pb")) + if err != nil { + t.Fatal(err) + } + + if res.Path().RootCid().String() != pbCid { + t.Errorf("got wrong cid: %s", res.Path().RootCid().String()) + } +} + +func (tp *TestSuite) TestBlockPutHash(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + res, err := api.Block().Put( + ctx, + cborBlock(), + opt.Block.Hash(mh.KECCAK_512, -1), + opt.Block.CidCodec("dag-cbor"), + ) + if err != nil { + t.Fatal(err) + } + + if res.Path().RootCid().String() != cborKCid { + t.Errorf("got wrong cid: %s", res.Path().RootCid().String()) + } +} + +func (tp *TestSuite) TestBlockGet(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + res, err := api.Block().Put(ctx, strings.NewReader(`Hello`), opt.Block.Format("raw")) + if err != nil { + t.Fatal(err) + } + + r, err := api.Block().Get(ctx, res.Path()) + if err != nil { + t.Fatal(err) + } + + d, err := io.ReadAll(r) + if err != nil { + t.Fatal(err) + } + + if string(d) != "Hello" { + t.Error("didn't get correct data back") + } + + p := path.FromCid(res.Path().RootCid()) + + rp, _, err := api.ResolvePath(ctx, p) + if err != nil { + t.Fatal(err) + } + if rp.RootCid().String() != res.Path().RootCid().String() { + t.Error("paths didn't match") + } +} + +func (tp *TestSuite) TestBlockRm(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + res, err := api.Block().Put(ctx, strings.NewReader(`Hello`), opt.Block.Format("raw")) + if err != nil { + t.Fatal(err) + } + + r, err := api.Block().Get(ctx, res.Path()) + if err != nil { + t.Fatal(err) + } + + d, err := io.ReadAll(r) + if err != nil { + t.Fatal(err) + } + + if string(d) != "Hello" { + t.Error("didn't get correct data back") + } + + err = api.Block().Rm(ctx, res.Path()) + if err != nil { + t.Fatal(err) + } + + _, err = api.Block().Get(ctx, res.Path()) + if err == nil { + t.Fatal("expected err to exist") + } + if !ipld.IsNotFound(err) { + t.Errorf("unexpected error; %s", err.Error()) + } + + err = api.Block().Rm(ctx, res.Path()) + if err == nil { + t.Fatal("expected err to exist") + } + if !ipld.IsNotFound(err) { + t.Errorf("unexpected error; %s", err.Error()) + } + + err = api.Block().Rm(ctx, res.Path(), opt.Block.Force(true)) + if err != nil { + t.Fatal(err) + } +} + +func (tp *TestSuite) TestBlockStat(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + res, err := api.Block().Put(ctx, strings.NewReader(`Hello`), opt.Block.Format("raw")) + if err != nil { + t.Fatal(err) + } + + stat, err := api.Block().Stat(ctx, res.Path()) + if err != nil { + t.Fatal(err) + } + + if stat.Path().String() != res.Path().String() { + t.Error("paths don't match") + } + + if stat.Size() != len("Hello") { + t.Error("length doesn't match") + } +} + +func (tp *TestSuite) TestBlockPin(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + _, err = api.Block().Put(ctx, strings.NewReader(`Hello`), opt.Block.Format("raw")) + if err != nil { + t.Fatal(err) + } + + if pins, err := api.Pin().Ls(ctx); err != nil || len(pins) != 0 { + t.Fatal("expected 0 pins") + } + + res, err := api.Block().Put( + ctx, + strings.NewReader(`Hello`), + opt.Block.Pin(true), + opt.Block.Format("raw"), + ) + if err != nil { + t.Fatal(err) + } + + pins, err := accPins(api.Pin().Ls(ctx)) + if err != nil { + t.Fatal(err) + } + if len(pins) != 1 { + t.Fatal("expected 1 pin") + } + if pins[0].Type() != "recursive" { + t.Error("expected a recursive pin") + } + if pins[0].Path().String() != res.Path().String() { + t.Error("pin path didn't match") + } +} diff --git a/core/coreiface/tests/dag.go b/core/coreiface/tests/dag.go new file mode 100644 index 000000000..3a388c556 --- /dev/null +++ b/core/coreiface/tests/dag.go @@ -0,0 +1,199 @@ +package tests + +import ( + "context" + "math" + "strings" + "testing" + + "github.com/ipfs/boxo/path" + coreiface "github.com/ipfs/kubo/core/coreiface" + + ipldcbor "github.com/ipfs/go-ipld-cbor" + ipld "github.com/ipfs/go-ipld-format" + mh "github.com/multiformats/go-multihash" +) + +func (tp *TestSuite) TestDag(t *testing.T) { + tp.hasApi(t, func(api coreiface.CoreAPI) error { + if api.Dag() == nil { + return errAPINotImplemented + } + return nil + }) + + t.Run("TestPut", tp.TestPut) + t.Run("TestPutWithHash", tp.TestPutWithHash) + t.Run("TestPath", tp.TestDagPath) + t.Run("TestTree", tp.TestTree) + t.Run("TestBatch", tp.TestBatch) +} + +var treeExpected = map[string]struct{}{ + "a": {}, + "b": {}, + "c": {}, + "c/d": {}, + "c/e": {}, +} + +func (tp *TestSuite) TestPut(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + nd, err := ipldcbor.FromJSON(strings.NewReader(`"Hello"`), math.MaxUint64, -1) + if err != nil { + t.Fatal(err) + } + + err = api.Dag().Add(ctx, nd) + if err != nil { + t.Fatal(err) + } + + if nd.Cid().String() != "bafyreicnga62zhxnmnlt6ymq5hcbsg7gdhqdu6z4ehu3wpjhvqnflfy6nm" { + t.Errorf("got wrong cid: %s", nd.Cid().String()) + } +} + +func (tp *TestSuite) TestPutWithHash(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + nd, err := ipldcbor.FromJSON(strings.NewReader(`"Hello"`), mh.SHA3_256, -1) + if err != nil { + t.Fatal(err) + } + + err = api.Dag().Add(ctx, nd) + if err != nil { + t.Fatal(err) + } + + if nd.Cid().String() != "bafyrmifu7haikttpqqgc5ewvmp76z3z4ebp7h2ph4memw7dq4nt6btmxny" { + t.Errorf("got wrong cid: %s", nd.Cid().String()) + } +} + +func (tp *TestSuite) TestDagPath(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + snd, err := ipldcbor.FromJSON(strings.NewReader(`"foo"`), math.MaxUint64, -1) + if err != nil { + t.Fatal(err) + } + + err = api.Dag().Add(ctx, snd) + if err != nil { + t.Fatal(err) + } + + nd, err := ipldcbor.FromJSON(strings.NewReader(`{"lnk": {"/": "`+snd.Cid().String()+`"}}`), math.MaxUint64, -1) + if err != nil { + t.Fatal(err) + } + + err = api.Dag().Add(ctx, nd) + if err != nil { + t.Fatal(err) + } + + p, err := path.Join(path.FromCid(nd.Cid()), "lnk") + if err != nil { + t.Fatal(err) + } + + rp, _, err := api.ResolvePath(ctx, p) + if err != nil { + t.Fatal(err) + } + + ndd, err := api.Dag().Get(ctx, rp.RootCid()) + if err != nil { + t.Fatal(err) + } + + if ndd.Cid().String() != snd.Cid().String() { + t.Errorf("got unexpected cid %s, expected %s", ndd.Cid().String(), snd.Cid().String()) + } +} + +func (tp *TestSuite) TestTree(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + nd, err := ipldcbor.FromJSON(strings.NewReader(`{"a": 123, "b": "foo", "c": {"d": 321, "e": 111}}`), math.MaxUint64, -1) + if err != nil { + t.Fatal(err) + } + + err = api.Dag().Add(ctx, nd) + if err != nil { + t.Fatal(err) + } + + res, err := api.Dag().Get(ctx, nd.Cid()) + if err != nil { + t.Fatal(err) + } + + lst := res.Tree("", -1) + if len(lst) != len(treeExpected) { + t.Errorf("tree length of %d doesn't match expected %d", len(lst), len(treeExpected)) + } + + for _, ent := range lst { + if _, ok := treeExpected[ent]; !ok { + t.Errorf("unexpected tree entry %s", ent) + } + } +} + +func (tp *TestSuite) TestBatch(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + nd, err := ipldcbor.FromJSON(strings.NewReader(`"Hello"`), math.MaxUint64, -1) + if err != nil { + t.Fatal(err) + } + + if nd.Cid().String() != "bafyreicnga62zhxnmnlt6ymq5hcbsg7gdhqdu6z4ehu3wpjhvqnflfy6nm" { + t.Errorf("got wrong cid: %s", nd.Cid().String()) + } + + _, err = api.Dag().Get(ctx, nd.Cid()) + if err == nil || !strings.Contains(err.Error(), "not found") { + t.Fatal(err) + } + + if err := api.Dag().AddMany(ctx, []ipld.Node{nd}); err != nil { + t.Fatal(err) + } + + _, err = api.Dag().Get(ctx, nd.Cid()) + if err != nil { + t.Fatal(err) + } +} diff --git a/core/coreiface/tests/key.go b/core/coreiface/tests/key.go new file mode 100644 index 000000000..90936b0e2 --- /dev/null +++ b/core/coreiface/tests/key.go @@ -0,0 +1,427 @@ +package tests + +import ( + "context" + "strings" + "testing" + + "github.com/ipfs/boxo/ipns" + "github.com/ipfs/go-cid" + iface "github.com/ipfs/kubo/core/coreiface" + opt "github.com/ipfs/kubo/core/coreiface/options" + "github.com/libp2p/go-libp2p/core/peer" + mbase "github.com/multiformats/go-multibase" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func (tp *TestSuite) TestKey(t *testing.T) { + tp.hasApi(t, func(api iface.CoreAPI) error { + if api.Key() == nil { + return errAPINotImplemented + } + return nil + }) + + t.Run("TestListSelf", tp.TestListSelf) + t.Run("TestRenameSelf", tp.TestRenameSelf) + t.Run("TestRemoveSelf", tp.TestRemoveSelf) + t.Run("TestGenerate", tp.TestGenerate) + t.Run("TestGenerateSize", tp.TestGenerateSize) + t.Run("TestGenerateType", tp.TestGenerateType) + t.Run("TestGenerateExisting", tp.TestGenerateExisting) + t.Run("TestList", tp.TestList) + t.Run("TestRename", tp.TestRename) + t.Run("TestRenameToSelf", tp.TestRenameToSelf) + t.Run("TestRenameToSelfForce", tp.TestRenameToSelfForce) + t.Run("TestRenameOverwriteNoForce", tp.TestRenameOverwriteNoForce) + t.Run("TestRenameOverwrite", tp.TestRenameOverwrite) + t.Run("TestRenameSameNameNoForce", tp.TestRenameSameNameNoForce) + t.Run("TestRenameSameName", tp.TestRenameSameName) + t.Run("TestSign", tp.TestSign) + t.Run("TestVerify", tp.TestVerify) +} + +func (tp *TestSuite) TestListSelf(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + api, err := tp.makeAPI(t, ctx) + require.NoError(t, err) + + self, err := api.Key().Self(ctx) + require.NoError(t, err) + + keys, err := api.Key().List(ctx) + require.NoError(t, err) + require.Len(t, keys, 1) + assert.Equal(t, "self", keys[0].Name()) + assert.Equal(t, "/ipns/"+iface.FormatKeyID(self.ID()), keys[0].Path().String()) +} + +func (tp *TestSuite) TestRenameSelf(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + api, err := tp.makeAPI(t, ctx) + require.NoError(t, err) + + _, _, err = api.Key().Rename(ctx, "self", "foo") + require.ErrorContains(t, err, "cannot rename key with name 'self'") + + _, _, err = api.Key().Rename(ctx, "self", "foo", opt.Key.Force(true)) + require.ErrorContains(t, err, "cannot rename key with name 'self'") +} + +func (tp *TestSuite) TestRemoveSelf(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + api, err := tp.makeAPI(t, ctx) + require.NoError(t, err) + + _, err = api.Key().Remove(ctx, "self") + require.ErrorContains(t, err, "cannot remove key with name 'self'") +} + +func (tp *TestSuite) TestGenerate(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + api, err := tp.makeAPI(t, ctx) + require.NoError(t, err) + + k, err := api.Key().Generate(ctx, "foo") + require.NoError(t, err) + require.Equal(t, "foo", k.Name()) + + verifyIPNSPath(t, k.Path().String()) +} + +func verifyIPNSPath(t *testing.T, p string) { + t.Helper() + + require.True(t, strings.HasPrefix(p, "/ipns/")) + + k := p[len("/ipns/"):] + c, err := cid.Decode(k) + require.NoError(t, err) + + b36, err := c.StringOfBase(mbase.Base36) + require.NoError(t, err) + require.Equal(t, k, b36) +} + +func (tp *TestSuite) TestGenerateSize(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + api, err := tp.makeAPI(t, ctx) + require.NoError(t, err) + + k, err := api.Key().Generate(ctx, "foo", opt.Key.Size(2048)) + require.NoError(t, err) + require.Equal(t, "foo", k.Name()) + + verifyIPNSPath(t, k.Path().String()) +} + +func (tp *TestSuite) TestGenerateType(t *testing.T) { + t.Skip("disabled until libp2p/specs#111 is fixed") + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + api, err := tp.makeAPI(t, ctx) + require.NoError(t, err) + + k, err := api.Key().Generate(ctx, "bar", opt.Key.Type(opt.Ed25519Key)) + require.NoError(t, err) + require.Equal(t, "bar", k.Name()) + // Expected to be an inlined identity hash. + require.True(t, strings.HasPrefix(k.Path().String(), "/ipns/12")) +} + +func (tp *TestSuite) TestGenerateExisting(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + api, err := tp.makeAPI(t, ctx) + require.NoError(t, err) + + _, err = api.Key().Generate(ctx, "foo") + require.NoError(t, err) + + _, err = api.Key().Generate(ctx, "foo") + require.ErrorContains(t, err, "key with name 'foo' already exists") + + _, err = api.Key().Generate(ctx, "self") + require.ErrorContains(t, err, "cannot create key with name 'self'") +} + +func (tp *TestSuite) TestList(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + api, err := tp.makeAPI(t, ctx) + require.NoError(t, err) + + _, err = api.Key().Generate(ctx, "foo") + require.NoError(t, err) + + l, err := api.Key().List(ctx) + require.NoError(t, err) + require.Len(t, l, 2) + require.Equal(t, "self", l[0].Name()) + require.Equal(t, "foo", l[1].Name()) + + verifyIPNSPath(t, l[0].Path().String()) + verifyIPNSPath(t, l[1].Path().String()) +} + +func (tp *TestSuite) TestRename(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + api, err := tp.makeAPI(t, ctx) + require.NoError(t, err) + + _, err = api.Key().Generate(ctx, "foo") + require.NoError(t, err) + + k, overwrote, err := api.Key().Rename(ctx, "foo", "bar") + require.NoError(t, err) + assert.False(t, overwrote) + assert.Equal(t, "bar", k.Name()) +} + +func (tp *TestSuite) TestRenameToSelf(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + api, err := tp.makeAPI(t, ctx) + require.NoError(t, err) + + _, err = api.Key().Generate(ctx, "foo") + require.NoError(t, err) + + _, _, err = api.Key().Rename(ctx, "foo", "self") + require.ErrorContains(t, err, "cannot overwrite key with name 'self'") +} + +func (tp *TestSuite) TestRenameToSelfForce(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + api, err := tp.makeAPI(t, ctx) + require.NoError(t, err) + + _, err = api.Key().Generate(ctx, "foo") + require.NoError(t, err) + + _, _, err = api.Key().Rename(ctx, "foo", "self", opt.Key.Force(true)) + require.ErrorContains(t, err, "cannot overwrite key with name 'self'") +} + +func (tp *TestSuite) TestRenameOverwriteNoForce(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + api, err := tp.makeAPI(t, ctx) + require.NoError(t, err) + + _, err = api.Key().Generate(ctx, "foo") + require.NoError(t, err) + + _, err = api.Key().Generate(ctx, "bar") + require.NoError(t, err) + + _, _, err = api.Key().Rename(ctx, "foo", "bar") + require.ErrorContains(t, err, "key by that name already exists, refusing to overwrite") +} + +func (tp *TestSuite) TestRenameOverwrite(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + api, err := tp.makeAPI(t, ctx) + require.NoError(t, err) + + kfoo, err := api.Key().Generate(ctx, "foo") + require.NoError(t, err) + + _, err = api.Key().Generate(ctx, "bar") + require.NoError(t, err) + + k, overwrote, err := api.Key().Rename(ctx, "foo", "bar", opt.Key.Force(true)) + require.NoError(t, err) + require.True(t, overwrote) + assert.Equal(t, "bar", k.Name()) + assert.Equal(t, kfoo.Path().String(), k.Path().String()) +} + +func (tp *TestSuite) TestRenameSameNameNoForce(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + api, err := tp.makeAPI(t, ctx) + require.NoError(t, err) + + _, err = api.Key().Generate(ctx, "foo") + require.NoError(t, err) + + k, overwrote, err := api.Key().Rename(ctx, "foo", "foo") + require.NoError(t, err) + assert.False(t, overwrote) + assert.Equal(t, "foo", k.Name()) +} + +func (tp *TestSuite) TestRenameSameName(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + api, err := tp.makeAPI(t, ctx) + require.NoError(t, err) + + _, err = api.Key().Generate(ctx, "foo") + require.NoError(t, err) + + k, overwrote, err := api.Key().Rename(ctx, "foo", "foo", opt.Key.Force(true)) + require.NoError(t, err) + assert.False(t, overwrote) + assert.Equal(t, "foo", k.Name()) +} + +func (tp *TestSuite) TestRemove(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + api, err := tp.makeAPI(t, ctx) + require.NoError(t, err) + + k, err := api.Key().Generate(ctx, "foo") + require.NoError(t, err) + + l, err := api.Key().List(ctx) + require.NoError(t, err) + require.Len(t, l, 2) + + p, err := api.Key().Remove(ctx, "foo") + require.NoError(t, err) + assert.Equal(t, p.Path().String(), k.Path().String()) + + l, err = api.Key().List(ctx) + require.NoError(t, err) + require.Len(t, l, 1) + assert.Equal(t, "self", l[0].Name()) +} + +func (tp *TestSuite) TestSign(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + api, err := tp.makeAPI(t, ctx) + require.NoError(t, err) + + key1, err := api.Key().Generate(ctx, "foo", opt.Key.Type(opt.Ed25519Key)) + require.NoError(t, err) + + data := []byte("hello world") + + key2, signature, err := api.Key().Sign(ctx, "foo", data) + require.NoError(t, err) + + require.Equal(t, key1.Name(), key2.Name()) + require.Equal(t, key1.ID(), key2.ID()) + + pk, err := key1.ID().ExtractPublicKey() + require.NoError(t, err) + + valid, err := pk.Verify(append([]byte("libp2p-key signed message:"), data...), signature) + require.NoError(t, err) + require.True(t, valid) +} + +func (tp *TestSuite) TestVerify(t *testing.T) { + t.Parallel() + + t.Run("Verify Own Key", func(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + api, err := tp.makeAPI(t, ctx) + require.NoError(t, err) + + _, err = api.Key().Generate(ctx, "foo", opt.Key.Type(opt.Ed25519Key)) + require.NoError(t, err) + + data := []byte("hello world") + + _, signature, err := api.Key().Sign(ctx, "foo", data) + require.NoError(t, err) + + _, valid, err := api.Key().Verify(ctx, "foo", signature, data) + require.NoError(t, err) + require.True(t, valid) + }) + + t.Run("Verify Self", func(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + api, err := tp.makeAPIWithIdentityAndOffline(t, ctx) + require.NoError(t, err) + + data := []byte("hello world") + + _, signature, err := api.Key().Sign(ctx, "", data) + require.NoError(t, err) + + _, valid, err := api.Key().Verify(ctx, "", signature, data) + require.NoError(t, err) + require.True(t, valid) + }) + + t.Run("Verify With Key In Different Formats", func(t *testing.T) { + t.Parallel() + + // Spin some node and get signature out. + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + api, err := tp.makeAPI(t, ctx) + require.NoError(t, err) + + key, err := api.Key().Generate(ctx, "foo", opt.Key.Type(opt.Ed25519Key)) + require.NoError(t, err) + + data := []byte("hello world") + + _, signature, err := api.Key().Sign(ctx, "foo", data) + require.NoError(t, err) + + for _, testCase := range [][]string{ + {"Base58 Encoded Peer ID", key.ID().String()}, + {"CIDv1 Encoded Peer ID", peer.ToCid(key.ID()).String()}, + {"CIDv1 Encoded IPNS Name", ipns.NameFromPeer(key.ID()).String()}, + {"Prefixed IPNS Path", ipns.NameFromPeer(key.ID()).AsPath().String()}, + } { + t.Run(testCase[0], func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // Spin new node. + api, err := tp.makeAPI(t, ctx) + require.NoError(t, err) + + _, valid, err := api.Key().Verify(ctx, testCase[1], signature, data) + require.NoError(t, err) + require.True(t, valid) + }) + } + }) +} diff --git a/core/coreiface/tests/name.go b/core/coreiface/tests/name.go new file mode 100644 index 000000000..1e739fdd0 --- /dev/null +++ b/core/coreiface/tests/name.go @@ -0,0 +1,168 @@ +package tests + +import ( + "context" + "io" + "math/rand" + "testing" + "time" + + "github.com/ipfs/boxo/files" + "github.com/ipfs/boxo/ipns" + "github.com/ipfs/boxo/path" + coreiface "github.com/ipfs/kubo/core/coreiface" + opt "github.com/ipfs/kubo/core/coreiface/options" + "github.com/stretchr/testify/require" +) + +func (tp *TestSuite) TestName(t *testing.T) { + tp.hasApi(t, func(api coreiface.CoreAPI) error { + if api.Name() == nil { + return errAPINotImplemented + } + return nil + }) + + t.Run("TestPublishResolve", tp.TestPublishResolve) + t.Run("TestBasicPublishResolveKey", tp.TestBasicPublishResolveKey) + t.Run("TestBasicPublishResolveTimeout", tp.TestBasicPublishResolveTimeout) +} + +var rnd = rand.New(rand.NewSource(0x62796532303137)) + +func addTestObject(ctx context.Context, api coreiface.CoreAPI) (path.Path, error) { + return api.Unixfs().Add(ctx, files.NewReaderFile(&io.LimitedReader{R: rnd, N: 4092})) +} + +func (tp *TestSuite) TestPublishResolve(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + init := func() (coreiface.CoreAPI, path.Path) { + apis, err := tp.MakeAPISwarm(t, ctx, 5) + require.NoError(t, err) + api := apis[0] + + p, err := addTestObject(ctx, api) + require.NoError(t, err) + return api, p + } + run := func(t *testing.T, ropts []opt.NameResolveOption) { + t.Run("basic", func(t *testing.T) { + api, p := init() + name, err := api.Name().Publish(ctx, p) + require.NoError(t, err) + + self, err := api.Key().Self(ctx) + require.NoError(t, err) + require.Equal(t, name.String(), ipns.NameFromPeer(self.ID()).String()) + + resPath, err := api.Name().Resolve(ctx, name.String(), ropts...) + require.NoError(t, err) + require.Equal(t, p.String(), resPath.String()) + }) + + t.Run("publishPath", func(t *testing.T) { + api, p := init() + p, err := path.Join(p, "/test") + require.NoError(t, err) + + name, err := api.Name().Publish(ctx, p) + require.NoError(t, err) + + self, err := api.Key().Self(ctx) + require.NoError(t, err) + require.Equal(t, name.String(), ipns.NameFromPeer(self.ID()).String()) + + resPath, err := api.Name().Resolve(ctx, name.String(), ropts...) + require.NoError(t, err) + require.Equal(t, p.String(), resPath.String()) + }) + + t.Run("revolvePath", func(t *testing.T) { + api, p := init() + name, err := api.Name().Publish(ctx, p) + require.NoError(t, err) + + self, err := api.Key().Self(ctx) + require.NoError(t, err) + require.Equal(t, name.String(), ipns.NameFromPeer(self.ID()).String()) + + resPath, err := api.Name().Resolve(ctx, name.String()+"/test", ropts...) + require.NoError(t, err) + require.Equal(t, p.String()+"/test", resPath.String()) + }) + + t.Run("publishRevolvePath", func(t *testing.T) { + api, p := init() + p, err := path.Join(p, "/a") + require.NoError(t, err) + + name, err := api.Name().Publish(ctx, p) + require.NoError(t, err) + + self, err := api.Key().Self(ctx) + require.NoError(t, err) + require.Equal(t, name.String(), ipns.NameFromPeer(self.ID()).String()) + + resPath, err := api.Name().Resolve(ctx, name.String()+"/b", ropts...) + require.NoError(t, err) + require.Equal(t, p.String()+"/b", resPath.String()) + }) + } + + t.Run("default", func(t *testing.T) { + run(t, []opt.NameResolveOption{}) + }) + + t.Run("nocache", func(t *testing.T) { + run(t, []opt.NameResolveOption{opt.Name.Cache(false)}) + }) +} + +func (tp *TestSuite) TestBasicPublishResolveKey(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + apis, err := tp.MakeAPISwarm(t, ctx, 5) + require.NoError(t, err) + api := apis[0] + + k, err := api.Key().Generate(ctx, "foo") + require.NoError(t, err) + + p, err := addTestObject(ctx, api) + require.NoError(t, err) + + name, err := api.Name().Publish(ctx, p, opt.Name.Key(k.Name())) + require.NoError(t, err) + require.Equal(t, name.String(), ipns.NameFromPeer(k.ID()).String()) + + resPath, err := api.Name().Resolve(ctx, name.String()) + require.NoError(t, err) + require.Equal(t, p.String(), resPath.String()) +} + +func (tp *TestSuite) TestBasicPublishResolveTimeout(t *testing.T) { + t.Skip("ValidTime doesn't appear to work at this time resolution") + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + apis, err := tp.MakeAPISwarm(t, ctx, 5) + require.NoError(t, err) + api := apis[0] + p, err := addTestObject(ctx, api) + require.NoError(t, err) + + self, err := api.Key().Self(ctx) + require.NoError(t, err) + + name, err := api.Name().Publish(ctx, p, opt.Name.ValidTime(time.Millisecond*100)) + require.NoError(t, err) + require.Equal(t, name.String(), ipns.NameFromPeer(self.ID()).String()) + + time.Sleep(time.Second) + + _, err = api.Name().Resolve(ctx, name.String()) + require.NoError(t, err) +} + +// TODO: When swarm api is created, add multinode tests diff --git a/core/coreiface/tests/object.go b/core/coreiface/tests/object.go new file mode 100644 index 000000000..239b022e1 --- /dev/null +++ b/core/coreiface/tests/object.go @@ -0,0 +1,144 @@ +package tests + +import ( + "context" + "testing" + + dag "github.com/ipfs/boxo/ipld/merkledag" + "github.com/ipfs/boxo/path" + ipld "github.com/ipfs/go-ipld-format" + iface "github.com/ipfs/kubo/core/coreiface" + opt "github.com/ipfs/kubo/core/coreiface/options" + "github.com/stretchr/testify/require" +) + +func (tp *TestSuite) TestObject(t *testing.T) { + tp.hasApi(t, func(api iface.CoreAPI) error { + if api.Object() == nil { + return errAPINotImplemented + } + return nil + }) + + t.Run("TestObjectAddLink", tp.TestObjectAddLink) + t.Run("TestObjectAddLinkCreate", tp.TestObjectAddLinkCreate) + t.Run("TestObjectRmLink", tp.TestObjectRmLink) + t.Run("TestDiffTest", tp.TestDiffTest) +} + +func putDagPbNode(t *testing.T, ctx context.Context, api iface.CoreAPI, data string, links []*ipld.Link) path.ImmutablePath { + dagnode := new(dag.ProtoNode) + + if data != "" { + dagnode.SetData([]byte(data)) + } + + if links != nil { + err := dagnode.SetLinks(links) + require.NoError(t, err) + } + + err := api.Dag().Add(ctx, dagnode) + require.NoError(t, err) + + return path.FromCid(dagnode.Cid()) +} + +func (tp *TestSuite) TestObjectAddLink(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + require.NoError(t, err) + + p1 := putDagPbNode(t, ctx, api, "foo", nil) + p2 := putDagPbNode(t, ctx, api, "bazz", []*ipld.Link{ + { + Name: "bar", + Cid: p1.RootCid(), + Size: 3, + }, + }) + + p3, err := api.Object().AddLink(ctx, p2, "abc", p2) + require.NoError(t, err) + + nd, err := api.Dag().Get(ctx, p3.RootCid()) + require.NoError(t, err) + + links := nd.Links() + require.Len(t, links, 2) + require.Equal(t, "abc", links[0].Name) + require.Equal(t, "bar", links[1].Name) +} + +func (tp *TestSuite) TestObjectAddLinkCreate(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + require.NoError(t, err) + + p1 := putDagPbNode(t, ctx, api, "foo", nil) + p2 := putDagPbNode(t, ctx, api, "bazz", []*ipld.Link{ + { + Name: "bar", + Cid: p1.RootCid(), + Size: 3, + }, + }) + + _, err = api.Object().AddLink(ctx, p2, "abc/d", p2) + require.ErrorContains(t, err, "no link by that name") + + p3, err := api.Object().AddLink(ctx, p2, "abc/d", p2, opt.Object.Create(true)) + require.NoError(t, err) + + nd, err := api.Dag().Get(ctx, p3.RootCid()) + require.NoError(t, err) + + links := nd.Links() + require.Len(t, links, 2) + require.Equal(t, "abc", links[0].Name) + require.Equal(t, "bar", links[1].Name) +} + +func (tp *TestSuite) TestObjectRmLink(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + require.NoError(t, err) + + p1 := putDagPbNode(t, ctx, api, "foo", nil) + p2 := putDagPbNode(t, ctx, api, "bazz", []*ipld.Link{ + { + Name: "bar", + Cid: p1.RootCid(), + Size: 3, + }, + }) + + p3, err := api.Object().RmLink(ctx, p2, "bar") + require.NoError(t, err) + + nd, err := api.Dag().Get(ctx, p3.RootCid()) + require.NoError(t, err) + + links := nd.Links() + require.Len(t, links, 0) +} + +func (tp *TestSuite) TestDiffTest(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + require.NoError(t, err) + + p1 := putDagPbNode(t, ctx, api, "foo", nil) + p2 := putDagPbNode(t, ctx, api, "bar", nil) + + changes, err := api.Object().Diff(ctx, p1, p2) + require.NoError(t, err) + require.Len(t, changes, 1) + require.Equal(t, iface.DiffMod, changes[0].Type) + require.Equal(t, p1.String(), changes[0].Before.String()) + require.Equal(t, p2.String(), changes[0].After.String()) +} diff --git a/core/coreiface/tests/path.go b/core/coreiface/tests/path.go new file mode 100644 index 000000000..87dce2c91 --- /dev/null +++ b/core/coreiface/tests/path.go @@ -0,0 +1,148 @@ +package tests + +import ( + "context" + "fmt" + "math" + "strings" + "testing" + + "github.com/ipfs/boxo/path" + "github.com/ipfs/go-cid" + ipldcbor "github.com/ipfs/go-ipld-cbor" + "github.com/ipfs/kubo/core/coreiface/options" + "github.com/stretchr/testify/require" +) + +func newIPLDPath(t *testing.T, cid cid.Cid) path.ImmutablePath { + p, err := path.NewPath(fmt.Sprintf("/%s/%s", path.IPLDNamespace, cid.String())) + require.NoError(t, err) + im, err := path.NewImmutablePath(p) + require.NoError(t, err) + return im +} + +func (tp *TestSuite) TestPath(t *testing.T) { + t.Run("TestMutablePath", tp.TestMutablePath) + t.Run("TestPathRemainder", tp.TestPathRemainder) + t.Run("TestEmptyPathRemainder", tp.TestEmptyPathRemainder) + t.Run("TestInvalidPathRemainder", tp.TestInvalidPathRemainder) + t.Run("TestPathRoot", tp.TestPathRoot) + t.Run("TestPathJoin", tp.TestPathJoin) +} + +func (tp *TestSuite) TestMutablePath(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + api, err := tp.makeAPI(t, ctx) + require.NoError(t, err) + + blk, err := api.Block().Put(ctx, strings.NewReader(`foo`)) + require.NoError(t, err) + require.False(t, blk.Path().Mutable()) + require.NotNil(t, api.Key()) + + keys, err := api.Key().List(ctx) + require.NoError(t, err) + require.True(t, keys[0].Path().Mutable()) +} + +func (tp *TestSuite) TestPathRemainder(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + api, err := tp.makeAPI(t, ctx) + require.NoError(t, err) + require.NotNil(t, api.Dag()) + + nd, err := ipldcbor.FromJSON(strings.NewReader(`{"foo": {"bar": "baz"}}`), math.MaxUint64, -1) + require.NoError(t, err) + + err = api.Dag().Add(ctx, nd) + require.NoError(t, err) + + p, err := path.Join(path.FromCid(nd.Cid()), "foo", "bar") + require.NoError(t, err) + + _, remainder, err := api.ResolvePath(ctx, p) + require.NoError(t, err) + require.Equal(t, "/foo/bar", path.SegmentsToString(remainder...)) +} + +func (tp *TestSuite) TestEmptyPathRemainder(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + api, err := tp.makeAPI(t, ctx) + require.NoError(t, err) + require.NotNil(t, api.Dag()) + + nd, err := ipldcbor.FromJSON(strings.NewReader(`{"foo": {"bar": "baz"}}`), math.MaxUint64, -1) + require.NoError(t, err) + + err = api.Dag().Add(ctx, nd) + require.NoError(t, err) + + _, remainder, err := api.ResolvePath(ctx, path.FromCid(nd.Cid())) + require.NoError(t, err) + require.Empty(t, remainder) +} + +func (tp *TestSuite) TestInvalidPathRemainder(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + api, err := tp.makeAPI(t, ctx) + require.NoError(t, err) + require.NotNil(t, api.Dag()) + + nd, err := ipldcbor.FromJSON(strings.NewReader(`{"foo": {"bar": "baz"}}`), math.MaxUint64, -1) + require.NoError(t, err) + + err = api.Dag().Add(ctx, nd) + require.NoError(t, err) + + p, err := path.Join(newIPLDPath(t, nd.Cid()), "/bar/baz") + require.NoError(t, err) + + _, _, err = api.ResolvePath(ctx, p) + require.NotNil(t, err) + require.ErrorContains(t, err, `no link named "bar"`) +} + +func (tp *TestSuite) TestPathRoot(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + api, err := tp.makeAPI(t, ctx) + require.NoError(t, err) + require.NotNil(t, api.Block()) + + blk, err := api.Block().Put(ctx, strings.NewReader(`foo`), options.Block.Format("raw")) + require.NoError(t, err) + require.NotNil(t, api.Dag()) + + nd, err := ipldcbor.FromJSON(strings.NewReader(`{"foo": {"/": "`+blk.Path().RootCid().String()+`"}}`), math.MaxUint64, -1) + require.NoError(t, err) + + err = api.Dag().Add(ctx, nd) + require.NoError(t, err) + + p, err := path.Join(newIPLDPath(t, nd.Cid()), "/foo") + require.NoError(t, err) + + rp, _, err := api.ResolvePath(ctx, p) + require.NoError(t, err) + require.Equal(t, rp.RootCid().String(), blk.Path().RootCid().String()) +} + +func (tp *TestSuite) TestPathJoin(t *testing.T) { + p1, err := path.NewPath("/ipfs/QmYNmQKp6SuaVrpgWRsPTgCQCnpxUYGq76YEKBXuj2N4H6/bar/baz") + require.NoError(t, err) + + p2, err := path.Join(p1, "foo") + require.NoError(t, err) + + require.Equal(t, "/ipfs/QmYNmQKp6SuaVrpgWRsPTgCQCnpxUYGq76YEKBXuj2N4H6/bar/baz/foo", p2.String()) +} diff --git a/core/coreiface/tests/pin.go b/core/coreiface/tests/pin.go new file mode 100644 index 000000000..fdd7c15cc --- /dev/null +++ b/core/coreiface/tests/pin.go @@ -0,0 +1,611 @@ +package tests + +import ( + "context" + "math" + "strings" + "testing" + + "github.com/ipfs/boxo/path" + "github.com/ipfs/go-cid" + ipldcbor "github.com/ipfs/go-ipld-cbor" + ipld "github.com/ipfs/go-ipld-format" + iface "github.com/ipfs/kubo/core/coreiface" + opt "github.com/ipfs/kubo/core/coreiface/options" +) + +func (tp *TestSuite) TestPin(t *testing.T) { + tp.hasApi(t, func(api iface.CoreAPI) error { + if api.Pin() == nil { + return errAPINotImplemented + } + return nil + }) + + t.Run("TestPinAdd", tp.TestPinAdd) + t.Run("TestPinSimple", tp.TestPinSimple) + t.Run("TestPinRecursive", tp.TestPinRecursive) + t.Run("TestPinLsIndirect", tp.TestPinLsIndirect) + t.Run("TestPinLsPrecedence", tp.TestPinLsPrecedence) + t.Run("TestPinIsPinned", tp.TestPinIsPinned) +} + +func (tp *TestSuite) TestPinAdd(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + p, err := api.Unixfs().Add(ctx, strFile("foo")()) + if err != nil { + t.Fatal(err) + } + + err = api.Pin().Add(ctx, p) + if err != nil { + t.Fatal(err) + } +} + +func (tp *TestSuite) TestPinSimple(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + p, err := api.Unixfs().Add(ctx, strFile("foo")()) + if err != nil { + t.Fatal(err) + } + + err = api.Pin().Add(ctx, p) + if err != nil { + t.Fatal(err) + } + + list, err := accPins(api.Pin().Ls(ctx)) + if err != nil { + t.Fatal(err) + } + + if len(list) != 1 { + t.Errorf("unexpected pin list len: %d", len(list)) + } + + if list[0].Path().RootCid().String() != p.RootCid().String() { + t.Error("paths don't match") + } + + if list[0].Type() != "recursive" { + t.Error("unexpected pin type") + } + + assertIsPinned(t, ctx, api, p, "recursive") + + err = api.Pin().Rm(ctx, p) + if err != nil { + t.Fatal(err) + } + + list, err = accPins(api.Pin().Ls(ctx)) + if err != nil { + t.Fatal(err) + } + + if len(list) != 0 { + t.Errorf("unexpected pin list len: %d", len(list)) + } +} + +func (tp *TestSuite) TestPinRecursive(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + p0, err := api.Unixfs().Add(ctx, strFile("foo")()) + if err != nil { + t.Fatal(err) + } + + p1, err := api.Unixfs().Add(ctx, strFile("bar")()) + if err != nil { + t.Fatal(err) + } + + nd2, err := ipldcbor.FromJSON(strings.NewReader(`{"lnk": {"/": "`+p0.RootCid().String()+`"}}`), math.MaxUint64, -1) + if err != nil { + t.Fatal(err) + } + + nd3, err := ipldcbor.FromJSON(strings.NewReader(`{"lnk": {"/": "`+p1.RootCid().String()+`"}}`), math.MaxUint64, -1) + if err != nil { + t.Fatal(err) + } + + if err := api.Dag().AddMany(ctx, []ipld.Node{nd2, nd3}); err != nil { + t.Fatal(err) + } + + err = api.Pin().Add(ctx, path.FromCid(nd2.Cid())) + if err != nil { + t.Fatal(err) + } + + err = api.Pin().Add(ctx, path.FromCid(nd3.Cid()), opt.Pin.Recursive(false)) + if err != nil { + t.Fatal(err) + } + + list, err := accPins(api.Pin().Ls(ctx)) + if err != nil { + t.Fatal(err) + } + + if len(list) != 3 { + t.Errorf("unexpected pin list len: %d", len(list)) + } + + list, err = accPins(api.Pin().Ls(ctx, opt.Pin.Ls.Direct())) + if err != nil { + t.Fatal(err) + } + + if len(list) != 1 { + t.Errorf("unexpected pin list len: %d", len(list)) + } + + if list[0].Path().String() != path.FromCid(nd3.Cid()).String() { + t.Errorf("unexpected path, %s != %s", list[0].Path().String(), path.FromCid(nd3.Cid()).String()) + } + + list, err = accPins(api.Pin().Ls(ctx, opt.Pin.Ls.Recursive())) + if err != nil { + t.Fatal(err) + } + + if len(list) != 1 { + t.Errorf("unexpected pin list len: %d", len(list)) + } + + if list[0].Path().String() != path.FromCid(nd2.Cid()).String() { + t.Errorf("unexpected path, %s != %s", list[0].Path().String(), path.FromCid(nd2.Cid()).String()) + } + + list, err = accPins(api.Pin().Ls(ctx, opt.Pin.Ls.Indirect())) + if err != nil { + t.Fatal(err) + } + + if len(list) != 1 { + t.Errorf("unexpected pin list len: %d", len(list)) + } + + if list[0].Path().RootCid().String() != p0.RootCid().String() { + t.Errorf("unexpected path, %s != %s", list[0].Path().RootCid().String(), p0.RootCid().String()) + } + + res, err := api.Pin().Verify(ctx) + if err != nil { + t.Fatal(err) + } + n := 0 + for r := range res { + if err := r.Err(); err != nil { + t.Error(err) + } + if !r.Ok() { + t.Error("expected pin to be ok") + } + n++ + } + + if n != 1 { + t.Errorf("unexpected verify result count: %d", n) + } + + // TODO: figure out a way to test verify without touching IpfsNode + /* + err = api.Block().Rm(ctx, p0, opt.Block.Force(true)) + if err != nil { + t.Fatal(err) + } + + res, err = api.Pin().Verify(ctx) + if err != nil { + t.Fatal(err) + } + n = 0 + for r := range res { + if r.Ok() { + t.Error("expected pin to not be ok") + } + + if len(r.BadNodes()) != 1 { + t.Fatalf("unexpected badNodes len") + } + + if r.BadNodes()[0].Path().Cid().String() != p0.Cid().String() { + t.Error("unexpected badNode path") + } + + if r.BadNodes()[0].Err().Error() != "merkledag: not found" { + t.Errorf("unexpected badNode error: %s", r.BadNodes()[0].Err().Error()) + } + n++ + } + + if n != 1 { + t.Errorf("unexpected verify result count: %d", n) + } + */ +} + +// TestPinLsIndirect verifies that indirect nodes are listed by pin ls even if a parent node is directly pinned +func (tp *TestSuite) TestPinLsIndirect(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + leaf, parent, grandparent := getThreeChainedNodes(t, ctx, api, "foo") + + err = api.Pin().Add(ctx, path.FromCid(grandparent.Cid())) + if err != nil { + t.Fatal(err) + } + + err = api.Pin().Add(ctx, path.FromCid(parent.Cid()), opt.Pin.Recursive(false)) + if err != nil { + t.Fatal(err) + } + + assertPinTypes(t, ctx, api, []cidContainer{grandparent}, []cidContainer{parent}, []cidContainer{leaf}) +} + +// TestPinLsPrecedence verifies the precedence of pins (recursive > direct > indirect) +func (tp *TestSuite) TestPinLsPrecedence(t *testing.T) { + // Testing precedence of recursive, direct and indirect pins + // Results should be recursive > indirect, direct > indirect, and recursive > direct + + t.Run("TestPinLsPredenceRecursiveIndirect", tp.TestPinLsPredenceRecursiveIndirect) + t.Run("TestPinLsPrecedenceDirectIndirect", tp.TestPinLsPrecedenceDirectIndirect) + t.Run("TestPinLsPrecedenceRecursiveDirect", tp.TestPinLsPrecedenceRecursiveDirect) +} + +func (tp *TestSuite) TestPinLsPredenceRecursiveIndirect(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + // Test recursive > indirect + leaf, parent, grandparent := getThreeChainedNodes(t, ctx, api, "recursive > indirect") + + err = api.Pin().Add(ctx, path.FromCid(grandparent.Cid())) + if err != nil { + t.Fatal(err) + } + + err = api.Pin().Add(ctx, path.FromCid(parent.Cid())) + if err != nil { + t.Fatal(err) + } + + assertPinTypes(t, ctx, api, []cidContainer{grandparent, parent}, []cidContainer{}, []cidContainer{leaf}) +} + +func (tp *TestSuite) TestPinLsPrecedenceDirectIndirect(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + // Test direct > indirect + leaf, parent, grandparent := getThreeChainedNodes(t, ctx, api, "direct > indirect") + + err = api.Pin().Add(ctx, path.FromCid(grandparent.Cid())) + if err != nil { + t.Fatal(err) + } + + err = api.Pin().Add(ctx, path.FromCid(parent.Cid()), opt.Pin.Recursive(false)) + if err != nil { + t.Fatal(err) + } + + assertPinTypes(t, ctx, api, []cidContainer{grandparent}, []cidContainer{parent}, []cidContainer{leaf}) +} + +func (tp *TestSuite) TestPinLsPrecedenceRecursiveDirect(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + // Test recursive > direct + leaf, parent, grandparent := getThreeChainedNodes(t, ctx, api, "recursive + direct = error") + + err = api.Pin().Add(ctx, path.FromCid(parent.Cid())) + if err != nil { + t.Fatal(err) + } + + err = api.Pin().Add(ctx, path.FromCid(parent.Cid()), opt.Pin.Recursive(false)) + if err == nil { + t.Fatal("expected error directly pinning a recursively pinned node") + } + + assertPinTypes(t, ctx, api, []cidContainer{parent}, []cidContainer{}, []cidContainer{leaf}) + + err = api.Pin().Add(ctx, path.FromCid(grandparent.Cid()), opt.Pin.Recursive(false)) + if err != nil { + t.Fatal(err) + } + + err = api.Pin().Add(ctx, path.FromCid(grandparent.Cid())) + if err != nil { + t.Fatal(err) + } + + assertPinTypes(t, ctx, api, []cidContainer{grandparent, parent}, []cidContainer{}, []cidContainer{leaf}) +} + +func (tp *TestSuite) TestPinIsPinned(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + leaf, parent, grandparent := getThreeChainedNodes(t, ctx, api, "foofoo") + + assertNotPinned(t, ctx, api, newIPLDPath(t, grandparent.Cid())) + assertNotPinned(t, ctx, api, newIPLDPath(t, parent.Cid())) + assertNotPinned(t, ctx, api, newIPLDPath(t, leaf.Cid())) + + err = api.Pin().Add(ctx, newIPLDPath(t, parent.Cid()), opt.Pin.Recursive(true)) + if err != nil { + t.Fatal(err) + } + + assertNotPinned(t, ctx, api, newIPLDPath(t, grandparent.Cid())) + assertIsPinned(t, ctx, api, newIPLDPath(t, parent.Cid()), "recursive") + assertIsPinned(t, ctx, api, newIPLDPath(t, leaf.Cid()), "indirect") + + err = api.Pin().Add(ctx, newIPLDPath(t, grandparent.Cid()), opt.Pin.Recursive(false)) + if err != nil { + t.Fatal(err) + } + + assertIsPinned(t, ctx, api, newIPLDPath(t, grandparent.Cid()), "direct") + assertIsPinned(t, ctx, api, newIPLDPath(t, parent.Cid()), "recursive") + assertIsPinned(t, ctx, api, newIPLDPath(t, leaf.Cid()), "indirect") +} + +type cidContainer interface { + Cid() cid.Cid +} + +type immutablePathCidContainer struct { + path.ImmutablePath +} + +func (i immutablePathCidContainer) Cid() cid.Cid { + return i.RootCid() +} + +func getThreeChainedNodes(t *testing.T, ctx context.Context, api iface.CoreAPI, leafData string) (cidContainer, cidContainer, cidContainer) { + leaf, err := api.Unixfs().Add(ctx, strFile(leafData)()) + if err != nil { + t.Fatal(err) + } + + parent, err := ipldcbor.FromJSON(strings.NewReader(`{"lnk": {"/": "`+leaf.RootCid().String()+`"}}`), math.MaxUint64, -1) + if err != nil { + t.Fatal(err) + } + + grandparent, err := ipldcbor.FromJSON(strings.NewReader(`{"lnk": {"/": "`+parent.Cid().String()+`"}}`), math.MaxUint64, -1) + if err != nil { + t.Fatal(err) + } + + if err := api.Dag().AddMany(ctx, []ipld.Node{parent, grandparent}); err != nil { + t.Fatal(err) + } + + return immutablePathCidContainer{leaf}, parent, grandparent +} + +func assertPinTypes(t *testing.T, ctx context.Context, api iface.CoreAPI, recusive, direct, indirect []cidContainer) { + assertPinLsAllConsistency(t, ctx, api) + + list, err := accPins(api.Pin().Ls(ctx, opt.Pin.Ls.Recursive())) + if err != nil { + t.Fatal(err) + } + + assertPinCids(t, list, recusive...) + + list, err = accPins(api.Pin().Ls(ctx, opt.Pin.Ls.Direct())) + if err != nil { + t.Fatal(err) + } + + assertPinCids(t, list, direct...) + + list, err = accPins(api.Pin().Ls(ctx, opt.Pin.Ls.Indirect())) + if err != nil { + t.Fatal(err) + } + + assertPinCids(t, list, indirect...) +} + +// assertPinCids verifies that the pins match the expected cids +func assertPinCids(t *testing.T, pins []iface.Pin, cids ...cidContainer) { + t.Helper() + + if expected, actual := len(cids), len(pins); expected != actual { + t.Fatalf("expected pin list to have len %d, was %d", expected, actual) + } + + cSet := cid.NewSet() + for _, c := range cids { + cSet.Add(c.Cid()) + } + + valid := true + for _, p := range pins { + c := p.Path().RootCid() + if cSet.Has(c) { + cSet.Remove(c) + } else { + valid = false + break + } + } + + valid = valid && cSet.Len() == 0 + + if !valid { + pinStrs := make([]string, len(pins)) + for i, p := range pins { + pinStrs[i] = p.Path().RootCid().String() + } + pathStrs := make([]string, len(cids)) + for i, c := range cids { + pathStrs[i] = c.Cid().String() + } + t.Fatalf("expected: %s \nactual: %s", strings.Join(pathStrs, ", "), strings.Join(pinStrs, ", ")) + } +} + +// assertPinLsAllConsistency verifies that listing all pins gives the same result as listing the pin types individually +func assertPinLsAllConsistency(t *testing.T, ctx context.Context, api iface.CoreAPI) { + t.Helper() + allPins, err := accPins(api.Pin().Ls(ctx)) + if err != nil { + t.Fatal(err) + } + + type pinTypeProps struct { + *cid.Set + opt.PinLsOption + } + + all, recursive, direct, indirect := cid.NewSet(), cid.NewSet(), cid.NewSet(), cid.NewSet() + typeMap := map[string]*pinTypeProps{ + "recursive": {recursive, opt.Pin.Ls.Recursive()}, + "direct": {direct, opt.Pin.Ls.Direct()}, + "indirect": {indirect, opt.Pin.Ls.Indirect()}, + } + + for _, p := range allPins { + if !all.Visit(p.Path().RootCid()) { + t.Fatalf("pin ls returned the same cid multiple times") + } + + typeStr := p.Type() + if typeSet, ok := typeMap[p.Type()]; ok { + typeSet.Add(p.Path().RootCid()) + } else { + t.Fatalf("unknown pin type: %s", typeStr) + } + } + + for typeStr, pinProps := range typeMap { + pins, err := accPins(api.Pin().Ls(ctx, pinProps.PinLsOption)) + if err != nil { + t.Fatal(err) + } + + if expected, actual := len(pins), pinProps.Set.Len(); expected != actual { + t.Fatalf("pin ls all has %d pins of type %s, but pin ls for the type has %d", expected, typeStr, actual) + } + + for _, p := range pins { + if pinType := p.Type(); pinType != typeStr { + t.Fatalf("returned wrong pin type: expected %s, got %s", typeStr, pinType) + } + + if c := p.Path().RootCid(); !pinProps.Has(c) { + t.Fatalf("%s expected to be in pin ls all as type %s", c.String(), typeStr) + } + } + } +} + +func assertIsPinned(t *testing.T, ctx context.Context, api iface.CoreAPI, p path.Path, typeStr string) { + t.Helper() + withType, err := opt.Pin.IsPinned.Type(typeStr) + if err != nil { + t.Fatal("unhandled pin type") + } + + whyPinned, pinned, err := api.Pin().IsPinned(ctx, p, withType) + if err != nil { + t.Fatal(err) + } + + if !pinned { + t.Fatalf("%s expected to be pinned with type %s", p, typeStr) + } + + switch typeStr { + case "recursive", "direct": + if typeStr != whyPinned { + t.Fatalf("reason for pinning expected to be %s for %s, got %s", typeStr, p, whyPinned) + } + case "indirect": + if whyPinned == "" { + t.Fatalf("expected to have a pin reason for %s", p) + } + } +} + +func assertNotPinned(t *testing.T, ctx context.Context, api iface.CoreAPI, p path.Path) { + t.Helper() + + _, pinned, err := api.Pin().IsPinned(ctx, p) + if err != nil { + t.Fatal(err) + } + + if pinned { + t.Fatalf("%s expected to not be pinned", p) + } +} + +func accPins(pins <-chan iface.Pin, err error) ([]iface.Pin, error) { + if err != nil { + return nil, err + } + + var result []iface.Pin + + for pin := range pins { + if pin.Err() != nil { + return nil, pin.Err() + } + result = append(result, pin) + } + + return result, nil +} diff --git a/core/coreiface/tests/pubsub.go b/core/coreiface/tests/pubsub.go new file mode 100644 index 000000000..6ae95f27b --- /dev/null +++ b/core/coreiface/tests/pubsub.go @@ -0,0 +1,136 @@ +package tests + +import ( + "context" + "testing" + "time" + + iface "github.com/ipfs/kubo/core/coreiface" + "github.com/ipfs/kubo/core/coreiface/options" +) + +func (tp *TestSuite) TestPubSub(t *testing.T) { + tp.hasApi(t, func(api iface.CoreAPI) error { + if api.PubSub() == nil { + return errAPINotImplemented + } + return nil + }) + + t.Run("TestBasicPubSub", tp.TestBasicPubSub) +} + +func (tp *TestSuite) TestBasicPubSub(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + apis, err := tp.MakeAPISwarm(t, ctx, 2) + if err != nil { + t.Fatal(err) + } + + sub, err := apis[0].PubSub().Subscribe(ctx, "testch") + if err != nil { + t.Fatal(err) + } + + done := make(chan struct{}) + go func() { + defer close(done) + + ticker := time.NewTicker(100 * time.Millisecond) + defer ticker.Stop() + + for { + err := apis[1].PubSub().Publish(ctx, "testch", []byte("hello world")) + switch err { + case nil: + case context.Canceled: + return + default: + t.Error(err) + cancel() + return + } + select { + case <-ticker.C: + case <-ctx.Done(): + return + } + } + }() + + // Wait for the sender to finish before we return. + // Otherwise, we can get random errors as publish fails. + defer func() { + cancel() + <-done + }() + + m, err := sub.Next(ctx) + if err != nil { + t.Fatal(err) + } + + if string(m.Data()) != "hello world" { + t.Errorf("got invalid data: %s", string(m.Data())) + } + + self1, err := apis[1].Key().Self(ctx) + if err != nil { + t.Fatal(err) + } + + if m.From() != self1.ID() { + t.Errorf("m.From didn't match") + } + + peers, err := apis[1].PubSub().Peers(ctx, options.PubSub.Topic("testch")) + if err != nil { + t.Fatal(err) + } + + if len(peers) != 1 { + t.Fatalf("got incorrect number of peers: %d", len(peers)) + } + + self0, err := apis[0].Key().Self(ctx) + if err != nil { + t.Fatal(err) + } + + if peers[0] != self0.ID() { + t.Errorf("peer didn't match") + } + + peers, err = apis[1].PubSub().Peers(ctx, options.PubSub.Topic("nottestch")) + if err != nil { + t.Fatal(err) + } + + if len(peers) != 0 { + t.Fatalf("got incorrect number of peers: %d", len(peers)) + } + + topics, err := apis[0].PubSub().Ls(ctx) + if err != nil { + t.Fatal(err) + } + + if len(topics) != 1 { + t.Fatalf("got incorrect number of topics: %d", len(peers)) + } + + if topics[0] != "testch" { + t.Errorf("topic didn't match") + } + + topics, err = apis[1].PubSub().Ls(ctx) + if err != nil { + t.Fatal(err) + } + + if len(topics) != 0 { + t.Fatalf("got incorrect number of topics: %d", len(peers)) + } +} diff --git a/core/coreiface/tests/routing.go b/core/coreiface/tests/routing.go new file mode 100644 index 000000000..753d49550 --- /dev/null +++ b/core/coreiface/tests/routing.go @@ -0,0 +1,246 @@ +package tests + +import ( + "context" + "io" + "testing" + "time" + + "github.com/ipfs/boxo/ipns" + "github.com/ipfs/boxo/path" + iface "github.com/ipfs/kubo/core/coreiface" + "github.com/ipfs/kubo/core/coreiface/options" + "github.com/stretchr/testify/require" +) + +func (tp *TestSuite) TestRouting(t *testing.T) { + tp.hasApi(t, func(api iface.CoreAPI) error { + if api.Routing() == nil { + return errAPINotImplemented + } + return nil + }) + + t.Run("TestRoutingGet", tp.TestRoutingGet) + t.Run("TestRoutingPut", tp.TestRoutingPut) + t.Run("TestRoutingPutOffline", tp.TestRoutingPutOffline) + t.Run("TestRoutingFindPeer", tp.TestRoutingFindPeer) + t.Run("TestRoutingFindProviders", tp.TestRoutingFindProviders) + t.Run("TestRoutingProvide", tp.TestRoutingProvide) +} + +func (tp *TestSuite) testRoutingPublishKey(t *testing.T, ctx context.Context, api iface.CoreAPI, opts ...options.NamePublishOption) (path.Path, ipns.Name) { + p, err := addTestObject(ctx, api) + require.NoError(t, err) + + name, err := api.Name().Publish(ctx, p, opts...) + require.NoError(t, err) + + time.Sleep(3 * time.Second) + return p, name +} + +func (tp *TestSuite) TestRoutingGet(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + apis, err := tp.MakeAPISwarm(t, ctx, 2) + require.NoError(t, err) + + // Node 1: publishes an IPNS name + p, name := tp.testRoutingPublishKey(t, ctx, apis[0]) + + // Node 2: retrieves the best value for the IPNS name. + data, err := apis[1].Routing().Get(ctx, ipns.NamespacePrefix+name.String()) + require.NoError(t, err) + + rec, err := ipns.UnmarshalRecord(data) + require.NoError(t, err) + + val, err := rec.Value() + require.NoError(t, err) + require.Equal(t, p.String(), val.String()) +} + +func (tp *TestSuite) TestRoutingPut(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + apis, err := tp.MakeAPISwarm(t, ctx, 2) + require.NoError(t, err) + + // Create and publish IPNS entry. + _, name := tp.testRoutingPublishKey(t, ctx, apis[0]) + + // Get valid routing value. + data, err := apis[0].Routing().Get(ctx, ipns.NamespacePrefix+name.String()) + require.NoError(t, err) + + // Put routing value. + err = apis[1].Routing().Put(ctx, ipns.NamespacePrefix+name.String(), data) + require.NoError(t, err) +} + +func (tp *TestSuite) TestRoutingPutOffline(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // init a swarm & publish an IPNS entry to get a valid payload + apis, err := tp.MakeAPISwarm(t, ctx, 2) + require.NoError(t, err) + + _, name := tp.testRoutingPublishKey(t, ctx, apis[0], options.Name.AllowOffline(true)) + data, err := apis[0].Routing().Get(ctx, ipns.NamespacePrefix+name.String()) + require.NoError(t, err) + + // init our offline node and try to put the payload + api, err := tp.makeAPIWithIdentityAndOffline(t, ctx) + require.NoError(t, err) + + err = api.Routing().Put(ctx, ipns.NamespacePrefix+name.String(), data) + require.Error(t, err, "this operation should fail because we are offline") + + err = api.Routing().Put(ctx, ipns.NamespacePrefix+name.String(), data, options.Routing.AllowOffline(true)) + require.NoError(t, err) +} + +func (tp *TestSuite) TestRoutingFindPeer(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + apis, err := tp.MakeAPISwarm(t, ctx, 5) + if err != nil { + t.Fatal(err) + } + + self0, err := apis[0].Key().Self(ctx) + if err != nil { + t.Fatal(err) + } + + laddrs0, err := apis[0].Swarm().LocalAddrs(ctx) + if err != nil { + t.Fatal(err) + } + if len(laddrs0) != 1 { + t.Fatal("unexpected number of local addrs") + } + + time.Sleep(3 * time.Second) + + pi, err := apis[2].Routing().FindPeer(ctx, self0.ID()) + if err != nil { + t.Fatal(err) + } + + if pi.Addrs[0].String() != laddrs0[0].String() { + t.Errorf("got unexpected address from FindPeer: %s", pi.Addrs[0].String()) + } + + self2, err := apis[2].Key().Self(ctx) + if err != nil { + t.Fatal(err) + } + + pi, err = apis[1].Routing().FindPeer(ctx, self2.ID()) + if err != nil { + t.Fatal(err) + } + + laddrs2, err := apis[2].Swarm().LocalAddrs(ctx) + if err != nil { + t.Fatal(err) + } + if len(laddrs2) != 1 { + t.Fatal("unexpected number of local addrs") + } + + if pi.Addrs[0].String() != laddrs2[0].String() { + t.Errorf("got unexpected address from FindPeer: %s", pi.Addrs[0].String()) + } +} + +func (tp *TestSuite) TestRoutingFindProviders(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + apis, err := tp.MakeAPISwarm(t, ctx, 5) + if err != nil { + t.Fatal(err) + } + + p, err := addTestObject(ctx, apis[0]) + if err != nil { + t.Fatal(err) + } + + time.Sleep(3 * time.Second) + + out, err := apis[2].Routing().FindProviders(ctx, p, options.Routing.NumProviders(1)) + if err != nil { + t.Fatal(err) + } + + provider := <-out + + self0, err := apis[0].Key().Self(ctx) + if err != nil { + t.Fatal(err) + } + + if provider.ID.String() != self0.ID().String() { + t.Errorf("got wrong provider: %s != %s", provider.ID.String(), self0.ID().String()) + } +} + +func (tp *TestSuite) TestRoutingProvide(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + apis, err := tp.MakeAPISwarm(t, ctx, 5) + if err != nil { + t.Fatal(err) + } + + off0, err := apis[0].WithOptions(options.Api.Offline(true)) + if err != nil { + t.Fatal(err) + } + + s, err := off0.Block().Put(ctx, &io.LimitedReader{R: rnd, N: 4092}) + if err != nil { + t.Fatal(err) + } + + p := s.Path() + + time.Sleep(3 * time.Second) + + out, err := apis[2].Routing().FindProviders(ctx, p, options.Routing.NumProviders(1)) + if err != nil { + t.Fatal(err) + } + + _, ok := <-out + + if ok { + t.Fatal("did not expect to find any providers") + } + + self0, err := apis[0].Key().Self(ctx) + if err != nil { + t.Fatal(err) + } + + err = apis[0].Routing().Provide(ctx, p) + if err != nil { + t.Fatal(err) + } + + out, err = apis[2].Routing().FindProviders(ctx, p, options.Routing.NumProviders(1)) + if err != nil { + t.Fatal(err) + } + + provider := <-out + + if provider.ID.String() != self0.ID().String() { + t.Errorf("got wrong provider: %s != %s", provider.ID.String(), self0.ID().String()) + } +} diff --git a/core/coreiface/tests/unixfs.go b/core/coreiface/tests/unixfs.go new file mode 100644 index 000000000..9d3362b9a --- /dev/null +++ b/core/coreiface/tests/unixfs.go @@ -0,0 +1,1074 @@ +package tests + +import ( + "bytes" + "context" + "encoding/hex" + "fmt" + "io" + "math" + "math/rand" + "os" + "strconv" + "strings" + "sync" + "testing" + + "github.com/ipfs/boxo/path" + coreiface "github.com/ipfs/kubo/core/coreiface" + "github.com/ipfs/kubo/core/coreiface/options" + + "github.com/ipfs/boxo/files" + mdag "github.com/ipfs/boxo/ipld/merkledag" + "github.com/ipfs/boxo/ipld/unixfs" + "github.com/ipfs/boxo/ipld/unixfs/importer/helpers" + "github.com/ipfs/go-cid" + cbor "github.com/ipfs/go-ipld-cbor" + ipld "github.com/ipfs/go-ipld-format" + mh "github.com/multiformats/go-multihash" +) + +func (tp *TestSuite) TestUnixfs(t *testing.T) { + tp.hasApi(t, func(api coreiface.CoreAPI) error { + if api.Unixfs() == nil { + return errAPINotImplemented + } + return nil + }) + + t.Run("TestAdd", tp.TestAdd) + t.Run("TestAddPinned", tp.TestAddPinned) + t.Run("TestAddHashOnly", tp.TestAddHashOnly) + t.Run("TestGetEmptyFile", tp.TestGetEmptyFile) + t.Run("TestGetDir", tp.TestGetDir) + t.Run("TestGetNonUnixfs", tp.TestGetNonUnixfs) + t.Run("TestLs", tp.TestLs) + t.Run("TestEntriesExpired", tp.TestEntriesExpired) + t.Run("TestLsEmptyDir", tp.TestLsEmptyDir) + t.Run("TestLsNonUnixfs", tp.TestLsNonUnixfs) + t.Run("TestAddCloses", tp.TestAddCloses) + t.Run("TestGetSeek", tp.TestGetSeek) + t.Run("TestGetReadAt", tp.TestGetReadAt) +} + +// `echo -n 'hello, world!' | ipfs add` +var ( + hello = "/ipfs/QmQy2Dw4Wk7rdJKjThjYXzfFJNaRKRHhHP5gHHXroJMYxk" + helloStr = "hello, world!" +) + +// `echo -n | ipfs add` +var emptyFile = "/ipfs/QmbFMke1KXqnYyBBWxB74N4c5SBnJMVAiMNRcGu6x1AwQH" + +func strFile(data string) func() files.Node { + return func() files.Node { + return files.NewBytesFile([]byte(data)) + } +} + +func twoLevelDir() func() files.Node { + return func() files.Node { + return files.NewMapDirectory(map[string]files.Node{ + "abc": files.NewMapDirectory(map[string]files.Node{ + "def": files.NewBytesFile([]byte("world")), + }), + + "bar": files.NewBytesFile([]byte("hello2")), + "foo": files.NewBytesFile([]byte("hello1")), + }) + } +} + +func flatDir() files.Node { + return files.NewMapDirectory(map[string]files.Node{ + "bar": files.NewBytesFile([]byte("hello2")), + "foo": files.NewBytesFile([]byte("hello1")), + }) +} + +func wrapped(names ...string) func(f files.Node) files.Node { + return func(f files.Node) files.Node { + for i := range names { + f = files.NewMapDirectory(map[string]files.Node{ + names[len(names)-i-1]: f, + }) + } + return f + } +} + +func (tp *TestSuite) TestAdd(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + p := func(h string) path.ImmutablePath { + c, err := cid.Parse(h) + if err != nil { + t.Fatal(err) + } + return path.FromCid(c) + } + + rf, err := os.CreateTemp(os.TempDir(), "unixfs-add-real") + if err != nil { + t.Fatal(err) + } + rfp := rf.Name() + + if _, err := rf.Write([]byte(helloStr)); err != nil { + t.Fatal(err) + } + + stat, err := rf.Stat() + if err != nil { + t.Fatal(err) + } + + if err := rf.Close(); err != nil { + t.Fatal(err) + } + defer os.Remove(rfp) + + realFile := func() files.Node { + n, err := files.NewReaderPathFile(rfp, io.NopCloser(strings.NewReader(helloStr)), stat) + if err != nil { + t.Fatal(err) + } + return n + } + + cases := []struct { + name string + data func() files.Node + expect func(files.Node) files.Node + + apiOpts []options.ApiOption + + path string + err string + + wrap string + + events []coreiface.AddEvent + + opts []options.UnixfsAddOption + }{ + // Simple cases + { + name: "simpleAdd", + data: strFile(helloStr), + path: hello, + opts: []options.UnixfsAddOption{}, + }, + { + name: "addEmpty", + data: strFile(""), + path: emptyFile, + }, + // CIDv1 version / rawLeaves + { + name: "addCidV1", + data: strFile(helloStr), + path: "/ipfs/bafkreidi4zlleupgp2bvrpxyja5lbvi4mym7hz5bvhyoowby2qp7g2hxfa", + opts: []options.UnixfsAddOption{options.Unixfs.CidVersion(1)}, + }, + { + name: "addCidV1NoLeaves", + data: strFile(helloStr), + path: "/ipfs/bafybeibhbcn7k7o2m6xsqkrlfiokod3nxwe47viteynhruh6uqx7hvkjfu", + opts: []options.UnixfsAddOption{options.Unixfs.CidVersion(1), options.Unixfs.RawLeaves(false)}, + }, + // Non sha256 hash vs CID + { + name: "addCidSha3", + data: strFile(helloStr), + path: "/ipfs/bafkrmichjflejeh6aren53o7pig7zk3m3vxqcoc2i5dv326k3x6obh7jry", + opts: []options.UnixfsAddOption{options.Unixfs.Hash(mh.SHA3_256)}, + }, + { + name: "addCidSha3Cid0", + data: strFile(helloStr), + err: "CIDv0 only supports sha2-256", + opts: []options.UnixfsAddOption{options.Unixfs.CidVersion(0), options.Unixfs.Hash(mh.SHA3_256)}, + }, + // Inline + { + name: "addInline", + data: strFile(helloStr), + path: "/ipfs/bafyaafikcmeaeeqnnbswy3dpfqqho33snrsccgan", + opts: []options.UnixfsAddOption{options.Unixfs.Inline(true)}, + }, + { + name: "addInlineLimit", + data: strFile(helloStr), + path: "/ipfs/bafyaafikcmeaeeqnnbswy3dpfqqho33snrsccgan", + opts: []options.UnixfsAddOption{options.Unixfs.InlineLimit(32), options.Unixfs.Inline(true)}, + }, + { + name: "addInlineZero", + data: strFile(""), + path: "/ipfs/bafkqaaa", + opts: []options.UnixfsAddOption{options.Unixfs.InlineLimit(0), options.Unixfs.Inline(true), options.Unixfs.RawLeaves(true)}, + }, + { // TODO: after coreapi add is used in `ipfs add`, consider making this default for inline + name: "addInlineRaw", + data: strFile(helloStr), + path: "/ipfs/bafkqadlimvwgy3zmeb3w64tmmqqq", + opts: []options.UnixfsAddOption{options.Unixfs.InlineLimit(32), options.Unixfs.Inline(true), options.Unixfs.RawLeaves(true)}, + }, + // Chunker / Layout + { + name: "addChunks", + data: strFile(strings.Repeat("aoeuidhtns", 200)), + path: "/ipfs/QmRo11d4QJrST47aaiGVJYwPhoNA4ihRpJ5WaxBWjWDwbX", + opts: []options.UnixfsAddOption{options.Unixfs.Chunker("size-4")}, + }, + { + name: "addChunksTrickle", + data: strFile(strings.Repeat("aoeuidhtns", 200)), + path: "/ipfs/QmNNhDGttafX3M1wKWixGre6PrLFGjnoPEDXjBYpTv93HP", + opts: []options.UnixfsAddOption{options.Unixfs.Chunker("size-4"), options.Unixfs.Layout(options.TrickleLayout)}, + }, + // Local + { + name: "addLocal", // better cases in sharness + data: strFile(helloStr), + path: hello, + apiOpts: []options.ApiOption{options.Api.Offline(true)}, + }, + { + name: "hashOnly", // test (non)fetchability + data: strFile(helloStr), + path: hello, + opts: []options.UnixfsAddOption{options.Unixfs.HashOnly(true)}, + }, + // multi file + { + name: "simpleDirNoWrap", + data: flatDir, + path: "/ipfs/QmRKGpFfR32FVXdvJiHfo4WJ5TDYBsM1P9raAp1p6APWSp", + }, + { + name: "simpleDir", + data: flatDir, + wrap: "t", + expect: wrapped("t"), + path: "/ipfs/Qmc3nGXm1HtUVCmnXLQHvWcNwfdZGpfg2SRm1CxLf7Q2Rm", + }, + { + name: "twoLevelDir", + data: twoLevelDir(), + wrap: "t", + expect: wrapped("t"), + path: "/ipfs/QmPwsL3T5sWhDmmAWZHAzyjKtMVDS9a11aHNRqb3xoVnmg", + }, + // wrapped + { + name: "addWrapped", + path: "/ipfs/QmVE9rNpj5doj7XHzp5zMUxD7BJgXEqx4pe3xZ3JBReWHE", + data: func() files.Node { + return files.NewBytesFile([]byte(helloStr)) + }, + wrap: "foo", + expect: wrapped("foo"), + }, + // hidden + { + name: "hiddenFilesAdded", + data: func() files.Node { + return files.NewMapDirectory(map[string]files.Node{ + ".bar": files.NewBytesFile([]byte("hello2")), + "bar": files.NewBytesFile([]byte("hello2")), + "foo": files.NewBytesFile([]byte("hello1")), + }) + }, + wrap: "t", + expect: wrapped("t"), + path: "/ipfs/QmPXLSBX382vJDLrGakcbrZDkU3grfkjMox7EgSC9KFbtQ", + }, + // NoCopy + { + name: "simpleNoCopy", + data: realFile, + path: "/ipfs/bafkreidi4zlleupgp2bvrpxyja5lbvi4mym7hz5bvhyoowby2qp7g2hxfa", + opts: []options.UnixfsAddOption{options.Unixfs.Nocopy(true)}, + }, + { + name: "noCopyNoRaw", + data: realFile, + path: "/ipfs/bafkreidi4zlleupgp2bvrpxyja5lbvi4mym7hz5bvhyoowby2qp7g2hxfa", + opts: []options.UnixfsAddOption{options.Unixfs.Nocopy(true), options.Unixfs.RawLeaves(false)}, + err: "nocopy option requires '--raw-leaves' to be enabled as well", + }, + { + name: "noCopyNoPath", + data: strFile(helloStr), + path: "/ipfs/bafkreidi4zlleupgp2bvrpxyja5lbvi4mym7hz5bvhyoowby2qp7g2hxfa", + opts: []options.UnixfsAddOption{options.Unixfs.Nocopy(true)}, + err: helpers.ErrMissingFsRef.Error(), + }, + // Events / Progress + { + name: "simpleAddEvent", + data: strFile(helloStr), + path: "/ipfs/bafkreidi4zlleupgp2bvrpxyja5lbvi4mym7hz5bvhyoowby2qp7g2hxfa", + events: []coreiface.AddEvent{ + {Name: "bafkreidi4zlleupgp2bvrpxyja5lbvi4mym7hz5bvhyoowby2qp7g2hxfa", Path: p("bafkreidi4zlleupgp2bvrpxyja5lbvi4mym7hz5bvhyoowby2qp7g2hxfa"), Size: strconv.Itoa(len(helloStr))}, + }, + opts: []options.UnixfsAddOption{options.Unixfs.RawLeaves(true)}, + }, + { + name: "silentAddEvent", + data: twoLevelDir(), + path: "/ipfs/QmVG2ZYCkV1S4TK8URA3a4RupBF17A8yAr4FqsRDXVJASr", + events: []coreiface.AddEvent{ + {Name: "abc", Path: p("QmU7nuGs2djqK99UNsNgEPGh6GV4662p6WtsgccBNGTDxt"), Size: "62"}, + {Name: "", Path: p("QmVG2ZYCkV1S4TK8URA3a4RupBF17A8yAr4FqsRDXVJASr"), Size: "229"}, + }, + opts: []options.UnixfsAddOption{options.Unixfs.Silent(true)}, + }, + { + name: "dirAddEvents", + data: twoLevelDir(), + path: "/ipfs/QmVG2ZYCkV1S4TK8URA3a4RupBF17A8yAr4FqsRDXVJASr", + events: []coreiface.AddEvent{ + {Name: "abc/def", Path: p("QmNyJpQkU1cEkBwMDhDNFstr42q55mqG5GE5Mgwug4xyGk"), Size: "13"}, + {Name: "bar", Path: p("QmS21GuXiRMvJKHos4ZkEmQDmRBqRaF5tQS2CQCu2ne9sY"), Size: "14"}, + {Name: "foo", Path: p("QmfAjGiVpTN56TXi6SBQtstit5BEw3sijKj1Qkxn6EXKzJ"), Size: "14"}, + {Name: "abc", Path: p("QmU7nuGs2djqK99UNsNgEPGh6GV4662p6WtsgccBNGTDxt"), Size: "62"}, + {Name: "", Path: p("QmVG2ZYCkV1S4TK8URA3a4RupBF17A8yAr4FqsRDXVJASr"), Size: "229"}, + }, + }, + { + name: "progress1M", + data: func() files.Node { + return files.NewReaderFile(bytes.NewReader(bytes.Repeat([]byte{0}, 1000000))) + }, + path: "/ipfs/QmXXNNbwe4zzpdMg62ZXvnX1oU7MwSrQ3vAEtuwFKCm1oD", + events: []coreiface.AddEvent{ + {Name: "", Bytes: 262144}, + {Name: "", Bytes: 524288}, + {Name: "", Bytes: 786432}, + {Name: "", Bytes: 1000000}, + {Name: "QmXXNNbwe4zzpdMg62ZXvnX1oU7MwSrQ3vAEtuwFKCm1oD", Path: p("QmXXNNbwe4zzpdMg62ZXvnX1oU7MwSrQ3vAEtuwFKCm1oD"), Size: "1000256"}, + }, + wrap: "", + opts: []options.UnixfsAddOption{options.Unixfs.Progress(true)}, + }, + } + + for _, testCase := range cases { + t.Run(testCase.name, func(t *testing.T) { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + // recursive logic + + data := testCase.data() + if testCase.wrap != "" { + data = files.NewMapDirectory(map[string]files.Node{ + testCase.wrap: data, + }) + } + + // handle events if relevant to test case + + opts := testCase.opts + eventOut := make(chan interface{}) + var evtWg sync.WaitGroup + if len(testCase.events) > 0 { + opts = append(opts, options.Unixfs.Events(eventOut)) + evtWg.Add(1) + + go func() { + defer evtWg.Done() + expected := testCase.events + + for evt := range eventOut { + event, ok := evt.(*coreiface.AddEvent) + if !ok { + t.Error("unexpected event type") + continue + } + + if len(expected) < 1 { + t.Error("got more events than expected") + continue + } + + if expected[0].Size != event.Size { + t.Errorf("Event.Size didn't match, %s != %s", expected[0].Size, event.Size) + } + + if expected[0].Name != event.Name { + t.Errorf("Event.Name didn't match, %s != %s", expected[0].Name, event.Name) + } + + if (expected[0].Path != path.ImmutablePath{} && event.Path != path.ImmutablePath{}) { + if expected[0].Path.RootCid().String() != event.Path.RootCid().String() { + t.Errorf("Event.Hash didn't match, %s != %s", expected[0].Path, event.Path) + } + } else if event.Path != expected[0].Path { + t.Errorf("Event.Hash didn't match, %s != %s", expected[0].Path, event.Path) + } + if expected[0].Bytes != event.Bytes { + t.Errorf("Event.Bytes didn't match, %d != %d", expected[0].Bytes, event.Bytes) + } + + expected = expected[1:] + } + + if len(expected) > 0 { + t.Errorf("%d event(s) didn't arrive", len(expected)) + } + }() + } + + tapi, err := api.WithOptions(testCase.apiOpts...) + if err != nil { + t.Fatal(err) + } + + // Add! + + p, err := tapi.Unixfs().Add(ctx, data, opts...) + close(eventOut) + evtWg.Wait() + if testCase.err != "" { + if err == nil { + t.Fatalf("expected an error: %s", testCase.err) + } + if err.Error() != testCase.err { + t.Fatalf("expected an error: '%s' != '%s'", err.Error(), testCase.err) + } + return + } + if err != nil { + t.Fatal(err) + } + + if p.String() != testCase.path { + t.Errorf("expected path %s, got: %s", testCase.path, p) + } + + // compare file structure with Unixfs().Get + + var cmpFile func(origName string, orig files.Node, gotName string, got files.Node) + cmpFile = func(origName string, orig files.Node, gotName string, got files.Node) { + _, origDir := orig.(files.Directory) + _, gotDir := got.(files.Directory) + + if origName != gotName { + t.Errorf("file name mismatch, orig='%s', got='%s'", origName, gotName) + } + + if origDir != gotDir { + t.Fatalf("file type mismatch on %s", origName) + } + + if !gotDir { + defer orig.Close() + defer got.Close() + + do, err := io.ReadAll(orig.(files.File)) + if err != nil { + t.Fatal(err) + } + + dg, err := io.ReadAll(got.(files.File)) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(do, dg) { + t.Fatal("data not equal") + } + + return + } + + origIt := orig.(files.Directory).Entries() + gotIt := got.(files.Directory).Entries() + + for { + if origIt.Next() { + if !gotIt.Next() { + t.Fatal("gotIt out of entries before origIt") + } + } else { + if gotIt.Next() { + t.Fatal("origIt out of entries before gotIt") + } + break + } + + cmpFile(origIt.Name(), origIt.Node(), gotIt.Name(), gotIt.Node()) + } + if origIt.Err() != nil { + t.Fatal(origIt.Err()) + } + if gotIt.Err() != nil { + t.Fatal(gotIt.Err()) + } + } + + f, err := tapi.Unixfs().Get(ctx, p) + if err != nil { + t.Fatal(err) + } + + orig := testCase.data() + if testCase.expect != nil { + orig = testCase.expect(orig) + } + + cmpFile("", orig, "", f) + }) + } +} + +func (tp *TestSuite) TestAddPinned(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + _, err = api.Unixfs().Add(ctx, strFile(helloStr)(), options.Unixfs.Pin(true)) + if err != nil { + t.Fatal(err) + } + + pins, err := accPins(api.Pin().Ls(ctx)) + if err != nil { + t.Fatal(err) + } + if len(pins) != 1 { + t.Fatalf("expected 1 pin, got %d", len(pins)) + } + + if pins[0].Path().String() != "/ipfs/QmQy2Dw4Wk7rdJKjThjYXzfFJNaRKRHhHP5gHHXroJMYxk" { + t.Fatalf("got unexpected pin: %s", pins[0].Path().String()) + } +} + +func (tp *TestSuite) TestAddHashOnly(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + p, err := api.Unixfs().Add(ctx, strFile(helloStr)(), options.Unixfs.HashOnly(true)) + if err != nil { + t.Fatal(err) + } + + if p.String() != hello { + t.Errorf("unexpected path: %s", p.String()) + } + + _, err = api.Block().Get(ctx, p) + if err == nil { + t.Fatal("expected an error") + } + if !ipld.IsNotFound(err) { + t.Errorf("unexpected error: %s", err.Error()) + } +} + +func (tp *TestSuite) TestGetEmptyFile(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + _, err = api.Unixfs().Add(ctx, files.NewBytesFile([]byte{})) + if err != nil { + t.Fatal(err) + } + + emptyFilePath, err := path.NewPath(emptyFile) + if err != nil { + t.Fatal(err) + } + + r, err := api.Unixfs().Get(ctx, emptyFilePath) + if err != nil { + t.Fatal(err) + } + + buf := make([]byte, 1) // non-zero so that Read() actually tries to read + n, err := io.ReadFull(r.(files.File), buf) + if err != nil && err != io.EOF { + t.Error(err) + } + if !bytes.HasPrefix(buf, []byte{0x00}) { + t.Fatalf("expected empty data, got [%s] [read=%d]", buf, n) + } +} + +func (tp *TestSuite) TestGetDir(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + edir := unixfs.EmptyDirNode() + err = api.Dag().Add(ctx, edir) + if err != nil { + t.Fatal(err) + } + p := path.FromCid(edir.Cid()) + + if p.String() != path.FromCid(edir.Cid()).String() { + t.Fatalf("expected path %s, got: %s", edir.Cid(), p.String()) + } + + r, err := api.Unixfs().Get(ctx, path.FromCid(edir.Cid())) + if err != nil { + t.Fatal(err) + } + + if _, ok := r.(files.Directory); !ok { + t.Fatalf("expected a directory") + } +} + +func (tp *TestSuite) TestGetNonUnixfs(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + nd := new(mdag.ProtoNode) + err = api.Dag().Add(ctx, nd) + if err != nil { + t.Fatal(err) + } + + _, err = api.Unixfs().Get(ctx, path.FromCid(nd.Cid())) + if !strings.Contains(err.Error(), "proto: required field") { + t.Fatalf("expected protobuf error, got: %s", err) + } +} + +func (tp *TestSuite) TestLs(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + r := strings.NewReader("content-of-file") + p, err := api.Unixfs().Add(ctx, files.NewMapDirectory(map[string]files.Node{ + "name-of-file": files.NewReaderFile(r), + "name-of-symlink": files.NewLinkFile("/foo/bar", nil), + })) + if err != nil { + t.Fatal(err) + } + + entries, err := api.Unixfs().Ls(ctx, p) + if err != nil { + t.Fatal(err) + } + + entry := <-entries + if entry.Err != nil { + t.Fatal(entry.Err) + } + if entry.Size != 15 { + t.Errorf("expected size = 15, got %d", entry.Size) + } + if entry.Name != "name-of-file" { + t.Errorf("expected name = name-of-file, got %s", entry.Name) + } + if entry.Type != coreiface.TFile { + t.Errorf("wrong type %s", entry.Type) + } + if entry.Cid.String() != "QmX3qQVKxDGz3URVC3861Z3CKtQKGBn6ffXRBBWGMFz9Lr" { + t.Errorf("expected cid = QmX3qQVKxDGz3URVC3861Z3CKtQKGBn6ffXRBBWGMFz9Lr, got %s", entry.Cid) + } + entry = <-entries + if entry.Err != nil { + t.Fatal(entry.Err) + } + if entry.Type != coreiface.TSymlink { + t.Errorf("wrong type %s", entry.Type) + } + if entry.Name != "name-of-symlink" { + t.Errorf("expected name = name-of-symlink, got %s", entry.Name) + } + if entry.Target != "/foo/bar" { + t.Errorf("expected symlink target to be /foo/bar, got %s", entry.Target) + } + + if l, ok := <-entries; ok { + t.Errorf("didn't expect a second link") + if l.Err != nil { + t.Error(l.Err) + } + } +} + +func (tp *TestSuite) TestEntriesExpired(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + r := strings.NewReader("content-of-file") + p, err := api.Unixfs().Add(ctx, files.NewMapDirectory(map[string]files.Node{ + "name-of-file": files.NewReaderFile(r), + })) + if err != nil { + t.Fatal(err) + } + + ctx, cancel = context.WithCancel(ctx) + + nd, err := api.Unixfs().Get(ctx, p) + if err != nil { + t.Fatal(err) + } + cancel() + + it := files.ToDir(nd).Entries() + if it == nil { + t.Fatal("it was nil") + } + + if it.Next() { + t.Fatal("Next succeeded") + } + + if it.Err() != context.Canceled { + t.Fatalf("unexpected error %s", it.Err()) + } + + if it.Next() { + t.Fatal("Next succeeded") + } +} + +func (tp *TestSuite) TestLsEmptyDir(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + p, err := api.Unixfs().Add(ctx, files.NewSliceDirectory([]files.DirEntry{})) + if err != nil { + t.Fatal(err) + } + + links, err := api.Unixfs().Ls(ctx, p) + if err != nil { + t.Fatal(err) + } + + if len(links) != 0 { + t.Fatalf("expected 0 links, got %d", len(links)) + } +} + +// TODO(lgierth) this should test properly, with len(links) > 0 +func (tp *TestSuite) TestLsNonUnixfs(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + nd, err := cbor.WrapObject(map[string]interface{}{"foo": "bar"}, math.MaxUint64, -1) + if err != nil { + t.Fatal(err) + } + + err = api.Dag().Add(ctx, nd) + if err != nil { + t.Fatal(err) + } + + links, err := api.Unixfs().Ls(ctx, path.FromCid(nd.Cid())) + if err != nil { + t.Fatal(err) + } + + if len(links) != 0 { + t.Fatalf("expected 0 links, got %d", len(links)) + } +} + +type closeTestF struct { + files.File + closed bool + + t *testing.T +} + +type closeTestD struct { + files.Directory + closed bool + + t *testing.T +} + +func (f *closeTestD) Close() error { + f.t.Helper() + if f.closed { + f.t.Fatal("already closed") + } + f.closed = true + return nil +} + +func (f *closeTestF) Close() error { + if f.closed { + f.t.Fatal("already closed") + } + f.closed = true + return nil +} + +func (tp *TestSuite) TestAddCloses(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + n4 := &closeTestF{files.NewBytesFile([]byte("foo")), false, t} + d3 := &closeTestD{files.NewMapDirectory(map[string]files.Node{ + "sub": n4, + }), false, t} + n2 := &closeTestF{files.NewBytesFile([]byte("bar")), false, t} + n1 := &closeTestF{files.NewBytesFile([]byte("baz")), false, t} + d0 := &closeTestD{files.NewMapDirectory(map[string]files.Node{ + "a": d3, + "b": n1, + "c": n2, + }), false, t} + + _, err = api.Unixfs().Add(ctx, d0) + if err != nil { + t.Fatal(err) + } + + for i, n := range []*closeTestF{n1, n2, n4} { + if !n.closed { + t.Errorf("file %d not closed!", i) + } + } + + for i, n := range []*closeTestD{d0, d3} { + if !n.closed { + t.Errorf("dir %d not closed!", i) + } + } +} + +func (tp *TestSuite) TestGetSeek(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + dataSize := int64(100000) + tf := files.NewReaderFile(io.LimitReader(rand.New(rand.NewSource(1403768328)), dataSize)) + + p, err := api.Unixfs().Add(ctx, tf, options.Unixfs.Chunker("size-100")) + if err != nil { + t.Fatal(err) + } + + r, err := api.Unixfs().Get(ctx, p) + if err != nil { + t.Fatal(err) + } + + f := files.ToFile(r) + if f == nil { + t.Fatal("not a file") + } + + orig := make([]byte, dataSize) + if _, err := io.ReadFull(f, orig); err != nil { + t.Fatal(err) + } + f.Close() + + origR := bytes.NewReader(orig) + + r, err = api.Unixfs().Get(ctx, p) + if err != nil { + t.Fatal(err) + } + + f = files.ToFile(r) + if f == nil { + t.Fatal("not a file") + } + + test := func(offset int64, whence int, read int, expect int64, shouldEof bool) { + t.Run(fmt.Sprintf("seek%d+%d-r%d-%d", whence, offset, read, expect), func(t *testing.T) { + n, err := f.Seek(offset, whence) + if err != nil { + t.Fatal(err) + } + origN, err := origR.Seek(offset, whence) + if err != nil { + t.Fatal(err) + } + + if n != origN { + t.Fatalf("offsets didn't match, expected %d, got %d", origN, n) + } + + buf := make([]byte, read) + origBuf := make([]byte, read) + origRead, err := origR.Read(origBuf) + if err != nil { + t.Fatalf("orig: %s", err) + } + r, err := io.ReadFull(f, buf) + switch { + case shouldEof && err != nil && err != io.ErrUnexpectedEOF: + fallthrough + case !shouldEof && err != nil: + t.Fatalf("f: %s", err) + case shouldEof: + _, err := f.Read([]byte{0}) + if err != io.EOF { + t.Fatal("expected EOF") + } + _, err = origR.Read([]byte{0}) + if err != io.EOF { + t.Fatal("expected EOF (orig)") + } + } + + if int64(r) != expect { + t.Fatal("read wrong amount of data") + } + if r != origRead { + t.Fatal("read different amount of data than bytes.Reader") + } + if !bytes.Equal(buf, origBuf) { + fmt.Fprintf(os.Stderr, "original:\n%s\n", hex.Dump(origBuf)) + fmt.Fprintf(os.Stderr, "got:\n%s\n", hex.Dump(buf)) + t.Fatal("data didn't match") + } + }) + } + + test(3, io.SeekCurrent, 10, 10, false) + test(3, io.SeekCurrent, 10, 10, false) + test(500, io.SeekCurrent, 10, 10, false) + test(350, io.SeekStart, 100, 100, false) + test(-123, io.SeekCurrent, 100, 100, false) + test(0, io.SeekStart, int(dataSize), dataSize, false) + test(dataSize-50, io.SeekStart, 100, 50, true) + test(-5, io.SeekEnd, 100, 5, true) +} + +func (tp *TestSuite) TestGetReadAt(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + api, err := tp.makeAPI(t, ctx) + if err != nil { + t.Fatal(err) + } + + dataSize := int64(100000) + tf := files.NewReaderFile(io.LimitReader(rand.New(rand.NewSource(1403768328)), dataSize)) + + p, err := api.Unixfs().Add(ctx, tf, options.Unixfs.Chunker("size-100")) + if err != nil { + t.Fatal(err) + } + + r, err := api.Unixfs().Get(ctx, p) + if err != nil { + t.Fatal(err) + } + + f, ok := r.(interface { + files.File + io.ReaderAt + }) + if !ok { + t.Skip("ReaderAt not implemented") + } + + orig := make([]byte, dataSize) + if _, err := io.ReadFull(f, orig); err != nil { + t.Fatal(err) + } + f.Close() + + origR := bytes.NewReader(orig) + + if _, err := api.Unixfs().Get(ctx, p); err != nil { + t.Fatal(err) + } + + test := func(offset int64, read int, expect int64, shouldEof bool) { + t.Run(fmt.Sprintf("readat%d-r%d-%d", offset, read, expect), func(t *testing.T) { + origBuf := make([]byte, read) + origRead, err := origR.ReadAt(origBuf, offset) + if err != nil && err != io.EOF { + t.Fatalf("orig: %s", err) + } + buf := make([]byte, read) + r, err := f.ReadAt(buf, offset) + if shouldEof { + if err != io.EOF { + t.Fatal("expected EOF, got: ", err) + } + } else if err != nil { + t.Fatal("got: ", err) + } + + if int64(r) != expect { + t.Fatal("read wrong amount of data") + } + if r != origRead { + t.Fatal("read different amount of data than bytes.Reader") + } + if !bytes.Equal(buf, origBuf) { + fmt.Fprintf(os.Stderr, "original:\n%s\n", hex.Dump(origBuf)) + fmt.Fprintf(os.Stderr, "got:\n%s\n", hex.Dump(buf)) + t.Fatal("data didn't match") + } + }) + } + + test(3, 10, 10, false) + test(13, 10, 10, false) + test(513, 10, 10, false) + test(350, 100, 100, false) + test(0, int(dataSize), dataSize, false) + test(dataSize-50, 100, 50, true) +} diff --git a/core/coreiface/unixfs.go b/core/coreiface/unixfs.go new file mode 100644 index 000000000..c0150bd12 --- /dev/null +++ b/core/coreiface/unixfs.go @@ -0,0 +1,87 @@ +package iface + +import ( + "context" + "os" + "time" + + "github.com/ipfs/boxo/files" + "github.com/ipfs/boxo/path" + "github.com/ipfs/go-cid" + "github.com/ipfs/kubo/core/coreiface/options" +) + +type AddEvent struct { + Name string + Path path.ImmutablePath `json:",omitempty"` + Bytes int64 `json:",omitempty"` + Size string `json:",omitempty"` + Mode os.FileMode `json:",omitempty"` + Mtime int64 `json:",omitempty"` + MtimeNsecs int `json:",omitempty"` +} + +// FileType is an enum of possible UnixFS file types. +type FileType int32 + +const ( + // TUnknown means the file type isn't known (e.g., it hasn't been + // resolved). + TUnknown FileType = iota + // TFile is a regular file. + TFile + // TDirectory is a directory. + TDirectory + // TSymlink is a symlink. + TSymlink +) + +func (t FileType) String() string { + switch t { + case TUnknown: + return "unknown" + case TFile: + return "file" + case TDirectory: + return "directory" + case TSymlink: + return "symlink" + default: + return "" + } +} + +// DirEntry is a directory entry returned by `Ls`. +type DirEntry struct { + Name string + Cid cid.Cid + + // Only filled when asked to resolve the directory entry. + Size uint64 // The size of the file in bytes (or the size of the symlink). + Type FileType // The type of the file. + Target string // The symlink target (if a symlink). + + Mode os.FileMode + ModTime time.Time + + Err error +} + +// UnixfsAPI is the basic interface to immutable files in IPFS +// NOTE: This API is heavily WIP, things are guaranteed to break frequently +type UnixfsAPI interface { + // Add imports the data from the reader into merkledag file + // + // TODO: a long useful comment on how to use this for many different scenarios + Add(context.Context, files.Node, ...options.UnixfsAddOption) (path.ImmutablePath, error) + + // Get returns a read-only handle to a file tree referenced by a path + // + // Note that some implementations of this API may apply the specified context + // to operations performed on the returned file + Get(context.Context, path.Path) (files.Node, error) + + // Ls returns the list of links in a directory. Links aren't guaranteed to be + // returned in order + Ls(context.Context, path.Path, ...options.UnixfsLsOption) (<-chan DirEntry, error) +} diff --git a/core/coreiface/util.go b/core/coreiface/util.go new file mode 100644 index 000000000..6d58bf40d --- /dev/null +++ b/core/coreiface/util.go @@ -0,0 +1,20 @@ +package iface + +import ( + "context" + "io" +) + +type Reader interface { + ReadSeekCloser + Size() uint64 + CtxReadFull(context.Context, []byte) (int, error) +} + +// A ReadSeekCloser implements interfaces to read, copy, seek and close. +type ReadSeekCloser interface { + io.Reader + io.Seeker + io.Closer + io.WriterTo +} diff --git a/core/corerepo/gc.go b/core/corerepo/gc.go index de3d16980..cf89587d6 100644 --- a/core/corerepo/gc.go +++ b/core/corerepo/gc.go @@ -11,9 +11,9 @@ import ( "github.com/ipfs/kubo/repo" "github.com/dustin/go-humanize" + "github.com/ipfs/boxo/mfs" "github.com/ipfs/go-cid" logging "github.com/ipfs/go-log" - "github.com/ipfs/go-mfs" ) var log = logging.Logger("corerepo") diff --git a/core/coreunix/add.go b/core/coreunix/add.go index 16ccbaefa..5f7cbb610 100644 --- a/core/coreunix/add.go +++ b/core/coreunix/add.go @@ -5,25 +5,28 @@ import ( "errors" "fmt" "io" + "os" gopath "path" "strconv" + "time" + bstore "github.com/ipfs/boxo/blockstore" + chunker "github.com/ipfs/boxo/chunker" + "github.com/ipfs/boxo/files" + posinfo "github.com/ipfs/boxo/filestore/posinfo" + dag "github.com/ipfs/boxo/ipld/merkledag" + "github.com/ipfs/boxo/ipld/unixfs" + "github.com/ipfs/boxo/ipld/unixfs/importer/balanced" + ihelper "github.com/ipfs/boxo/ipld/unixfs/importer/helpers" + "github.com/ipfs/boxo/ipld/unixfs/importer/trickle" + "github.com/ipfs/boxo/mfs" + "github.com/ipfs/boxo/path" + pin "github.com/ipfs/boxo/pinning/pinner" "github.com/ipfs/go-cid" - bstore "github.com/ipfs/go-ipfs-blockstore" - chunker "github.com/ipfs/go-ipfs-chunker" - files "github.com/ipfs/go-ipfs-files" - pin "github.com/ipfs/go-ipfs-pinner" - posinfo "github.com/ipfs/go-ipfs-posinfo" ipld "github.com/ipfs/go-ipld-format" logging "github.com/ipfs/go-log" - dag "github.com/ipfs/go-merkledag" - "github.com/ipfs/go-mfs" - "github.com/ipfs/go-unixfs" - "github.com/ipfs/go-unixfs/importer/balanced" - ihelper "github.com/ipfs/go-unixfs/importer/helpers" - "github.com/ipfs/go-unixfs/importer/trickle" - coreiface "github.com/ipfs/interface-go-ipfs-core" - "github.com/ipfs/interface-go-ipfs-core/path" + coreiface "github.com/ipfs/kubo/core/coreiface" + "github.com/ipfs/kubo/tracing" ) @@ -80,6 +83,11 @@ type Adder struct { tempRoot cid.Cid CidBuilder cid.Builder liveNodes uint64 + + PreserveMode bool + PreserveMtime bool + FileMode os.FileMode + FileMtime time.Time } func (adder *Adder) mfsRoot() (*mfs.Root, error) { @@ -112,11 +120,13 @@ func (adder *Adder) add(reader io.Reader) (ipld.Node, error) { } params := ihelper.DagBuilderParams{ - Dagserv: adder.bufferedDS, - RawLeaves: adder.RawLeaves, - Maxlinks: ihelper.DefaultLinksPerBlock, - NoCopy: adder.NoCopy, - CidBuilder: adder.CidBuilder, + Dagserv: adder.bufferedDS, + RawLeaves: adder.RawLeaves, + Maxlinks: ihelper.DefaultLinksPerBlock, + NoCopy: adder.NoCopy, + CidBuilder: adder.CidBuilder, + FileMode: adder.FileMode, + FileModTime: adder.FileMtime, } db, err := params.New(chnk) @@ -185,7 +195,11 @@ func (adder *Adder) PinRoot(ctx context.Context, root ipld.Node) error { adder.tempRoot = rnk } - adder.pinning.PinWithMode(rnk, pin.Recursive) + err = adder.pinning.PinWithMode(ctx, rnk, pin.Recursive, "") + if err != nil { + return err + } + return adder.pinning.Flush(ctx) } @@ -354,6 +368,14 @@ func (adder *Adder) addFileNode(ctx context.Context, path string, file files.Nod return err } + if adder.PreserveMtime { + adder.FileMtime = file.ModTime() + } + + if adder.PreserveMode { + adder.FileMode = file.Mode() + } + if adder.liveNodes >= liveCacheSize { // TODO: A smarter cache that uses some sort of lru cache with an eviction handler mr, err := adder.mfsRoot() @@ -386,6 +408,18 @@ func (adder *Adder) addSymlink(path string, l *files.Symlink) error { return err } + if !adder.FileMtime.IsZero() { + fsn, err := unixfs.FSNodeFromBytes(sdata) + if err != nil { + return err + } + + fsn.SetModTime(adder.FileMtime) + if sdata, err = fsn.GetBytes(); err != nil { + return err + } + } + dagnode := dag.NodeWithData(sdata) err = dagnode.SetCidBuilder(adder.CidBuilder) if err != nil { @@ -424,6 +458,20 @@ func (adder *Adder) addFile(path string, file files.File) error { func (adder *Adder) addDir(ctx context.Context, path string, dir files.Directory, toplevel bool) error { log.Infof("adding directory: %s", path) + // if we need to store mode or modification time then create a new root which includes that data + if toplevel && (adder.FileMode != 0 || !adder.FileMtime.IsZero()) { + nd := unixfs.EmptyDirNodeWithStat(adder.FileMode, adder.FileMtime) + err := nd.SetCidBuilder(adder.CidBuilder) + if err != nil { + return err + } + mr, err := mfs.NewRoot(ctx, adder.dagService, nd, nil) + if err != nil { + return err + } + adder.SetMfsRoot(mr) + } + if !(toplevel && path == "") { mr, err := adder.mfsRoot() if err != nil { @@ -433,6 +481,8 @@ func (adder *Adder) addDir(ctx context.Context, path string, dir files.Directory Mkparents: true, Flush: false, CidBuilder: adder.CidBuilder, + Mode: adder.FileMode, + ModTime: adder.FileMtime, }) if err != nil { return err @@ -501,7 +551,7 @@ func getOutput(dagnode ipld.Node) (*coreiface.AddEvent, error) { } output := &coreiface.AddEvent{ - Path: path.IpfsPath(c), + Path: path.FromCid(c), Size: strconv.FormatUint(s, 10), } diff --git a/core/coreunix/add_test.go b/core/coreunix/add_test.go index 6d5d941f7..1eb050ee9 100644 --- a/core/coreunix/add_test.go +++ b/core/coreunix/add_test.go @@ -14,17 +14,17 @@ import ( "github.com/ipfs/kubo/gc" "github.com/ipfs/kubo/repo" + "github.com/ipfs/boxo/blockservice" + blockstore "github.com/ipfs/boxo/blockstore" + "github.com/ipfs/boxo/files" + pi "github.com/ipfs/boxo/filestore/posinfo" + dag "github.com/ipfs/boxo/ipld/merkledag" blocks "github.com/ipfs/go-block-format" - "github.com/ipfs/go-blockservice" "github.com/ipfs/go-cid" "github.com/ipfs/go-datastore" syncds "github.com/ipfs/go-datastore/sync" - blockstore "github.com/ipfs/go-ipfs-blockstore" - files "github.com/ipfs/go-ipfs-files" - pi "github.com/ipfs/go-ipfs-posinfo" - dag "github.com/ipfs/go-merkledag" - coreiface "github.com/ipfs/interface-go-ipfs-core" config "github.com/ipfs/kubo/config" + coreiface "github.com/ipfs/kubo/core/coreiface" ) const testPeerID = "QmTFauExutTsy4XP6JbMFcw2Wa9645HJt2bTqL6qYDCKfe" @@ -133,7 +133,7 @@ func TestAddMultipleGCLive(t *testing.T) { } for o := range out { - if _, ok := removedHashes[o.(*coreiface.AddEvent).Path.Cid().String()]; ok { + if _, ok := removedHashes[o.(*coreiface.AddEvent).Path.RootCid().String()]; ok { t.Fatal("gc'ed a hash we just added") } } @@ -179,17 +179,15 @@ func TestAddGCLive(t *testing.T) { defer close(addDone) defer close(out) _, err := adder.AddAllAndPin(context.Background(), slf) - if err != nil { t.Error(err) } - }() addedHashes := make(map[string]struct{}) select { case o := <-out: - addedHashes[o.(*coreiface.AddEvent).Path.Cid().String()] = struct{}{} + addedHashes[o.(*coreiface.AddEvent).Path.RootCid().String()] = struct{}{} case <-addDone: t.Fatal("add shouldn't complete yet") } @@ -219,7 +217,7 @@ func TestAddGCLive(t *testing.T) { // receive next object from adder o := <-out - addedHashes[o.(*coreiface.AddEvent).Path.Cid().String()] = struct{}{} + addedHashes[o.(*coreiface.AddEvent).Path.RootCid().String()] = struct{}{} <-gcstarted @@ -235,7 +233,7 @@ func TestAddGCLive(t *testing.T) { var last cid.Cid for a := range out { // wait for it to finish - c, err := cid.Decode(a.(*coreiface.AddEvent).Path.Cid().String()) + c, err := cid.Decode(a.(*coreiface.AddEvent).Path.RootCid().String()) if err != nil { t.Fatal(err) } diff --git a/core/coreunix/metadata.go b/core/coreunix/metadata.go index 5454b213f..bed171efc 100644 --- a/core/coreunix/metadata.go +++ b/core/coreunix/metadata.go @@ -1,9 +1,9 @@ package coreunix import ( + dag "github.com/ipfs/boxo/ipld/merkledag" + ft "github.com/ipfs/boxo/ipld/unixfs" cid "github.com/ipfs/go-cid" - dag "github.com/ipfs/go-merkledag" - ft "github.com/ipfs/go-unixfs" core "github.com/ipfs/kubo/core" ) diff --git a/core/coreunix/metadata_test.go b/core/coreunix/metadata_test.go index f745ec5ff..c7d1b94db 100644 --- a/core/coreunix/metadata_test.go +++ b/core/coreunix/metadata_test.go @@ -6,21 +6,21 @@ import ( "io" "testing" - bserv "github.com/ipfs/go-blockservice" - merkledag "github.com/ipfs/go-merkledag" - ft "github.com/ipfs/go-unixfs" - importer "github.com/ipfs/go-unixfs/importer" - uio "github.com/ipfs/go-unixfs/io" + bserv "github.com/ipfs/boxo/blockservice" + merkledag "github.com/ipfs/boxo/ipld/merkledag" + ft "github.com/ipfs/boxo/ipld/unixfs" + importer "github.com/ipfs/boxo/ipld/unixfs/importer" + uio "github.com/ipfs/boxo/ipld/unixfs/io" core "github.com/ipfs/kubo/core" + bstore "github.com/ipfs/boxo/blockstore" + chunker "github.com/ipfs/boxo/chunker" + offline "github.com/ipfs/boxo/exchange/offline" cid "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-datastore/sync" - bstore "github.com/ipfs/go-ipfs-blockstore" - chunker "github.com/ipfs/go-ipfs-chunker" - offline "github.com/ipfs/go-ipfs-exchange-offline" - u "github.com/ipfs/go-ipfs-util" ipld "github.com/ipfs/go-ipld-format" + "github.com/ipfs/go-test/random" ) func getDagserv(t *testing.T) ipld.DAGService { @@ -35,7 +35,7 @@ func TestMetadata(t *testing.T) { // Make some random node ds := getDagserv(t) data := make([]byte, 1000) - _, err := io.ReadFull(u.NewTimeSeededRand(), data) + _, err := io.ReadFull(random.NewRand(), data) if err != nil { t.Fatal(err) } diff --git a/core/mock/mock.go b/core/mock/mock.go index 7b718c43e..f9e0876f8 100644 --- a/core/mock/mock.go +++ b/core/mock/mock.go @@ -34,7 +34,16 @@ func NewMockNode() (*core.IpfsNode, error) { } func MockHostOption(mn mocknet.Mocknet) libp2p2.HostOption { - return func(id peer.ID, ps pstore.Peerstore, _ ...libp2p.Option) (host.Host, error) { + return func(id peer.ID, ps pstore.Peerstore, opts ...libp2p.Option) (host.Host, error) { + var cfg libp2p.Config + if err := cfg.Apply(opts...); err != nil { + return nil, err + } + + // The mocknet does not use the provided libp2p.Option. This options include + // the listening addresses we want our peer listening on. Therefore, we have + // to manually parse the configuration and add them here. + ps.AddAddrs(id, cfg.ListenAddrs, pstore.PermanentAddrTTL) return mn.AddPeerWithPeerstore(id, ps) } } diff --git a/core/node/bitswap.go b/core/node/bitswap.go index 42b948f75..4132d5a01 100644 --- a/core/node/bitswap.go +++ b/core/node/bitswap.go @@ -4,10 +4,10 @@ import ( "context" "time" - "github.com/ipfs/go-bitswap" - "github.com/ipfs/go-bitswap/network" - blockstore "github.com/ipfs/go-ipfs-blockstore" - exchange "github.com/ipfs/go-ipfs-exchange-interface" + "github.com/ipfs/boxo/bitswap" + "github.com/ipfs/boxo/bitswap/network" + blockstore "github.com/ipfs/boxo/blockstore" + exchange "github.com/ipfs/boxo/exchange" "github.com/ipfs/kubo/config" irouting "github.com/ipfs/kubo/routing" "github.com/libp2p/go-libp2p/core/host" @@ -23,6 +23,7 @@ const ( DefaultEngineTaskWorkerCount = 8 DefaultMaxOutstandingBytesPerPeer = 1 << 20 DefaultProviderSearchDelay = 1000 * time.Millisecond + DefaultWantHaveReplaceSize = 1024 ) type bitswapOptionsOut struct { @@ -47,6 +48,7 @@ func BitswapOptions(cfg *config.Config, provide bool) interface{} { bitswap.TaskWorkerCount(int(internalBsCfg.TaskWorkerCount.WithDefault(DefaultTaskWorkerCount))), bitswap.EngineTaskWorkerCount(int(internalBsCfg.EngineTaskWorkerCount.WithDefault(DefaultEngineTaskWorkerCount))), bitswap.MaxOutstandingBytesPerPeer(int(internalBsCfg.MaxOutstandingBytesPerPeer.WithDefault(DefaultMaxOutstandingBytesPerPeer))), + bitswap.WithWantHaveReplaceSize(int(internalBsCfg.WantHaveReplaceSize.WithDefault(DefaultWantHaveReplaceSize))), } return bitswapOptionsOut{BitswapOpts: opts} diff --git a/core/node/builder.go b/core/node/builder.go index cd68bbfcb..411e3228c 100644 --- a/core/node/builder.go +++ b/core/node/builder.go @@ -4,7 +4,6 @@ import ( "context" "crypto/rand" "encoding/base64" - "errors" "go.uber.org/fx" @@ -34,9 +33,6 @@ type BuildCfg struct { // DO NOT SET THIS UNLESS YOU'RE TESTING. DisableEncryptedConnections bool - // If NilRepo is set, a Repo backed by a nil datastore will be constructed - NilRepo bool - Routing libp2p.RoutingOption Host libp2p.HostOption Repo repo.Repo @@ -51,18 +47,8 @@ func (cfg *BuildCfg) getOpt(key string) bool { } func (cfg *BuildCfg) fillDefaults() error { - if cfg.Repo != nil && cfg.NilRepo { - return errors.New("cannot set a Repo and specify nilrepo at the same time") - } - if cfg.Repo == nil { - var d ds.Datastore - if cfg.NilRepo { - d = ds.NewNullDatastore() - } else { - d = ds.NewMapDatastore() - } - r, err := defaultRepo(dsync.MutexWrap(d)) + r, err := defaultRepo(dsync.MutexWrap(ds.NewMapDatastore())) if err != nil { return err } @@ -140,8 +126,8 @@ func defaultRepo(dstore repo.Datastore) (repo.Repo, error) { } c.Bootstrap = cfg.DefaultBootstrapAddresses - c.Addresses.Swarm = []string{"/ip4/0.0.0.0/tcp/4001", "/ip4/0.0.0.0/udp/4001/quic"} - c.Identity.PeerID = pid.Pretty() + c.Addresses.Swarm = []string{"/ip4/0.0.0.0/tcp/4001", "/ip4/0.0.0.0/udp/4001/quic-v1"} + c.Identity.PeerID = pid.String() c.Identity.PrivKey = base64.StdEncoding.EncodeToString(privkeyb) return &repo.Mock{ diff --git a/core/node/core.go b/core/node/core.go index 8aed8b05e..ad259c962 100644 --- a/core/node/core.go +++ b/core/node/core.go @@ -4,25 +4,24 @@ import ( "context" "fmt" - "github.com/ipfs/go-blockservice" + "github.com/ipfs/boxo/blockservice" + blockstore "github.com/ipfs/boxo/blockstore" + exchange "github.com/ipfs/boxo/exchange" + offline "github.com/ipfs/boxo/exchange/offline" + "github.com/ipfs/boxo/fetcher" + bsfetcher "github.com/ipfs/boxo/fetcher/impl/blockservice" + "github.com/ipfs/boxo/filestore" + "github.com/ipfs/boxo/ipld/merkledag" + "github.com/ipfs/boxo/ipld/unixfs" + "github.com/ipfs/boxo/mfs" + pathresolver "github.com/ipfs/boxo/path/resolver" + pin "github.com/ipfs/boxo/pinning/pinner" + "github.com/ipfs/boxo/pinning/pinner/dspinner" "github.com/ipfs/go-cid" "github.com/ipfs/go-datastore" - "github.com/ipfs/go-fetcher" - bsfetcher "github.com/ipfs/go-fetcher/impl/blockservice" - "github.com/ipfs/go-filestore" - blockstore "github.com/ipfs/go-ipfs-blockstore" - exchange "github.com/ipfs/go-ipfs-exchange-interface" - pin "github.com/ipfs/go-ipfs-pinner" - "github.com/ipfs/go-ipfs-pinner/dspinner" format "github.com/ipfs/go-ipld-format" - "github.com/ipfs/go-merkledag" - "github.com/ipfs/go-mfs" - "github.com/ipfs/go-unixfs" "github.com/ipfs/go-unixfsnode" dagpb "github.com/ipld/go-codec-dagpb" - "github.com/ipld/go-ipld-prime" - basicnode "github.com/ipld/go-ipld-prime/node/basic" - "github.com/ipld/go-ipld-prime/schema" "go.uber.org/fx" "github.com/ipfs/kubo/core/node/helpers" @@ -83,24 +82,62 @@ func (s *syncDagService) Session(ctx context.Context) format.NodeGetter { return merkledag.NewSession(ctx, s.DAGService) } -type fetchersOut struct { +// FetchersOut allows injection of fetchers. +type FetchersOut struct { fx.Out - IPLDFetcher fetcher.Factory `name:"ipldFetcher"` - UnixfsFetcher fetcher.Factory `name:"unixfsFetcher"` + IPLDFetcher fetcher.Factory `name:"ipldFetcher"` + UnixfsFetcher fetcher.Factory `name:"unixfsFetcher"` + OfflineIPLDFetcher fetcher.Factory `name:"offlineIpldFetcher"` + OfflineUnixfsFetcher fetcher.Factory `name:"offlineUnixfsFetcher"` +} + +// FetchersIn allows using fetchers for other dependencies. +type FetchersIn struct { + fx.In + IPLDFetcher fetcher.Factory `name:"ipldFetcher"` + UnixfsFetcher fetcher.Factory `name:"unixfsFetcher"` + OfflineIPLDFetcher fetcher.Factory `name:"offlineIpldFetcher"` + OfflineUnixfsFetcher fetcher.Factory `name:"offlineUnixfsFetcher"` } // FetcherConfig returns a fetcher config that can build new fetcher instances -func FetcherConfig(bs blockservice.BlockService) fetchersOut { +func FetcherConfig(bs blockservice.BlockService) FetchersOut { ipldFetcher := bsfetcher.NewFetcherConfig(bs) - ipldFetcher.PrototypeChooser = dagpb.AddSupportToChooser(func(lnk ipld.Link, lnkCtx ipld.LinkContext) (ipld.NodePrototype, error) { - if tlnkNd, ok := lnkCtx.LinkNode.(schema.TypedLinkNode); ok { - return tlnkNd.LinkTargetNodePrototype(), nil - } - return basicnode.Prototype.Any, nil - }) - + ipldFetcher.PrototypeChooser = dagpb.AddSupportToChooser(bsfetcher.DefaultPrototypeChooser) unixFSFetcher := ipldFetcher.WithReifier(unixfsnode.Reify) - return fetchersOut{IPLDFetcher: ipldFetcher, UnixfsFetcher: unixFSFetcher} + + // Construct offline versions which we can safely use in contexts where + // path resolution should not fetch new blocks via exchange. + offlineBs := blockservice.New(bs.Blockstore(), offline.Exchange(bs.Blockstore())) + offlineIpldFetcher := bsfetcher.NewFetcherConfig(offlineBs) + offlineIpldFetcher.PrototypeChooser = dagpb.AddSupportToChooser(bsfetcher.DefaultPrototypeChooser) + offlineUnixFSFetcher := offlineIpldFetcher.WithReifier(unixfsnode.Reify) + + return FetchersOut{ + IPLDFetcher: ipldFetcher, + UnixfsFetcher: unixFSFetcher, + OfflineIPLDFetcher: offlineIpldFetcher, + OfflineUnixfsFetcher: offlineUnixFSFetcher, + } +} + +// PathResolversOut allows injection of path resolvers +type PathResolversOut struct { + fx.Out + IPLDPathResolver pathresolver.Resolver `name:"ipldPathResolver"` + UnixFSPathResolver pathresolver.Resolver `name:"unixFSPathResolver"` + OfflineIPLDPathResolver pathresolver.Resolver `name:"offlineIpldPathResolver"` + OfflineUnixFSPathResolver pathresolver.Resolver `name:"offlineUnixFSPathResolver"` +} + +// PathResolverConfig creates path resolvers with the given fetchers. +func PathResolverConfig(fetchers FetchersIn) PathResolversOut { + return PathResolversOut{ + IPLDPathResolver: pathresolver.NewBasicResolver(fetchers.IPLDFetcher), + UnixFSPathResolver: pathresolver.NewBasicResolver(fetchers.UnixfsFetcher), + OfflineIPLDPathResolver: pathresolver.NewBasicResolver(fetchers.OfflineIPLDFetcher), + OfflineUnixFSPathResolver: pathresolver.NewBasicResolver(fetchers.OfflineUnixfsFetcher), + } } // Dag creates new DAGService @@ -109,7 +146,7 @@ func Dag(bs blockservice.BlockService) format.DAGService { } // Files loads persisted MFS root -func Files(mctx helpers.MetricsCtx, lc fx.Lifecycle, repo repo.Repo, dag format.DAGService) (*mfs.Root, error) { +func Files(mctx helpers.MetricsCtx, lc fx.Lifecycle, repo repo.Repo, dag format.DAGService, bs blockstore.Blockstore) (*mfs.Root, error) { dsk := datastore.NewKey("/local/filesroot") pf := func(ctx context.Context, c cid.Cid) error { rootDS := repo.Datastore() @@ -135,7 +172,7 @@ func Files(mctx helpers.MetricsCtx, lc fx.Lifecycle, repo repo.Repo, dag format. nd = unixfs.EmptyDirNode() err := dag.Add(ctx, nd) if err != nil { - return nil, fmt.Errorf("failure writing to dagstore: %s", err) + return nil, fmt.Errorf("failure writing filesroot to dagstore: %s", err) } case err == nil: c, err := cid.Cast(val) @@ -143,9 +180,10 @@ func Files(mctx helpers.MetricsCtx, lc fx.Lifecycle, repo repo.Repo, dag format. return nil, err } - rnd, err := dag.Get(ctx, c) + offineDag := merkledag.NewDAGService(blockservice.New(bs, offline.Exchange(bs))) + rnd, err := offineDag.Get(ctx, c) if err != nil { - return nil, fmt.Errorf("error loading filesroot from DAG: %s", err) + return nil, fmt.Errorf("error loading filesroot from dagservice: %s", err) } pbnd, ok := rnd.(*merkledag.ProtoNode) diff --git a/core/node/dns.go b/core/node/dns.go index d66cccc54..d338e0e8b 100644 --- a/core/node/dns.go +++ b/core/node/dns.go @@ -1,88 +1,20 @@ package node import ( - "fmt" "math" - "strings" "time" + "github.com/ipfs/boxo/gateway" config "github.com/ipfs/kubo/config" doh "github.com/libp2p/go-doh-resolver" madns "github.com/multiformats/go-multiaddr-dns" - - "github.com/miekg/dns" ) -var defaultResolvers = map[string]string{ - "eth.": "https://resolver.cloudflare-eth.com/dns-query", - "crypto.": "https://resolver.cloudflare-eth.com/dns-query", -} - -func newResolver(url string, opts ...doh.Option) (madns.BasicResolver, error) { - if !strings.HasPrefix(url, "https://") { - return nil, fmt.Errorf("invalid resolver url: %s", url) - } - - return doh.NewResolver(url, opts...) -} - func DNSResolver(cfg *config.Config) (*madns.Resolver, error) { - var opts []madns.Option - var err error - var dohOpts []doh.Option if !cfg.DNS.MaxCacheTTL.IsDefault() { dohOpts = append(dohOpts, doh.WithMaxCacheTTL(cfg.DNS.MaxCacheTTL.WithDefault(time.Duration(math.MaxUint32)*time.Second))) } - domains := make(map[string]struct{}) // to track overridden default resolvers - rslvrs := make(map[string]madns.BasicResolver) // to reuse resolvers for the same URL - - for domain, url := range cfg.DNS.Resolvers { - if domain != "." && !dns.IsFqdn(domain) { - return nil, fmt.Errorf("invalid domain %s; must be FQDN", domain) - } - - domains[domain] = struct{}{} - if url == "" { - // allow overriding of implicit defaults with the default resolver - continue - } - - rslv, ok := rslvrs[url] - if !ok { - rslv, err = newResolver(url, dohOpts...) - if err != nil { - return nil, fmt.Errorf("bad resolver for %s: %w", domain, err) - } - rslvrs[url] = rslv - } - - if domain != "." { - opts = append(opts, madns.WithDomainResolver(domain, rslv)) - } else { - opts = append(opts, madns.WithDefaultResolver(rslv)) - } - } - - // fill in defaults if not overridden by the user - for domain, url := range defaultResolvers { - _, ok := domains[domain] - if ok { - continue - } - - rslv, ok := rslvrs[url] - if !ok { - rslv, err = newResolver(url) - if err != nil { - return nil, fmt.Errorf("bad resolver for %s: %w", domain, err) - } - rslvrs[url] = rslv - } - - opts = append(opts, madns.WithDomainResolver(domain, rslv)) - } - - return madns.NewResolver(opts...) + return gateway.NewDNSResolver(cfg.DNS.Resolvers, dohOpts...) } diff --git a/core/node/graphsync.go b/core/node/graphsync.go deleted file mode 100644 index 548888d66..000000000 --- a/core/node/graphsync.go +++ /dev/null @@ -1,23 +0,0 @@ -package node - -import ( - "github.com/ipfs/go-graphsync" - gsimpl "github.com/ipfs/go-graphsync/impl" - "github.com/ipfs/go-graphsync/network" - "github.com/ipfs/go-graphsync/storeutil" - blockstore "github.com/ipfs/go-ipfs-blockstore" - libp2p "github.com/libp2p/go-libp2p/core" - "go.uber.org/fx" - - "github.com/ipfs/kubo/core/node/helpers" -) - -// Graphsync constructs a graphsync -func Graphsync(lc fx.Lifecycle, mctx helpers.MetricsCtx, host libp2p.Host, bs blockstore.GCBlockstore) graphsync.GraphExchange { - ctx := helpers.LifecycleCtx(mctx, lc) - - network := network.NewFromLibp2pHost(host) - return gsimpl.New(ctx, network, - storeutil.LinkSystemForBlockstore(bs), - ) -} diff --git a/core/node/groups.go b/core/node/groups.go index aa650ddf5..c74d0be8a 100644 --- a/core/node/groups.go +++ b/core/node/groups.go @@ -4,22 +4,22 @@ import ( "context" "errors" "fmt" + "strings" "time" - blockstore "github.com/ipfs/go-ipfs-blockstore" - util "github.com/ipfs/go-ipfs-util" + "github.com/dustin/go-humanize" + blockstore "github.com/ipfs/boxo/blockstore" + offline "github.com/ipfs/boxo/exchange/offline" + uio "github.com/ipfs/boxo/ipld/unixfs/io" + util "github.com/ipfs/boxo/util" "github.com/ipfs/go-log" "github.com/ipfs/kubo/config" - pubsub "github.com/libp2p/go-libp2p-pubsub" - "github.com/libp2p/go-libp2p/core/peer" - "github.com/ipfs/kubo/core/node/libp2p" "github.com/ipfs/kubo/p2p" - - offline "github.com/ipfs/go-ipfs-exchange-offline" - uio "github.com/ipfs/go-unixfs/io" - - "github.com/dustin/go-humanize" + pubsub "github.com/libp2p/go-libp2p-pubsub" + "github.com/libp2p/go-libp2p-pubsub/timecache" + "github.com/libp2p/go-libp2p/core/peer" + rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager" "go.uber.org/fx" ) @@ -36,7 +36,7 @@ var BaseLibP2P = fx.Options( fx.Invoke(libp2p.PNetChecker), ) -func LibP2P(bcfg *BuildCfg, cfg *config.Config) fx.Option { +func LibP2P(bcfg *BuildCfg, cfg *config.Config, userResourceOverrides rcmgr.PartialLimitConfig) fx.Option { var connmgr fx.Option // set connmgr based on Swarm.ConnMgr.Type @@ -66,6 +66,18 @@ func LibP2P(bcfg *BuildCfg, cfg *config.Config) fx.Option { pubsub.WithSeenMessagesTTL(cfg.Pubsub.SeenMessagesTTL.WithDefault(pubsub.TimeCacheDuration)), ) + var seenMessagesStrategy timecache.Strategy + configSeenMessagesStrategy := cfg.Pubsub.SeenMessagesStrategy.WithDefault(config.DefaultSeenMessagesStrategy) + switch configSeenMessagesStrategy { + case config.LastSeenMessagesStrategy: + seenMessagesStrategy = timecache.Strategy_LastSeen + case config.FirstSeenMessagesStrategy: + seenMessagesStrategy = timecache.Strategy_FirstSeen + default: + return fx.Error(fmt.Errorf("unsupported Pubsub.SeenMessagesStrategy %q", configSeenMessagesStrategy)) + } + pubsubOptions = append(pubsubOptions, pubsub.WithSeenMessagesStrategy(seenMessagesStrategy)) + switch cfg.Pubsub.Router { case "": fallthrough @@ -94,12 +106,15 @@ func LibP2P(bcfg *BuildCfg, cfg *config.Config) fx.Option { // to dhtclient. fallthrough case config.AutoNATServiceEnabled: - autonat = fx.Provide(libp2p.AutoNATService(cfg.AutoNAT.Throttle)) + autonat = fx.Provide(libp2p.AutoNATService(cfg.AutoNAT.Throttle, false)) + case config.AutoNATServiceEnabledV1Only: + autonat = fx.Provide(libp2p.AutoNATService(cfg.AutoNAT.Throttle, true)) } enableRelayTransport := cfg.Swarm.Transports.Network.Relay.WithDefault(true) // nolint enableRelayService := cfg.Swarm.RelayService.Enabled.WithDefault(enableRelayTransport) enableRelayClient := cfg.Swarm.RelayClient.Enabled.WithDefault(enableRelayTransport) + enableAutoTLS := cfg.AutoTLS.Enabled.WithDefault(config.DefaultAutoTLSEnabled) // Log error when relay subsystem could not be initialized due to missing dependency if !enableRelayTransport { @@ -110,23 +125,22 @@ func LibP2P(bcfg *BuildCfg, cfg *config.Config) fx.Option { logger.Fatal("Failed to enable `Swarm.RelayClient`, it requires `Swarm.Transports.Network.Relay` to be true.") } } + if enableAutoTLS { + if !cfg.Swarm.Transports.Network.Websocket.WithDefault(true) { + logger.Fatal("Invalid configuration: AutoTLS.Enabled=true requires Swarm.Transports.Network.Websocket to be true as well.") + } - // Force users to migrate old config. - // nolint - if cfg.Swarm.DisableRelay { - logger.Fatal("The 'Swarm.DisableRelay' config field was removed." + - "Use the 'Swarm.Transports.Network.Relay' instead.") - } - // nolint - if cfg.Swarm.EnableAutoRelay { - logger.Fatal("The 'Swarm.EnableAutoRelay' config field was removed." + - "Use the 'Swarm.RelayClient.Enabled' instead.") - } - // nolint - if cfg.Swarm.EnableRelayHop { - logger.Fatal("The `Swarm.EnableRelayHop` config field was removed.\n" + - "Use `Swarm.RelayService` to configure the circuit v2 relay.\n" + - "If you want to continue running a circuit v1 relay, please use the standalone relay daemon: https://dist.ipfs.tech/#libp2p-relay-daemon (with RelayV1.Enabled: true)") + wssWildcard := fmt.Sprintf("/tls/sni/*.%s/ws", cfg.AutoTLS.DomainSuffix.WithDefault(config.DefaultDomainSuffix)) + wssWildcardPresent := false + for _, listener := range cfg.Addresses.Swarm { + if strings.Contains(listener, wssWildcard) { + wssWildcardPresent = true + break + } + } + if !wssWildcardPresent { + logger.Fatal(fmt.Sprintf("Invalid configuration: AutoTLS.Enabled=true requires a catch-all Addresses.Swarm listener ending with %q to be present, see https://github.com/ipfs/kubo/blob/master/docs/config.md#autotls", wssWildcard)) + } } // Gather all the options @@ -137,14 +151,16 @@ func LibP2P(bcfg *BuildCfg, cfg *config.Config) fx.Option { fx.Provide(libp2p.UserAgent()), // Services (resource management) - fx.Provide(libp2p.ResourceManager(cfg.Swarm)), + fx.Provide(libp2p.ResourceManager(bcfg.Repo.Path(), cfg.Swarm, userResourceOverrides)), + maybeProvide(libp2p.P2PForgeCertMgr(bcfg.Repo.Path(), cfg.AutoTLS), enableAutoTLS), + maybeInvoke(libp2p.StartP2PAutoTLS, enableAutoTLS), fx.Provide(libp2p.AddrFilters(cfg.Swarm.AddrFilters)), fx.Provide(libp2p.AddrsFactory(cfg.Addresses.Announce, cfg.Addresses.AppendAnnounce, cfg.Addresses.NoAnnounce)), fx.Provide(libp2p.SmuxTransport(cfg.Swarm.Transports)), fx.Provide(libp2p.RelayTransport(enableRelayTransport)), fx.Provide(libp2p.RelayService(enableRelayService, cfg.Swarm.RelayService)), fx.Provide(libp2p.Transports(cfg.Swarm.Transports)), - fx.Invoke(libp2p.StartListening(cfg.Addresses.Swarm)), + fx.Provide(libp2p.ListenOn(cfg.Addresses.Swarm)), fx.Invoke(libp2p.SetupDiscovery(cfg.Discovery.MDNS.Enabled)), fx.Provide(libp2p.ForceReachability(cfg.Internal.Libp2pForceReachability)), fx.Provide(libp2p.HolePunching(cfg.Swarm.EnableHolePunching, enableRelayClient)), @@ -154,7 +170,7 @@ func LibP2P(bcfg *BuildCfg, cfg *config.Config) fx.Option { fx.Provide(libp2p.Routing), fx.Provide(libp2p.ContentRouting), - fx.Provide(libp2p.BaseRouting(cfg.Experimental.AcceleratedDHTClient)), + fx.Provide(libp2p.BaseRouting(cfg)), maybeProvide(libp2p.PubsubRouter, bcfg.getOpt("ipnsps")), maybeProvide(libp2p.BandwidthCounter, !cfg.Swarm.DisableBandwidthMetrics), @@ -185,7 +201,7 @@ func Storage(bcfg *BuildCfg, cfg *config.Config) fx.Option { return fx.Options( fx.Provide(RepoConfig), fx.Provide(Datastore), - fx.Provide(BaseBlockstoreCtor(cacheOpts, bcfg.NilRepo, cfg.Datastore.HashOnRead)), + fx.Provide(BaseBlockstoreCtor(cacheOpts, cfg.Datastore.HashOnRead)), finalBstore, ) } @@ -236,8 +252,7 @@ var IPNS = fx.Options( ) // Online groups online-only units -func Online(bcfg *BuildCfg, cfg *config.Config) fx.Option { - +func Online(bcfg *BuildCfg, cfg *config.Config, userResourceOverrides rcmgr.PartialLimitConfig) fx.Option { // Namesys params ipnsCacheSize := cfg.Ipns.ResolveCacheSize @@ -280,9 +295,8 @@ func Online(bcfg *BuildCfg, cfg *config.Config) fx.Option { return fx.Options( fx.Provide(BitswapOptions(cfg, shouldBitswapProvide)), fx.Provide(OnlineExchange()), - maybeProvide(Graphsync, cfg.Experimental.GraphsyncEnabled), fx.Provide(DNSResolver), - fx.Provide(Namesys(ipnsCacheSize)), + fx.Provide(Namesys(ipnsCacheSize, cfg.Ipns.MaxCacheTTL.WithDefault(config.DefaultIpnsMaxCacheTTL))), fx.Provide(Peering), PeerWith(cfg.Peering.Peers...), @@ -290,12 +304,12 @@ func Online(bcfg *BuildCfg, cfg *config.Config) fx.Option { fx.Provide(p2p.New), - LibP2P(bcfg, cfg), + LibP2P(bcfg, cfg, userResourceOverrides), OnlineProviders( cfg.Experimental.StrategicProviding, - cfg.Experimental.AcceleratedDHTClient, cfg.Reprovider.Strategy.WithDefault(config.DefaultReproviderStrategy), cfg.Reprovider.Interval.WithDefault(config.DefaultReproviderInterval), + cfg.Routing.AcceleratedDHTClient.WithDefault(config.DefaultAcceleratedDHTClient), ), ) } @@ -305,16 +319,11 @@ func Offline(cfg *config.Config) fx.Option { return fx.Options( fx.Provide(offline.Exchange), fx.Provide(DNSResolver), - fx.Provide(Namesys(0)), + fx.Provide(Namesys(0, 0)), fx.Provide(libp2p.Routing), fx.Provide(libp2p.ContentRouting), fx.Provide(libp2p.OfflineRouting), - OfflineProviders( - cfg.Experimental.StrategicProviding, - cfg.Experimental.AcceleratedDHTClient, - cfg.Reprovider.Strategy.WithDefault(config.DefaultReproviderStrategy), - cfg.Reprovider.Interval.WithDefault(config.DefaultReproviderInterval), - ), + OfflineProviders(), ) } @@ -323,13 +332,14 @@ var Core = fx.Options( fx.Provide(BlockService), fx.Provide(Dag), fx.Provide(FetcherConfig), + fx.Provide(PathResolverConfig), fx.Provide(Pinning), fx.Provide(Files), ) -func Networked(bcfg *BuildCfg, cfg *config.Config) fx.Option { +func Networked(bcfg *BuildCfg, cfg *config.Config, userResourceOverrides rcmgr.PartialLimitConfig) fx.Option { if bcfg.Online { - return Online(bcfg, cfg) + return Online(bcfg, cfg, userResourceOverrides) } return Offline(cfg) } @@ -345,6 +355,11 @@ func IPFS(ctx context.Context, bcfg *BuildCfg) fx.Option { return bcfgOpts // error } + userResourceOverrides, err := bcfg.Repo.UserResourceOverrides() + if err != nil { + return fx.Error(err) + } + // Auto-sharding settings shardSizeString := cfg.Internal.UnixFSShardingSizeThreshold.WithDefault("256kiB") shardSizeInt, err := humanize.ParseBytes(shardSizeString) @@ -368,7 +383,7 @@ func IPFS(ctx context.Context, bcfg *BuildCfg) fx.Option { Storage(bcfg, cfg), Identity(cfg), IPNS, - Networked(bcfg, cfg), + Networked(bcfg, cfg, userResourceOverrides), Core, ) diff --git a/core/node/helpers.go b/core/node/helpers.go index 6e6cb2920..63e76ead7 100644 --- a/core/node/helpers.go +++ b/core/node/helpers.go @@ -2,9 +2,9 @@ package node import ( "context" + "errors" "github.com/jbenet/goprocess" - "github.com/pkg/errors" "go.uber.org/fx" ) diff --git a/core/node/helpers/helpers.go b/core/node/helpers/helpers.go index 546c8e977..4c3a04881 100644 --- a/core/node/helpers/helpers.go +++ b/core/node/helpers/helpers.go @@ -2,6 +2,7 @@ package helpers import ( "context" + "go.uber.org/fx" ) diff --git a/core/node/ipns.go b/core/node/ipns.go index 0500c7be4..5f516d035 100644 --- a/core/node/ipns.go +++ b/core/node/ipns.go @@ -4,15 +4,15 @@ import ( "fmt" "time" - util "github.com/ipfs/go-ipfs-util" - "github.com/ipfs/go-ipns" + "github.com/ipfs/boxo/ipns" + util "github.com/ipfs/boxo/util" record "github.com/libp2p/go-libp2p-record" "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/peerstore" madns "github.com/multiformats/go-multiaddr-dns" - "github.com/ipfs/go-namesys" - "github.com/ipfs/go-namesys/republisher" + "github.com/ipfs/boxo/namesys" + "github.com/ipfs/boxo/namesys/republisher" "github.com/ipfs/kubo/repo" irouting "github.com/ipfs/kubo/routing" ) @@ -28,11 +28,12 @@ func RecordValidator(ps peerstore.Peerstore) record.Validator { } // Namesys creates new name system -func Namesys(cacheSize int) func(rt irouting.ProvideManyRouter, rslv *madns.Resolver, repo repo.Repo) (namesys.NameSystem, error) { +func Namesys(cacheSize int, cacheMaxTTL time.Duration) func(rt irouting.ProvideManyRouter, rslv *madns.Resolver, repo repo.Repo) (namesys.NameSystem, error) { return func(rt irouting.ProvideManyRouter, rslv *madns.Resolver, repo repo.Repo) (namesys.NameSystem, error) { opts := []namesys.Option{ namesys.WithDatastore(repo.Datastore()), namesys.WithDNSResolver(rslv), + namesys.WithMaxCacheTTL(cacheMaxTTL), } if cacheSize > 0 { diff --git a/core/node/libp2p/addrs.go b/core/node/libp2p/addrs.go index 480b045f8..acb5bb9d6 100644 --- a/core/node/libp2p/addrs.go +++ b/core/node/libp2p/addrs.go @@ -1,13 +1,23 @@ package libp2p import ( + "context" "fmt" + "os" + "path/filepath" + logging "github.com/ipfs/go-log" + version "github.com/ipfs/kubo" + "github.com/ipfs/kubo/config" + p2pforge "github.com/ipshipyard/p2p-forge/client" "github.com/libp2p/go-libp2p" "github.com/libp2p/go-libp2p/core/host" p2pbhost "github.com/libp2p/go-libp2p/p2p/host/basic" ma "github.com/multiformats/go-multiaddr" mamask "github.com/whyrusleeping/multiaddr-filter" + + "github.com/caddyserver/certmagic" + "go.uber.org/fx" ) func AddrFilters(filters []string) func() (*ma.Filters, Libp2pOpts, error) { @@ -88,48 +98,73 @@ func makeAddrsFactory(announce []string, appendAnnouce []string, noAnnounce []st }, nil } -func AddrsFactory(announce []string, appendAnnouce []string, noAnnounce []string) func() (opts Libp2pOpts, err error) { - return func() (opts Libp2pOpts, err error) { - addrsFactory, err := makeAddrsFactory(announce, appendAnnouce, noAnnounce) +func AddrsFactory(announce []string, appendAnnouce []string, noAnnounce []string) interface{} { + return func(params struct { + fx.In + ForgeMgr *p2pforge.P2PForgeCertMgr `optional:"true"` + }, + ) (opts Libp2pOpts, err error) { + var addrsFactory p2pbhost.AddrsFactory + announceAddrsFactory, err := makeAddrsFactory(announce, appendAnnouce, noAnnounce) if err != nil { return opts, err } + if params.ForgeMgr == nil { + addrsFactory = announceAddrsFactory + } else { + addrsFactory = func(multiaddrs []ma.Multiaddr) []ma.Multiaddr { + forgeProcessing := params.ForgeMgr.AddressFactory()(multiaddrs) + annouceProcessing := announceAddrsFactory(forgeProcessing) + return annouceProcessing + } + } opts.Opts = append(opts.Opts, libp2p.AddrsFactory(addrsFactory)) return } } -func listenAddresses(addresses []string) ([]ma.Multiaddr, error) { - listen := make([]ma.Multiaddr, len(addresses)) - for i, addr := range addresses { - maddr, err := ma.NewMultiaddr(addr) - if err != nil { - return nil, fmt.Errorf("failure to parse config.Addresses.Swarm: %s", addresses) +func ListenOn(addresses []string) interface{} { + return func() (opts Libp2pOpts) { + return Libp2pOpts{ + Opts: []libp2p.Option{ + libp2p.ListenAddrStrings(addresses...), + }, } - listen[i] = maddr - } - - return listen, nil -} - -func StartListening(addresses []string) func(host host.Host) error { - return func(host host.Host) error { - listenAddrs, err := listenAddresses(addresses) - if err != nil { - return err - } - - // Actually start listening: - if err := host.Network().Listen(listenAddrs...); err != nil { - return err - } - - // list out our addresses - addrs, err := host.Network().InterfaceListenAddresses() - if err != nil { - return err - } - log.Infof("Swarm listening at: %s", addrs) - return nil } } + +func P2PForgeCertMgr(repoPath string, cfg config.AutoTLS) interface{} { + return func() (*p2pforge.P2PForgeCertMgr, error) { + storagePath := filepath.Join(repoPath, "p2p-forge-certs") + + forgeLogger := logging.Logger("autotls").Desugar() + certStorage := &certmagic.FileStorage{Path: storagePath} + certMgr, err := p2pforge.NewP2PForgeCertMgr( + p2pforge.WithLogger(forgeLogger.Sugar()), + p2pforge.WithForgeDomain(cfg.DomainSuffix.WithDefault(config.DefaultDomainSuffix)), + p2pforge.WithForgeRegistrationEndpoint(cfg.RegistrationEndpoint.WithDefault(config.DefaultRegistrationEndpoint)), + p2pforge.WithCAEndpoint(cfg.CAEndpoint.WithDefault(config.DefaultCAEndpoint)), + p2pforge.WithForgeAuth(cfg.RegistrationToken.WithDefault(os.Getenv(p2pforge.ForgeAuthEnv))), + p2pforge.WithUserAgent(version.GetUserAgentVersion()), + p2pforge.WithCertificateStorage(certStorage), + ) + if err != nil { + return nil, err + } + + return certMgr, nil + } +} + +func StartP2PAutoTLS(lc fx.Lifecycle, certMgr *p2pforge.P2PForgeCertMgr, h host.Host) { + lc.Append(fx.Hook{ + OnStart: func(ctx context.Context) error { + certMgr.ProvideHost(h) + return certMgr.Start() + }, + OnStop: func(ctx context.Context) error { + certMgr.Stop() + return nil + }, + }) +} diff --git a/core/node/libp2p/dns.go b/core/node/libp2p/dns.go index 1c56a2c0a..2ee73b4c9 100644 --- a/core/node/libp2p/dns.go +++ b/core/node/libp2p/dns.go @@ -2,10 +2,11 @@ package libp2p import ( "github.com/libp2p/go-libp2p" + "github.com/libp2p/go-libp2p/p2p/net/swarm" madns "github.com/multiformats/go-multiaddr-dns" ) func MultiaddrResolver(rslv *madns.Resolver) (opts Libp2pOpts, err error) { - opts.Opts = append(opts.Opts, libp2p.MultiaddrResolver(rslv)) + opts.Opts = append(opts.Opts, libp2p.MultiaddrResolver(swarm.ResolverFromMaDNS{Resolver: rslv})) return opts, nil } diff --git a/core/node/libp2p/host.go b/core/node/libp2p/host.go index 15d815812..7950f3dc6 100644 --- a/core/node/libp2p/host.go +++ b/core/node/libp2p/host.go @@ -11,6 +11,7 @@ import ( "github.com/libp2p/go-libp2p/core/routing" routedhost "github.com/libp2p/go-libp2p/p2p/host/routed" + "github.com/ipfs/kubo/config" "github.com/ipfs/kubo/core/node/helpers" "github.com/ipfs/kubo/repo" @@ -53,13 +54,19 @@ func Host(mctx helpers.MetricsCtx, lc fx.Lifecycle, params P2PHostIn) (out P2PHo return out, err } + routingOptArgs := RoutingOptionArgs{ + Ctx: ctx, + Datastore: params.Repo.Datastore(), + Validator: params.Validator, + BootstrapPeers: bootstrappers, + OptimisticProvide: cfg.Experimental.OptimisticProvide, + OptimisticProvideJobsPoolSize: cfg.Experimental.OptimisticProvideJobsPoolSize, + LoopbackAddressesOnLanDHT: cfg.Routing.LoopbackAddressesOnLanDHT.WithDefault(config.DefaultLoopbackAddressesOnLanDHT), + } opts = append(opts, libp2p.Routing(func(h host.Host) (routing.PeerRouting, error) { - r, err := params.RoutingOption( - ctx, h, - params.Repo.Datastore(), - params.Validator, - bootstrappers..., - ) + args := routingOptArgs + args.Host = h + r, err := params.RoutingOption(args) out.Routing = r return r, err })) @@ -69,10 +76,12 @@ func Host(mctx helpers.MetricsCtx, lc fx.Lifecycle, params P2PHostIn) (out P2PHo return P2PHostOut{}, err } + routingOptArgs.Host = out.Host + // this code is necessary just for tests: mock network constructions // ignore the libp2p constructor options that actually construct the routing! if out.Routing == nil { - r, err := params.RoutingOption(ctx, out.Host, params.Repo.Datastore(), params.Validator, bootstrappers...) + r, err := params.RoutingOption(routingOptArgs) if err != nil { return P2PHostOut{}, err } diff --git a/core/node/libp2p/hostopt.go b/core/node/libp2p/hostopt.go index 74d6e5723..1dc671487 100644 --- a/core/node/libp2p/hostopt.go +++ b/core/node/libp2p/hostopt.go @@ -17,7 +17,7 @@ var DefaultHostOption HostOption = constructPeerHost func constructPeerHost(id peer.ID, ps peerstore.Peerstore, options ...libp2p.Option) (host.Host, error) { pkey := ps.PrivKey(id) if pkey == nil { - return nil, fmt.Errorf("missing private key for node ID: %s", id.Pretty()) + return nil, fmt.Errorf("missing private key for node ID: %s", id) } options = append([]libp2p.Option{libp2p.Identity(pkey), libp2p.Peerstore(ps)}, options...) return libp2p.New(options...) diff --git a/core/node/libp2p/nat.go b/core/node/libp2p/nat.go index fc72d7fb3..6d3cd09c3 100644 --- a/core/node/libp2p/nat.go +++ b/core/node/libp2p/nat.go @@ -9,7 +9,7 @@ import ( var NatPortMap = simpleOpt(libp2p.NATPortMap()) -func AutoNATService(throttle *config.AutoNATThrottleConfig) func() Libp2pOpts { +func AutoNATService(throttle *config.AutoNATThrottleConfig, v1only bool) func() Libp2pOpts { return func() (opts Libp2pOpts) { opts.Opts = append(opts.Opts, libp2p.EnableNATService()) if throttle != nil { @@ -21,6 +21,13 @@ func AutoNATService(throttle *config.AutoNATThrottleConfig) func() Libp2pOpts { ), ) } + + // While V1 still exists and V2 rollout is in progress + // (https://github.com/ipfs/kubo/issues/10091) we check a flag that + // allows users to disable V2 and run V1-only mode + if !v1only { + opts.Opts = append(opts.Opts, libp2p.EnableAutoNATv2()) + } return opts } } diff --git a/core/node/libp2p/rcmgr.go b/core/node/libp2p/rcmgr.go index 5d23a874d..80bfec34a 100644 --- a/core/node/libp2p/rcmgr.go +++ b/core/node/libp2p/rcmgr.go @@ -2,10 +2,10 @@ package libp2p import ( "context" + "encoding/json" "fmt" "os" "path/filepath" - "strings" "github.com/benbjohnson/clock" logging "github.com/ipfs/go-log/v2" @@ -14,9 +14,7 @@ import ( "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/protocol" rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager" - rcmgrObs "github.com/libp2p/go-libp2p/p2p/host/resource-manager/obs" "github.com/multiformats/go-multiaddr" - "go.opencensus.io/stats/view" "go.uber.org/fx" "github.com/ipfs/kubo/config" @@ -24,12 +22,13 @@ import ( "github.com/ipfs/kubo/repo" ) -const NetLimitDefaultFilename = "limit.json" +var rcmgrLogger = logging.Logger("rcmgr") + const NetLimitTraceFilename = "rcmgr.json.gz" var ErrNoResourceMgr = fmt.Errorf("missing ResourceMgr: make sure the daemon is running with Swarm.ResourceMgr.Enabled") -func ResourceManager(cfg config.SwarmConfig) interface{} { +func ResourceManager(repoPath string, cfg config.SwarmConfig, userResourceOverrides rcmgr.PartialLimitConfig) interface{} { return func(mctx helpers.MetricsCtx, lc fx.Lifecycle, repo repo.Repo) (network.ResourceManager, Libp2pOpts, error) { var manager network.ResourceManager var opts Libp2pOpts @@ -47,35 +46,46 @@ func ResourceManager(cfg config.SwarmConfig) interface{} { if enabled { log.Debug("libp2p resource manager is enabled") - repoPath, err := config.PathRoot() + limitConfig, msg, err := LimitConfig(cfg, userResourceOverrides) if err != nil { - return nil, opts, fmt.Errorf("opening IPFS_PATH: %w", err) + return nil, opts, fmt.Errorf("creating final Resource Manager config: %w", err) } - limitConfig, err := createDefaultLimitConfig(cfg) + if !isPartialConfigEmpty(userResourceOverrides) { + rcmgrLogger.Info(` +libp2p-resource-limit-overrides.json has been loaded, "default" fields will be +filled in with autocomputed defaults.`) + } + + // We want to see this message on startup, that's why we are using fmt instead of log. + rcmgrLogger.Info(msg) + + if err := ensureConnMgrMakeSenseVsResourceMgr(limitConfig, cfg); err != nil { + return nil, opts, err + } + + str, err := rcmgr.NewStatsTraceReporter() if err != nil { return nil, opts, err } - // The logic for defaults and overriding with specified SwarmConfig.ResourceMgr.Limits - // is documented in docs/config.md. - // Any changes here should be reflected there. - if cfg.ResourceMgr.Limits != nil { - l := *cfg.ResourceMgr.Limits - // This effectively overrides the computed default LimitConfig with any vlues from cfg.ResourceMgr.Limits - l.Apply(limitConfig) - limitConfig = l + ropts := []rcmgr.Option{ + rcmgr.WithMetrics(createRcmgrMetrics()), + rcmgr.WithTraceReporter(str), + rcmgr.WithLimitPerSubnet( + nil, + []rcmgr.ConnLimitPerSubnet{ + { + ConnCount: 16, + PrefixLength: 56, + }, + { + ConnCount: 8 * 16, + PrefixLength: 48, + }, + }), } - limiter := rcmgr.NewFixedLimiter(limitConfig) - - str, err := rcmgrObs.NewStatsTraceReporter() - if err != nil { - return nil, opts, err - } - - ropts := []rcmgr.Option{rcmgr.WithMetrics(createRcmgrMetrics()), rcmgr.WithTraceReporter(str)} - if len(cfg.ResourceMgr.Allowlist) > 0 { var mas []multiaddr.Multiaddr for _, maStr := range cfg.ResourceMgr.Allowlist { @@ -90,16 +100,13 @@ func ResourceManager(cfg config.SwarmConfig) interface{} { log.Infof("Setting allowlist to: %v", mas) } - err = view.Register(rcmgrObs.DefaultViews...) - if err != nil { - return nil, opts, fmt.Errorf("registering rcmgr obs views: %w", err) - } - if os.Getenv("LIBP2P_DEBUG_RCMGR") != "" { traceFilePath := filepath.Join(repoPath, NetLimitTraceFilename) ropts = append(ropts, rcmgr.WithTrace(traceFilePath)) } + limiter := rcmgr.NewFixedLimiter(limitConfig) + manager, err = rcmgr.NewResourceManager(limiter, ropts...) if err != nil { return nil, opts, fmt.Errorf("creating libp2p resource manager: %w", err) @@ -112,7 +119,7 @@ func ResourceManager(cfg config.SwarmConfig) interface{} { lrm.start(helpers.LifecycleCtx(mctx, lc)) manager = lrm } else { - fmt.Println("go-libp2p resource manager protection disabled") + rcmgrLogger.Info("go-libp2p resource manager protection disabled") manager = &network.NullResourceManager{} } @@ -121,480 +128,374 @@ func ResourceManager(cfg config.SwarmConfig) interface{} { lc.Append(fx.Hook{ OnStop: func(_ context.Context) error { return manager.Close() - }}) + }, + }) return manager, opts, nil } } -type NetStatOut struct { - System *rcmgr.BaseLimit `json:",omitempty"` - Transient *rcmgr.BaseLimit `json:",omitempty"` - Services map[string]rcmgr.BaseLimit `json:",omitempty"` - Protocols map[string]rcmgr.BaseLimit `json:",omitempty"` - Peers map[string]rcmgr.BaseLimit `json:",omitempty"` -} - -func NetStat(mgr network.ResourceManager, scope string, percentage int) (NetStatOut, error) { - var err error - var result NetStatOut - switch { - case scope == "all": - rapi, ok := mgr.(rcmgr.ResourceManagerState) - if !ok { // NullResourceManager - return result, ErrNoResourceMgr - } - - limits, err := NetLimitAll(mgr) - if err != nil { - return result, err - } - - stat := rapi.Stat() - result.System = compareLimits(scopeToLimit(&stat.System), limits.System, percentage) - result.Transient = compareLimits(scopeToLimit(&stat.Transient), limits.Transient, percentage) - if len(stat.Services) > 0 { - result.Services = make(map[string]rcmgr.BaseLimit, len(stat.Services)) - for srv, stat := range stat.Services { - ls := limits.Services[srv] - fstat := compareLimits(scopeToLimit(&stat), &ls, percentage) - if fstat != nil { - result.Services[srv] = *fstat - } - } - } - if len(stat.Protocols) > 0 { - result.Protocols = make(map[string]rcmgr.BaseLimit, len(stat.Protocols)) - for proto, stat := range stat.Protocols { - ls := limits.Protocols[string(proto)] - fstat := compareLimits(scopeToLimit(&stat), &ls, percentage) - if fstat != nil { - result.Protocols[string(proto)] = *fstat - } - } - } - if len(stat.Peers) > 0 { - result.Peers = make(map[string]rcmgr.BaseLimit, len(stat.Peers)) - for p, stat := range stat.Peers { - ls := limits.Peers[p.Pretty()] - fstat := compareLimits(scopeToLimit(&stat), &ls, percentage) - if fstat != nil { - result.Peers[p.Pretty()] = *fstat - } - } - } - - return result, nil - - case scope == config.ResourceMgrSystemScope: - err = mgr.ViewSystem(func(s network.ResourceScope) error { - stat := s.Stat() - result.System = scopeToLimit(&stat) - return nil - }) - return result, err - - case scope == config.ResourceMgrTransientScope: - err = mgr.ViewTransient(func(s network.ResourceScope) error { - stat := s.Stat() - result.Transient = scopeToLimit(&stat) - return nil - }) - return result, err - - case strings.HasPrefix(scope, config.ResourceMgrServiceScopePrefix): - svc := strings.TrimPrefix(scope, config.ResourceMgrServiceScopePrefix) - err = mgr.ViewService(svc, func(s network.ServiceScope) error { - stat := s.Stat() - result.Services = map[string]rcmgr.BaseLimit{ - svc: *scopeToLimit(&stat), - } - return nil - }) - return result, err - - case strings.HasPrefix(scope, config.ResourceMgrProtocolScopePrefix): - proto := strings.TrimPrefix(scope, config.ResourceMgrProtocolScopePrefix) - err = mgr.ViewProtocol(protocol.ID(proto), func(s network.ProtocolScope) error { - stat := s.Stat() - result.Protocols = map[string]rcmgr.BaseLimit{ - proto: *scopeToLimit(&stat), - } - return nil - }) - return result, err - - case strings.HasPrefix(scope, config.ResourceMgrPeerScopePrefix): - p := strings.TrimPrefix(scope, config.ResourceMgrPeerScopePrefix) - pid, err := peer.Decode(p) - if err != nil { - return result, fmt.Errorf("invalid peer ID: %q: %w", p, err) - } - err = mgr.ViewPeer(pid, func(s network.PeerScope) error { - stat := s.Stat() - result.Peers = map[string]rcmgr.BaseLimit{ - p: *scopeToLimit(&stat), - } - return nil - }) - return result, err - - default: - return result, fmt.Errorf("invalid scope %q", scope) - } -} - -var scopes = []string{ - config.ResourceMgrSystemScope, - config.ResourceMgrTransientScope, - config.ResourceMgrServiceScopePrefix, - config.ResourceMgrProtocolScopePrefix, - config.ResourceMgrPeerScopePrefix, -} - -func scopeToLimit(s *network.ScopeStat) *rcmgr.BaseLimit { - return &rcmgr.BaseLimit{ - Streams: s.NumStreamsInbound + s.NumStreamsOutbound, - StreamsInbound: s.NumStreamsInbound, - StreamsOutbound: s.NumStreamsOutbound, - Conns: s.NumConnsInbound + s.NumConnsOutbound, - ConnsInbound: s.NumConnsInbound, - ConnsOutbound: s.NumConnsOutbound, - FD: s.NumFD, - Memory: s.Memory, - } -} - -// compareLimits compares stat and limit. -// If any of the stats value are equals or above the specified percentage, -// stat object is returned. -func compareLimits(stat, limit *rcmgr.BaseLimit, percentage int) *rcmgr.BaseLimit { - if stat == nil || limit == nil { - return nil - } - if abovePercentage(int(stat.Memory), int(limit.Memory), percentage) { - return stat - } - if abovePercentage(stat.ConnsInbound, limit.ConnsInbound, percentage) { - return stat - } - if abovePercentage(stat.ConnsOutbound, limit.ConnsOutbound, percentage) { - return stat - } - if abovePercentage(stat.Conns, limit.Conns, percentage) { - return stat - } - if abovePercentage(stat.FD, limit.FD, percentage) { - return stat - } - if abovePercentage(stat.StreamsInbound, limit.StreamsInbound, percentage) { - return stat - } - if abovePercentage(stat.StreamsOutbound, limit.StreamsOutbound, percentage) { - return stat - } - if abovePercentage(stat.Streams, limit.Streams, percentage) { - return stat - } - - return nil -} - -func abovePercentage(v1, v2, percentage int) bool { - if percentage == 0 { - return true - } - - if v2 == 0 { +func isPartialConfigEmpty(cfg rcmgr.PartialLimitConfig) bool { + var emptyResourceConfig rcmgr.ResourceLimits + if cfg.System != emptyResourceConfig || + cfg.Transient != emptyResourceConfig || + cfg.AllowlistedSystem != emptyResourceConfig || + cfg.AllowlistedTransient != emptyResourceConfig || + cfg.ServiceDefault != emptyResourceConfig || + cfg.ServicePeerDefault != emptyResourceConfig || + cfg.ProtocolDefault != emptyResourceConfig || + cfg.ProtocolPeerDefault != emptyResourceConfig || + cfg.PeerDefault != emptyResourceConfig || + cfg.Conn != emptyResourceConfig || + cfg.Stream != emptyResourceConfig { return false } - - return int((float64(v1)/float64(v2))*100) >= percentage -} - -func NetLimitAll(mgr network.ResourceManager) (*NetStatOut, error) { - var result = &NetStatOut{} - lister, ok := mgr.(rcmgr.ResourceManagerState) - if !ok { // NullResourceManager - return result, ErrNoResourceMgr - } - - for _, s := range scopes { - switch s { - case config.ResourceMgrSystemScope: - s, err := NetLimit(mgr, config.ResourceMgrSystemScope) - if err != nil { - return nil, err - } - result.System = &s - case config.ResourceMgrTransientScope: - s, err := NetLimit(mgr, config.ResourceMgrSystemScope) - if err != nil { - return nil, err - } - result.Transient = &s - case config.ResourceMgrServiceScopePrefix: - result.Services = make(map[string]rcmgr.BaseLimit) - for _, serv := range lister.ListServices() { - s, err := NetLimit(mgr, config.ResourceMgrServiceScopePrefix+serv) - if err != nil { - return nil, err - } - result.Services[serv] = s - } - case config.ResourceMgrProtocolScopePrefix: - result.Protocols = make(map[string]rcmgr.BaseLimit) - for _, prot := range lister.ListProtocols() { - ps := string(prot) - s, err := NetLimit(mgr, config.ResourceMgrProtocolScopePrefix+ps) - if err != nil { - return nil, err - } - result.Protocols[ps] = s - } - case config.ResourceMgrPeerScopePrefix: - result.Peers = make(map[string]rcmgr.BaseLimit) - for _, peer := range lister.ListPeers() { - ps := peer.Pretty() - s, err := NetLimit(mgr, config.ResourceMgrPeerScopePrefix+ps) - if err != nil { - return nil, err - } - result.Peers[ps] = s - } + for _, v := range cfg.Service { + if v != emptyResourceConfig { + return false } } - - return result, nil + for _, v := range cfg.ServicePeer { + if v != emptyResourceConfig { + return false + } + } + for _, v := range cfg.Protocol { + if v != emptyResourceConfig { + return false + } + } + for _, v := range cfg.ProtocolPeer { + if v != emptyResourceConfig { + return false + } + } + for _, v := range cfg.Peer { + if v != emptyResourceConfig { + return false + } + } + return true } -func NetLimit(mgr network.ResourceManager, scope string) (rcmgr.BaseLimit, error) { - var result rcmgr.BaseLimit - getLimit := func(s network.ResourceScope) error { - limiter, ok := s.(rcmgr.ResourceScopeLimiter) - if !ok { // NullResourceManager - return ErrNoResourceMgr +// LimitConfig returns the union of the Computed Default Limits and the User Supplied Override Limits. +func LimitConfig(cfg config.SwarmConfig, userResourceOverrides rcmgr.PartialLimitConfig) (limitConfig rcmgr.ConcreteLimitConfig, logMessageForStartup string, err error) { + limitConfig, msg, err := createDefaultLimitConfig(cfg) + if err != nil { + return rcmgr.ConcreteLimitConfig{}, msg, err + } + + // The logic for defaults and overriding with specified userResourceOverrides + // is documented in docs/libp2p-resource-management.md. + // Any changes here should be reflected there. + + // This effectively overrides the computed default LimitConfig with any non-"useDefault" values from the userResourceOverrides file. + // Because of how how Build works, any rcmgr.Default value in userResourceOverrides + // will be overridden with a computed default value. + limitConfig = userResourceOverrides.Build(limitConfig) + + return limitConfig, msg, nil +} + +type ResourceLimitsAndUsage struct { + // This is duplicated from rcmgr.ResourceResourceLimits but adding *Usage fields. + Memory rcmgr.LimitVal64 + MemoryUsage int64 + FD rcmgr.LimitVal + FDUsage int + Conns rcmgr.LimitVal + ConnsUsage int + ConnsInbound rcmgr.LimitVal + ConnsInboundUsage int + ConnsOutbound rcmgr.LimitVal + ConnsOutboundUsage int + Streams rcmgr.LimitVal + StreamsUsage int + StreamsInbound rcmgr.LimitVal + StreamsInboundUsage int + StreamsOutbound rcmgr.LimitVal + StreamsOutboundUsage int +} + +func (u ResourceLimitsAndUsage) ToResourceLimits() rcmgr.ResourceLimits { + return rcmgr.ResourceLimits{ + Memory: u.Memory, + FD: u.FD, + Conns: u.Conns, + ConnsInbound: u.ConnsInbound, + ConnsOutbound: u.ConnsOutbound, + Streams: u.Streams, + StreamsInbound: u.StreamsInbound, + StreamsOutbound: u.StreamsOutbound, + } +} + +type LimitsConfigAndUsage struct { + // This is duplicated from rcmgr.ResourceManagerStat but using ResourceLimitsAndUsage + // instead of network.ScopeStat. + System ResourceLimitsAndUsage `json:",omitempty"` + Transient ResourceLimitsAndUsage `json:",omitempty"` + Services map[string]ResourceLimitsAndUsage `json:",omitempty"` + Protocols map[protocol.ID]ResourceLimitsAndUsage `json:",omitempty"` + Peers map[peer.ID]ResourceLimitsAndUsage `json:",omitempty"` +} + +func (u LimitsConfigAndUsage) MarshalJSON() ([]byte, error) { + // we want to marshal the encoded peer id + encodedPeerMap := make(map[string]ResourceLimitsAndUsage, len(u.Peers)) + for p, v := range u.Peers { + encodedPeerMap[p.String()] = v + } + + type Alias LimitsConfigAndUsage + return json.Marshal(&struct { + *Alias + Peers map[string]ResourceLimitsAndUsage `json:",omitempty"` + }{ + Alias: (*Alias)(&u), + Peers: encodedPeerMap, + }) +} + +func (u LimitsConfigAndUsage) ToPartialLimitConfig() (result rcmgr.PartialLimitConfig) { + result.System = u.System.ToResourceLimits() + result.Transient = u.Transient.ToResourceLimits() + + result.Service = make(map[string]rcmgr.ResourceLimits, len(u.Services)) + for s, l := range u.Services { + result.Service[s] = l.ToResourceLimits() + } + result.Protocol = make(map[protocol.ID]rcmgr.ResourceLimits, len(u.Protocols)) + for p, l := range u.Protocols { + result.Protocol[p] = l.ToResourceLimits() + } + result.Peer = make(map[peer.ID]rcmgr.ResourceLimits, len(u.Peers)) + for p, l := range u.Peers { + result.Peer[p] = l.ToResourceLimits() + } + + return +} + +func MergeLimitsAndStatsIntoLimitsConfigAndUsage(l rcmgr.ConcreteLimitConfig, stats rcmgr.ResourceManagerStat) LimitsConfigAndUsage { + limits := l.ToPartialLimitConfig() + + return LimitsConfigAndUsage{ + System: mergeResourceLimitsAndScopeStatToResourceLimitsAndUsage(limits.System, stats.System), + Transient: mergeResourceLimitsAndScopeStatToResourceLimitsAndUsage(limits.Transient, stats.Transient), + Services: mergeLimitsAndStatsMapIntoLimitsConfigAndUsageMap(limits.Service, stats.Services), + Protocols: mergeLimitsAndStatsMapIntoLimitsConfigAndUsageMap(limits.Protocol, stats.Protocols), + Peers: mergeLimitsAndStatsMapIntoLimitsConfigAndUsageMap(limits.Peer, stats.Peers), + } +} + +func mergeLimitsAndStatsMapIntoLimitsConfigAndUsageMap[K comparable](limits map[K]rcmgr.ResourceLimits, stats map[K]network.ScopeStat) map[K]ResourceLimitsAndUsage { + r := make(map[K]ResourceLimitsAndUsage, maxInt(len(limits), len(stats))) + for p, s := range stats { + var l rcmgr.ResourceLimits + if limits != nil { + if rl, ok := limits[p]; ok { + l = rl + } } - limit := limiter.Limit() - switch l := limit.(type) { - case *rcmgr.BaseLimit: - result.Memory = l.Memory - result.Streams = l.Streams - result.StreamsInbound = l.StreamsInbound - result.StreamsOutbound = l.StreamsOutbound - result.Conns = l.Conns - result.ConnsInbound = l.ConnsInbound - result.ConnsOutbound = l.ConnsOutbound - result.FD = l.FD - default: - return fmt.Errorf("unknown limit type %T", limit) + r[p] = mergeResourceLimitsAndScopeStatToResourceLimitsAndUsage(l, s) + } + for p, s := range limits { + if _, ok := stats[p]; ok { + continue // we already processed this element in the loop above } + r[p] = mergeResourceLimitsAndScopeStatToResourceLimitsAndUsage(s, network.ScopeStat{}) + } + return r +} + +func maxInt(x, y int) int { + if x > y { + return x + } + return y +} + +func mergeResourceLimitsAndScopeStatToResourceLimitsAndUsage(rl rcmgr.ResourceLimits, ss network.ScopeStat) ResourceLimitsAndUsage { + return ResourceLimitsAndUsage{ + Memory: rl.Memory, + MemoryUsage: ss.Memory, + FD: rl.FD, + FDUsage: ss.NumFD, + Conns: rl.Conns, + ConnsUsage: ss.NumConnsOutbound + ss.NumConnsInbound, + ConnsOutbound: rl.ConnsOutbound, + ConnsOutboundUsage: ss.NumConnsOutbound, + ConnsInbound: rl.ConnsInbound, + ConnsInboundUsage: ss.NumConnsInbound, + Streams: rl.Streams, + StreamsUsage: ss.NumStreamsOutbound + ss.NumStreamsInbound, + StreamsOutbound: rl.StreamsOutbound, + StreamsOutboundUsage: ss.NumStreamsOutbound, + StreamsInbound: rl.StreamsInbound, + StreamsInboundUsage: ss.NumStreamsInbound, + } +} + +type ResourceInfos []ResourceInfo + +type ResourceInfo struct { + ScopeName string + LimitName string + LimitValue rcmgr.LimitVal64 + CurrentUsage int64 +} + +// LimitConfigsToInfo gets limits and stats and generates a list of scopes and limits to be printed. +func LimitConfigsToInfo(stats LimitsConfigAndUsage) ResourceInfos { + result := ResourceInfos{} + + result = append(result, resourceLimitsAndUsageToResourceInfo(config.ResourceMgrSystemScope, stats.System)...) + result = append(result, resourceLimitsAndUsageToResourceInfo(config.ResourceMgrTransientScope, stats.Transient)...) + + for i, s := range stats.Services { + result = append(result, resourceLimitsAndUsageToResourceInfo( + config.ResourceMgrServiceScopePrefix+i, + s, + )...) + } + + for i, p := range stats.Protocols { + result = append(result, resourceLimitsAndUsageToResourceInfo( + config.ResourceMgrProtocolScopePrefix+string(i), + p, + )...) + } + + for i, p := range stats.Peers { + result = append(result, resourceLimitsAndUsageToResourceInfo( + config.ResourceMgrPeerScopePrefix+i.String(), + p, + )...) + } + + return result +} + +const ( + limitNameMemory = "Memory" + limitNameFD = "FD" + limitNameConns = "Conns" + limitNameConnsInbound = "ConnsInbound" + limitNameConnsOutbound = "ConnsOutbound" + limitNameStreams = "Streams" + limitNameStreamsInbound = "StreamsInbound" + limitNameStreamsOutbound = "StreamsOutbound" +) + +var limits = []string{ + limitNameMemory, + limitNameFD, + limitNameConns, + limitNameConnsInbound, + limitNameConnsOutbound, + limitNameStreams, + limitNameStreamsInbound, + limitNameStreamsOutbound, +} + +func resourceLimitsAndUsageToResourceInfo(scopeName string, stats ResourceLimitsAndUsage) ResourceInfos { + result := ResourceInfos{} + for _, l := range limits { + ri := ResourceInfo{ + ScopeName: scopeName, + } + switch l { + case limitNameMemory: + ri.LimitName = limitNameMemory + ri.LimitValue = stats.Memory + ri.CurrentUsage = stats.MemoryUsage + case limitNameFD: + ri.LimitName = limitNameFD + ri.LimitValue = rcmgr.LimitVal64(stats.FD) + ri.CurrentUsage = int64(stats.FDUsage) + case limitNameConns: + ri.LimitName = limitNameConns + ri.LimitValue = rcmgr.LimitVal64(stats.Conns) + ri.CurrentUsage = int64(stats.ConnsUsage) + case limitNameConnsInbound: + ri.LimitName = limitNameConnsInbound + ri.LimitValue = rcmgr.LimitVal64(stats.ConnsInbound) + ri.CurrentUsage = int64(stats.ConnsInboundUsage) + case limitNameConnsOutbound: + ri.LimitName = limitNameConnsOutbound + ri.LimitValue = rcmgr.LimitVal64(stats.ConnsOutbound) + ri.CurrentUsage = int64(stats.ConnsOutboundUsage) + case limitNameStreams: + ri.LimitName = limitNameStreams + ri.LimitValue = rcmgr.LimitVal64(stats.Streams) + ri.CurrentUsage = int64(stats.StreamsUsage) + case limitNameStreamsInbound: + ri.LimitName = limitNameStreamsInbound + ri.LimitValue = rcmgr.LimitVal64(stats.StreamsInbound) + ri.CurrentUsage = int64(stats.StreamsInboundUsage) + case limitNameStreamsOutbound: + ri.LimitName = limitNameStreamsOutbound + ri.LimitValue = rcmgr.LimitVal64(stats.StreamsOutbound) + ri.CurrentUsage = int64(stats.StreamsOutboundUsage) + } + + if ri.LimitValue == rcmgr.Unlimited64 || ri.LimitValue == rcmgr.DefaultLimit64 { + // ignore unlimited and unset limits to remove noise from output. + continue + } + + result = append(result, ri) + } + + return result +} + +func ensureConnMgrMakeSenseVsResourceMgr(concreteLimits rcmgr.ConcreteLimitConfig, cfg config.SwarmConfig) error { + if cfg.ConnMgr.Type.WithDefault(config.DefaultConnMgrType) == "none" || len(cfg.ResourceMgr.Allowlist) != 0 { + // no connmgr OR + // If an allowlist is set, a user may be enacting some form of DoS defense. + // We don't want want to modify the System.ConnsInbound in that case for example + // as it may make sense for it to be (and stay) as "blockAll" + // so that only connections within the allowlist of multiaddrs get established. return nil } - switch { - case scope == config.ResourceMgrSystemScope: - return result, mgr.ViewSystem(func(s network.ResourceScope) error { return getLimit(s) }) - case scope == config.ResourceMgrTransientScope: - return result, mgr.ViewTransient(func(s network.ResourceScope) error { return getLimit(s) }) - case strings.HasPrefix(scope, config.ResourceMgrServiceScopePrefix): - svc := strings.TrimPrefix(scope, config.ResourceMgrServiceScopePrefix) - return result, mgr.ViewService(svc, func(s network.ServiceScope) error { return getLimit(s) }) - case strings.HasPrefix(scope, config.ResourceMgrProtocolScopePrefix): - proto := strings.TrimPrefix(scope, config.ResourceMgrProtocolScopePrefix) - return result, mgr.ViewProtocol(protocol.ID(proto), func(s network.ProtocolScope) error { return getLimit(s) }) - case strings.HasPrefix(scope, config.ResourceMgrPeerScopePrefix): - p := strings.TrimPrefix(scope, config.ResourceMgrPeerScopePrefix) - pid, err := peer.Decode(p) - if err != nil { - return result, fmt.Errorf("invalid peer ID: %q: %w", p, err) - } - return result, mgr.ViewPeer(pid, func(s network.PeerScope) error { return getLimit(s) }) - default: - return result, fmt.Errorf("invalid scope %q", scope) + rcm := concreteLimits.ToPartialLimitConfig() + + highWater := cfg.ConnMgr.HighWater.WithDefault(config.DefaultConnMgrHighWater) + if (rcm.System.Conns > rcmgr.DefaultLimit || rcm.System.Conns == rcmgr.BlockAllLimit) && int64(rcm.System.Conns) <= highWater { + // nolint + return fmt.Errorf(` +Unable to initialize libp2p due to conflicting resource manager limit configuration. +resource manager System.Conns (%d) must be bigger than ConnMgr.HighWater (%d) +See: https://github.com/ipfs/kubo/blob/master/docs/libp2p-resource-management.md#how-does-the-resource-manager-resourcemgr-relate-to-the-connection-manager-connmgr +`, rcm.System.Conns, highWater) } -} - -// NetSetLimit sets new ResourceManager limits for the given scope. The limits take effect immediately, and are also persisted to the repo config. -func NetSetLimit(mgr network.ResourceManager, repo repo.Repo, scope string, limit rcmgr.BaseLimit) error { - setLimit := func(s network.ResourceScope) error { - limiter, ok := s.(rcmgr.ResourceScopeLimiter) - if !ok { // NullResourceManager - return ErrNoResourceMgr - } - - limiter.SetLimit(&limit) - return nil + if (rcm.System.ConnsInbound > rcmgr.DefaultLimit || rcm.System.ConnsInbound == rcmgr.BlockAllLimit) && int64(rcm.System.ConnsInbound) <= highWater { + // nolint + return fmt.Errorf(` +Unable to initialize libp2p due to conflicting resource manager limit configuration. +resource manager System.ConnsInbound (%d) must be bigger than ConnMgr.HighWater (%d) +See: https://github.com/ipfs/kubo/blob/master/docs/libp2p-resource-management.md#how-does-the-resource-manager-resourcemgr-relate-to-the-connection-manager-connmgr +`, rcm.System.ConnsInbound, highWater) } - - cfg, err := repo.Config() - if err != nil { - return fmt.Errorf("reading config to set limit: %w", err) + if (rcm.System.Streams > rcmgr.DefaultLimit || rcm.System.Streams == rcmgr.BlockAllLimit) && int64(rcm.System.Streams) <= highWater { + // nolint + return fmt.Errorf(` +Unable to initialize libp2p due to conflicting resource manager limit configuration. +resource manager System.Streams (%d) must be bigger than ConnMgr.HighWater (%d) +See: https://github.com/ipfs/kubo/blob/master/docs/libp2p-resource-management.md#how-does-the-resource-manager-resourcemgr-relate-to-the-connection-manager-connmgr +`, rcm.System.Streams, highWater) } - - if cfg.Swarm.ResourceMgr.Limits == nil { - cfg.Swarm.ResourceMgr.Limits = &rcmgr.LimitConfig{} + if (rcm.System.StreamsInbound > rcmgr.DefaultLimit || rcm.System.StreamsInbound == rcmgr.BlockAllLimit) && int64(rcm.System.StreamsInbound) <= highWater { + // nolint + return fmt.Errorf(` +Unable to initialize libp2p due to conflicting resource manager limit configuration. +resource manager System.StreamsInbound (%d) must be bigger than ConnMgr.HighWater (%d) +See: https://github.com/ipfs/kubo/blob/master/docs/libp2p-resource-management.md#how-does-the-resource-manager-resourcemgr-relate-to-the-connection-manager-connmgr +`, rcm.System.StreamsInbound, highWater) } - configLimits := cfg.Swarm.ResourceMgr.Limits - - var setConfigFunc func() - switch { - case scope == config.ResourceMgrSystemScope: - err = mgr.ViewSystem(func(s network.ResourceScope) error { return setLimit(s) }) - setConfigFunc = func() { configLimits.System = limit } - case scope == config.ResourceMgrTransientScope: - err = mgr.ViewTransient(func(s network.ResourceScope) error { return setLimit(s) }) - setConfigFunc = func() { configLimits.Transient = limit } - case strings.HasPrefix(scope, config.ResourceMgrServiceScopePrefix): - svc := strings.TrimPrefix(scope, config.ResourceMgrServiceScopePrefix) - err = mgr.ViewService(svc, func(s network.ServiceScope) error { return setLimit(s) }) - setConfigFunc = func() { - if configLimits.Service == nil { - configLimits.Service = map[string]rcmgr.BaseLimit{} - } - configLimits.Service[svc] = limit - } - case strings.HasPrefix(scope, config.ResourceMgrProtocolScopePrefix): - proto := strings.TrimPrefix(scope, config.ResourceMgrProtocolScopePrefix) - err = mgr.ViewProtocol(protocol.ID(proto), func(s network.ProtocolScope) error { return setLimit(s) }) - setConfigFunc = func() { - if configLimits.Protocol == nil { - configLimits.Protocol = map[protocol.ID]rcmgr.BaseLimit{} - } - configLimits.Protocol[protocol.ID(proto)] = limit - } - case strings.HasPrefix(scope, config.ResourceMgrPeerScopePrefix): - p := strings.TrimPrefix(scope, config.ResourceMgrPeerScopePrefix) - var pid peer.ID - pid, err = peer.Decode(p) - if err != nil { - return fmt.Errorf("invalid peer ID: %q: %w", p, err) - } - err = mgr.ViewPeer(pid, func(s network.PeerScope) error { return setLimit(s) }) - setConfigFunc = func() { - if configLimits.Peer == nil { - configLimits.Peer = map[peer.ID]rcmgr.BaseLimit{} - } - configLimits.Peer[pid] = limit - } - default: - return fmt.Errorf("invalid scope %q", scope) - } - - if err != nil { - return fmt.Errorf("setting new limits on resource manager: %w", err) - } - - if cfg.Swarm.ResourceMgr.Limits == nil { - cfg.Swarm.ResourceMgr.Limits = &rcmgr.LimitConfig{} - } - setConfigFunc() - - if err := repo.SetConfig(cfg); err != nil { - return fmt.Errorf("writing new limits to repo config: %w", err) - } - return nil } - -// NetResetLimit resets ResourceManager limits to defaults. The limits take effect immediately, and are also persisted to the repo config. -func NetResetLimit(mgr network.ResourceManager, repo repo.Repo, scope string) (rcmgr.BaseLimit, error) { - var result rcmgr.BaseLimit - - setLimit := func(s network.ResourceScope, l rcmgr.Limit) error { - limiter, ok := s.(rcmgr.ResourceScopeLimiter) - if !ok { - return ErrNoResourceMgr - } - - limiter.SetLimit(l) - return nil - } - - cfg, err := repo.Config() - if err != nil { - return result, fmt.Errorf("reading config to reset limit: %w", err) - } - - defaults, err := createDefaultLimitConfig(cfg.Swarm) - if err != nil { - return result, fmt.Errorf("creating default limit config: %w", err) - } - - if cfg.Swarm.ResourceMgr.Limits == nil { - cfg.Swarm.ResourceMgr.Limits = &rcmgr.LimitConfig{} - } - configLimits := cfg.Swarm.ResourceMgr.Limits - - var setConfigFunc func() rcmgr.BaseLimit - switch { - case scope == config.ResourceMgrSystemScope: - err = mgr.ViewSystem(func(s network.ResourceScope) error { return setLimit(s, &defaults.System) }) - setConfigFunc = func() rcmgr.BaseLimit { - configLimits.System = defaults.System - return defaults.System - } - case scope == config.ResourceMgrTransientScope: - err = mgr.ViewTransient(func(s network.ResourceScope) error { return setLimit(s, &defaults.Transient) }) - setConfigFunc = func() rcmgr.BaseLimit { - configLimits.Transient = defaults.Transient - return defaults.Transient - } - case strings.HasPrefix(scope, config.ResourceMgrServiceScopePrefix): - svc := strings.TrimPrefix(scope, config.ResourceMgrServiceScopePrefix) - - err = mgr.ViewService(svc, func(s network.ServiceScope) error { return setLimit(s, &defaults.ServiceDefault) }) - setConfigFunc = func() rcmgr.BaseLimit { - if configLimits.Service == nil { - configLimits.Service = map[string]rcmgr.BaseLimit{} - } - configLimits.Service[svc] = defaults.ServiceDefault - return defaults.ServiceDefault - } - case strings.HasPrefix(scope, config.ResourceMgrProtocolScopePrefix): - proto := strings.TrimPrefix(scope, config.ResourceMgrProtocolScopePrefix) - - err = mgr.ViewProtocol(protocol.ID(proto), func(s network.ProtocolScope) error { return setLimit(s, &defaults.ProtocolDefault) }) - setConfigFunc = func() rcmgr.BaseLimit { - if configLimits.Protocol == nil { - configLimits.Protocol = map[protocol.ID]rcmgr.BaseLimit{} - } - configLimits.Protocol[protocol.ID(proto)] = defaults.ProtocolDefault - - return defaults.ProtocolDefault - } - case strings.HasPrefix(scope, config.ResourceMgrPeerScopePrefix): - p := strings.TrimPrefix(scope, config.ResourceMgrPeerScopePrefix) - - var pid peer.ID - pid, err = peer.Decode(p) - if err != nil { - return result, fmt.Errorf("invalid peer ID: %q: %w", p, err) - } - - err = mgr.ViewPeer(pid, func(s network.PeerScope) error { return setLimit(s, &defaults.PeerDefault) }) - setConfigFunc = func() rcmgr.BaseLimit { - if configLimits.Peer == nil { - configLimits.Peer = map[peer.ID]rcmgr.BaseLimit{} - } - configLimits.Peer[pid] = defaults.PeerDefault - - return defaults.PeerDefault - } - default: - return result, fmt.Errorf("invalid scope %q", scope) - } - - if err != nil { - return result, fmt.Errorf("resetting new limits on resource manager: %w", err) - } - - result = setConfigFunc() - - if err := repo.SetConfig(cfg); err != nil { - return result, fmt.Errorf("writing new limits to repo config: %w", err) - } - - return result, nil -} diff --git a/core/node/libp2p/rcmgr_defaults.go b/core/node/libp2p/rcmgr_defaults.go index d3c294258..98fdccb99 100644 --- a/core/node/libp2p/rcmgr_defaults.go +++ b/core/node/libp2p/rcmgr_defaults.go @@ -4,187 +4,146 @@ import ( "fmt" "github.com/dustin/go-humanize" + "github.com/ipfs/kubo/config" + "github.com/ipfs/kubo/core/node/libp2p/fd" "github.com/libp2p/go-libp2p" rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager" "github.com/pbnjay/memory" - - "github.com/ipfs/kubo/config" - "github.com/ipfs/kubo/core/node/libp2p/fd" ) -// We are doing some magic when parsing config files (we are using a map[string]interface{} to compare config files). -// When you don't have a type the JSON Parse function cast numbers to float64 by default, -// losing precision when writing the final number. So if we use math.MaxInt as our infinite number, -// after writing the config file we will have 9223372036854776000 instead of 9223372036854775807, -// making the parsing process fail. Setting 1e9 (1000000000) as "no limit" value. It also avoids to overflow on 32 bit architectures. -const bigEnough = 1e9 - -var infiniteBaseLimit = rcmgr.BaseLimit{ - Streams: bigEnough, - StreamsInbound: bigEnough, - StreamsOutbound: bigEnough, - Conns: bigEnough, - ConnsInbound: bigEnough, - ConnsOutbound: bigEnough, - FD: bigEnough, - Memory: bigEnough, -} - -var noLimitIncrease = rcmgr.BaseLimitIncrease{ - ConnsInbound: 0, - ConnsOutbound: 0, - Conns: 0, - StreamsInbound: 0, - StreamsOutbound: 0, - Streams: 0, - Memory: 0, - FDFraction: 0, -} +var infiniteResourceLimits = rcmgr.InfiniteLimits.ToPartialLimitConfig().System // This file defines implicit limit defaults used when Swarm.ResourceMgr.Enabled // createDefaultLimitConfig creates LimitConfig to pass to libp2p's resource manager. -// The defaults follow the documentation in docs/config.md. +// The defaults follow the documentation in docs/libp2p-resource-management.md. // Any changes in the logic here should be reflected there. -func createDefaultLimitConfig(cfg config.SwarmConfig) (rcmgr.LimitConfig, error) { - maxMemoryDefaultString := humanize.Bytes(uint64(memory.TotalMemory()) / 4) +func createDefaultLimitConfig(cfg config.SwarmConfig) (limitConfig rcmgr.ConcreteLimitConfig, logMessageForStartup string, err error) { + maxMemoryDefaultString := humanize.Bytes(uint64(memory.TotalMemory()) / 2) maxMemoryString := cfg.ResourceMgr.MaxMemory.WithDefault(maxMemoryDefaultString) maxMemory, err := humanize.ParseBytes(maxMemoryString) if err != nil { - return rcmgr.LimitConfig{}, err + return rcmgr.ConcreteLimitConfig{}, "", err } - numFD := cfg.ResourceMgr.MaxFileDescriptors.WithDefault(int64(fd.GetNumFDs()) / 2) + maxMemoryMB := maxMemory / (1024 * 1024) + maxFD := int(cfg.ResourceMgr.MaxFileDescriptors.WithDefault(int64(fd.GetNumFDs()) / 2)) - // We want to see this message on startup, that's why we are using fmt instead of log. - fmt.Printf(` -Computing default go-libp2p Resource Manager limits based on: - - 'Swarm.ResourceMgr.MaxMemory': %q - - 'Swarm.ResourceMgr.MaxFileDescriptors': %d + // At least as of 2023-01-25, it's possible to open a connection that + // doesn't ask for any memory usage with the libp2p Resource Manager/Accountant + // (see https://github.com/libp2p/go-libp2p/issues/2010#issuecomment-1404280736). + // As a result, we can't currently rely on Memory limits to full protect us. + // Until https://github.com/libp2p/go-libp2p/issues/2010 is addressed, + // we take a proxy now of restricting to 1 inbound connection per MB. + // Note: this is more generous than go-libp2p's default autoscaled limits which do + // 64 connections per 1GB + // (see https://github.com/libp2p/go-libp2p/blob/master/p2p/host/resource-manager/limit_defaults.go#L357 ). + systemConnsInbound := int(1 * maxMemoryMB) -Applying any user-supplied overrides on top. -Run 'ipfs swarm limit all' to see the resulting limits. + partialLimits := rcmgr.PartialLimitConfig{ + System: rcmgr.ResourceLimits{ + Memory: rcmgr.LimitVal64(maxMemory), + FD: rcmgr.LimitVal(maxFD), -`, maxMemoryString, numFD) + Conns: rcmgr.Unlimited, + ConnsInbound: rcmgr.LimitVal(systemConnsInbound), + ConnsOutbound: rcmgr.Unlimited, - scalingLimitConfig := rcmgr.ScalingLimitConfig{ - SystemBaseLimit: rcmgr.BaseLimit{ - Memory: int64(maxMemory), - FD: int(numFD), - - // By default, we just limit connections on the inbound side. - Conns: bigEnough, - ConnsInbound: rcmgr.DefaultLimits.SystemBaseLimit.ConnsInbound, // same as libp2p default - ConnsOutbound: bigEnough, - - // We limit streams since they not only take up memory and CPU. - // The Memory limit protects us on the memory side, - // but a StreamsInbound limit helps protect against unbound CPU consumption from stream processing. - Streams: bigEnough, - StreamsInbound: rcmgr.DefaultLimits.SystemBaseLimit.StreamsInbound, - StreamsOutbound: bigEnough, - }, - // Most limits don't see an increase because they're already infinite/bigEnough or at their max value. - // The values that should scale based on the amount of memory allocated to libp2p need to increase accordingly. - SystemLimitIncrease: rcmgr.BaseLimitIncrease{ - Memory: 0, - FDFraction: 0, - - Conns: 0, - ConnsInbound: rcmgr.DefaultLimits.SystemLimitIncrease.ConnsInbound, - ConnsOutbound: 0, - - Streams: 0, - StreamsInbound: rcmgr.DefaultLimits.SystemLimitIncrease.StreamsInbound, - StreamsOutbound: 0, + Streams: rcmgr.Unlimited, + StreamsOutbound: rcmgr.Unlimited, + StreamsInbound: rcmgr.Unlimited, }, - TransientBaseLimit: rcmgr.BaseLimit{ - Memory: rcmgr.DefaultLimits.TransientBaseLimit.Memory, - FD: rcmgr.DefaultLimits.TransientBaseLimit.FD, + // Transient connections won't cause any memory to be accounted for by the resource manager/accountant. + // Only established connections do. + // As a result, we can't rely on System.Memory to protect us from a bunch of transient connection being opened. + // We limit the same values as the System scope, but only allow the Transient scope to take 25% of what is allowed for the System scope. + Transient: rcmgr.ResourceLimits{ + Memory: rcmgr.LimitVal64(maxMemory / 4), + FD: rcmgr.LimitVal(maxFD / 4), - Conns: bigEnough, - ConnsInbound: rcmgr.DefaultLimits.TransientBaseLimit.ConnsInbound, - ConnsOutbound: bigEnough, + Conns: rcmgr.Unlimited, + ConnsInbound: rcmgr.LimitVal(systemConnsInbound / 4), + ConnsOutbound: rcmgr.Unlimited, - Streams: bigEnough, - StreamsInbound: rcmgr.DefaultLimits.TransientBaseLimit.StreamsInbound, - StreamsOutbound: bigEnough, - }, - - TransientLimitIncrease: rcmgr.BaseLimitIncrease{ - Memory: rcmgr.DefaultLimits.TransientLimitIncrease.Memory, - FDFraction: rcmgr.DefaultLimits.TransientLimitIncrease.FDFraction, - - Conns: 0, - ConnsInbound: rcmgr.DefaultLimits.TransientLimitIncrease.ConnsInbound, - ConnsOutbound: 0, - - Streams: 0, - StreamsInbound: rcmgr.DefaultLimits.TransientLimitIncrease.StreamsInbound, - StreamsOutbound: 0, + Streams: rcmgr.Unlimited, + StreamsInbound: rcmgr.Unlimited, + StreamsOutbound: rcmgr.Unlimited, }, // Lets get out of the way of the allow list functionality. // If someone specified "Swarm.ResourceMgr.Allowlist" we should let it go through. - AllowlistedSystemBaseLimit: infiniteBaseLimit, - AllowlistedSystemLimitIncrease: noLimitIncrease, + AllowlistedSystem: infiniteResourceLimits, - AllowlistedTransientBaseLimit: infiniteBaseLimit, - AllowlistedTransientLimitIncrease: noLimitIncrease, + AllowlistedTransient: infiniteResourceLimits, // Keep it simple by not having Service, ServicePeer, Protocol, ProtocolPeer, Conn, or Stream limits. - ServiceBaseLimit: infiniteBaseLimit, - ServiceLimitIncrease: noLimitIncrease, + ServiceDefault: infiniteResourceLimits, - ServicePeerBaseLimit: infiniteBaseLimit, - ServicePeerLimitIncrease: noLimitIncrease, + ServicePeerDefault: infiniteResourceLimits, - ProtocolBaseLimit: infiniteBaseLimit, - ProtocolLimitIncrease: noLimitIncrease, + ProtocolDefault: infiniteResourceLimits, - ProtocolPeerBaseLimit: infiniteBaseLimit, - ProtocolPeerLimitIncrease: noLimitIncrease, + ProtocolPeerDefault: infiniteResourceLimits, - ConnBaseLimit: infiniteBaseLimit, - ConnLimitIncrease: noLimitIncrease, + Conn: infiniteResourceLimits, - StreamBaseLimit: infiniteBaseLimit, - StreamLimitIncrease: noLimitIncrease, + Stream: infiniteResourceLimits, // Limit the resources consumed by a peer. // This doesn't protect us against intentional DoS attacks since an attacker can easily spin up multiple peers. // We specify this limit against unintentional DoS attacks (e.g., a peer has a bug and is sending too much traffic intentionally). // In that case we want to keep that peer's resource consumption contained. // To keep this simple, we only constrain inbound connections and streams. - PeerBaseLimit: rcmgr.BaseLimit{ - Memory: bigEnough, - FD: bigEnough, - Conns: bigEnough, - ConnsInbound: rcmgr.DefaultLimits.PeerBaseLimit.ConnsInbound, - ConnsOutbound: bigEnough, - Streams: bigEnough, - StreamsInbound: rcmgr.DefaultLimits.PeerBaseLimit.StreamsInbound, - StreamsOutbound: bigEnough, - }, - // Most limits don't see an increase because they're already infinite/bigEnough. - // The values that should scale based on the amount of memory allocated to libp2p need to increase accordingly. - PeerLimitIncrease: rcmgr.BaseLimitIncrease{ - Memory: 0, - FDFraction: 0, - Conns: 0, - ConnsInbound: rcmgr.DefaultLimits.PeerLimitIncrease.ConnsInbound, - ConnsOutbound: 0, - Streams: 0, - StreamsInbound: rcmgr.DefaultLimits.PeerLimitIncrease.StreamsInbound, - StreamsOutbound: 0, + PeerDefault: rcmgr.ResourceLimits{ + Memory: rcmgr.Unlimited64, + FD: rcmgr.Unlimited, + Conns: rcmgr.Unlimited, + ConnsInbound: rcmgr.DefaultLimit, + ConnsOutbound: rcmgr.Unlimited, + Streams: rcmgr.Unlimited, + StreamsInbound: rcmgr.DefaultLimit, + StreamsOutbound: rcmgr.Unlimited, }, } - // Whatever limits libp2p has specifically tuned for its protocols/services we'll apply. + scalingLimitConfig := rcmgr.DefaultLimits libp2p.SetDefaultServiceLimits(&scalingLimitConfig) - defaultLimitConfig := scalingLimitConfig.Scale(int64(maxMemory), int(numFD)) + // Anything set above in partialLimits that had a value of rcmgr.DefaultLimit will be overridden. + // Anything in scalingLimitConfig that wasn't defined in partialLimits above will be added (e.g., libp2p's default service limits). + partialLimits = partialLimits.Build(scalingLimitConfig.Scale(int64(maxMemory), maxFD)).ToPartialLimitConfig() - return defaultLimitConfig, nil + // Simple checks to override autoscaling ensuring limits make sense versus the connmgr values. + // There are ways to break this, but this should catch most problems already. + // We might improve this in the future. + // See: https://github.com/ipfs/kubo/issues/9545 + if partialLimits.System.ConnsInbound > rcmgr.DefaultLimit && cfg.ConnMgr.Type.WithDefault(config.DefaultConnMgrType) != "none" { + maxInboundConns := int64(partialLimits.System.ConnsInbound) + if connmgrHighWaterTimesTwo := cfg.ConnMgr.HighWater.WithDefault(config.DefaultConnMgrHighWater) * 2; maxInboundConns < connmgrHighWaterTimesTwo { + maxInboundConns = connmgrHighWaterTimesTwo + } + + if maxInboundConns < config.DefaultResourceMgrMinInboundConns { + maxInboundConns = config.DefaultResourceMgrMinInboundConns + } + + // Scale System.StreamsInbound as well, but use the existing ratio of StreamsInbound to ConnsInbound + if partialLimits.System.StreamsInbound > rcmgr.DefaultLimit { + partialLimits.System.StreamsInbound = rcmgr.LimitVal(maxInboundConns * int64(partialLimits.System.StreamsInbound) / int64(partialLimits.System.ConnsInbound)) + } + partialLimits.System.ConnsInbound = rcmgr.LimitVal(maxInboundConns) + } + + msg := fmt.Sprintf(` +Computed default go-libp2p Resource Manager limits based on: + - 'Swarm.ResourceMgr.MaxMemory': %q + - 'Swarm.ResourceMgr.MaxFileDescriptors': %d + +These can be inspected with 'ipfs swarm resources'. + +`, maxMemoryString, maxFD) + + // We already have a complete value thus pass in an empty ConcreteLimitConfig. + return partialLimits.Build(rcmgr.ConcreteLimitConfig{}), msg, nil } diff --git a/core/node/libp2p/rcmgr_logging.go b/core/node/libp2p/rcmgr_logging.go index 978222bfe..56e017b82 100644 --- a/core/node/libp2p/rcmgr_logging.go +++ b/core/node/libp2p/rcmgr_logging.go @@ -31,8 +31,10 @@ type loggingScope struct { countErrs func(error) } -var _ network.ResourceManager = (*loggingResourceManager)(nil) -var _ rcmgr.ResourceManagerState = (*loggingResourceManager)(nil) +var ( + _ network.ResourceManager = (*loggingResourceManager)(nil) + _ rcmgr.ResourceManagerState = (*loggingResourceManager)(nil) +) func (n *loggingResourceManager) start(ctx context.Context) { logInterval := n.logInterval @@ -85,36 +87,43 @@ func (n *loggingResourceManager) countErrs(err error) { func (n *loggingResourceManager) ViewSystem(f func(network.ResourceScope) error) error { return n.delegate.ViewSystem(f) } + func (n *loggingResourceManager) ViewTransient(f func(network.ResourceScope) error) error { return n.delegate.ViewTransient(func(s network.ResourceScope) error { return f(&loggingScope{logger: n.logger, delegate: s, countErrs: n.countErrs}) }) } + func (n *loggingResourceManager) ViewService(svc string, f func(network.ServiceScope) error) error { return n.delegate.ViewService(svc, func(s network.ServiceScope) error { return f(&loggingScope{logger: n.logger, delegate: s, countErrs: n.countErrs}) }) } + func (n *loggingResourceManager) ViewProtocol(p protocol.ID, f func(network.ProtocolScope) error) error { return n.delegate.ViewProtocol(p, func(s network.ProtocolScope) error { return f(&loggingScope{logger: n.logger, delegate: s, countErrs: n.countErrs}) }) } + func (n *loggingResourceManager) ViewPeer(p peer.ID, f func(network.PeerScope) error) error { return n.delegate.ViewPeer(p, func(s network.PeerScope) error { return f(&loggingScope{logger: n.logger, delegate: s, countErrs: n.countErrs}) }) } + func (n *loggingResourceManager) OpenConnection(dir network.Direction, usefd bool, remote ma.Multiaddr) (network.ConnManagementScope, error) { connMgmtScope, err := n.delegate.OpenConnection(dir, usefd, remote) n.countErrs(err) return connMgmtScope, err } + func (n *loggingResourceManager) OpenStream(p peer.ID, dir network.Direction) (network.StreamManagementScope, error) { connMgmtScope, err := n.delegate.OpenStream(p, dir) n.countErrs(err) return connMgmtScope, err } + func (n *loggingResourceManager) Close() error { return n.delegate.Close() } @@ -127,6 +136,7 @@ func (n *loggingResourceManager) ListServices() []string { return rapi.ListServices() } + func (n *loggingResourceManager) ListProtocols() []protocol.ID { rapi, ok := n.delegate.(rcmgr.ResourceManagerState) if !ok { @@ -135,6 +145,7 @@ func (n *loggingResourceManager) ListProtocols() []protocol.ID { return rapi.ListProtocols() } + func (n *loggingResourceManager) ListPeers() []peer.ID { rapi, ok := n.delegate.(rcmgr.ResourceManagerState) if !ok { @@ -158,54 +169,69 @@ func (s *loggingScope) ReserveMemory(size int, prio uint8) error { s.countErrs(err) return err } + func (s *loggingScope) ReleaseMemory(size int) { s.delegate.ReleaseMemory(size) } + func (s *loggingScope) Stat() network.ScopeStat { return s.delegate.Stat() } + func (s *loggingScope) BeginSpan() (network.ResourceScopeSpan, error) { return s.delegate.BeginSpan() } + func (s *loggingScope) Done() { s.delegate.(network.ResourceScopeSpan).Done() } + func (s *loggingScope) Name() string { return s.delegate.(network.ServiceScope).Name() } + func (s *loggingScope) Protocol() protocol.ID { return s.delegate.(network.ProtocolScope).Protocol() } + func (s *loggingScope) Peer() peer.ID { return s.delegate.(network.PeerScope).Peer() } + func (s *loggingScope) PeerScope() network.PeerScope { return s.delegate.(network.PeerScope) } + func (s *loggingScope) SetPeer(p peer.ID) error { err := s.delegate.(network.ConnManagementScope).SetPeer(p) s.countErrs(err) return err } + func (s *loggingScope) ProtocolScope() network.ProtocolScope { return s.delegate.(network.ProtocolScope) } + func (s *loggingScope) SetProtocol(proto protocol.ID) error { err := s.delegate.(network.StreamManagementScope).SetProtocol(proto) s.countErrs(err) return err } + func (s *loggingScope) ServiceScope() network.ServiceScope { return s.delegate.(network.ServiceScope) } + func (s *loggingScope) SetService(srv string) error { err := s.delegate.(network.StreamManagementScope).SetService(srv) s.countErrs(err) return err } + func (s *loggingScope) Limit() rcmgr.Limit { return s.delegate.(rcmgr.ResourceScopeLimiter).Limit() } + func (s *loggingScope) SetLimit(limit rcmgr.Limit) { s.delegate.(rcmgr.ResourceScopeLimiter).SetLimit(limit) } diff --git a/core/node/libp2p/rcmgr_logging_test.go b/core/node/libp2p/rcmgr_logging_test.go index 512168d4a..559a3fec3 100644 --- a/core/node/libp2p/rcmgr_logging_test.go +++ b/core/node/libp2p/rcmgr_logging_test.go @@ -16,11 +16,12 @@ import ( func TestLoggingResourceManager(t *testing.T) { clock := clock.NewMock() - limits := rcmgr.DefaultLimits.AutoScale() + orig := rcmgr.DefaultLimits.AutoScale() + limits := orig.ToPartialLimitConfig() limits.System.Conns = 1 limits.System.ConnsInbound = 1 limits.System.ConnsOutbound = 1 - limiter := rcmgr.NewFixedLimiter(limits) + limiter := rcmgr.NewFixedLimiter(limits.Build(orig)) rm, err := rcmgr.NewResourceManager(limiter) if err != nil { t.Fatal(err) diff --git a/core/node/libp2p/rcmgr_test.go b/core/node/libp2p/rcmgr_test.go deleted file mode 100644 index e273ff756..000000000 --- a/core/node/libp2p/rcmgr_test.go +++ /dev/null @@ -1,12 +0,0 @@ -package libp2p - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestPercentage(t *testing.T) { - require.True(t, abovePercentage(10, 100, 10)) - require.True(t, abovePercentage(100, 100, 99)) -} diff --git a/core/node/libp2p/relay.go b/core/node/libp2p/relay.go index c39b2fae8..dd56835fb 100644 --- a/core/node/libp2p/relay.go +++ b/core/node/libp2p/relay.go @@ -33,13 +33,12 @@ func RelayService(enable bool, relayOpts config.RelayService) func() (opts Libp2 Data: relayOpts.ConnectionDataLimit.WithDefault(def.Limit.Data), Duration: relayOpts.ConnectionDurationLimit.WithDefault(def.Limit.Duration), }, - MaxCircuits: int(relayOpts.MaxCircuits.WithDefault(int64(def.MaxCircuits))), - BufferSize: int(relayOpts.BufferSize.WithDefault(int64(def.BufferSize))), - ReservationTTL: relayOpts.ReservationTTL.WithDefault(def.ReservationTTL), - MaxReservations: int(relayOpts.MaxReservations.WithDefault(int64(def.MaxReservations))), - MaxReservationsPerIP: int(relayOpts.MaxReservations.WithDefault(int64(def.MaxReservationsPerIP))), - MaxReservationsPerPeer: int(relayOpts.MaxReservations.WithDefault(int64(def.MaxReservationsPerPeer))), - MaxReservationsPerASN: int(relayOpts.MaxReservations.WithDefault(int64(def.MaxReservationsPerASN))), + MaxCircuits: int(relayOpts.MaxCircuits.WithDefault(int64(def.MaxCircuits))), + BufferSize: int(relayOpts.BufferSize.WithDefault(int64(def.BufferSize))), + ReservationTTL: relayOpts.ReservationTTL.WithDefault(def.ReservationTTL), + MaxReservations: int(relayOpts.MaxReservations.WithDefault(int64(def.MaxReservations))), + MaxReservationsPerIP: int(relayOpts.MaxReservationsPerIP.WithDefault(int64(def.MaxReservationsPerIP))), + MaxReservationsPerASN: int(relayOpts.MaxReservationsPerASN.WithDefault(int64(def.MaxReservationsPerASN))), }))) } return @@ -63,10 +62,7 @@ func MaybeAutoRelay(staticRelays []string, cfgPeering config.Peering, enabled bo } static = append(static, *addr) } - opts.Opts = append(opts.Opts, libp2p.EnableAutoRelay( - autorelay.WithStaticRelays(static), - autorelay.WithCircuitV1Support(), - )) + opts.Opts = append(opts.Opts, libp2p.EnableAutoRelayWithStaticRelays(static)) } return }) @@ -76,29 +72,33 @@ func MaybeAutoRelay(staticRelays []string, cfgPeering config.Peering, enabled bo return fx.Options( // Provide AutoRelay option fx.Provide(func() (opts Libp2pOpts, err error) { - opts.Opts = append(opts.Opts, libp2p.EnableAutoRelay(autorelay.WithPeerSource(func(ctx context.Context, numPeers int) <-chan peer.AddrInfo { - // TODO(9257): make this code smarter (have a state and actually try to grow the search outward) instead of a long running task just polling our K cluster. - r := make(chan peer.AddrInfo) - go func() { - defer close(r) - for ; numPeers != 0; numPeers-- { - select { - case v, ok := <-peerChan: - if !ok { - return + opts.Opts = append(opts.Opts, + libp2p.EnableAutoRelayWithPeerSource( + func(ctx context.Context, numPeers int) <-chan peer.AddrInfo { + // TODO(9257): make this code smarter (have a state and actually try to grow the search outward) instead of a long running task just polling our K cluster. + r := make(chan peer.AddrInfo) + go func() { + defer close(r) + for ; numPeers != 0; numPeers-- { + select { + case v, ok := <-peerChan: + if !ok { + return + } + select { + case r <- v: + case <-ctx.Done(): + return + } + case <-ctx.Done(): + return + } } - select { - case r <- v: - case <-ctx.Done(): - return - } - case <-ctx.Done(): - return - } - } - }() - return r - }, 0))) + }() + return r + }, + autorelay.WithMinInterval(0), + )) return }), autoRelayFeeder(cfgPeering, peerChan), diff --git a/core/node/libp2p/routing.go b/core/node/libp2p/routing.go index 21afe292d..697bf0f2e 100644 --- a/core/node/libp2p/routing.go +++ b/core/node/libp2p/routing.go @@ -7,13 +7,9 @@ import ( "sort" "time" - "github.com/ipfs/kubo/core/node/helpers" - irouting "github.com/ipfs/kubo/routing" - + "github.com/cenkalti/backoff/v4" + offroute "github.com/ipfs/boxo/routing/offline" ds "github.com/ipfs/go-datastore" - offroute "github.com/ipfs/go-ipfs-routing/offline" - config "github.com/ipfs/kubo/config" - "github.com/ipfs/kubo/repo" dht "github.com/libp2p/go-libp2p-kad-dht" ddht "github.com/libp2p/go-libp2p-kad-dht/dual" "github.com/libp2p/go-libp2p-kad-dht/fullrt" @@ -24,9 +20,12 @@ import ( "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/routing" - - "github.com/cenkalti/backoff/v4" "go.uber.org/fx" + + config "github.com/ipfs/kubo/config" + "github.com/ipfs/kubo/core/node/helpers" + "github.com/ipfs/kubo/repo" + irouting "github.com/ipfs/kubo/routing" ) type Router struct { @@ -64,20 +63,34 @@ type processInitialRoutingOut struct { type AddrInfoChan chan peer.AddrInfo -func BaseRouting(experimentalDHTClient bool) interface{} { +func BaseRouting(cfg *config.Config) interface{} { return func(lc fx.Lifecycle, in processInitialRoutingIn) (out processInitialRoutingOut, err error) { - var dr *ddht.DHT + var dualDHT *ddht.DHT if dht, ok := in.Router.(*ddht.DHT); ok { - dr = dht + dualDHT = dht lc.Append(fx.Hook{ OnStop: func(ctx context.Context) error { - return dr.Close() + return dualDHT.Close() }, }) } - if dr != nil && experimentalDHTClient { + if cr, ok := in.Router.(routinghelpers.ComposableRouter); ok { + for _, r := range cr.Routers() { + if dht, ok := r.(*ddht.DHT); ok { + dualDHT = dht + lc.Append(fx.Hook{ + OnStop: func(ctx context.Context) error { + return dualDHT.Close() + }, + }) + break + } + } + } + + if dualDHT != nil && cfg.Routing.AcceleratedDHTClient.WithDefault(config.DefaultAcceleratedDHTClient) { cfg, err := in.Repo.Config() if err != nil { return out, err @@ -87,7 +100,7 @@ func BaseRouting(experimentalDHTClient bool) interface{} { return out, err } - expClient, err := fullrt.NewFullRT(in.Host, + fullRTClient, err := fullrt.NewFullRT(in.Host, dht.DefaultPrefix, fullrt.DHTOption( dht.Validator(in.Validator), @@ -102,18 +115,30 @@ func BaseRouting(experimentalDHTClient bool) interface{} { lc.Append(fx.Hook{ OnStop: func(ctx context.Context) error { - return expClient.Close() + return fullRTClient.Close() }, }) + // we want to also use the default HTTP routers, so wrap the FullRT client + // in a parallel router that calls them in parallel + httpRouters, err := constructDefaultHTTPRouters(cfg) + if err != nil { + return out, err + } + routers := []*routinghelpers.ParallelRouter{ + {Router: fullRTClient, DoNotWaitForSearchValue: true}, + } + routers = append(routers, httpRouters...) + router := routinghelpers.NewComposableParallel(routers) + return processInitialRoutingOut{ Router: Router{ - Routing: expClient, Priority: 1000, + Routing: router, }, - DHT: dr, - DHTClient: expClient, - ContentRouter: expClient, + DHT: dualDHT, + DHTClient: fullRTClient, + ContentRouter: fullRTClient, }, nil } @@ -122,8 +147,8 @@ func BaseRouting(experimentalDHTClient bool) interface{} { Priority: 1000, Routing: in.Router, }, - DHT: dr, - DHTClient: dr, + DHT: dualDHT, + DHTClient: dualDHT, ContentRouter: in.Router, }, nil } @@ -172,9 +197,9 @@ func Routing(in p2pOnlineRoutingIn) irouting.ProvideManyRouter { var cRouters []*routinghelpers.ParallelRouter for _, v := range routers { cRouters = append(cRouters, &routinghelpers.ParallelRouter{ - Timeout: 5 * time.Minute, - IgnoreError: true, - Router: v.Routing, + IgnoreError: true, + DoNotWaitForSearchValue: true, + Router: v.Routing, }) } @@ -207,7 +232,6 @@ func PubsubRouter(mctx helpers.MetricsCtx, lc fx.Lifecycle, in p2pPSRoutingIn) ( in.Validator, namesys.WithRebroadcastInterval(time.Minute), ) - if err != nil { return p2pRouterOut{}, nil, err } diff --git a/core/node/libp2p/routingopt.go b/core/node/libp2p/routingopt.go index 2d2b7570c..292f49fe4 100644 --- a/core/node/libp2p/routingopt.go +++ b/core/node/libp2p/routingopt.go @@ -16,135 +16,129 @@ import ( routing "github.com/libp2p/go-libp2p/core/routing" ) -type RoutingOption func( - context.Context, - host.Host, - datastore.Batching, - record.Validator, - ...peer.AddrInfo, -) (routing.Routing, error) +type RoutingOptionArgs struct { + Ctx context.Context + Host host.Host + Datastore datastore.Batching + Validator record.Validator + BootstrapPeers []peer.AddrInfo + OptimisticProvide bool + OptimisticProvideJobsPoolSize int + LoopbackAddressesOnLanDHT bool +} -// Default HTTP routers used in parallel to DHT when Routing.Type = "auto" -var defaultHTTPRouters = []string{ - "https://cid.contact", // https://github.com/ipfs/kubo/issues/9422#issuecomment-1338142084 - // TODO: add an independent router from Cloudflare +type RoutingOption func(args RoutingOptionArgs) (routing.Routing, error) + +func constructDefaultHTTPRouters(cfg *config.Config) ([]*routinghelpers.ParallelRouter, error) { + var routers []*routinghelpers.ParallelRouter + // Append HTTP routers for additional speed + for _, endpoint := range config.DefaultHTTPRouters { + httpRouter, err := irouting.ConstructHTTPRouter(endpoint, cfg.Identity.PeerID, httpAddrsFromConfig(cfg.Addresses), cfg.Identity.PrivKey) + if err != nil { + return nil, err + } + + r := &irouting.Composer{ + GetValueRouter: routinghelpers.Null{}, + PutValueRouter: routinghelpers.Null{}, + ProvideRouter: routinghelpers.Null{}, // modify this when indexers supports provide + FindPeersRouter: routinghelpers.Null{}, + FindProvidersRouter: httpRouter, + } + + routers = append(routers, &routinghelpers.ParallelRouter{ + Router: r, + IgnoreError: true, // https://github.com/ipfs/kubo/pull/9475#discussion_r1042507387 + Timeout: 15 * time.Second, // 5x server value from https://github.com/ipfs/kubo/pull/9475#discussion_r1042428529 + DoNotWaitForSearchValue: true, + ExecuteAfter: 0, + }) + } + return routers, nil } // ConstructDefaultRouting returns routers used when Routing.Type is unset or set to "auto" -func ConstructDefaultRouting(peerID string, addrs []string, privKey string) func( - ctx context.Context, - host host.Host, - dstore datastore.Batching, - validator record.Validator, - bootstrapPeers ...peer.AddrInfo, -) (routing.Routing, error) { - return func( - ctx context.Context, - host host.Host, - dstore datastore.Batching, - validator record.Validator, - bootstrapPeers ...peer.AddrInfo, - ) (routing.Routing, error) { +func ConstructDefaultRouting(cfg *config.Config, routingOpt RoutingOption) RoutingOption { + return func(args RoutingOptionArgs) (routing.Routing, error) { // Defined routers will be queried in parallel (optimizing for response speed) // Different trade-offs can be made by setting Routing.Type = "custom" with own Routing.Routers var routers []*routinghelpers.ParallelRouter - // Run the default DHT routing (same as Routing.Type = "dht") - dhtRouting, err := DHTOption(ctx, host, dstore, validator, bootstrapPeers...) + dhtRouting, err := routingOpt(args) if err != nil { return nil, err } routers = append(routers, &routinghelpers.ParallelRouter{ - Router: dhtRouting, - IgnoreError: false, - Timeout: 5 * time.Minute, // https://github.com/ipfs/kubo/pull/9475#discussion_r1042501333 - ExecuteAfter: 0, + Router: dhtRouting, + IgnoreError: false, + DoNotWaitForSearchValue: true, + ExecuteAfter: 0, }) - // Append HTTP routers for additional speed - for _, endpoint := range defaultHTTPRouters { - httpRouter, err := irouting.ConstructHTTPRouter(endpoint, peerID, addrs, privKey) - if err != nil { - return nil, err - } - routers = append(routers, &routinghelpers.ParallelRouter{ - Router: httpRouter, - IgnoreError: true, // https://github.com/ipfs/kubo/pull/9475#discussion_r1042507387 - Timeout: 15 * time.Second, // 5x server value from https://github.com/ipfs/kubo/pull/9475#discussion_r1042428529 - ExecuteAfter: 0, - }) + httpRouters, err := constructDefaultHTTPRouters(cfg) + if err != nil { + return nil, err } + routers = append(routers, httpRouters...) + routing := routinghelpers.NewComposableParallel(routers) return routing, nil } } // constructDHTRouting is used when Routing.Type = "dht" -func constructDHTRouting(mode dht.ModeOpt) func( - ctx context.Context, - host host.Host, - dstore datastore.Batching, - validator record.Validator, - bootstrapPeers ...peer.AddrInfo, -) (routing.Routing, error) { - return func( - ctx context.Context, - host host.Host, - dstore datastore.Batching, - validator record.Validator, - bootstrapPeers ...peer.AddrInfo, - ) (routing.Routing, error) { +func constructDHTRouting(mode dht.ModeOpt) RoutingOption { + return func(args RoutingOptionArgs) (routing.Routing, error) { + dhtOpts := []dht.Option{ + dht.Concurrency(10), + dht.Mode(mode), + dht.Datastore(args.Datastore), + dht.Validator(args.Validator), + } + if args.OptimisticProvide { + dhtOpts = append(dhtOpts, dht.EnableOptimisticProvide()) + } + if args.OptimisticProvideJobsPoolSize != 0 { + dhtOpts = append(dhtOpts, dht.OptimisticProvideJobsPoolSize(args.OptimisticProvideJobsPoolSize)) + } + wanOptions := []dht.Option{ + dht.BootstrapPeers(args.BootstrapPeers...), + } + lanOptions := []dht.Option{} + if args.LoopbackAddressesOnLanDHT { + lanOptions = append(lanOptions, dht.AddressFilter(nil)) + } return dual.New( - ctx, host, - dual.DHTOption( - dht.Concurrency(10), - dht.Mode(mode), - dht.Datastore(dstore), - dht.Validator(validator)), - dual.WanDHTOption(dht.BootstrapPeers(bootstrapPeers...)), + args.Ctx, args.Host, + dual.DHTOption(dhtOpts...), + dual.WanDHTOption(wanOptions...), + dual.LanDHTOption(lanOptions...), ) } } // ConstructDelegatedRouting is used when Routing.Type = "custom" -func ConstructDelegatedRouting(routers config.Routers, methods config.Methods, peerID string, addrs []string, privKey string) func( - ctx context.Context, - host host.Host, - dstore datastore.Batching, - validator record.Validator, - bootstrapPeers ...peer.AddrInfo, -) (routing.Routing, error) { - return func( - ctx context.Context, - host host.Host, - dstore datastore.Batching, - validator record.Validator, - bootstrapPeers ...peer.AddrInfo, - ) (routing.Routing, error) { +func ConstructDelegatedRouting(routers config.Routers, methods config.Methods, peerID string, addrs config.Addresses, privKey string) RoutingOption { + return func(args RoutingOptionArgs) (routing.Routing, error) { return irouting.Parse(routers, methods, &irouting.ExtraDHTParams{ - BootstrapPeers: bootstrapPeers, - Host: host, - Validator: validator, - Datastore: dstore, - Context: ctx, + BootstrapPeers: args.BootstrapPeers, + Host: args.Host, + Validator: args.Validator, + Datastore: args.Datastore, + Context: args.Ctx, }, &irouting.ExtraHTTPParams{ PeerID: peerID, - Addrs: addrs, + Addrs: httpAddrsFromConfig(addrs), PrivKeyB64: privKey, - }) + }, + ) } } -func constructNilRouting( - ctx context.Context, - host host.Host, - dstore datastore.Batching, - validator record.Validator, - bootstrapPeers ...peer.AddrInfo, -) (routing.Routing, error) { +func constructNilRouting(_ RoutingOptionArgs) (routing.Routing, error) { return routinghelpers.Null{}, nil } @@ -154,3 +148,31 @@ var ( DHTServerOption = constructDHTRouting(dht.ModeServer) NilRouterOption = constructNilRouting ) + +// httpAddrsFromConfig creates a list of addresses from the provided configuration to be used by HTTP delegated routers. +func httpAddrsFromConfig(cfgAddrs config.Addresses) []string { + // Swarm addrs are announced by default + addrs := cfgAddrs.Swarm + // if Announce addrs are specified - override Swarm + if len(cfgAddrs.Announce) > 0 { + addrs = cfgAddrs.Announce + } else if len(cfgAddrs.NoAnnounce) > 0 { + // if Announce adds are not specified - filter Swarm addrs with NoAnnounce list + maddrs := map[string]struct{}{} + for _, addr := range addrs { + maddrs[addr] = struct{}{} + } + for _, addr := range cfgAddrs.NoAnnounce { + delete(maddrs, addr) + } + addrs = make([]string, 0, len(maddrs)) + for k := range maddrs { + addrs = append(addrs, k) + } + } + // append AppendAnnounce addrs to the result list + if len(cfgAddrs.AppendAnnounce) > 0 { + addrs = append(addrs, cfgAddrs.AppendAnnounce...) + } + return addrs +} diff --git a/core/node/libp2p/routingopt_test.go b/core/node/libp2p/routingopt_test.go new file mode 100644 index 000000000..801fc0344 --- /dev/null +++ b/core/node/libp2p/routingopt_test.go @@ -0,0 +1,34 @@ +package libp2p + +import ( + "testing" + + config "github.com/ipfs/kubo/config" + "github.com/stretchr/testify/require" +) + +func TestHttpAddrsFromConfig(t *testing.T) { + require.Equal(t, []string{"/ip4/0.0.0.0/tcp/4001", "/ip4/0.0.0.0/udp/4001/quic-v1"}, + httpAddrsFromConfig(config.Addresses{ + Swarm: []string{"/ip4/0.0.0.0/tcp/4001", "/ip4/0.0.0.0/udp/4001/quic-v1"}, + }), "Swarm addrs should be taken by default") + + require.Equal(t, []string{"/ip4/192.168.0.1/tcp/4001"}, + httpAddrsFromConfig(config.Addresses{ + Swarm: []string{"/ip4/0.0.0.0/tcp/4001", "/ip4/0.0.0.0/udp/4001/quic-v1"}, + Announce: []string{"/ip4/192.168.0.1/tcp/4001"}, + }), "Announce addrs should override Swarm if specified") + + require.Equal(t, []string{"/ip4/0.0.0.0/udp/4001/quic-v1"}, + httpAddrsFromConfig(config.Addresses{ + Swarm: []string{"/ip4/0.0.0.0/tcp/4001", "/ip4/0.0.0.0/udp/4001/quic-v1"}, + NoAnnounce: []string{"/ip4/0.0.0.0/tcp/4001"}, + }), "Swarm addrs should not contain NoAnnounce addrs") + + require.Equal(t, []string{"/ip4/192.168.0.1/tcp/4001", "/ip4/192.168.0.2/tcp/4001"}, + httpAddrsFromConfig(config.Addresses{ + Swarm: []string{"/ip4/0.0.0.0/tcp/4001", "/ip4/0.0.0.0/udp/4001/quic-v1"}, + Announce: []string{"/ip4/192.168.0.1/tcp/4001"}, + AppendAnnounce: []string{"/ip4/192.168.0.2/tcp/4001"}, + }), "AppendAnnounce addrs should be included if specified") +} diff --git a/core/node/libp2p/sec.go b/core/node/libp2p/sec.go index 820ba22d6..5a3e95e31 100644 --- a/core/node/libp2p/sec.go +++ b/core/node/libp2p/sec.go @@ -8,11 +8,6 @@ import ( tls "github.com/libp2p/go-libp2p/p2p/security/tls" ) -const secioEnabledWarning = `The SECIO security transport was enabled in the config but is no longer supported. - -SECIO disabled by default in go-ipfs 0.7 removed in go-ipfs 0.9. Please remove -Swarm.Transports.Security.SECIO from your IPFS config.` - func Security(enabled bool, tptConfig config.Transports) interface{} { if !enabled { return func() (opts Libp2pOpts) { @@ -23,19 +18,15 @@ func Security(enabled bool, tptConfig config.Transports) interface{} { } } - if _, enabled := tptConfig.Security.SECIO.WithDefault(config.Disabled); enabled { - log.Error(secioEnabledWarning) - } - // Using the new config options. return func() (opts Libp2pOpts) { opts.Opts = append(opts.Opts, prioritizeOptions([]priorityOption{{ priority: tptConfig.Security.TLS, - defaultPriority: 200, + defaultPriority: 100, opt: libp2p.Security(tls.ID, tls.New), }, { priority: tptConfig.Security.Noise, - defaultPriority: 100, + defaultPriority: 200, opt: libp2p.Security(noise.ID, noise.New), }})) return opts diff --git a/core/node/libp2p/smux.go b/core/node/libp2p/smux.go index c906b6e02..d52b306d8 100644 --- a/core/node/libp2p/smux.go +++ b/core/node/libp2p/smux.go @@ -3,65 +3,22 @@ package libp2p import ( "fmt" "os" - "strings" "github.com/ipfs/kubo/config" "github.com/libp2p/go-libp2p" - "github.com/libp2p/go-libp2p/core/network" - "github.com/libp2p/go-libp2p/p2p/muxer/mplex" "github.com/libp2p/go-libp2p/p2p/muxer/yamux" ) -func yamuxTransport() network.Multiplexer { - tpt := *yamux.DefaultTransport - tpt.AcceptBacklog = 512 - if os.Getenv("YAMUX_DEBUG") != "" { - tpt.LogOutput = os.Stderr - } - return &tpt -} - func makeSmuxTransportOption(tptConfig config.Transports) (libp2p.Option, error) { - const yamuxID = "/yamux/1.0.0" - const mplexID = "/mplex/6.7.0" - if prefs := os.Getenv("LIBP2P_MUX_PREFS"); prefs != "" { - // Using legacy LIBP2P_MUX_PREFS variable. - log.Error("LIBP2P_MUX_PREFS is now deprecated.") - log.Error("Use the `Swarm.Transports.Multiplexers' config field.") - muxers := strings.Fields(prefs) - enabled := make(map[string]bool, len(muxers)) - - var opts []libp2p.Option - for _, tpt := range muxers { - if enabled[tpt] { - return nil, fmt.Errorf( - "duplicate muxer found in LIBP2P_MUX_PREFS: %s", - tpt, - ) - } - switch tpt { - case yamuxID: - opts = append(opts, libp2p.Muxer(tpt, yamuxTransport())) - case mplexID: - opts = append(opts, libp2p.Muxer(tpt, mplex.DefaultTransport)) - default: - return nil, fmt.Errorf("unknown muxer: %s", tpt) - } - } - return libp2p.ChainOptions(opts...), nil - } else { - return prioritizeOptions([]priorityOption{{ - priority: tptConfig.Multiplexers.Yamux, - defaultPriority: 100, - opt: libp2p.Muxer(yamuxID, yamuxTransport()), - }, { - priority: tptConfig.Multiplexers.Mplex, - defaultPriority: 200, - opt: libp2p.Muxer(mplexID, mplex.DefaultTransport), - }}), nil + return nil, fmt.Errorf("configuring muxers with LIBP2P_MUX_PREFS is no longer supported, use Swarm.Transports.Multiplexers") } + if tptConfig.Multiplexers.Yamux < 0 { + return nil, fmt.Errorf("running libp2p with Swarm.Transports.Multiplexers.Yamux disabled is not supported") + } + + return libp2p.Muxer(yamux.ID, yamux.DefaultTransport), nil } func SmuxTransport(tptConfig config.Transports) func() (opts Libp2pOpts, err error) { diff --git a/core/node/libp2p/topicdiscovery.go b/core/node/libp2p/topicdiscovery.go index 81bec285f..8d0254383 100644 --- a/core/node/libp2p/topicdiscovery.go +++ b/core/node/libp2p/topicdiscovery.go @@ -21,7 +21,6 @@ func TopicDiscovery() interface{} { baseDisc, backoff.NewExponentialBackoff(minBackoff, maxBackoff, backoff.FullJitter, time.Second, 5.0, 0, rng), ) - if err != nil { return nil, err } diff --git a/core/node/libp2p/transport.go b/core/node/libp2p/transport.go index f737c6086..61412ff4f 100644 --- a/core/node/libp2p/transport.go +++ b/core/node/libp2p/transport.go @@ -2,12 +2,13 @@ package libp2p import ( "fmt" - "github.com/ipfs/kubo/config" + "github.com/ipshipyard/p2p-forge/client" "github.com/libp2p/go-libp2p" "github.com/libp2p/go-libp2p/core/metrics" quic "github.com/libp2p/go-libp2p/p2p/transport/quic" "github.com/libp2p/go-libp2p/p2p/transport/tcp" + webrtc "github.com/libp2p/go-libp2p/p2p/transport/webrtc" "github.com/libp2p/go-libp2p/p2p/transport/websocket" webtransport "github.com/libp2p/go-libp2p/p2p/transport/webtransport" @@ -15,11 +16,13 @@ import ( ) func Transports(tptConfig config.Transports) interface{} { - return func(pnet struct { + return func(params struct { fx.In - Fprint PNetFingerprint `optional:"true"` - }) (opts Libp2pOpts, err error) { - privateNetworkEnabled := pnet.Fprint != nil + Fprint PNetFingerprint `optional:"true"` + ForgeMgr *client.P2PForgeCertMgr `optional:"true"` + }, + ) (opts Libp2pOpts, err error) { + privateNetworkEnabled := params.Fprint != nil if tptConfig.Network.TCP.WithDefault(true) { // TODO(9290): Make WithMetrics configurable @@ -27,7 +30,11 @@ func Transports(tptConfig config.Transports) interface{} { } if tptConfig.Network.Websocket.WithDefault(true) { - opts.Opts = append(opts.Opts, libp2p.Transport(websocket.New)) + if params.ForgeMgr == nil { + opts.Opts = append(opts.Opts, libp2p.Transport(websocket.New)) + } else { + opts.Opts = append(opts.Opts, libp2p.Transport(websocket.New, websocket.WithTLSConfig(params.ForgeMgr.TLSConfig()))) + } } if tptConfig.Network.QUIC.WithDefault(!privateNetworkEnabled) { @@ -48,6 +55,15 @@ func Transports(tptConfig config.Transports) interface{} { opts.Opts = append(opts.Opts, libp2p.Transport(webtransport.New)) } + if tptConfig.Network.WebRTCDirect.WithDefault(!privateNetworkEnabled) { + if privateNetworkEnabled { + return opts, fmt.Errorf( + "WebRTC Direct transport does not support private networks, please disable Swarm.Transports.Network.WebRTCDirect", + ) + } + opts.Opts = append(opts.Opts, libp2p.Transport(webrtc.New)) + } + return opts, nil } } diff --git a/core/node/peering.go b/core/node/peering.go index eba165ef9..d6b563835 100644 --- a/core/node/peering.go +++ b/core/node/peering.go @@ -3,7 +3,7 @@ package node import ( "context" - "github.com/ipfs/kubo/peering" + "github.com/ipfs/boxo/peering" "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/peer" "go.uber.org/fx" @@ -18,7 +18,8 @@ func Peering(lc fx.Lifecycle, host host.Host) *peering.PeeringService { return ps.Start() }, OnStop: func(context.Context) error { - return ps.Stop() + ps.Stop() + return nil }, }) return ps diff --git a/core/node/provider.go b/core/node/provider.go index 9110b41b2..7f37cd8b1 100644 --- a/core/node/provider.go +++ b/core/node/provider.go @@ -5,145 +5,175 @@ import ( "fmt" "time" - "github.com/ipfs/go-fetcher" - pin "github.com/ipfs/go-ipfs-pinner" - provider "github.com/ipfs/go-ipfs-provider" - "github.com/ipfs/go-ipfs-provider/batched" - q "github.com/ipfs/go-ipfs-provider/queue" - "github.com/ipfs/go-ipfs-provider/simple" - "go.uber.org/fx" - - "github.com/ipfs/kubo/core/node/helpers" + "github.com/ipfs/boxo/blockstore" + "github.com/ipfs/boxo/fetcher" + pin "github.com/ipfs/boxo/pinning/pinner" + provider "github.com/ipfs/boxo/provider" "github.com/ipfs/kubo/repo" irouting "github.com/ipfs/kubo/routing" + "go.uber.org/fx" ) -// SIMPLE - -// ProviderQueue creates new datastore backed provider queue -func ProviderQueue(mctx helpers.MetricsCtx, lc fx.Lifecycle, repo repo.Repo) (*q.Queue, error) { - return q.NewQueue(helpers.LifecycleCtx(mctx, lc), "provider-v1", repo.Datastore()) -} - -// SimpleProvider creates new record provider -func SimpleProvider(mctx helpers.MetricsCtx, lc fx.Lifecycle, queue *q.Queue, rt irouting.ProvideManyRouter) provider.Provider { - return simple.NewProvider(helpers.LifecycleCtx(mctx, lc), queue, rt) -} - -// SimpleReprovider creates new reprovider -func SimpleReprovider(reproviderInterval time.Duration) interface{} { - return func(mctx helpers.MetricsCtx, lc fx.Lifecycle, rt irouting.ProvideManyRouter, keyProvider simple.KeyChanFunc) (provider.Reprovider, error) { - return simple.NewReprovider(helpers.LifecycleCtx(mctx, lc), reproviderInterval, rt, keyProvider), nil - } -} - -// SimpleProviderSys creates new provider system -func SimpleProviderSys(isOnline bool) interface{} { - return func(lc fx.Lifecycle, p provider.Provider, r provider.Reprovider) provider.System { - sys := provider.NewSystem(p, r) - - if isOnline { - lc.Append(fx.Hook{ - OnStart: func(ctx context.Context) error { - sys.Run() - return nil - }, - OnStop: func(ctx context.Context) error { - return sys.Close() - }, - }) +func ProviderSys(reprovideInterval time.Duration, acceleratedDHTClient bool) fx.Option { + const magicThroughputReportCount = 128 + return fx.Provide(func(lc fx.Lifecycle, cr irouting.ProvideManyRouter, keyProvider provider.KeyChanFunc, repo repo.Repo, bs blockstore.Blockstore) (provider.System, error) { + opts := []provider.Option{ + provider.Online(cr), + provider.ReproviderInterval(reprovideInterval), + provider.KeyProvider(keyProvider), } + if !acceleratedDHTClient { + // The estimation kinda suck if you are running with accelerated DHT client, + // given this message is just trying to push people to use the acceleratedDHTClient + // let's not report on through if it's in use + opts = append(opts, + provider.ThroughputReport(func(reprovide bool, complete bool, keysProvided uint, duration time.Duration) bool { + avgProvideSpeed := duration / time.Duration(keysProvided) + count := uint64(keysProvided) - return sys - } -} + if !reprovide || !complete { + // We don't know how many CIDs we have to provide, try to fetch it from the blockstore. + // But don't try for too long as this might be very expensive if you have a huge datastore. + ctx, cancel := context.WithTimeout(context.Background(), time.Minute*5) + defer cancel() -// BatchedProviderSys creates new provider system -func BatchedProviderSys(isOnline bool, reprovideInterval time.Duration) interface{} { - return func(lc fx.Lifecycle, cr irouting.ProvideManyRouter, q *q.Queue, keyProvider simple.KeyChanFunc, repo repo.Repo) (provider.System, error) { - sys, err := batched.New(cr, q, - batched.ReproviderInterval(reprovideInterval), - batched.Datastore(repo.Datastore()), - batched.KeyProvider(keyProvider)) + // FIXME: I want a running counter of blocks so size of blockstore can be an O(1) lookup. + ch, err := bs.AllKeysChan(ctx) + if err != nil { + logger.Errorf("fetching AllKeysChain in provider ThroughputReport: %v", err) + return false + } + count = 0 + countLoop: + for { + select { + case _, ok := <-ch: + if !ok { + break countLoop + } + count++ + case <-ctx.Done(): + // really big blockstore mode + + // how many blocks would be in a 10TiB blockstore with 128KiB blocks. + const probableBigBlockstore = (10 * 1024 * 1024 * 1024 * 1024) / (128 * 1024) + // How long per block that lasts us. + expectedProvideSpeed := reprovideInterval / probableBigBlockstore + if avgProvideSpeed > expectedProvideSpeed { + logger.Errorf(` +🔔🔔🔔 YOU MAY BE FALLING BEHIND DHT REPROVIDES! 🔔🔔🔔 + +⚠️ Your system might be struggling to keep up with DHT reprovides! +This means your content could partially or completely inaccessible on the network. +We observed that you recently provided %d keys at an average rate of %v per key. + +🕑 An attempt to estimate your blockstore size timed out after 5 minutes, +implying your blockstore might be exceedingly large. Assuming a considerable +size of 10TiB, it would take %v to provide the complete set. + +⏰ The total provide time needs to stay under your reprovide interval (%v) to prevent falling behind! + +💡 Consider enabling the Accelerated DHT to enhance your system performance. See: +https://github.com/ipfs/kubo/blob/master/docs/config.md#routingaccelerateddhtclient`, + keysProvided, avgProvideSpeed, avgProvideSpeed*probableBigBlockstore, reprovideInterval) + return false + } + } + } + } + + // How long per block that lasts us. + expectedProvideSpeed := reprovideInterval + if count > 0 { + expectedProvideSpeed = reprovideInterval / time.Duration(count) + } + + if avgProvideSpeed > expectedProvideSpeed { + logger.Errorf(` +🔔🔔🔔 YOU ARE FALLING BEHIND DHT REPROVIDES! 🔔🔔🔔 + +⚠️ Your system is struggling to keep up with DHT reprovides! +This means your content could partially or completely inaccessible on the network. +We observed that you recently provided %d keys at an average rate of %v per key. + +💾 Your total CID count is ~%d which would total at %v reprovide process. + +⏰ The total provide time needs to stay under your reprovide interval (%v) to prevent falling behind! + +💡 Consider enabling the Accelerated DHT to enhance your reprovide throughput. See: +https://github.com/ipfs/kubo/blob/master/docs/config.md#routingaccelerateddhtclient`, + keysProvided, avgProvideSpeed, count, avgProvideSpeed*time.Duration(count), reprovideInterval) + } + return false + }, magicThroughputReportCount)) + } + sys, err := provider.New(repo.Datastore(), opts...) if err != nil { return nil, err } - if isOnline { - lc.Append(fx.Hook{ - OnStart: func(ctx context.Context) error { - sys.Run() - return nil - }, - OnStop: func(ctx context.Context) error { - return sys.Close() - }, - }) - } + lc.Append(fx.Hook{ + OnStop: func(ctx context.Context) error { + return sys.Close() + }, + }) return sys, nil - } + }) } // ONLINE/OFFLINE // OnlineProviders groups units managing provider routing records online -func OnlineProviders(useStrategicProviding bool, useBatchedProviding bool, reprovideStrategy string, reprovideInterval time.Duration) fx.Option { +func OnlineProviders(useStrategicProviding bool, reprovideStrategy string, reprovideInterval time.Duration, acceleratedDHTClient bool) fx.Option { if useStrategicProviding { - return fx.Provide(provider.NewOfflineProvider) + return OfflineProviders() + } + + var keyProvider fx.Option + switch reprovideStrategy { + case "all", "": + keyProvider = fx.Provide(newProvidingStrategy(false, false)) + case "roots": + keyProvider = fx.Provide(newProvidingStrategy(true, true)) + case "pinned": + keyProvider = fx.Provide(newProvidingStrategy(true, false)) + case "flat": + keyProvider = fx.Provide(provider.NewBlockstoreProvider) + default: + return fx.Error(fmt.Errorf("unknown reprovider strategy %q", reprovideStrategy)) } return fx.Options( - SimpleProviders(reprovideStrategy, reprovideInterval), - maybeProvide(SimpleProviderSys(true), !useBatchedProviding), - maybeProvide(BatchedProviderSys(true, reprovideInterval), useBatchedProviding), + keyProvider, + ProviderSys(reprovideInterval, acceleratedDHTClient), ) } // OfflineProviders groups units managing provider routing records offline -func OfflineProviders(useStrategicProviding bool, useBatchedProviding bool, reprovideStrategy string, reprovideInterval time.Duration) fx.Option { - if useStrategicProviding { - return fx.Provide(provider.NewOfflineProvider) - } - - return fx.Options( - SimpleProviders(reprovideStrategy, reprovideInterval), - maybeProvide(SimpleProviderSys(false), true), - //maybeProvide(BatchedProviderSys(false, reprovideInterval), useBatchedProviding), - ) +func OfflineProviders() fx.Option { + return fx.Provide(provider.NewNoopProvider) } -// SimpleProviders creates the simple provider/reprovider dependencies -func SimpleProviders(reprovideStrategy string, reproviderInterval time.Duration) fx.Option { - var keyProvider fx.Option - switch reprovideStrategy { - case "all": - fallthrough - case "": - keyProvider = fx.Provide(simple.NewBlockstoreProvider) - case "roots": - keyProvider = fx.Provide(pinnedProviderStrategy(true)) - case "pinned": - keyProvider = fx.Provide(pinnedProviderStrategy(false)) - default: - return fx.Error(fmt.Errorf("unknown reprovider strategy '%s'", reprovideStrategy)) - } - - return fx.Options( - fx.Provide(ProviderQueue), - fx.Provide(SimpleProvider), - keyProvider, - fx.Provide(SimpleReprovider(reproviderInterval)), - ) -} - -func pinnedProviderStrategy(onlyRoots bool) interface{} { +func newProvidingStrategy(onlyPinned, onlyRoots bool) interface{} { type input struct { fx.In Pinner pin.Pinner + Blockstore blockstore.Blockstore IPLDFetcher fetcher.Factory `name:"ipldFetcher"` } - return func(in input) simple.KeyChanFunc { - return simple.NewPinnedProvider(onlyRoots, in.Pinner, in.IPLDFetcher) + return func(in input) provider.KeyChanFunc { + if onlyRoots { + return provider.NewPinnedProvider(true, in.Pinner, in.IPLDFetcher) + } + + if onlyPinned { + return provider.NewPinnedProvider(false, in.Pinner, in.IPLDFetcher) + } + + return provider.NewPrioritizedProvider( + provider.NewPinnedProvider(true, in.Pinner, in.IPLDFetcher), + provider.NewBlockstoreProvider(in.Blockstore), + ) } } diff --git a/core/node/storage.go b/core/node/storage.go index 2e831fae2..aedf0ee6a 100644 --- a/core/node/storage.go +++ b/core/node/storage.go @@ -1,12 +1,12 @@ package node import ( + blockstore "github.com/ipfs/boxo/blockstore" "github.com/ipfs/go-datastore" - blockstore "github.com/ipfs/go-ipfs-blockstore" config "github.com/ipfs/kubo/config" "go.uber.org/fx" - "github.com/ipfs/go-filestore" + "github.com/ipfs/boxo/filestore" "github.com/ipfs/kubo/core/node/helpers" "github.com/ipfs/kubo/repo" "github.com/ipfs/kubo/thirdparty/verifbs" @@ -14,7 +14,8 @@ import ( // RepoConfig loads configuration from the repo func RepoConfig(repo repo.Repo) (*config.Config, error) { - return repo.Config() + cfg, err := repo.Config() + return cfg, err } // Datastore provides the datastore @@ -26,17 +27,14 @@ func Datastore(repo repo.Repo) datastore.Datastore { type BaseBlocks blockstore.Blockstore // BaseBlockstoreCtor creates cached blockstore backed by the provided datastore -func BaseBlockstoreCtor(cacheOpts blockstore.CacheOpts, nilRepo bool, hashOnRead bool) func(mctx helpers.MetricsCtx, repo repo.Repo, lc fx.Lifecycle) (bs BaseBlocks, err error) { +func BaseBlockstoreCtor(cacheOpts blockstore.CacheOpts, hashOnRead bool) func(mctx helpers.MetricsCtx, repo repo.Repo, lc fx.Lifecycle) (bs BaseBlocks, err error) { return func(mctx helpers.MetricsCtx, repo repo.Repo, lc fx.Lifecycle) (bs BaseBlocks, err error) { // hash security bs = blockstore.NewBlockstore(repo.Datastore()) bs = &verifbs.VerifBS{Blockstore: bs} - - if !nilRepo { - bs, err = blockstore.CachedBlockstore(helpers.LifecycleCtx(mctx, lc), bs, cacheOpts) - if err != nil { - return nil, err - } + bs, err = blockstore.CachedBlockstore(helpers.LifecycleCtx(mctx, lc), bs, cacheOpts) + if err != nil { + return nil, err } bs = blockstore.NewIdStore(bs) @@ -58,7 +56,7 @@ func GcBlockstoreCtor(bb BaseBlocks) (gclocker blockstore.GCLocker, gcbs blockst return } -// GcBlockstoreCtor wraps GcBlockstore and adds Filestore support +// FilestoreBlockstoreCtor wraps GcBlockstore and adds Filestore support func FilestoreBlockstoreCtor(repo repo.Repo, bb BaseBlocks) (gclocker blockstore.GCLocker, gcbs blockstore.GCBlockstore, bs blockstore.Blockstore, fstore *filestore.Filestore) { gclocker = blockstore.NewGCLocker() diff --git a/coverage/Rules.mk b/coverage/Rules.mk index fd4f33cf1..48fce2856 100644 --- a/coverage/Rules.mk +++ b/coverage/Rules.mk @@ -45,8 +45,8 @@ endif export IPFS_COVER_DIR:= $(realpath $(d))/sharnesscover/ -$(d)/sharness_tests.coverprofile: export TEST_NO_PLUGIN=1 -$(d)/sharness_tests.coverprofile: $(d)/ipfs cmd/ipfs/ipfs-test-cover $(d)/coverage_deps test_sharness_short +$(d)/sharness_tests.coverprofile: export TEST_PLUGIN=0 +$(d)/sharness_tests.coverprofile: $(d)/ipfs cmd/ipfs/ipfs-test-cover $(d)/coverage_deps test_sharness (cd $(@D)/sharnesscover && find . -type f | gocovmerge -list -) > $@ diff --git a/docs/EARLY_TESTERS.md b/docs/EARLY_TESTERS.md index fb2fedc2a..e3280b0eb 100644 --- a/docs/EARLY_TESTERS.md +++ b/docs/EARLY_TESTERS.md @@ -22,15 +22,15 @@ We will ask early testers to participate at two points in the process: ## Who has signed up? -- [ ] pacman.store (@RubenKelevra) -- [ ] Infura (@MichaelMure) -- [ ] Textile (@sanderpick) -- [ ] Pinata (@obo20) -- [ ] RTrade (@postables) -- [ ] Siderus (@koalalorenzo) - [ ] Charity Engine (@rytiss, @tristanolive) - [ ] Fission (@bmann) -- [ ] OrbitDB (@aphelionz) +- [ ] Infura (@MichaelMure) +- [ ] OrbitDB (@haydenyoung) +- [ ] Pinata (@obo20) +- [ ] Shipyard (@cewood, @ns4plabs) +- [ ] Siderus (@koalalorenzo) +- [ ] Textile (@sanderpick) +- [ ] @RubenKelevra ## How to sign up? diff --git a/docs/PATCH_RELEASE_TEMPLATE.md b/docs/PATCH_RELEASE_TEMPLATE.md deleted file mode 100644 index a2599d9fd..000000000 --- a/docs/PATCH_RELEASE_TEMPLATE.md +++ /dev/null @@ -1,30 +0,0 @@ -# Patch Release Checklist - -This process handles patch releases from version `vX.Y.Z` to `vX.Y.Z+1` assuming that `vX.Y.Z` is the latest released version of Kubo. - -- [ ] Get temporary permissions to force-push to `release-*` branches -- [ ] Fork a new branch (`release-vX.Y.Z`) from `release` and cherry-pick the relevant commits from master (or custom fixes) onto this branch - - [ ] Use `git cherry-pick -x` so that the commit message says `(cherry picked from commit ...)` -- [ ] Make a minimal changelog update tracking the relevant fixes to CHANGELOG, as its own commit e.g. `docs: update changelog vX.Y.Z+1` -- [ ] version string in `version.go` has been updated (in the `release-vX.Y.Z+1` branch), as its own commit. -- [ ] Make a PR merging `release-vX.Y.Z+1` into the release branch - - This may be unnecessary, e.g. for backports -- [ ] Tag the merge commit in the `release` branch with `vX.Y.Z+1` (ensure the tag is signed) -- [ ] Add artifacts to https://dist.ipfs.tech/kubo - 1. Make a PR against [ipfs/distributions](https://github.com/ipfs/distributions) with local changes produced by `add-version` (see [usage](https://github.com/ipfs/distributions#usage)) - 2. Wait for PR to build artifacts and generate diff - 3. Inspect results, merge if CI is green and the diff looks ok - 4. Wait for `master` branch to build and update DNSLink at https://dist.ipfs.tech -- [ ] Cut a release on [github](https://github.com/ipfs/kubo/releases) and reuse signed artifacts from https://dist.ipfs.tech/kubo (run [sync-release-assets.yml workflow](https://github.com/ipfs/kubo/actions/workflows/sync-release-assets.yml)). -- [ ] Announce the Release: - - [ ] On [discuss.ipfs.tech](https://discuss.ipfs.tech) - - This will automatically post to Matrix (`#lobby:ipfs.io`) and IPFS Discord (`#ipfs-chatter`) - - Examples from the past: [0.13.1](https://discuss.ipfs.tech/t/go-ipfs-v0-13-1-has-been-released/14599) - - [ ] Pin the discuss topic -- [ ] Release published - - [ ] to [dist.ipfs.tech](https://dist.ipfs.tech) - - [ ] to [npm-go-ipfs](https://www.npmjs.com/package/go-ipfs) (should be done by [ipfs/npm-go-ipfs](https://github.com/ipfs/npm-go-ipfs), but ok to dispatch [this job](https://github.com/ipfs/npm-go-ipfs/actions/workflows/main.yml) manually) - - [ ] to [github](https://github.com/ipfs/kubo/releases) - - [ ] to [arch](https://www.archlinux.org/packages/community/x86_64/go-ipfs/) (flag it out of date) -- [ ] Cut a new ipfs-desktop release -- [ ] Merge the `release` branch back into `master`, ignoring the changes to `version.go` (keep the `-dev` version from master). diff --git a/docs/README.md b/docs/README.md index 78235622a..ab7ac9cc3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,11 +1,17 @@ # Developer Documentation and Guides -If you are looking for User Documentation & Guides, please visit [docs.ipfs.tech](https://docs.ipfs.tech/). +If you are looking for User Documentation & Guides, please visit [docs.ipfs.tech](https://docs.ipfs.tech/) or check [General Documentation](#general-documentation). If you’re experiencing an issue with IPFS, **please follow [our issue guide](github-issue-guide.md) when filing an issue!** Otherwise, check out the following guides to using and developing IPFS: +## General Documentation + +- [Configuration reference](config.md) + - [Datastore configuration](datastores.md) + - [Experimental features](experimental-features.md) + ## Developing `kubo` - First, please read the Contributing Guidelines [for IPFS projects](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md) and then the Contributing Guidelines for [Go code specifically](https://github.com/ipfs/community/blob/master/CONTRIBUTING_GO.md) @@ -22,9 +28,6 @@ Otherwise, check out the following guides to using and developing IPFS: ## Advanced User Guides - [Transferring a File Over IPFS](file-transfer.md) -- [Configuration reference](config.md) - - [Datastore configuration](datastores.md) - - [Experimental features](experimental-features.md) - [Installing command completion](command-completion.md) - [Mounting IPFS with FUSE](fuse.md) - [Installing plugins](plugins.md) diff --git a/docs/RELEASE_CHECKLIST.md b/docs/RELEASE_CHECKLIST.md new file mode 100644 index 000000000..9a9d7f113 --- /dev/null +++ b/docs/RELEASE_CHECKLIST.md @@ -0,0 +1,178 @@ + + +# ✅ Release Checklist (vX.Y.Z[-rcN]) + +## Labels + +If an item should be executed for a specific release type, it should be labeled with one of the following labels: + +- ![](https://img.shields.io/badge/only-RC-blue?style=flat-square) execute **ONLY** when releasing a Release Candidate +- ![](https://img.shields.io/badge/only-FINAL-green?style=flat-square) execute **ONLY** when releasing a Final Release + +Otherwise, it means it should be executed for **ALL** release types. + +Patch releases should follow the same process as `.0` releases. If some item should **NOT** be executed for a Patch Release, it should be labeled with: + +- ![](https://img.shields.io/badge/not-PATCH-yellow?style=flat-square) do **NOT** execute when releasing a Patch Release + +## Before the release + +This section covers tasks to be done ahead of the release. + +- [ ] Verify you have access to all the services and tools required for the release + - [ ] [GPG signature](https://docs.github.com/en/authentication/managing-commit-signature-verification) configured in local git and in GitHub + - [ ] [admin access to IPFS Discourse](https://discuss.ipfs.tech/g/admins) + - ask the previous release owner (or @2color) for an invite + - [ ] ![](https://img.shields.io/badge/not-PATCH-yellow?style=flat-square) [access to #shared-pl-marketing-requests](https://filecoinproject.slack.com/archives/C018EJ8LWH1) channel in FIL Slack + - ask the previous release owner for an invite + - [ ] [access to IPFS network metrics](https://github.com/protocol/pldw/blob/624f47cf4ec14ad2cec6adf601a9f7b203ef770d/docs/sources/ipfs.md#ipfs-network-metrics) dashboards in Grafana + - open an access request in the [pldw](https://github.com/protocol/pldw/issues/new/choose) + - [example](https://github.com/protocol/pldw/issues/158) + - [ ] [kuboreleaser](https://github.com/ipfs/kuboreleaser) checked out on your system (_only if you're using [kuboreleaser](https://github.com/ipfs/kuboreleaser)_) + - [ ] [Thunderdome](https://github.com/ipfs-shipyard/thunderdome) checked out on your system and configured (see the [Thunderdome release docs](./releases_thunderdome.md) for setup) + - [ ] [docker](https://docs.docker.com/get-docker/) installed on your system (_only if you're using [kuboreleaser](https://github.com/ipfs/kuboreleaser)_) + - [ ] [npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) installed on your system (_only if you're **NOT** using [kuboreleaser](https://github.com/ipfs/kuboreleaser)_) + - [ ] [zsh](https://github.com/ohmyzsh/ohmyzsh/wiki/Installing-ZSH#install-and-set-up-zsh-as-default) installed on your system + - [ ] [kubo](https://github.com/ipfs/kubo) checked out under `$(go env GOPATH)/src/github.com/ipfs/kubo` + - you can also symlink your clone to the expected location by running `mkdir -p $(go env GOPATH)/src/github.com/ipfs && ln -s $(pwd) $(go env GOPATH)/src/github.com/ipfs/kubo` + - [ ] ![](https://img.shields.io/badge/not-PATCH-yellow?style=flat-square) [Reddit](https://www.reddit.com) account +- ![](https://img.shields.io/badge/not-PATCH-yellow?style=flat-square) Upgrade Go used in CI to the latest patch release available in [CircleCI](https://hub.docker.com/r/cimg/go/tags) in: + - [ ] ![](https://img.shields.io/badge/not-PATCH-yellow?style=flat-square) [ipfs/distributions](https://github.com/ipfs/distributions) + - [example](https://github.com/ipfs/distributions/pull/756) + - [ ] ![](https://img.shields.io/badge/not-PATCH-yellow?style=flat-square) [ipfs/ipfs-docs](https://github.com/ipfs/ipfs-docs) + - [example](https://github.com/ipfs/ipfs-docs/pull/1298) +- [ ] Verify there is nothing [left for release](-what-s-left-for-release) +- [ ] Create a release process improvement PR + - [ ] update the [release issue template](docs/RELEASE_ISSUE_TEMPLATE.md) as you go + - [ ] link it in the [Meta](#meta) section + +## The release + +This section covers tasks to be done during each release. + +- [ ] Prepare the release branch and update version numbers accordingly
using `./kuboreleaser --skip-check-before release --version vX.Y.Z(-rcN) prepare-branch` or ... + - [ ] create a new branch `release-vX.Y.Z` + - use `master` as base if `Z == 0` + - use `release` as base if `Z > 0` + - [ ] ![](https://img.shields.io/badge/only-RC-blue?style=flat-square) update the `CurrentVersionNumber` in [version.go](version.go) in the `master` branch to `vX.Y+1.0-dev` + - [example](https://github.com/ipfs/kubo/pull/9305) + - [ ] update the `CurrentVersionNumber` in [version.go](version.go) in the `release-vX.Y` branch to `vX.Y.Z(-RCN)` + - [example](https://github.com/ipfs/kubo/pull/9394) + - [ ] create a draft PR from `release-vX.Y` to `release` + - [example](https://github.com/ipfs/kubo/pull/9306) + - [ ] Cherry-pick commits from `master` to the `release-vX.Y.Z` using `git cherry-pick -x ` + - [ ] ![](https://img.shields.io/badge/only-FINAL-green?style=flat-square) Add full changelog and contributors to the [changelog](docs/changelogs/vX.Y.md) + - [ ] ![](https://img.shields.io/badge/only-FINAL-green?style=flat-square) Replace the `Changelog` and `Contributors` sections of the [changelog](docs/changelogs/vX.Y.md) with the stdout of `./bin/mkreleaselog`. Note that the command expects your `$GOPATH/src/github.com/ipfs/kubo` to include latest commits from `release-vX.Y` + - do **NOT** copy the stderr + - [ ] verify all CI checks on the PR from `release-vX.Y` to `release` are passing + - [ ] ![](https://img.shields.io/badge/only-FINAL-green?style=flat-square) Merge the PR from `release-vX.Y` to `release` using the `Create a merge commit` + - do **NOT** use `Squash and merge` nor `Rebase and merge` because we need to be able to sign the merge commit + - do **NOT** delete the `release-vX.Y` branch +
+- [ ] Create the release tag
using `./kuboreleaser release --version vX.Y.Z(-rcN) tag` or ... + - This is a dangerous operation! Go and Docker publishing are difficult to reverse! Have the release reviewer verify all the commands marked with ⚠️! + - [ ] ⚠️ ![](https://img.shields.io/badge/only-RC-blue?style=flat-square) tag the HEAD commit using `git tag -s vX.Y.Z(-RCN) -m 'Prerelease X.Y.Z(-RCN)'` + - [ ] ⚠️ ![](https://img.shields.io/badge/only-FINAL-green?style=flat-square) tag the HEAD commit of the `release` branch using `git tag -s vX.Y.Z -m 'Release X.Y.Z'` + - [ ] ⚠️ verify the tag is signed and tied to the correct commit using `git show vX.Y.Z(-RCN)` + - [ ] ⚠️ push the tag to GitHub using `git push origin vX.Y.Z(-RCN)` + - do **NOT** use `git push --tags` because it pushes all your local tags +
+- [ ] Verify [ipfs/distributions](https://github.com/ipfs/distributions)'s `.tool-versions`'s `golang` entry is set to the [latest go release](https://go.dev/doc/devel/release) on the major go branch [Kubo is being tested on](https://github.com/ipfs/kubo/blob/master/.github/workflows/gotest.yml) (see `go-version:`). +- [ ] Publish to Dockerhub, NPM, and dist.ipfs.tech and GitHub using `./kuboreleaser --skip-check-before --skip-run release --version vX.Y.Z(-rcN) publish-to-all` or follow each step below: + - [ ] Publish the release to [DockerHub](https://hub.docker.com/r/ipfs/kubo/)
using `./kuboreleaser --skip-check-before --skip-run release --version vX.Y.Z(-rcN) publish-to-dockerhub` or ... + - [ ] Wait for [Publish docker image](https://github.com/ipfs/kubo/actions/workflows/docker-image.yml) workflow run initiated by the tag push to finish + - [ ] verify the image is available on [Docker Hub](https://hub.docker.com/r/ipfs/kubo/tags) + - [ ] Publish the release to [dist.ipfs.tech](https://dist.ipfs.tech)
using `./kuboreleaser release --version vX.Y.Z(-rcN) publish-to-distributions` or ... + - [ ] check out [ipfs/distributions](https://github.com/ipfs/distributions) + - [ ] create new branch: run `git checkout -b release-kubo-X.Y.Z(-rcN)` + - [ ] run `./dist.sh add-version kubo vX.Y.Z(-RCN)` to add the new version to the `versions` file + - [usage](https://github.com/ipfs/distributions#usage) + - [ ] create and merge the PR which updates `dists/kubo/versions` and `dists/go-ipfs/versions` (![](https://img.shields.io/badge/only-FINAL-green?style=flat-square) and `dists/kubo/current` and `dists/go-ipfs/current`) + - [example](https://github.com/ipfs/distributions/pull/760) + - [ ] wait for the [CI](https://github.com/ipfs/distributions/actions/workflows/main.yml) workflow run initiated by the merge to master to finish + - [ ] verify the release is available on [dist.ipfs.tech](https://dist.ipfs.tech/#kubo) +
+ - [ ] Publish the release to [NPM](https://www.npmjs.com/package/kubo?activeTab=versions)
using `./kuboreleaser release --version vX.Y.Z(-rcN) publish-to-npm` (⚠️ you might need to run the command a couple of times because GHA might not be able to see the new distribution straight away due to caching) or ... + - [ ] run the [Release to npm](https://github.com/ipfs/npm-go-ipfs/actions/workflows/main.yml) workflow + - [ ] check [Release to npm](https://github.com/ipfs/npm-go-ipfs/actions/workflows/main.yml) workflow run logs to verify it discovered the new release + - [ ] verify the release is available on [NPM](https://www.npmjs.com/package/kubo?activeTab=versions) +
+ - [ ] Publish the release to [GitHub](https://github.com/ipfs/kubo/releases)
using `./kuboreleaser release --version vX.Y.Z(-rcN) publish-to-github` or ... + - [ ] create a new release on [GitHub](https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository#creating-a-release) + - [RC example](https://github.com/ipfs/kubo/releases/tag/v0.17.0-rc1) + - [FINAL example](https://github.com/ipfs/kubo/releases/tag/v0.17.0) + - [ ] use the `vX.Y.Z(-RCN)` tag + - [ ] link to the release issue + - [ ] ![](https://img.shields.io/badge/only-RC-blue?style=flat-square) link to the changelog in the description + - [ ] ![](https://img.shields.io/badge/only-RC-blue?style=flat-square) check the `This is a pre-release` checkbox + - [ ] ![](https://img.shields.io/badge/only-FINAL-green?style=flat-square) copy the changelog (without the header) in the description + - [ ] ![](https://img.shields.io/badge/only-FINAL-green?style=flat-square) do **NOT** check the `This is a pre-release` checkbox + - [ ] run the [sync-release-assets](https://github.com/ipfs/kubo/actions/workflows/sync-release-assets.yml) workflow + - [ ] wait for the [sync-release-assets](https://github.com/ipfs/kubo/actions/workflows/sync-release-assets.yml) workflow run to finish + - [ ] verify the release assets are present in the [GitHub release](https://github.com/ipfs/kubo/releases/tag/vX.Y.Z(-RCN)) +
+- [ ] Update Kubo staging environment, see the [Running Kubo tests on staging](https://www.notion.so/Running-Kubo-tests-on-staging-488578bb46154f9bad982e4205621af8) for details. + - [ ] ![](https://img.shields.io/badge/only-RC-blue?style=flat-square) Test last release against the current RC + - [ ] ![](https://img.shields.io/badge/only-FINAL-green?style=flat-square) Test last release against the current one +- [ ] Promote the release
using `./kuboreleaser release --version vX.Y.Z(-rcN) promote` or ... + - [ ] create an [IPFS Discourse](https://discuss.ipfs.tech) topic + - [prerelease example](https://discuss.ipfs.tech/t/kubo-v0-16-0-rc1-release-candidate-is-out/15248) + - [release example](https://discuss.ipfs.tech/t/kubo-v0-16-0-release-is-out/15249) + - [ ] use `Kubo vX.Y.Z(-RCN) is out!` as the title + - [ ] use `kubo` and `go-ipfs` as topics + - [ ] repeat the title as a heading (`##`) in the description + - [ ] link to the GitHub Release, binaries on IPNS, docker pull command and release notes in the description + - [ ] pin the [IPFS Discourse](https://discuss.ipfs.tech) topic globally + - you can make the topic a banner if there is no banner already + - verify the [IPFS Discourse](https://discuss.ipfs.tech) topic was copied to: + - [ ] [#ipfs-chatter](https://discord.com/channels/669268347736686612/669268347736686615) in IPFS Discord + - [ ] [#ipfs-chatter](https://filecoinproject.slack.com/archives/C018EJ8LWH1) in FIL Slack + - [ ] [#ipfs-chatter:ipfs.io](https://matrix.to/#/#ipfs-chatter:ipfs.io) in Matrix + - [ ] ![](https://img.shields.io/badge/only-FINAL-green?style=flat-square) Add the link to the [IPFS Discourse](https://discuss.ipfs.tech) topic to the [GitHub Release](https://github.com/ipfs/kubo/releases/tag/vX.Y.Z(-RCN)) description + - [example](https://github.com/ipfs/kubo/releases/tag/v0.17.0) + - [ ] ![](https://img.shields.io/badge/only-RC-blue?style=flat-square) create an issue comment mentioning early testers on the release issue + - [example](https://github.com/ipfs/kubo/issues/9319#issuecomment-1311002478) + - [ ] ![](https://img.shields.io/badge/only-FINAL-green?style=flat-square) create an issue comment linking to the release on the release issue + - [example](https://github.com/ipfs/kubo/issues/9417#issuecomment-1400740975) + - [ ] ![](https://img.shields.io/badge/only-FINAL-green?style=flat-square) ![](https://img.shields.io/badge/not-PATCH-yellow?style=flat-square) ask the marketing team to tweet about the release in [#shared-pl-marketing-requests](https://filecoinproject.slack.com/archives/C018EJ8LWH1) in FIL Slack + - [example](https://filecoinproject.slack.com/archives/C018EJ8LWH1/p1664885305374900) + - [ ] ![](https://img.shields.io/badge/only-FINAL-green?style=flat-square) ![](https://img.shields.io/badge/not-PATCH-yellow?style=flat-square) post the link to the [GitHub Release](https://github.com/ipfs/kubo/releases/tag/vX.Y.Z(-RCN)) to [Reddit](https://reddit.com/r/ipfs) + - [example](https://www.reddit.com/r/ipfs/comments/9x0q0k/kubo_v0160_release_is_out/) +
+- [ ] ~~Test the new version with `ipfs-companion`~~ ([currently skipped](https://github.com/ipfs/ipfs-companion/issues/1300))
using `./kuboreleaser release --version vX.Y.Z(-rcN) test-ipfs-companion` or ... + - [ ] run the [e2e](https://github.com/ipfs/ipfs-companion/actions/workflows/e2e.yml) + - use `vX.Y.Z(-RCN)` as the Kubo image version + - [ ] wait for the [e2e](https://github.com/ipfs/ipfs-companion/actions/workflows/e2e.yml) workflow run to finish +
+- [ ] ![](https://img.shields.io/badge/only-FINAL-green?style=flat-square) Update Kubo in [ipfs-desktop](https://github.com/ipfs/ipfs-desktop)
using `./kuboreleaser release --version vX.Y.Z(-rcN) update-ipfs-desktop` or ... + - [ ] check out [ipfs/ipfs-desktop](https://github.com/ipfs/ipfs-desktop) + - [ ] run `npm install` + - [ ] create a PR which updates `package.json` and `package-lock.json` + - [ ] ![](https://img.shields.io/badge/only-FINAL-green?style=flat-square) add @SgtPooki as reviewer +
+- [ ] ![](https://img.shields.io/badge/only-FINAL-green?style=flat-square) Update Kubo docs
using `./kuboreleaser release --version vX.Y.Z(-rcN) update-ipfs-docs` or ... + - [ ] ![](https://img.shields.io/badge/only-FINAL-green?style=flat-square) run the [update-on-new-ipfs-tag.yml](https://github.com/ipfs/ipfs-docs/actions/workflows/update-on-new-ipfs-tag.yml) workflow + - [ ] ![](https://img.shields.io/badge/only-FINAL-green?style=flat-square) merge the PR created by the [update-on-new-ipfs-tag.yml](https://github.com/ipfs/ipfs-docs/actions/workflows/update-on-new-ipfs-tag.yml) workflow run +
+- [ ] ![](https://img.shields.io/badge/only-FINAL-green?style=flat-square) Create a blog entry on [blog.ipfs.tech](https://blog.ipfs.tech)
using `./kuboreleaser release --version vX.Y.Z(-rcN) update-ipfs-blog --date YYYY-MM-DD` or ... + - [ ] ![](https://img.shields.io/badge/only-FINAL-green?style=flat-square) create a PR which adds a release note for the new Kubo version + - [example](https://github.com/ipfs/ipfs-blog/pull/529) + - [ ] ![](https://img.shields.io/badge/only-FINAL-green?style=flat-square) merge the PR + - [ ] ![](https://img.shields.io/badge/only-FINAL-green?style=flat-square) verify the blog entry was published +
+- [ ] ![](https://img.shields.io/badge/only-FINAL-green?style=flat-square) Merge the [release](https://github.com/ipfs/kubo/tree/release) branch back into [master](https://github.com/ipfs/kubo/tree/master), ignoring the changes to [version.go](version.go) (keep the `-dev`) version,
using `./kuboreleaser release --version vX.Y.Z(-rcN) merge-branch` or ... + - [ ] create a new branch `merge-release-vX.Y.Z` from `release` + - [ ] create and merge a PR from `merge-release-vX.Y.Z` to `master` +
+- [ ] ![](https://img.shields.io/badge/only-FINAL-green?style=flat-square) ![](https://img.shields.io/badge/not-PATCH-yellow?style=flat-square) Prepare for the next release
using `./kuboreleaser release --version vX.Y.Z(-rcN) prepare-next` or ... + - [ ] ![](https://img.shields.io/badge/only-FINAL-green?style=flat-square) ![](https://img.shields.io/badge/not-PATCH-yellow?style=flat-square) Create the next [changelog](https://github.com/ipfs/kubo/blob/master/docs/changelogs/vX.(Y+1).md) + - [ ] ![](https://img.shields.io/badge/only-FINAL-green?style=flat-square) ![](https://img.shields.io/badge/not-PATCH-yellow?style=flat-square) Link to the new changelog in the [CHANGELOG.md](CHANGELOG.md) file + - [ ] ![](https://img.shields.io/badge/only-FINAL-green?style=flat-square) ![](https://img.shields.io/badge/not-PATCH-yellow?style=flat-square) Create the next release issue +
+- [ ] ![](https://img.shields.io/badge/only-FINAL-green?style=flat-square) ![](https://img.shields.io/badge/not-PATCH-yellow?style=flat-square) Create a dependency update PR + - [ ] ![](https://img.shields.io/badge/only-FINAL-green?style=flat-square) ![](https://img.shields.io/badge/not-PATCH-yellow?style=flat-square) check out [ipfs/kubo](https://github.com/ipfs/kubo) + - [ ] ![](https://img.shields.io/badge/only-FINAL-green?style=flat-square) ![](https://img.shields.io/badge/not-PATCH-yellow?style=flat-square) go over direct dependencies from `go.mod` in the root directory (NOTE: do not run `go get -u` as it will upgrade indirect dependencies which may cause problems) + - [ ] ![](https://img.shields.io/badge/only-FINAL-green?style=flat-square) ![](https://img.shields.io/badge/not-PATCH-yellow?style=flat-square) run `make mod_tidy` + - [ ] ![](https://img.shields.io/badge/only-FINAL-green?style=flat-square) ![](https://img.shields.io/badge/not-PATCH-yellow?style=flat-square) create a PR which updates `go.mod` and `go.sum` + - [ ] ![](https://img.shields.io/badge/only-FINAL-green?style=flat-square) ![](https://img.shields.io/badge/not-PATCH-yellow?style=flat-square) add the PR to the next release milestone +- [ ] ![](https://img.shields.io/badge/only-FINAL-green?style=flat-square) Close the release issue diff --git a/docs/RELEASE_ISSUE_TEMPLATE.md b/docs/RELEASE_ISSUE_TEMPLATE.md index 13b14c8a4..321026ea6 100644 --- a/docs/RELEASE_ISSUE_TEMPLATE.md +++ b/docs/RELEASE_ISSUE_TEMPLATE.md @@ -1,218 +1,36 @@ - - -> Release Issue Template. If doing a patch release, see [here](https://github.com/ipfs/kubo/blob/master/docs/PATCH_RELEASE_TEMPLATE.md) + # Items to do upon creating the release issue + - [ ] Fill in the Meta section - [ ] Assign the issue to the release owner and reviewer. - [ ] Name the issue "Release vX.Y.Z" - [ ] Set the proper values for X.Y.Z - [ ] Pin the issue + + # Meta + * Release owner: @who * Release reviewer: @who * Expected RC date: week of YYYY-MM-DD * 🚢 Expected final release date: YYYY-MM-DD -* Accompanying PR for improving the release process: (example: https://github.com/ipfs/kubo/pull/9391) +* Release PR: +* Accompanying PR for improving the release process: ([example](https://github.com/ipfs/kubo/pull/9391)) +* Changelog: https://github.com/ipfs/kubo/blob/master/docs/changelogs/vX.Y.md -See the [Kubo release process](https://pl-strflt.notion.site/Kubo-Release-Process-5a5d066264704009a28a79cff93062c4) for more info. +# Items In Scope -# Kubo X.Y.Z Release +## Required -We're happy to announce Kubo X.Y.Z! + -As usual, this release includes important fixes, some of which may be critical for security. Unless the fix addresses a bug being exploited in the wild, the fix will _not_ be called out in the release notes. Please make sure to update ASAP. See our [security fix policy](https://github.com/ipfs/go-ipfs/tree/master/docs/releases.md#security-fix-policy) for details. +## Nice To Have (Optional) -## 🗺 What's left for release - - - -### Required - -### Nice to have - -## 🔦 Highlights - -< top highlights for this release notes. For ANY version (final or RCs) > - -## ✅ Release Checklist - -Checklist: - -- [ ] **Stage 0 - Prerequisites** - - [ ] Open an issue against [bifrost-infra](https://github.com/protocol/bifrost-infra) ahead of the release ([example](https://github.com/protocol/bifrost-infra/issues/2109)). **Idealy, do this multiple days in advance of the RC** to give Bifrost the heads up that asks will be coming their way. - - [ ] Spell out all that we want updated - gateways, the bootstraper and the cluster/preload nodes - - [ ] Mention @protocol/bifrost-team in the issue and let them know the expected date of the release - - Issue link: - - [ ] Ensure that the `What's left for release` section has all the checkboxes checked. If that's not the case, discuss the open items with Kubo maintainers and update the release schedule accordingly. - - [ ] Create `docs-release-vX.Y.Z` branch, open a draft PR and keep updating `docs/RELEASE_ISSUE_TEMPLATE.md` on that branch as you go. - - [ ] Link it in the "Meta" section above. - - [ ] Ensure you have a [GPG key generated](https://docs.github.com/en/authentication/managing-commit-signature-verification/generating-a-new-gpg-key) and [added to your GitHub account](https://docs.github.com/en/authentication/managing-commit-signature-verification/adding-a-gpg-key-to-your-github-account). This will enable you to created signed tags. - - [ ] Ensure you have [admin access](https://discuss.ipfs.tech/g/admins) to [IPFS Discourse](https://discuss.ipfs.tech/). Admin access is required to globally pin posts and create banners. @2color might be able to assist you. - - [ ] Access to [#bifrost](https://filecoinproject.slack.com/archives/C03MMMF606T) channel in FIL Slack might come in handy. Ask the release reviewer to invite you over. - - [ ] Access to [#shared-pl-marketing-requests](https://filecoinproject.slack.com/archives/C018EJ8LWH1) channel in FIL Slack will be required to request social shares. Ask the release reviewer to invite you over. - - [ ] After the release is deployed to our internal infrastructure, you're going to need read access to [IPFS network metrics](https://github.com/protocol/pldw/blob/624f47cf4ec14ad2cec6adf601a9f7b203ef770d/docs/sources/ipfs.md#ipfs-network-metrics) dashboards. Open an access request in https://github.com/protocol/pldw/issues/new/choose if you don't have it yet ([example](https://github.com/protocol/pldw/issues/158)). - - [ ] You're also going to need NPM installed on your system. See [here](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) for instructions. - - [ ] Prepare changelog proposal in [docs/changelogs/vX.Y.md](https://github.com/ipfs/kubo/blob/master/docs/changelogs/). - - Skip filling out the `### Changelog` section (the one where which lists all the commits and contributors) for now. We will populate it after the release branch is cut. - - PR link: - - [ ] Ensure the new changelog is linked in the [CHANGELOG.md](CHANGELOG.md) file. - - [ ] Install ZSH ([instructions](https://github.com/ohmyzsh/ohmyzsh/wiki/Installing-ZSH#install-and-set-up-zsh-as-default)). It is needed by the changelog creation script. - - [ ] Ensure you have `kubo` checked out under `$(go env GOPATH)/src/github.com/ipfs/kubo`. This is required by the changelog creation script. - - If you want your clone to live in a different location, you can symlink it to the expected location by running `mkdir -p $(go env GOPATH)/src/github.com/ipfs && ln -s $(pwd) $(go env GOPATH)/src/github.com/ipfs/kubo`. - - [ ] Ensure that [README.md](https://github.com/ipfs/go-ipfs/tree/master/README.md) is up to date. -- [ ] **Stage 1 - Initial Preparations** - - [ ] Upgrade to the latest patch release of Go that CircleCI has published (currently used version: `1.19.1`) - - [ ] See the list here: https://hub.docker.com/r/cimg/go/tags - - [ ] [ipfs/distributions](https://github.com/ipfs/distributions): bump [this version](https://github.com/ipfs/distributions/blob/master/.tool-versions#L2) - - [ ] [ipfs/kubo](https://github.com/ipfs/kubo): [example PR](https://github.com/ipfs/kubo/pull/8599) - - [ ] [ipfs/ipfs-docs](https://github.com/ipfs/ipfs-docs): [example PR](https://github.com/ipfs/ipfs-docs/pull/1298) - only if the major version changed - - [ ] Fork a new branch (`release-vX.Y.Z`) from `master`. - - [ ] Bump the version in `version.go` in the `master` branch to `vX.(Y+1).0-dev` via a PR ([example](https://github.com/ipfs/kubo/pull/9305)). -- [ ] **Stage 2 - Release Candidate** - _if any [non-trivial](docs/releases.md#footnotes) changes need to be included in the release, return to this stage_ - - [ ] If it's not a first RC, add new commits to the `release-vX.Y.Z` branch from `master` using `git cherry-pick -x ...` - - Note: `release-*` branches are protected. You can do all needed updates on a separated branch (e.g. `wip-release-vX.Y.Z`) and when everything is settled push to `release-vX.Y.Z` - - [ ] Bump the version in `version.go` in the `release-vX.Y.Z` branch to `vX.Y.Z-rcN`. - - [ ] If it's a first RC, create a draft PR targetting `release` branch if it doesn't exist yet ([example](https://github.com/ipfs/kubo/pull/9306)). - - [ ] Wait for CI to run and complete PR checks. All checks should pass. - - [ ] Create a signed tag for the release candidate. - - [ ] This is a dangerous operation, as it is difficult to reverse due to Go modules and automated Docker image publishing. Remember to verify the commands you intend to run for items marked with ⚠️ with the release reviewer. - - [ ] ⚠️ Tag HEAD `release-vX.Y.Z` commit with `vX.Y.Z-rcN` (`git tag -s vX.Y.Z-rcN -m 'Pre-release X.Y.Z-rcn'`) - - [ ] Run `git show vX.Y.Z-rcN` to ensure the tag is correct. - - [ ] ⚠️ Push the `vX.Y.Z-rcN` tag to GitHub (`git push origin vX.Y.Z-rcN`; DO NOT USE `git push --tags` because it pushes all your local tags). - - [ ] Add artifacts to https://dist.ipfs.tech by making a PR against [ipfs/distributions](https://github.com/ipfs/distributions) - - [ ] Clone the `ipfs/distributions` repo locally. - - [ ] Create a new branch (`kubo-release-vX.Y.Z-rcn`) from `master`. - - [ ] Run `./dist.sh add-version kubo vX.Y.Z-rcN` to add the new version to the `versions` file ([instructions](https://github.com/ipfs/distributions#usage)). - - `dist.sh` will print _WARNING: not marking pre-release kubo vX.Y.Z-rcN as the current version._. - - [ ] Push the `kubo-release-vX.Y.Z-rcn` branch to GitHub and create a PR from that branch ([example](https://github.com/ipfs/distributions/pull/760)). - - [ ] Ask for a review from the release reviewer. - - [ ] Enable auto-merge for the PR. - - PR build will build the artifacts and generate a diff in around 30 minutes - - PR will be merged automatically once the diff is approved - - `master` build will publish the artifacts to https://dist.ipfs.io in around 30 minutes - - [ ] Ensure that the artifacts are available at https://dist.ipfs.io - - [ ] Publish the RC to [the NPM package](https://www.npmjs.com/package/go-ipfs?activeTab=versions) by running https://github.com/ipfs/npm-go-ipfs/actions/workflows/main.yml (it happens automatically but it is safe to speed up the process and kick of a run manually) - - [ ] Cut a pre-release on [GitHub](https://github.com/ipfs/kubo/releases) ([instructions](https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository#creating-a-release), [example](https://github.com/ipfs/kubo/releases/tag/v0.17.0-rc1)) - - Use `vX.Y.Z-rcN` as the tag. - - Link to the release issue in the description. - - Link to the relevant [changelog](https://github.com/ipfs/kubo/blob/master/docs/changelogs/) in the description. - - Check `This is a pre-release`. - - [ ] Synchronize release artifacts by running [sync-release-assets](https://github.com/ipfs/kubo/actions/workflows/sync-release-assets.yml) workflow. - - [ ] Announce the RC - - [ ] Create a new post on [IPFS Discourse](https://discuss.ipfs.tech). ([example](https://discuss.ipfs.tech/t/kubo-v0-16-0-rc1-release-candidate-is-out/15248)) - - Use `Kubo vX.Y.Z-rcn Release Candidate is out!` as the title. - - Use `kubo` and `go-ipfs` as topics. - - Repeat the title as a heading (`##`) in the description. - - Link to the GitHub Release, binaries on IPNS, docker pull command and release notes in the description. - - [ ] Pin the topic globally so that it stays at the top of the category. - - [ ] If there is no more important banner currently set on Discourse (e.g. IPFS Camp announcement), make the topic into a banner. - - [ ] Check if Discourse post was automatically copied to: - - [ ] IPFS Discord #ipfs-chatter - - [ ] FIL Slack #ipfs-chatter - - [ ] Matrix https://matrix.to/#/#ipfs-chatter:ipfs.io - - [ ] Mention [early testers](https://github.com/ipfs/go-ipfs/tree/master/docs/EARLY_TESTERS.md) in the comment under the release issue ([example](https://github.com/ipfs/kubo/issues/9319#issuecomment-1311002478)). -- [ ] **Stage 3 - Internal Testing** - - [ ] Infrastructure Testing. - - [ ] Update the issue against [bifrost-infra](https://github.com/protocol/bifrost-infra) ([example](https://github.com/protocol/bifrost-infra/issues/2109)). - - [ ] Mention @protocol/bifrost-team in the issue to let them know the release is ready - - [ ] [Optional] Reply under a message about the issue in the #bifrost channel on FIL Slack once the RC is out. Send the message to the channel. - - [ ] Check [metrics](https://protocollabs.grafana.net/d/8zlhkKTZk/gateway-slis-precomputed?orgId=1) every day. - - Compare the metrics trends week over week. - - If there is an unexpected variation in the trend, message the #bifrost channel on FIL Slack and ask for help investigation the cause. - - [ ] IPFS Application Testing. - - [ ] [IPFS Desktop](https://github.com/ipfs-shipyard/ipfs-desktop) - - [ ] Upgrade to the RC in [ipfs-desktop](https://github.com/ipfs-shipyard/ipfs-desktop) - - [ ] Run `npm install` to update `package-lock.json`. - - [ ] Push to a branch ([example](https://github.com/ipfs/ipfs-desktop/pull/1826/commits/b0a23db31ce942b46d95965ee6fe770fb24d6bde)) - - [ ] Open a draft PR to track through the final release ([example](https://github.com/ipfs/ipfs-desktop/pull/1826)) - - [ ] Ensure CI tests pass - - [ ] [IPFS Companion](https://github.com/ipfs-shipyard/ipfs-companion) - - [ ] Start kubo daemon of the version to release. - - [ ] Start a fresh chromium or chrome instance using `chromium --user-data-dir=$(mktemp -d)` (macos `/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --user-data-dir=$(mktemp -d)`) - - [ ] Start a fresh firefox instance using `firefox --profile $(mktemp -d)` (macos `/Applications/Firefox.app/Contents/MacOS/firefox --profile $(mktemp -d)`) - - [ ] Install IPFS Companion from [vendor-specific store](https://github.com/ipfs/ipfs-companion/#readme). - - [ ] Check that the comunication between Kubo daemon and IPFS companion is working properly checking if the number of connected peers changes. -- [ ] **Stage 5 - Release** - _ONLY FOR FINAL RELEASE_ - - [ ] Prepare the `release` branch. - - [ ] Bump the version in `version.go` in the `release-vX.Y.Z` branch to `X.Y.Z`. - - [ ] Update the [docs/changelogs/vX.Y.md](docs/changelogs) with the new commits and contributors. - - [ ] Run `./bin/mkreleaselog` twice to generate the changelog and copy the output. - - The first run of the script might be poluted with `git clone` output. - - [ ] Paste the output into the `### Changelog` section of the changelog file inside the `
` block. - - [ ] Commit the changelog changes. - - [ ] Push the `release-vX.Y.Z` branch to GitHub (`git push origin release-vX.Y.Z`) - - [ ] Mark the PR created from `release-vX.Y.Z` as ready for review. - - [ ] Ensure the PR is targetting `release` branch. - - [ ] Ensure that CI is green. - - [ ] Have release reviewer review the PR. - - [ ] Merge the PR into `release` branch using the `Create a merge commit` (do **NOT** use `Squash and merge` nor `Rebase and merge` because we need to be able to sign the merge commit). - - Do not delete the `release-vX.Y.Z` branch. - - [ ] Checkout the `release` branch locally. - - Remember to pull the latest changes. - - [ ] Create a signed tag for the release. - - [ ] This is a dangerous operation, as it is difficult to reverse due to Go modules and automated Docker image publishing. Remember to verify the commands you intend to run for items marked with ⚠️ with the release reviewer. - - [ ] ⚠️ Tag HEAD `release` commit with `vX.Y.Z` (`git tag -s vX.Y.Z -m 'Release X.Y.Z'`) - - [ ] Run `git show vX.Y.Z` to ensure the tag is correct. - - [ ] ⚠️ Push the `vX.Y.Z` tag to GitHub (`git push origin vX.Y.Z`; DO NOT USE `git push --tags` because it pushes all your local tags). - - [ ] Publish the release. - - [ ] Wait for [Publish docker image](https://github.com/ipfs/kubo/actions/workflows/docker-image.yml) workflow run initiated by the tag push to finish. - - [ ] Add artifacts to https://dist.ipfs.tech by making a PR against [ipfs/distributions](https://github.com/ipfs/distributions) - - [ ] Clone the `ipfs/distributions` repo locally. - - [ ] Create a new branch (`kubo-release-vX.Y.Z`) from `master`. - - [ ] Run `./dist.sh add-version kubo vX.Y.Z` to add the new version to the `versions` file ([instructions](https://github.com/ipfs/distributions#usage)). - - [ ] Push the `kubo-release-vX.Y.Z` branch to GitHub and create a PR from that branch ([example](https://github.com/ipfs/distributions/pull/768)). - - [ ] Ask for a review from the release reviewer. - - [ ] Enable auto-merge for the PR. - - PR build will build the artifacts and generate a diff in around 30 minutes - - PR will be merged automatically once the diff is approved - - `master` build will publish the artifacts to https://dist.ipfs.io in around 30 minutes - - [ ] Ensure that the artifacts are available at https://dist.ipfs.io - - [ ] Publish the release to [the NPM package](https://www.npmjs.com/package/go-ipfs?activeTab=versions) by running https://github.com/ipfs/npm-go-ipfs/actions/workflows/main.yml (it happens automatically but it is safe to speed up the process and kick of a run manually) - - [ ] Cut the release on [GitHub](https://github.com/ipfs/kubo/releases) ([instructions](https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository#creating-a-release), [example](https://github.com/ipfs/kubo/releases/tag/v0.17.0)) - - Use `vX.Y.Z` as the tag. - - Copy the relevant [changelog](https://github.com/ipfs/kubo/blob/release/docs/changelogs/) into the release description. - - Keep the release notes as trim as possible (e.g. remove top headers where possible, [example](https://github.com/ipfs/kubo/releases/tag/v0.17.0)) - - [ ] Synchronize release artifacts by running [sync-release-assets](https://github.com/ipfs/kubo/actions/workflows/sync-release-assets.yml) workflow. - - [ ] TODO: https://github.com/protocol/bifrost-infra/issues/2184#issuecomment-1315279257 - - [ ] Announce the release - - [ ] Add a link to the release to this release issue as a comment. - - [ ] Create a new post on [IPFS Discourse](https://discuss.ipfs.tech). ([example](https://discuss.ipfs.tech/t/kubo-v0-16-0-release-is-out/15286)) - - Use `Kubo vX.Y.Z Release is out!` as the title. - - Use `kubo` and `go-ipfs` as topics. - - Repeat the title as a heading (`##`) in the description. - - Link to the GitHub Release, binaries on IPNS, docker pull command and release notes in the description. - - [ ] Pin the topic globally so that it stays at the top of the category. - - [ ] If there is no more important banner currently set on Discourse (e.g. IPFS Camp announcement), make the topic into a banner. - - [ ] Check if Discourse post was automatically copied to: - - [ ] IPFS Discord #ipfs-chatter - - [ ] FIL Slack #ipfs-chatter - - [ ] Matrix - - [ ] Add a link from release notes to Discuss post (like we did here: https://github.com/ipfs/kubo/releases/tag/v0.17.0) - - [ ] Update the draft PR created for [interop](https://github.com/ipfs/interop) to use the new release and mark it as ready for review. - - [ ] Update the draft PR created for [IPFS Desktop](https://github.com/ipfs-shipyard/ipfs-desktop) to use the new release and mark it as ready for review. - - [ ] Update docs - - [ ] Run https://github.com/ipfs/ipfs-docs/actions/workflows/update-on-new-ipfs-tag.yml to generate a PR to the docs repo - - [ ] Merge the auto-created PR in https://github.com/ipfs/ipfs-docs/pulls ([example](https://github.com/ipfs/ipfs-docs/pull/1263)) - - [ ] Get the blog post created - - [ ] Submit a request for blog post creation using [the form](https://airtable.com/shrNH8YWole1xc70I). - - Title: Just released: Kubo X.Y.Z! - - Link type: Release notes - - URL: https://github.com/ipfs/kubo/releases/tag/vX.Y.Z - - [ ] The post is live on https://blog.ipfs.io - - [ ] Share the link to the GitHub release - - [ ] Twitter (request in Filecoin Slack channel #shared-pl-marketing-requests; [example](https://filecoinproject.slack.com/archives/C018EJ8LWH1/p1664903524843269?thread_ts=1664885305.374909&cid=C018EJ8LWH1)) - - [ ] [Reddit](https://reddit.com/r/ipfs) -- [ ] **Stage 6 - Post-Release** - - [ ] Merge the `release` branch back into `master`, ignoring the changes to `version.go` (keep the `-dev` version from master). - - [ ] Create an issue using this release issue template for the _next_ release. - - [ ] Close this release issue. - -## How to contribute? - -Would you like to contribute to the IPFS project and don't know how? Well, there are a few places you can get started: - -- Check the issues with the `help wanted` label in the [ipfs/kubo repo](https://github.com/ipfs/kubo/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) -- Join the discussion at [discuss.ipfs.tech](https://discuss.ipfs.tech/) and help users finding their answers. -- See other options at https://docs.ipfs.tech/community/ + diff --git a/docs/changelogs/v0.10.md b/docs/changelogs/v0.10.md index b4bbf1047..ea92201a9 100644 --- a/docs/changelogs/v0.10.md +++ b/docs/changelogs/v0.10.md @@ -278,7 +278,7 @@ See `ipfs swarm peering --help` for more details. ([ipld/go-ipld-prime#228](https://github.com/ipld/go-ipld-prime/pull/228)) - Fix ExploreRecursive stopAt condition, add tests, add error return to Explore (#229) ([ipld/go-ipld-prime#229](https://github.com/ipld/go-ipld-prime/pull/229)) - selector: add tests which are driven by language-agnostic spec fixtures. ([ipld/go-ipld-prime#231](https://github.com/ipld/go-ipld-prime/pull/231)) - - selector: Improve docs for implementors. (#227) ([ipld/go-ipld-prime#227](https://github.com/ipld/go-ipld-prime/pull/227)) + - selector: Improve docs for implementers. (#227) ([ipld/go-ipld-prime#227](https://github.com/ipld/go-ipld-prime/pull/227)) - Bindnode fixes of opportunity ([ipld/go-ipld-prime#226](https://github.com/ipld/go-ipld-prime/pull/226)) - node/bindnode: redesign the shape of unions in Go ([ipld/go-ipld-prime#223](https://github.com/ipld/go-ipld-prime/pull/223)) - summary of the v0.11.0 changelog should holler even more about how cool bindnode is. diff --git a/docs/changelogs/v0.11.md b/docs/changelogs/v0.11.md index 9a3d2b7c9..98133052a 100644 --- a/docs/changelogs/v0.11.md +++ b/docs/changelogs/v0.11.md @@ -260,7 +260,7 @@ This work was [contributed](https://github.com/ipfs/go-ipfs/pull/8569) by [Ceram - feat(queryexecutor): merge RunTraversal into QueryExecutor - feat(responsemanager): QueryExecutor to separate module - use TaskQueue, add tests - Merge branch 'release/v0.10.5' - - fix(responseassembler): dont hold block data reference in passed on subscribed block link (#268) ([ipfs/go-graphsync#268](https://github.com/ipfs/go-graphsync/pull/268)) + - fix(responseassembler): don't hold block data reference in passed on subscribed block link (#268) ([ipfs/go-graphsync#268](https://github.com/ipfs/go-graphsync/pull/268)) - sync: update CI config files (#266) ([ipfs/go-graphsync#266](https://github.com/ipfs/go-graphsync/pull/266)) - Check IPLD context cancellation error type instead of string comparison - Use `context.CancelFunc` instead of `func()` (#257) ([ipfs/go-graphsync#257](https://github.com/ipfs/go-graphsync/pull/257)) @@ -622,7 +622,7 @@ This work was [contributed](https://github.com/ipfs/go-ipfs/pull/8569) by [Ceram - github.com/libp2p/go-libp2p-pubsub (v0.5.4 -> v0.6.0): - feat: plumb through context changes (#459) ([libp2p/go-libp2p-pubsub#459](https://github.com/libp2p/go-libp2p-pubsub/pull/459)) - support MinTopicSize without a discovery mechanism - - clear peerPromises map when fullfilling a promise + - clear peerPromises map when fulfilling a promise - README: remove obsolete notice, fix example code for tracing. - remove peer filter check from subscriptions (#453) ([libp2p/go-libp2p-pubsub#453](https://github.com/libp2p/go-libp2p-pubsub/pull/453)) - Create peer filter option diff --git a/docs/changelogs/v0.12.md b/docs/changelogs/v0.12.md index 85639dd2b..def891271 100644 --- a/docs/changelogs/v0.12.md +++ b/docs/changelogs/v0.12.md @@ -166,7 +166,7 @@ For this migration, if your datastore has fast renames you may want to consider - Version 1.1.0 (#91) ([ipfs/go-ipfs-blockstore#91](https://github.com/ipfs/go-ipfs-blockstore/pull/91)) - feat: add context to interfaces (#90) ([ipfs/go-ipfs-blockstore#90](https://github.com/ipfs/go-ipfs-blockstore/pull/90)) - sync: update CI config files (#88) ([ipfs/go-ipfs-blockstore#88](https://github.com/ipfs/go-ipfs-blockstore/pull/88)) - - add constructor that doesnt mess with datastore keys ([ipfs/go-ipfs-blockstore#83](https://github.com/ipfs/go-ipfs-blockstore/pull/83)) + - add constructor that doesn't mess with datastore keys ([ipfs/go-ipfs-blockstore#83](https://github.com/ipfs/go-ipfs-blockstore/pull/83)) - Use bloom filter in GetSize - fix staticcheck ([ipfs/go-ipfs-blockstore#73](https://github.com/ipfs/go-ipfs-blockstore/pull/73)) - add BenchmarkARCCacheConcurrentOps ([ipfs/go-ipfs-blockstore#70](https://github.com/ipfs/go-ipfs-blockstore/pull/70)) diff --git a/docs/changelogs/v0.13.md b/docs/changelogs/v0.13.md index d0635beb8..9bf4ee88a 100644 --- a/docs/changelogs/v0.13.md +++ b/docs/changelogs/v0.13.md @@ -29,7 +29,7 @@ View the linked [security advisory](https://github.com/ipfs/go-ipfs/security/adv - Remove support for `ForEach` enumeration from car-index-sorted - Use a fix code as the multihash code for `CarIndexSorted` - Fix testutil assertion logic and update index generation tests - - fix: tighter constraint of singleWidthIndex width, add index recommentation docs + - fix: tighter constraint of singleWidthIndex width, add index recommendation docs - fix: explicitly disable serialization of insertionindex - feat: MaxAllowed{Header,Section}Size option - feat: MaxAllowedSectionSize default to 32M @@ -66,7 +66,7 @@ View the linked [security advisory](https://github.com/ipfs/go-ipfs/security/adv - Traversal-based car creation (#269) ([ipld/go-car#269](https://github.com/ipld/go-car/pull/269)) - Seek to start before index generation in `ReadOnly` blockstore - support extraction of unixfs content stored in car files (#263) ([ipld/go-car#263](https://github.com/ipld/go-car/pull/263)) - - Add a barebones readme to the car CLI (#262) ([ipld/go-car#262](https://github.com/ipld/go-car/pull/262)) + - Add a bare bones readme to the car CLI (#262) ([ipld/go-car#262](https://github.com/ipld/go-car/pull/262)) - sync: update CI config files (#261) ([ipld/go-car#261](https://github.com/ipld/go-car/pull/261)) - fix!: use -version=n instead of -v1 for index command - feat: fix get-dag and add version=1 option @@ -223,7 +223,7 @@ Future releases will [add support for dag-json and dag-cbor responses](https://g There are two ways for requesting CID specific response format: 1. HTTP header: `Accept: application/vnd.ipld.{format}` - Examples: [application/vnd.ipld.car](https://www.iana.org/assignments/media-types/application/vnd.ipld.car), [application/vnd.ipld.raw](https://www.iana.org/assignments/media-types/application/vnd.ipld.raw) -2. URL paramerer: `?format=` +2. URL parameter: `?format=` - Useful for creating "Download CAR" links. *Usage examples:* @@ -629,7 +629,7 @@ The more fully featured yamux stream multiplexer is now prioritized over mplex f - fix: cannot call SetPrimaryCore after using a Tee logger ([ipfs/go-log#121](https://github.com/ipfs/go-log/pull/121)) - Document environment variables ([ipfs/go-log#120](https://github.com/ipfs/go-log/pull/120)) - sync: update CI config files (#119) ([ipfs/go-log#119](https://github.com/ipfs/go-log/pull/119)) - - Add WithStacktrace untility ([ipfs/go-log#118](https://github.com/ipfs/go-log/pull/118)) + - Add WithStacktrace utility ([ipfs/go-log#118](https://github.com/ipfs/go-log/pull/118)) - In addition to StdOut/Err check the outfile for TTYness ([ipfs/go-log#117](https://github.com/ipfs/go-log/pull/117)) - github.com/ipfs/go-merkledag (v0.5.1 -> v0.6.0): - v0.6.0 @@ -659,7 +659,7 @@ The more fully featured yamux stream multiplexer is now prioritized over mplex f - add AsLargeBytes support to unixfs files (#24) ([ipfs/go-unixfsnode#24](https://github.com/ipfs/go-unixfsnode/pull/24)) - fix: add extra test to span the shard/no-shard boundary - fix: more Tsize fixes, fix HAMT and make it match go-unixfs output - - fix: encode Tsize correctly everywhere (using wraped LinkSystem) + - fix: encode Tsize correctly everywhere (using wrapped LinkSystem) - docs(version): tag 1.2.0 - Update deps for ADL selectors ([ipfs/go-unixfsnode#20](https://github.com/ipfs/go-unixfsnode/pull/20)) - add license (#17) ([ipfs/go-unixfsnode#17](https://github.com/ipfs/go-unixfsnode/pull/17)) @@ -951,7 +951,7 @@ The more fully featured yamux stream multiplexer is now prioritized over mplex f - disable the incoming streams limit (#49) ([libp2p/go-libp2p-yamux#49](https://github.com/libp2p/go-libp2p-yamux/pull/49)) - Release v0.8.1 ([libp2p/go-libp2p-yamux#48](https://github.com/libp2p/go-libp2p-yamux/pull/48)) - release v0.8.0 (#47) ([libp2p/go-libp2p-yamux#47](https://github.com/libp2p/go-libp2p-yamux/pull/47)) - - pass the PeerScope to yamux (satifiying its MemoryManger interface) (#46) ([libp2p/go-libp2p-yamux#46](https://github.com/libp2p/go-libp2p-yamux/pull/46)) + - pass the PeerScope to yamux ( satisfying its MemoryManger interface) (#46) ([libp2p/go-libp2p-yamux#46](https://github.com/libp2p/go-libp2p-yamux/pull/46)) - release v0.7.0 (#43) ([libp2p/go-libp2p-yamux#43](https://github.com/libp2p/go-libp2p-yamux/pull/43)) - sync: update CI config files (#42) ([libp2p/go-libp2p-yamux#42](https://github.com/libp2p/go-libp2p-yamux/pull/42)) - reduce the number of max incoming stream to 256 ([libp2p/go-libp2p-yamux#41](https://github.com/libp2p/go-libp2p-yamux/pull/41)) @@ -973,7 +973,7 @@ The more fully featured yamux stream multiplexer is now prioritized over mplex f - multiplex: add (*Multiplex).CloseChan ([libp2p/go-mplex#89](https://github.com/libp2p/go-mplex/pull/89)) - add a Go Reference badge to the README ([libp2p/go-mplex#88](https://github.com/libp2p/go-mplex/pull/88)) - sync: update CI config files ([libp2p/go-mplex#85](https://github.com/libp2p/go-mplex/pull/85)) - - Fixup tests & vet ([libp2p/go-mplex#84](https://github.com/libp2p/go-mplex/pull/84)) + - Fix up tests & vet ([libp2p/go-mplex#84](https://github.com/libp2p/go-mplex/pull/84)) - Bump lodash from 4.17.19 to 4.17.21 in /interop/js ([libp2p/go-mplex#83](https://github.com/libp2p/go-mplex/pull/83)) - github.com/libp2p/go-msgio (v0.1.0 -> v0.2.0): - release v0.2.0 (#34) ([libp2p/go-msgio#34](https://github.com/libp2p/go-msgio/pull/34)) diff --git a/docs/changelogs/v0.15.md b/docs/changelogs/v0.15.md index 93033b26c..cc51aba1a 100644 --- a/docs/changelogs/v0.15.md +++ b/docs/changelogs/v0.15.md @@ -75,7 +75,7 @@ http://127.0.0.1:8080 - docs(add): skip binary name in helptext - docs(cli): clarify CID determinism in add command - docs(cli): clarify CAR format in dag export|import - - test(gw): cors preflight with custom hearder + - test(gw): cors preflight with custom header - feat: make corehttp a reusable component ([ipfs/kubo#9070](https://github.com/ipfs/kubo/pull/9070)) - feat: go-libp2p v0.21 (rcmgr auto scaling) ([ipfs/kubo#9074](https://github.com/ipfs/kubo/pull/9074)) - ([ipfs/kubo#9024](https://github.com/ipfs/kubo/pull/9024)) @@ -90,7 +90,7 @@ http://127.0.0.1:8080 - feat: add $IPFS_PATH/gateway file - docs: replace `docs.ipfs.io` with `docs.ipfs.tech` (#9158) ([ipfs/kubo#9158](https://github.com/ipfs/kubo/pull/9158)) - chore: fix markdown link syntax typo for AutoNAT.ServiceMode - - chore: bump go-blockservice to only do put onces + - chore: bump go-blockservice to only do put once - docs: update Arch Linux installation instructions - chore: update kubo-as-a-library example - docs(readme): add maintainer info (#9141) ([ipfs/kubo#9141](https://github.com/ipfs/kubo/pull/9141)) @@ -212,9 +212,9 @@ http://127.0.0.1:8080 - Add package docs ([libp2p/go-libp2p-resource-manager#75](https://github.com/libp2p/go-libp2p-resource-manager/pull/75)) - chore: Release v0.5.2 ([libp2p/go-libp2p-resource-manager#74](https://github.com/libp2p/go-libp2p-resource-manager/pull/74)) - Record which direction the resource was blocked ([libp2p/go-libp2p-resource-manager#72](https://github.com/libp2p/go-libp2p-resource-manager/pull/72)) - - Simplify mem graphs in stock grafana dashboard ([libp2p/go-libp2p-resource-manager#73](https://github.com/libp2p/go-libp2p-resource-manager/pull/73)) - - feat: Handle multiple instances in stock grafana dashboard ([libp2p/go-libp2p-resource-manager#70](https://github.com/libp2p/go-libp2p-resource-manager/pull/70)) - - Use templated version of grafana dashboard json ([libp2p/go-libp2p-resource-manager#69](https://github.com/libp2p/go-libp2p-resource-manager/pull/69)) + - Simplify mem graphs in stock Grafana dashboard ([libp2p/go-libp2p-resource-manager#73](https://github.com/libp2p/go-libp2p-resource-manager/pull/73)) + - feat: Handle multiple instances in stock Grafana dashboard ([libp2p/go-libp2p-resource-manager#70](https://github.com/libp2p/go-libp2p-resource-manager/pull/70)) + - Use templated version of Grafana dashboard json ([libp2p/go-libp2p-resource-manager#69](https://github.com/libp2p/go-libp2p-resource-manager/pull/69)) - Release v0.5.1 ([libp2p/go-libp2p-resource-manager#66](https://github.com/libp2p/go-libp2p-resource-manager/pull/66)) - Implement `json.Marshaler` interface for LimitConfig ([libp2p/go-libp2p-resource-manager#67](https://github.com/libp2p/go-libp2p-resource-manager/pull/67)) - Don't wait for a chan that will never close ([libp2p/go-libp2p-resource-manager#65](https://github.com/libp2p/go-libp2p-resource-manager/pull/65)) @@ -249,7 +249,7 @@ http://127.0.0.1:8080 - remove Travis package (#57) ([libp2p/go-libp2p-testing#57](https://github.com/libp2p/go-libp2p-testing/pull/57)) - github.com/lucas-clemente/quic-go (v0.27.1 -> v0.28.0): - update for Go 1.19beta1 (#3460) ([lucas-clemente/quic-go#3460](https://github.com/lucas-clemente/quic-go/pull/3460)) - - dedupe Alt-Svc header values (#3461) ([lucas-clemente/quic-go#3461](https://github.com/lucas-clemente/quic-go/pull/3461)) + - Deduplicate Alt-Svc header values (#3461) ([lucas-clemente/quic-go#3461](https://github.com/lucas-clemente/quic-go/pull/3461)) - only set DF for sockets that can handle it (#3448) ([lucas-clemente/quic-go#3448](https://github.com/lucas-clemente/quic-go/pull/3448)) - fix flaky HTTP/3 request body test (#3447) ([lucas-clemente/quic-go#3447](https://github.com/lucas-clemente/quic-go/pull/3447)) - make the keep alive interval configurable (#3444) ([lucas-clemente/quic-go#3444](https://github.com/lucas-clemente/quic-go/pull/3444)) diff --git a/docs/changelogs/v0.17.md b/docs/changelogs/v0.17.md index 867ddf3ae..3c06cfc02 100644 --- a/docs/changelogs/v0.17.md +++ b/docs/changelogs/v0.17.md @@ -40,7 +40,7 @@ and [Swarm.ResourceMgr.MaxFileDescriptors](https://github.com/ipfs/go-ipfs/blob/ See [Swarm.ResourceMgr](https://github.com/ipfs/go-ipfs/blob/master/docs/config.md#swarmresourcemgr) for 1. what limits are set by default, 2. example override configuration, -3. how to access prometheus metrics and view grafana dashboards of resource usage, and +3. how to access Prometheus metrics and view Grafana dashboards of resource usage, and 4. how to set explicit "allow lists" to protect against eclipse attacks. #### Implicit connection manager limits diff --git a/docs/changelogs/v0.18.md b/docs/changelogs/v0.18.md index 58077e214..f2a22d84e 100644 --- a/docs/changelogs/v0.18.md +++ b/docs/changelogs/v0.18.md @@ -1,5 +1,85 @@ # Kubo changelog v0.18 +## v0.18.1 + +This release includes improvements around Pubsub message deduplication, libp2p resource management, and more. + + + +- [Overview](#overview) +- [🔦 Highlights](#-highlights) + - [New default Pubsub.SeenMessagesStrategy](#new-default-pubsubseenmessagesstrategy) + - [Improving libp2p resource management integration](#improving-libp2p-resource-management-integration) +- [📝 Changelog](#-changelog) +- [👨‍👩‍👧‍👦 Contributors](#-contributors) + + + +### 🔦 Highlights + +#### New default `Pubsub.SeenMessagesStrategy` + +A new optional [`Pubsub.SeenMessagesStrategy`](../config.md#pubsubseenmessagesstrategy) configuration option has been added. + +This option allows you to choose between two different strategies for +deduplicating messages: `first-seen` and `last-seen`. + +When unset, the default strategy is `last-seen`, which calculates the +time-to-live (TTL) countdown based on the last time a message is seen. This +means that if a message is received and then seen again within the specified +TTL window based on the last time it was seen, it won't be emitted. + +If you prefer the old behavior, which calculates the TTL countdown based on the +first time a message is seen, you can set `Pubsub.SeenMessagesStrategy` to +`first-seen`. + +#### Improving libp2p resource management integration + +TL;DR: limit autoscaling improved, most users should start with default settings. +If you have old configuration, switch to implicit defaults: + +``` +ipfs config --json -- Swarm.ResourceMgr '{}' +ipfs config --json -- Swarm.ConnMgr '{}' +``` + +IF you run a server and want to utilize more than half of memory and file descriptors to p2p work, adjust [`Swarm.ResourceMgr.MaxMemory`](https://github.com/ipfs/kubo/blob/master/docs/config.md#swarmresourcemgrmaxmemory) and [`Swarm.ResourceMgr.MaxFileDescriptors`](https://github.com/ipfs/kubo/blob/master/docs/config.md#swarmresourcemgrmaxfiledescriptors). + +The 0.18.1 builds on the default protection nodes get against DoS (resource exhaustion) and eclipse attacks +with the [go-libp2p Network Resource Manager/Accountant](https://github.com/ipfs/kubo/blob/master/docs/libp2p-resource-management.md) +that was fine-tuned in [Kubo 0.18](https://github.com/ipfs/kubo/blob/biglep/resource-manager-example-of-what-want/docs/changelogs/v0.18.md#improving-libp2p-resource-management-integration). + +Adding default hard-limits from the Resource Manager/Accountant after the fact is tricky, +and some additional improvements have been made to improve the [computed defaults](https://github.com/ipfs/kubo/blob/master/docs/libp2p-resource-management.md#computed-default-limits). + +As much as possible, the aim is for a user to only think about how much memory they want to bound libp2p to, +and not need to think about translating that to hard numbers for connections, streams, etc. +More updates are likely in future Kubo releases, but with this release: +1. ``System.StreamsInbound`` is no longer bounded directly +2. ``System.ConnsInbound``, ``Transient.Memory``, ``Transiet.ConnsInbound`` have higher default computed values. + +### 📝 Changelog + +
Full Changelog + +- github.com/ipfs/kubo: + - Add overview section + - Adjust inbound connection limits depending on memory. + - feat: Pubsub.SeenMessagesStrategy (#9543) ([ipfs/kubo#9543](https://github.com/ipfs/kubo/pull/9543)) + - chore: update version +- github.com/libp2p/go-libp2p-pubsub (v0.8.2 -> v0.8.3): + - feat: expire messages from the cache based on last seen time (#513) ([libp2p/go-libp2p-pubsub#513](https://github.com/libp2p/go-libp2p-pubsub/pull/513)) + +
+ +### 👨‍👩‍👧‍👦 Contributors + +| Contributor | Commits | Lines ± | Files Changed | +|-------------|---------|---------|---------------| +| Mohsin Zaidi | 2 | +511/-55 | 12 | +| Antonio Navarro Perez | 2 | +57/-57 | 5 | +| galargh | 1 | +1/-1 | 1 | + ## v0.18.0 ### Overview @@ -10,18 +90,18 @@ Below is an outline of all that is in this release, so you get a sense of all th - [Overview](#overview) - [🔦 Highlights](#-highlights) - - [Content routing](#content-routing) - - [Default InterPlanetary Network Indexer](#default-interplanetary-network-indexer) - - [Increase provider record republish interval and expiration](#increase-provider-record-republish-interval-and-expiration) - - [Gateways](#gateways) - - [DAG-JSON and DAG-CBOR response formats](#dag-json-and-dag-cbor-response-formats) - - [🐎 Fast directory listings with DAG sizes](#-fast-directory-listings-with-dag-sizes) - - [QUIC and WebTransport](#quic-and-webtransport) - - [WebTransport enabled by default](#webtransport-enabled-by-default) - - [QUIC and WebTransport share a single port](#quic-and-webtransport-share-a-single-port) - - [Differentiating QUIC versions](#differentiating-quic-versions) - - [QUICv1 and WebTransport config migration](#quicv1-and-webtransport-config-migration) - - [Improving libp2p resource management integration](#improving-libp2p-resource-management-integration) + - [Content routing](#content-routing) + - [Default InterPlanetary Network Indexer](#default-interplanetary-network-indexer) + - [Increase provider record republish interval and expiration](#increase-provider-record-republish-interval-and-expiration) + - [Gateways](#gateways) + - [(DAG-)JSON and (DAG-)CBOR response formats](#dag-json-and-dag-cbor-response-formats) + - [🐎 Fast directory listings with DAG sizes](#-fast-directory-listings-with-dag-sizes) + - [QUIC and WebTransport](#quic-and-webtransport) + - [WebTransport enabled by default](#webtransport-enabled-by-default) + - [QUIC and WebTransport share a single port](#quic-and-webtransport-share-a-single-port) + - [Differentiating QUIC versions](#differentiating-quic-versions) + - [QUICv1 and WebTransport config migration](#quicv1-and-webtransport-config-migration) + - [Improving libp2p resource management integration](#improving-libp2p-resource-management-integration) - [📝 Changelog](#-changelog) - [👨‍👩‍👧‍👦 Contributors](#-contributors) @@ -54,7 +134,7 @@ Learn more in the [`Routing` docs](https://github.com/ipfs/kubo/blob/master/docs Default `Reprovider.Interval` changed from 12h to 22h to match new defaults for the Provider Record Expiration (48h) in [go-libp2p-kad-dht v0.20.0](https://github.com/libp2p/go-libp2p-kad-dht/releases/tag/v0.20.0). The rationale for increasing this can be found in -[RFM 17: Provider Record Livenes Report](https://github.com/protocol/network-measurements/blob/master/results/rfm17-provider-record-liveness.md), +[RFM 17: Provider Record Liveness Report](https://github.com/protocol/network-measurements/blob/master/results/rfm17-provider-record-liveness.md), [kubo#9326](https://github.com/ipfs/kubo/pull/9326), and the upstream DHT specifications at [libp2p/specs#451](https://github.com/libp2p/specs/pull/451). @@ -64,19 +144,74 @@ Learn more in the [`Reprovider` config](https://github.com/ipfs/go-ipfs/blob/mas ##### (DAG-)JSON and (DAG-)CBOR response formats -Implemented [IPIP-328](https://github.com/ipfs/specs/pull/328) which adds support -for DAG-JSON and DAG-CBOR, as well as their non-DAG variants, to the gateway. Now, -CIDs that encode JSON, CBOR, DAG-JSON and DAG-CBOR objects can be retrieved, and -traversed thanks to the [special meaning of CBOR Tag 42](https://github.com/ipld/cid-cbor/). +The IPFS project has reserved the corresponding media types at IANA: +- [`application/vnd.ipld.dag-json`](https://www.iana.org/assignments/media-types/application/vnd.ipld.dag-json) +- [`application/vnd.ipld.dag-cbor`](https://www.iana.org/assignments/media-types/application/vnd.ipld.dag-cbor) -HTTP clients can request JSON, CBOR, DAG-JSON, and DAG-CBOR responses by either -passing the query parameter `?format` or setting the `Accept` HTTP header to the -following values: +This release implements them as part of [IPIP-328](https://github.com/ipfs/specs/pull/328) +and adds Gateway support for CIDs with `json` (0x0200), `cbor` (0x51), +[`dag-json`](https://ipld.io/specs/codecs/dag-json/) (0x0129) +and [`dag-cbor`](https://ipld.io/specs/codecs/dag-cbor/spec/) (0x71) codecs. -- JSON: `?format=json`, or `Accept: application/json` -- CBOR: `?format=cbor`, or `Accept: application/cbor` -- DAG-JSON: `?format=dag-json`, or `Accept: application/vnd.ipld.dag-json` -- DAG-JSON: `?format=dag-cbor`, or `Accept: application/vnd.ipld.dag-cbor` +To specify the response `Content-Type` explicitly, the HTTP client can override +the codec present in the CID by using the `format` parameter +or setting the `Accept` HTTP header: + +- Plain JSON: `?format=json` or `Accept: application/json` +- Plain CBOR: `?format=cbor` or `Accept: application/cbor` +- DAG-JSON: `?format=dag-json` or `Accept: application/vnd.ipld.dag-json` +- DAG-CBOR: `?format=dag-cbor` or `Accept: application/vnd.ipld.dag-cbor` + +In addition, when DAG-JSON or DAG-CBOR is requested with the `Accept` header +set to `text/html`, the Gateway will return a basic HTML page with download +options, improving the user experience in web browsers. + +###### Example 1: DAG-CBOR and DAG-JSON Conversion on Gateway + +The Gateway supports conversion between DAG-CBOR and DAG-JSON for efficient +end-to-end data structure management: author in CBOR or JSON, store as binary +CBOR and retrieve as JSON via HTTP: + +```console +$ echo '{"test": "json"}' | ipfs dag put # implicit --input-codec dag-json --store-codec dag-cbor +bafyreico7mjtqtqhvawro3yud5uqn6sc33nzqb7b5j2d7pdmzer5nab4t4 + +$ ipfs block get bafyreico7mjtqtqhvawro3yud5uqn6sc33nzqb7b5j2d7pdmzer5nab4t4 | xxd +00000000: a164 7465 7374 646a 736f 6e .dtestdjson + +$ ipfs dag get bafyreico7mjtqtqhvawro3yud5uqn6sc33nzqb7b5j2d7pdmzer5nab4t4 # implicit --output-codec dag-json +{"test":"json"} + +$ curl "http://127.0.0.1:8080/ipfs/bafyreico7mjtqtqhvawro3yud5uqn6sc33nzqb7b5j2d7pdmzer5nab4t4?format=dag-json" +{"test":"json"} +``` + +###### Example 2: Traversing CBOR DAGs + +Placing a CID in [CBOR Tag 42](https://github.com/ipld/cid-cbor/) enables the +creation of arbitrary DAGs. The equivalent DAG-JSON notation for linking +to different blocks is represented by `{ "/": "cid" }`. + +The Gateway supports traversing these links, enabling access to data +referenced by structures other than regular UnixFS directories: + +```console +$ echo '{"test.jpg": {"/": "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi"}}' | ipfs dag put +bafyreihspwy3zlkzgphmec5d3xb5g5njrqwotd46lyubnelbzktnmsxkq4 # dag-cbor document linking to unixfs file + +$ ipfs resolve /ipfs/bafyreihspwy3zlkzgphmec5d3xb5g5njrqwotd46lyubnelbzktnmsxkq4/test.jpg +/ipfs/bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi + +$ ipfs dag stat bafyreihspwy3zlkzgphmec5d3xb5g5njrqwotd46lyubnelbzktnmsxkq4 +Size: 119827, NumBlocks: 2 + +$ curl "http://127.0.0.1:8080/ipfs/bafyreihspwy3zlkzgphmec5d3xb5g5njrqwotd46lyubnelbzktnmsxkq4/test.jpg" > test.jpg +``` + +###### Example 3: UnixFS directory listing as JSON + +Finally, Gateway now supports the same [logical format projection](https://ipld.io/specs/codecs/dag-pb/spec/#logical-format) from +DAG-PB to DAG-JSON as the `ipfs dag get` command, enabling the retrieval of directory listings as JSON instead of HTML: ```console $ export DIR_CID=bafybeigccimv3zqm5g4jt363faybagywkvqbrismoquogimy7kvz2sj7sq @@ -112,6 +247,8 @@ $ curl "http://127.0.0.1:8080/ipfs/$DIR_CID?format=dag-json" | jq } ] } +$ ipfs dag get $DIR_CID +{"Data":{"/":{"bytes":"CAE"}},"Links":[{"Hash":{"/":"Qmc3zqKcwzbbvw3MQm3hXdg8BQoFjGdZiGdAfXAyAGGdLi"},"Name":"1 - Barrel - Part 1 - alt.txt","Tsize":21},{"Hash":{"/":"QmdMxMx29KVYhHnaCc1icWYxQqXwUNCae6t1wS2NqruiHd"},"Name":"1 - Barrel - Part 1 - transcript.txt","Tsize":195},{"Hash":{"/":"QmawceGscqN4o8Y8Fv26UUmB454kn2bnkXV5tEQYc4jBd6"},"Name":"1 - Barrel - Part 1.png","Tsize":24862}]} ``` ##### 🐎 Fast directory listings with DAG sizes @@ -130,7 +267,7 @@ since Kubo 0.13, but in this release it will also include the size column. ##### WebTransport enabled by default [WebTransport](https://docs.libp2p.io/concepts/transports/webtransport/) is a new libp2p transport that [was introduced in v0.16](https://github.com/ipfs/kubo/blob/master/docs/changelogs/v0.16.md#-webtransport-new-experimental-transport) that is based on top of QUIC and HTTP3. -This allows browsers to contact Kubo nodes, so now instead of just serving requests for other system level applicative nodes, you can also serve requests directly to a browser. +This allows browser-based nodes to contact Kubo nodes, so now instead of just serving requests for other system-level application nodes, you can also serve requests directly to a node running inside a browser page. For the full story see [connectivity.libp2p.io](https://connectivity.libp2p.io/). @@ -148,7 +285,7 @@ go-libp2p do supports listening with both QUIC versions on one single listener. WebTransport has only supported QUICv1. `/webtransport` now needs to be prefixed by a `/quic-v1` component instead of a `/quic` component. -Support for QUIC Draft-29 will be removed at some point in 2023 ([tracking issue](https://github.com/ipfs/kubo/issues/9496)). As a result, new deployements should use `/quic-v1` instead of `/quic`. +Support for QUIC Draft-29 will be removed at some point in 2023 ([tracking issue](https://github.com/ipfs/kubo/issues/9496)). As a result, new deployments should use `/quic-v1` instead of `/quic`. ##### QUICv1 and WebTransport config migration To support QUICv1 and WebTransport by default a new config migration (`v13`) is run which automatically adds entries in addresses-related fields: @@ -159,7 +296,7 @@ To support QUICv1 and WebTransport by default a new config migration (`v13`) is To help protect nodes from DoS (resource exhaustion) and eclipse attacks, Kubo enabled the [go-libp2p Network Resource Manager](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager) by default in [Kubo 0.17](https://github.com/ipfs/kubo/blob/master/docs/changelogs/v0.17.md#libp2p-resource-management-enabled-by-default). - + Introducing limits like this by default after the fact is tricky, and various improvements have been made to improve the UX including: 1. [Dedicated docs concerning the resource manager integration](https://github.com/ipfs/kubo/blob/master/docs/libp2p-resource-management.md). This is a great place to go to learn more or get your FAQs answered. @@ -170,4 +307,541 @@ and various improvements have been made to improve the UX including: ### 📝 Changelog +
Full Changelog + +- github.com/ipfs/kubo: + - fix: clarity: no user supplied rcmgr limits of 0 (#9563) ([ipfs/kubo#9563](https://github.com/ipfs/kubo/pull/9563)) + - fix(gateway): undesired conversions to dag-json and friends (#9566) ([ipfs/kubo#9566](https://github.com/ipfs/kubo/pull/9566)) + - fix: ensure connmgr is smaller then autoscalled ressource limits + - fix: typo in ensureConnMgrMakeSenseVsResourcesMgr + - docs: clarify browser descriptions for webtransport + - fix: update saxon download path + - fix: refuse to start if connmgr is smaller than ressource limits and not using none connmgr + - fix: User-Agent sent to HTTP routers + - test: port gateway sharness tests to Go tests + - fix: do not download saxon in parallel + - docs: improve docs/README (#9539) ([ipfs/kubo#9539](https://github.com/ipfs/kubo/pull/9539)) + - test: port CircleCI to GH Actions and improve sharness reporting (#9355) ([ipfs/kubo#9355](https://github.com/ipfs/kubo/pull/9355)) + - chore: migrate from go-ipfs-files to go-libipfs/files (#9535) ([ipfs/kubo#9535](https://github.com/ipfs/kubo/pull/9535)) + - fix: stats dht command when Routing.Type=auto (#9538) ([ipfs/kubo#9538](https://github.com/ipfs/kubo/pull/9538)) + - fix: hint people to changing from RSA peer ids + - fix(gateway): JSON when Accept is a list + - fix(test): retry flaky t0125-twonode.sh + - docs: fix Router config Godoc (#9528) ([ipfs/kubo#9528](https://github.com/ipfs/kubo/pull/9528)) + - fix(ci): flaky sharness test + - docs(config): ProviderSearchDelay (#9526) ([ipfs/kubo#9526](https://github.com/ipfs/kubo/pull/9526)) + - docs: clarify debug environment variables + - chore: update version.go + - fix(test): stabilize flaky provider tests + - feat: port pins CLI test + - Removing QRI from early tester ([ipfs/kubo#9503](https://github.com/ipfs/kubo/pull/9503)) + - fix: disable provide over HTTP with Routing.Type=auto (#9511) ([ipfs/kubo#9511](https://github.com/ipfs/kubo/pull/9511)) + - Update version.go + - 'chore: update version.go' + - Clened up 0.18 changelog for release ([ipfs/kubo#9497](https://github.com/ipfs/kubo/pull/9497)) + - feat: turn on WebTransport by default ([ipfs/kubo#9492](https://github.com/ipfs/kubo/pull/9492)) + - feat: fast directory listings with DAG Size column (#9481) ([ipfs/kubo#9481](https://github.com/ipfs/kubo/pull/9481)) + - feat: add basic CLI tests using Go Test + - Changelog: v0.18.0 ([ipfs/kubo#9485](https://github.com/ipfs/kubo/pull/9485)) + - feat: go-libp2p-kad-dht with expiration 48h + - chore: update go-libp2p to v0.24.1 + - fix: remove the imports work-around + - fix: replace quic to quic-v1 for webtransport sharness + - fix: silence staticcheck warning for fx.Extract usage + - update go-libp2p to v0.24.0 + - stop using the deprecated go-libp2p-loggables package + - docs(readme): update package managers section (#9488) ([ipfs/kubo#9488](https://github.com/ipfs/kubo/pull/9488)) + - fix: support /quic-v1 in webui v0.21 + - feat: Routing.Type=auto (DHT+IPNI) (#9475) ([ipfs/kubo#9475](https://github.com/ipfs/kubo/pull/9475)) + - feat: adjust ConnMgr target to 32-96 (#9483) ([ipfs/kubo#9483](https://github.com/ipfs/kubo/pull/9483)) + - feat: increase default Reprovider.Interval (#9326) ([ipfs/kubo#9326](https://github.com/ipfs/kubo/pull/9326)) + - feat: add response body limiter to routing HTTP client (#9478) ([ipfs/kubo#9478](https://github.com/ipfs/kubo/pull/9478)) + - docs: libp2p resource management (#9468) ([ipfs/kubo#9468](https://github.com/ipfs/kubo/pull/9468)) + - chore: upgrade libipfs for routing HTTP API schema changes (#9477) ([ipfs/kubo#9477](https://github.com/ipfs/kubo/pull/9477)) + - feat: lower connection pool + - Add missing && + - Fix sharness test + - Added a message when RM is disabled. + - Requested changes. + - Fix sharness checking daemon output + - Update test/sharness/t0060-daemon.sh + - Try to fix sharness test. + - Fix: RM: Improve init RM message and fix final memory value. + - Fix: Resource Manager: Filter stats correctly by % + - Apply suggestions from code review + - Increase MaxMemory param to use half of total memory. + - Update libipfs dependency. + - Add sharness tests and documentation + - Fix variable name + - feature: delegated-routing: Add HTTP delegated routing. + - Fix: Change RM log output to WARN level + - Fix: RM: Set no-limit value to 1e9 (1000000000). + - Partial Revert "Revert "fix: ensure hasher is registered when using a hashing function"" + - Add logs to the routing system + - fix: apply agent-version-suffix to libp2p identify + - chore: migrate ipfs/tar-utils to libipfs + - feat(gateway): JSON and CBOR response formats (IPIP-328) (#9335) ([ipfs/kubo#9335](https://github.com/ipfs/kubo/pull/9335)) + - ([ipfs/kubo#9318](https://github.com/ipfs/kubo/pull/9318)) + - docs: release process updates from v0.17.0 ([ipfs/kubo#9391](https://github.com/ipfs/kubo/pull/9391)) + - fix(rcmgr): improve error phrasing + - docs: Update CHANGELOG.md adding 0.17 link + - feat(config): Pubsub.SeenMessagesTTL (#9372) ([ipfs/kubo#9372](https://github.com/ipfs/kubo/pull/9372)) + - docs: remove snap and chocolatey packages + - Merge release v0.17.0 ([ipfs/kubo#9431](https://github.com/ipfs/kubo/pull/9431)) + - docs: ipfs-http-client -> kubo-rpc-client (#9331) ([ipfs/kubo#9331](https://github.com/ipfs/kubo/pull/9331)) + - docs(readme): improve tldr + - Update config.md for resource management limits (#9421) ([ipfs/kubo#9421](https://github.com/ipfs/kubo/pull/9421)) + - Doc improvements and changelog for resource manager (#9413) ([ipfs/kubo#9413](https://github.com/ipfs/kubo/pull/9413)) + - Revert "Doc improvements for rcmgr" + - Doc improvements for rcmgr + - docs: document /wss fixes in 0.17 + - refactor(config): remove Swarm.ConnMgr defaults + - fix(config): skip nulls in ResourceMgr + - docs: replace tabcat with aphelionz in EARLY_TESTERS.md (#9404) ([ipfs/kubo#9404](https://github.com/ipfs/kubo/pull/9404)) + - docs: fix spoiler for 0.13.1 changelog + - fix(docs): typo + - chore: bump version to v0.18.0-dev ([ipfs/kubo#9393](https://github.com/ipfs/kubo/pull/9393)) +- github.com/ipfs/go-bitswap (v0.10.2 -> v0.11.0): + - chore: release v0.11.0 +- github.com/ipfs/go-blockservice (v0.4.0 -> v0.5.0): + - chore: release v0.5.0 +- github.com/ipfs/go-graphsync (v0.13.1 -> v0.14.1): + - chore: version 0.14.1 (#400) ([ipfs/go-graphsync#400](https://github.com/ipfs/go-graphsync/pull/400)) + - chore: migrate files (#399) ([ipfs/go-graphsync#399](https://github.com/ipfs/go-graphsync/pull/399)) + - docs(CHANGELOG): update for v0.14.0 release + - updates for libp2p v0.22 (#392) ([ipfs/go-graphsync#392](https://github.com/ipfs/go-graphsync/pull/392)) + - feat(ipld): use bindnode/registry (#386) ([ipfs/go-graphsync#386](https://github.com/ipfs/go-graphsync/pull/386)) + - Accept/Reject requests up front (#384) ([ipfs/go-graphsync#384](https://github.com/ipfs/go-graphsync/pull/384)) + - Remove protobuf protocol (#385) ([ipfs/go-graphsync#385](https://github.com/ipfs/go-graphsync/pull/385)) + - docs(CHANGELOG): update for v0.13.2 + - chore(deps): upgrade libp2p & ipld-prime (#389) ([ipfs/go-graphsync#389](https://github.com/ipfs/go-graphsync/pull/389)) + - chore(ipld): switch to using top-level ipld-prime codec helpers (#383) ([ipfs/go-graphsync#383](https://github.com/ipfs/go-graphsync/pull/383)) + - feat(requestmanager): read request from context (#381) ([ipfs/go-graphsync#381](https://github.com/ipfs/go-graphsync/pull/381)) + - fix: minor typo in error msg + - fix(panics): lift panic recovery up to top of network handling + - feat: expand use of panic handler to cover network and codec interaction + - feat(panics): capture panics from selector execution +- github.com/ipfs/go-ipfs-cmds (v0.8.1 -> v0.8.2): + - chore: version v0.8.2 (#235) ([ipfs/go-ipfs-cmds#235](https://github.com/ipfs/go-ipfs-cmds/pull/235)) + - chore: migrate files (#233) ([ipfs/go-ipfs-cmds#233](https://github.com/ipfs/go-ipfs-cmds/pull/233)) + - sync: update CI config files (#229) ([ipfs/go-ipfs-cmds#229](https://github.com/ipfs/go-ipfs-cmds/pull/229)) +- github.com/ipfs/go-ipfs-keystore (v0.0.2 -> v0.1.0): + - chore: release v0.1.0 + - chore: update go-libp2p + - sync: update CI config files ([ipfs/go-ipfs-keystore#10](https://github.com/ipfs/go-ipfs-keystore/pull/10)) + - sync: update CI config files ([ipfs/go-ipfs-keystore#8](https://github.com/ipfs/go-ipfs-keystore/pull/8)) + - Add link to pkg.go.dev + - README: this module does not use Gx +- github.com/ipfs/go-ipfs-provider (v0.7.1 -> v0.8.1): + - chore: release v0.8.1 + - fix: make queue 64bits on 32bits platforms too + - sync: update CI config files ([ipfs/go-ipfs-provider#36](https://github.com/ipfs/go-ipfs-provider/pull/36)) + - chore: update go-lib2p, avoid depending on go-libp2p-core, bump go.mod version +- github.com/ipfs/go-ipfs-routing (v0.2.1 -> v0.3.0): + - release v0.3.0 ([ipfs/go-ipfs-routing#36](https://github.com/ipfs/go-ipfs-routing/pull/36)) + - chore: update go-libp2p to v0.22.0 ([ipfs/go-ipfs-routing#35](https://github.com/ipfs/go-ipfs-routing/pull/35)) + - sync: update CI config files (#34) ([ipfs/go-ipfs-routing#34](https://github.com/ipfs/go-ipfs-routing/pull/34)) +- github.com/ipfs/go-ipld-cbor (v0.0.5 -> v0.0.6): + - Add contexts to IpldBlockstore ([ipfs/go-ipld-cbor#82](https://github.com/ipfs/go-ipld-cbor/pull/82)) + - sync: update CI config files (#81) ([ipfs/go-ipld-cbor#81](https://github.com/ipfs/go-ipld-cbor/pull/81)) + - sync: update CI config files ([ipfs/go-ipld-cbor#79](https://github.com/ipfs/go-ipld-cbor/pull/79)) + - Fix lint errors ([ipfs/go-ipld-cbor#78](https://github.com/ipfs/go-ipld-cbor/pull/78)) + - Add notice directing new projects to codec/dagcbor ([ipfs/go-ipld-cbor#77](https://github.com/ipfs/go-ipld-cbor/pull/77)) +- github.com/ipfs/go-merkledag (v0.6.0 -> v0.9.0): + - chore: bump version to 0.9.0 + - chore: bump version to 0.8.1 + - feat: remove panic() from non-error methods + - feat: improve broken cid.Builder testing for CidBuilder + - chore: bump version to 0.8.0 + - doc: document potential panics and how to avoid them + - fix: simplify Cid generation cache & usage + - feat: check links on setting and sanitise on encoding + - feat: check that the CidBuilder hasher is usable + - chore: bump version to 0.7.0 + - fix: remove use of ioutil + - run gofmt -s + - fix!: keep deserialised state stable until explicit mutation + - sync: update CI config files ([ipfs/go-merkledag#84](https://github.com/ipfs/go-merkledag/pull/84)) +- github.com/ipfs/go-namesys (v0.5.0 -> v0.6.0): + - chore: release v0.6.0 + - chore: update go-libp2p to v0.23.4, update go.mod version to 1.18 + - stop using the deprecated io/ioutil package +- github.com/ipfs/go-peertaskqueue (v0.7.1 -> v0.8.0): + - Release v0.8.0 ([ipfs/go-peertaskqueue#25](https://github.com/ipfs/go-peertaskqueue/pull/25)) + - chore: update go-libp2p to v0.22.0 ([ipfs/go-peertaskqueue#24](https://github.com/ipfs/go-peertaskqueue/pull/24)) + - sync: update CI config files (#23) ([ipfs/go-peertaskqueue#23](https://github.com/ipfs/go-peertaskqueue/pull/23)) + - sync: update CI config files (#21) ([ipfs/go-peertaskqueue#21](https://github.com/ipfs/go-peertaskqueue/pull/21)) +- github.com/ipfs/go-unixfs (v0.4.1 -> v0.4.2): + - chore: version 0.4.2 (#136) ([ipfs/go-unixfs#136](https://github.com/ipfs/go-unixfs/pull/136)) + - chore: migrate files (#134) ([ipfs/go-unixfs#134](https://github.com/ipfs/go-unixfs/pull/134)) + - ([ipfs/go-unixfs#128](https://github.com/ipfs/go-unixfs/pull/128)) +- github.com/ipfs/go-unixfsnode (v1.4.0 -> v1.5.1): + - v1.5.1 + - fix a possible `index out of range` crash ([ipfs/go-unixfsnode#39](https://github.com/ipfs/go-unixfsnode/pull/39)) + - add an ADL to preload hamt loading ([ipfs/go-unixfsnode#38](https://github.com/ipfs/go-unixfsnode/pull/38)) + - chore: bump version to 1.5.0 + - fix: remove use of ioutil + - run gofmt -s + - bump go.mod to Go 1.18 and run go fix + - test for reader / sizing behavior on large files ([ipfs/go-unixfsnode#34](https://github.com/ipfs/go-unixfsnode/pull/34)) + - add helper to approximate test creation patter from ipfs-files ([ipfs/go-unixfsnode#32](https://github.com/ipfs/go-unixfsnode/pull/32)) + - chore: remove Stebalien/go-bitfield in favour of ipfs/go-bitfield +- github.com/ipfs/interface-go-ipfs-core (v0.7.0 -> v0.8.2): + - chore: version 0.8.2 (#100) ([ipfs/interface-go-ipfs-core#100](https://github.com/ipfs/interface-go-ipfs-core/pull/100)) + - chore: migrate files (#97) ([ipfs/interface-go-ipfs-core#97](https://github.com/ipfs/interface-go-ipfs-core/pull/97)) + - chore: release v0.8.1 + - feat: add UseCumulativeSize UnixfsLs option (#95) ([ipfs/interface-go-ipfs-core#95](https://github.com/ipfs/interface-go-ipfs-core/pull/95)) + - chore: release v0.8.0 + - chore: update go-libp2p to v0.23.4 + - sync: update CI config files (#87) ([ipfs/interface-go-ipfs-core#87](https://github.com/ipfs/interface-go-ipfs-core/pull/87)) +- github.com/ipld/go-car/v2 (v2.4.0 -> v2.5.1): + - add a `SkipNext` method on block reader (#338) ([ipld/go-car#338](https://github.com/ipld/go-car/pull/338)) + - feat: Has() and Get() will respect StoreIdentityCIDs option + - chore: bump version to 0.5.0 + - fix: remove use of ioutil + - run gofmt -s + - bump go.mod to Go 1.18 and run go fix + - bump go.mod to Go 1.18 and run go fix + - OpenReadWriteFile: add test + - blockstore: allow to pass a file to write in (#323) ([ipld/go-car#323](https://github.com/ipld/go-car/pull/323)) + - feat: add `car inspect` command to cmd pkg (#320) ([ipld/go-car#320](https://github.com/ipld/go-car/pull/320)) + - Separate `index.ReadFrom` tests + - Only read index codec during inspection + - Upgrade to the latest `go-car/v2` + - Empty identity CID should be indexed when options are set +- github.com/ipld/go-codec-dagpb (v1.4.1 -> v1.5.0): + - chore: version bump to 1.5.0 + - fix: replace io/ioutil with io + - bump go.mod to Go 1.18 and run go fix + - v1.4.2 bump +- github.com/libp2p/go-libp2p (v0.23.4 -> v0.24.2): + - release v0.24.2 (#1969) ([libp2p/go-libp2p#1969](https://github.com/libp2p/go-libp2p/pull/1969)) + - webtransport: initialize a NullResourceManager if none is provided (#1962) ([libp2p/go-libp2p#1962](https://github.com/libp2p/go-libp2p/pull/1962)) + - release v0.24.1 (#1945) ([libp2p/go-libp2p#1945](https://github.com/libp2p/go-libp2p/pull/1945)) + - routed host: return Connect error if FindPeer doesn't yield new addresses (#1946) ([libp2p/go-libp2p#1946](https://github.com/libp2p/go-libp2p/pull/1946)) + - chore: update examples to v0.24.0 (#1936) ([libp2p/go-libp2p#1936](https://github.com/libp2p/go-libp2p/pull/1936)) + - webtransport: fix flaky accept queue test (#1938) ([libp2p/go-libp2p#1938](https://github.com/libp2p/go-libp2p/pull/1938)) + - quic: fix race condition in TestClientCanDialDifferentQUICVersions (#1937) ([libp2p/go-libp2p#1937](https://github.com/libp2p/go-libp2p/pull/1937)) + - quic: update quic-go to v0.31.1 (#1942) ([libp2p/go-libp2p#1942](https://github.com/libp2p/go-libp2p/pull/1942)) + - release v0.24.0 (#1934) ([libp2p/go-libp2p#1934](https://github.com/libp2p/go-libp2p/pull/1934)) + - Disable support for signed/static TLS certificates in WebTransport (#1927) ([libp2p/go-libp2p#1927](https://github.com/libp2p/go-libp2p/pull/1927)) + - webtransport: add PSK to constructor, and fail if it is used (#1929) ([libp2p/go-libp2p#1929](https://github.com/libp2p/go-libp2p/pull/1929)) + - use a different set of default transports when PSK is enabled (#1921) ([libp2p/go-libp2p#1921](https://github.com/libp2p/go-libp2p/pull/1921)) + - transport.Listener,quic: Support multiple QUIC versions with the same Listener. Only return a single multiaddr per listener. (#1923) ([libp2p/go-libp2p#1923](https://github.com/libp2p/go-libp2p/pull/1923)) + - quic / webtransport: make it possible to listen on the same address / port (#1905) ([libp2p/go-libp2p#1905](https://github.com/libp2p/go-libp2p/pull/1905)) + - autorelay: fix flaky TestReconnectToStaticRelays (#1903) ([libp2p/go-libp2p#1903](https://github.com/libp2p/go-libp2p/pull/1903)) + - swarm / rcmgr: synchronize the concurrent outbound dials with limits (#1898) ([libp2p/go-libp2p#1898](https://github.com/libp2p/go-libp2p/pull/1898)) + - add QUIC v1 addresses to the default listen addresses (#1914) ([libp2p/go-libp2p#1914](https://github.com/libp2p/go-libp2p/pull/1914)) + - webtransport: update webtransport-go to v0.3.0 (#1895) ([libp2p/go-libp2p#1895](https://github.com/libp2p/go-libp2p/pull/1895)) + - tls: fix flaky TestHandshakeConnectionCancellations test (#1896) ([libp2p/go-libp2p#1896](https://github.com/libp2p/go-libp2p/pull/1896)) + - holepunch: disable the resource manager in tests (#1897) ([libp2p/go-libp2p#1897](https://github.com/libp2p/go-libp2p/pull/1897)) + - transports: expose the name of the transport in the ConnectionState (#1911) ([libp2p/go-libp2p#1911](https://github.com/libp2p/go-libp2p/pull/1911)) + - respect the user's security protocol preference order ([libp2p/go-libp2p#1912](https://github.com/libp2p/go-libp2p/pull/1912)) + - circuitv2: disable the resource manager in tests (#1899) ([libp2p/go-libp2p#1899](https://github.com/libp2p/go-libp2p/pull/1899)) + - expose the security protocol on the ConnectionState ([libp2p/go-libp2p#1907](https://github.com/libp2p/go-libp2p/pull/1907)) + - fix: autorelay: treat static relays as just another peer source (#1875) ([libp2p/go-libp2p#1875](https://github.com/libp2p/go-libp2p/pull/1875)) + - feat: quic,webtransport: enable both quic-draft29 and quic-v1 addrs on quic. only quic-v1 on webtransport (#1881) ([libp2p/go-libp2p#1881](https://github.com/libp2p/go-libp2p/pull/1881)) + - holepunch: add multiaddress filter (#1839) ([libp2p/go-libp2p#1839](https://github.com/libp2p/go-libp2p/pull/1839)) + - README: remove broken links from table of contents (#1893) ([libp2p/go-libp2p#1893](https://github.com/libp2p/go-libp2p/pull/1893)) + - quic: update quic-go to v0.31.0 (#1882) ([libp2p/go-libp2p#1882](https://github.com/libp2p/go-libp2p/pull/1882)) + - add an integration test for muxer selection ([libp2p/go-libp2p#1887](https://github.com/libp2p/go-libp2p/pull/1887)) + - core/network: fix typo + - tls / noise: prefer the client's muxer preferences ([libp2p/go-libp2p#1888](https://github.com/libp2p/go-libp2p/pull/1888)) + - upgrader: absorb the muxer_multistream.Transport into the upgrader (#1885) ([libp2p/go-libp2p#1885](https://github.com/libp2p/go-libp2p/pull/1885)) + - Apply service peer default (#1878) ([libp2p/go-libp2p#1878](https://github.com/libp2p/go-libp2p/pull/1878)) + - webtransport: use deterministic TLS certificates (#1833) ([libp2p/go-libp2p#1833](https://github.com/libp2p/go-libp2p/pull/1833)) + - remove deprecated StaticRelays option (#1868) ([libp2p/go-libp2p#1868](https://github.com/libp2p/go-libp2p/pull/1868)) + - autorelay: remove the default static relay option (#1867) ([libp2p/go-libp2p#1867](https://github.com/libp2p/go-libp2p/pull/1867)) + - core/protocol: remove deprecated Negotiator.NegotiateLazy (#1869) ([libp2p/go-libp2p#1869](https://github.com/libp2p/go-libp2p/pull/1869)) + - config: use fx dependency injection to construct transports ([libp2p/go-libp2p#1858](https://github.com/libp2p/go-libp2p/pull/1858)) + - noise: add an option to allow unknown peer ID in SecureOutbound (#1823) ([libp2p/go-libp2p#1823](https://github.com/libp2p/go-libp2p/pull/1823)) + - Add some guard rails and docs (#1863) ([libp2p/go-libp2p#1863](https://github.com/libp2p/go-libp2p/pull/1863)) + - Fix concurrent map access in connmgr (#1860) ([libp2p/go-libp2p#1860](https://github.com/libp2p/go-libp2p/pull/1860)) + - fix: return filtered addrs (#1855) ([libp2p/go-libp2p#1855](https://github.com/libp2p/go-libp2p/pull/1855)) + - chore: preallocate slices (#1842) ([libp2p/go-libp2p#1842](https://github.com/libp2p/go-libp2p/pull/1842)) + - Close ping stream when we exit the loop (#1853) ([libp2p/go-libp2p#1853](https://github.com/libp2p/go-libp2p/pull/1853)) + - tls: don't set the deprecated tls.Config.PreferServerCipherSuites field (#1845) ([libp2p/go-libp2p#1845](https://github.com/libp2p/go-libp2p/pull/1845)) + - routed host: search for new multi addresses upon connect failure (#1835) ([libp2p/go-libp2p#1835](https://github.com/libp2p/go-libp2p/pull/1835)) + - core/peerstore: removed unused provider addr ttl constant (#1848) ([libp2p/go-libp2p#1848](https://github.com/libp2p/go-libp2p/pull/1848)) + - basichost: improve protocol negotiation debug message (#1846) ([libp2p/go-libp2p#1846](https://github.com/libp2p/go-libp2p/pull/1846)) + - noise: use Noise Extension to negotiate the muxer during the handshake (#1813) ([libp2p/go-libp2p#1813](https://github.com/libp2p/go-libp2p/pull/1813)) + - webtransport: use the rcmgr to control flow control window increases ([libp2p/go-libp2p#1832](https://github.com/libp2p/go-libp2p/pull/1832)) + - chore: update quic-go to v0.30.0 (#1838) ([libp2p/go-libp2p#1838](https://github.com/libp2p/go-libp2p/pull/1838)) + - roadmap: reorder priority, reorganize sections (#1831) ([libp2p/go-libp2p#1831](https://github.com/libp2p/go-libp2p/pull/1831)) + - websocket: set the HTTP host header in WSS(#1834) ([libp2p/go-libp2p#1834](https://github.com/libp2p/go-libp2p/pull/1834)) + - webtransport: make it possible to record qlogs (controlled by QLOGDIR env) ([libp2p/go-libp2p#1828](https://github.com/libp2p/go-libp2p/pull/1828)) + - ipfs /api/v0/id is post (#1819) ([libp2p/go-libp2p#1819](https://github.com/libp2p/go-libp2p/pull/1819)) + - examples: connect to all peers in example mdns chat app (#1798) ([libp2p/go-libp2p#1798](https://github.com/libp2p/go-libp2p/pull/1798)) + - roadmap: fix header level on "Mid Q4" (#1818) ([libp2p/go-libp2p#1818](https://github.com/libp2p/go-libp2p/pull/1818)) + - examples: use circuitv2 in relay example (#1795) ([libp2p/go-libp2p#1795](https://github.com/libp2p/go-libp2p/pull/1795)) + - add a roadmap for the next 6 months (#1784) ([libp2p/go-libp2p#1784](https://github.com/libp2p/go-libp2p/pull/1784)) + - tls: use ALPN to negotiate the stream multiplexer (#1772) ([libp2p/go-libp2p#1772](https://github.com/libp2p/go-libp2p/pull/1772)) + - tls: add tests for test vector from the spec (#1788) ([libp2p/go-libp2p#1788](https://github.com/libp2p/go-libp2p/pull/1788)) + - examples: update go-libp2p to v0.23.x (#1803) ([libp2p/go-libp2p#1803](https://github.com/libp2p/go-libp2p/pull/1803)) + - Try increasing timeouts if we're in CI for this test (#1796) ([libp2p/go-libp2p#1796](https://github.com/libp2p/go-libp2p/pull/1796)) + - Don't use rcmgr in this test (#1799) ([libp2p/go-libp2p#1799](https://github.com/libp2p/go-libp2p/pull/1799)) + - Bump timeout in CI for flaky test (#1800) ([libp2p/go-libp2p#1800](https://github.com/libp2p/go-libp2p/pull/1800)) + - Bump timeout in CI for flaky test (#1801) ([libp2p/go-libp2p#1801](https://github.com/libp2p/go-libp2p/pull/1801)) + - Fix comment in webtransport client auth handshake (#1793) ([libp2p/go-libp2p#1793](https://github.com/libp2p/go-libp2p/pull/1793)) + - examples: add basic pubsub-with-rendezvous example (#1738) ([libp2p/go-libp2p#1738](https://github.com/libp2p/go-libp2p/pull/1738)) + - quic: speed up the stateless reset test case (#1778) ([libp2p/go-libp2p#1778](https://github.com/libp2p/go-libp2p/pull/1778)) + - tls: fix flaky handshake cancellation test (#1779) ([libp2p/go-libp2p#1779](https://github.com/libp2p/go-libp2p/pull/1779)) +- github.com/libp2p/go-libp2p-gostream (v0.3.0 -> v0.5.0): + - release v0.5.0 (#74) ([libp2p/go-libp2p-gostream#74](https://github.com/libp2p/go-libp2p-gostream/pull/74)) + - update go-libp2p to v0.22.0 (#73) ([libp2p/go-libp2p-gostream#73](https://github.com/libp2p/go-libp2p-gostream/pull/73)) + - Expose some read-only methods on the underlying Stream interface (#67) ([libp2p/go-libp2p-gostream#67](https://github.com/libp2p/go-libp2p-gostream/pull/67)) + - Update libp2p ([libp2p/go-libp2p-gostream#69](https://github.com/libp2p/go-libp2p-gostream/pull/69)) + - sync: update CI config files (#65) ([libp2p/go-libp2p-gostream#65](https://github.com/libp2p/go-libp2p-gostream/pull/65)) + - sync: update CI config files ([libp2p/go-libp2p-gostream#62](https://github.com/libp2p/go-libp2p-gostream/pull/62)) + - fix staticcheck ([libp2p/go-libp2p-gostream#61](https://github.com/libp2p/go-libp2p-gostream/pull/61)) +- github.com/libp2p/go-libp2p-http (v0.2.1 -> v0.4.0): + - release v0.4.0 ([libp2p/go-libp2p-http#81](https://github.com/libp2p/go-libp2p-http/pull/81)) + - sync: update CI config files ([libp2p/go-libp2p-http#79](https://github.com/libp2p/go-libp2p-http/pull/79)) + - Update to latest go-libp2p ([libp2p/go-libp2p-http#80](https://github.com/libp2p/go-libp2p-http/pull/80)) + - Update to latest go-libp2p ([libp2p/go-libp2p-http#78](https://github.com/libp2p/go-libp2p-http/pull/78)) + - sync: update CI config files (#73) ([libp2p/go-libp2p-http#73](https://github.com/libp2p/go-libp2p-http/pull/73)) +- github.com/libp2p/go-libp2p-kad-dht (v0.18.0 -> v0.20.0): + - release v0.20.0 (#803) ([libp2p/go-libp2p-kad-dht#803](https://github.com/libp2p/go-libp2p-kad-dht/pull/803)) + - feat: increase the max record age to 48h (PUT_VALUE, RFM17) (#794) ([libp2p/go-libp2p-kad-dht#794](https://github.com/libp2p/go-libp2p-kad-dht/pull/794)) + - feat: increase expiration time for Provider Records to 48h (RFM17) + - release v0.19.0 (#801) ([libp2p/go-libp2p-kad-dht#801](https://github.com/libp2p/go-libp2p-kad-dht/pull/801)) + - define the ProviderAddrTTL in this repo (#797) ([libp2p/go-libp2p-kad-dht#797](https://github.com/libp2p/go-libp2p-kad-dht/pull/797)) +- github.com/libp2p/go-libp2p-kbucket (v0.4.7 -> v0.5.0): + - chore: release 0.5.0 (#111) ([libp2p/go-libp2p-kbucket#111](https://github.com/libp2p/go-libp2p-kbucket/pull/111)) + - deprecate go-libp2p-core and use go-libp2p instead (#109) ([libp2p/go-libp2p-kbucket#109](https://github.com/libp2p/go-libp2p-kbucket/pull/109)) + - sync: update CI config files (#108) ([libp2p/go-libp2p-kbucket#108](https://github.com/libp2p/go-libp2p-kbucket/pull/108)) + - sync: update CI config files ([libp2p/go-libp2p-kbucket#107](https://github.com/libp2p/go-libp2p-kbucket/pull/107)) + - sync: update CI config files (#104) ([libp2p/go-libp2p-kbucket#104](https://github.com/libp2p/go-libp2p-kbucket/pull/104)) + - sync: update CI config files ([libp2p/go-libp2p-kbucket#101](https://github.com/libp2p/go-libp2p-kbucket/pull/101)) + - sync: update CI config files ([libp2p/go-libp2p-kbucket#99](https://github.com/libp2p/go-libp2p-kbucket/pull/99)) + - fix staticcheck ([libp2p/go-libp2p-kbucket#98](https://github.com/libp2p/go-libp2p-kbucket/pull/98)) +- github.com/libp2p/go-libp2p-pubsub (v0.6.1 -> v0.8.2): + - Add docstring for WithAppSpecificRPCInspector (#510) ([libp2p/go-libp2p-pubsub#510](https://github.com/libp2p/go-libp2p-pubsub/pull/510)) + - Adds Application Specific RPC Inspector (#509) ([libp2p/go-libp2p-pubsub#509](https://github.com/libp2p/go-libp2p-pubsub/pull/509)) + - chore: ignore signing keys during WithLocalPublication publishing (#497) ([libp2p/go-libp2p-pubsub#497](https://github.com/libp2p/go-libp2p-pubsub/pull/497)) + - improve handling of dead peers (#508) ([libp2p/go-libp2p-pubsub#508](https://github.com/libp2p/go-libp2p-pubsub/pull/508)) + - perf: use pooled buffers for message writes (#507) ([libp2p/go-libp2p-pubsub#507](https://github.com/libp2p/go-libp2p-pubsub/pull/507)) + - perf: use msgio pooled buffers for received msgs (#500) ([libp2p/go-libp2p-pubsub#500](https://github.com/libp2p/go-libp2p-pubsub/pull/500)) + - Enables injectable GossipSub router (#503) ([libp2p/go-libp2p-pubsub#503](https://github.com/libp2p/go-libp2p-pubsub/pull/503)) + - Enables non-atomic validation for peer scoring parameters (#499) ([libp2p/go-libp2p-pubsub#499](https://github.com/libp2p/go-libp2p-pubsub/pull/499)) + - update go-libp2p to v0.22.0 (#498) ([libp2p/go-libp2p-pubsub#498](https://github.com/libp2p/go-libp2p-pubsub/pull/498)) + - fix handling of dead peers (#492) ([libp2p/go-libp2p-pubsub#492](https://github.com/libp2p/go-libp2p-pubsub/pull/492)) + - feat: WithLocalPublication option to enable local only publishing on a topic (#481) ([libp2p/go-libp2p-pubsub#481](https://github.com/libp2p/go-libp2p-pubsub/pull/481)) + - update pubsub deps (#491) ([libp2p/go-libp2p-pubsub#491](https://github.com/libp2p/go-libp2p-pubsub/pull/491)) + - Gossipsub: Unsubscribe backoff (#488) ([libp2p/go-libp2p-pubsub#488](https://github.com/libp2p/go-libp2p-pubsub/pull/488)) + - Adds exponential backoff to re-spawing new streams for supposedly dead peers (#483) ([libp2p/go-libp2p-pubsub#483](https://github.com/libp2p/go-libp2p-pubsub/pull/483)) + - Publishing option for signing a message with a custom private key (#486) ([libp2p/go-libp2p-pubsub#486](https://github.com/libp2p/go-libp2p-pubsub/pull/486)) + - fix unused GossipSubHistoryGossip, make seenMessages ttl configurable, make score params SeenMsgTTL configurable + - Update README.md + - Add in Backoff Check + - Modify comment + - Add Backoff For Pruned Peers + - tests: new test for WithTopicMsgIdFunction + - chore: better name + - feat: detach WithMsgIdFunction + - fix: use RawID in traceRPCMeta to avoid allocations + - feat: extract RawID from ID + - chore: hello mister mutex hat + - chore: go fmt and return timecache named import + - feat: new WithMsgIdFunction topic option to enable topics to have own msg id generation rules + - feat: integrate msgIdGenerator + - feat: introduce msgIdGenerator and add ID field to Message wrapper +- github.com/libp2p/go-libp2p-pubsub-router (v0.5.0 -> v0.6.0): + - release v0.6.0 (#99) ([libp2p/go-libp2p-pubsub-router#99](https://github.com/libp2p/go-libp2p-pubsub-router/pull/99)) + - sync: update CI config files (#93) ([libp2p/go-libp2p-pubsub-router#93](https://github.com/libp2p/go-libp2p-pubsub-router/pull/93)) +- github.com/libp2p/go-libp2p-routing-helpers (v0.4.0 -> v0.6.0): + - Update version.json + - Change interface name + - Add tests + - Feat: retrieve routers from composable routers + - Update version.json + - Update version.json + - chore: add regression test for compparallel deadlock + - Add logs to composable parallel + - Bump version to v0.4.1 + - ([libp2p/go-libp2p-routing-helpers#64](https://github.com/libp2p/go-libp2p-routing-helpers/pull/64)) +- github.com/lucas-clemente/quic-go (v0.29.1 -> v0.31.1): + - qerr: include role (remote / local) in error string representations (#3629) ([lucas-clemente/quic-go#3629](https://github.com/lucas-clemente/quic-go/pull/3629)) + - introduce a type for the stateless reset key (#3621) ([lucas-clemente/quic-go#3621](https://github.com/lucas-clemente/quic-go/pull/3621)) + - limit the exponential PTO backoff to 60s (#3595) ([lucas-clemente/quic-go#3595](https://github.com/lucas-clemente/quic-go/pull/3595)) + - expose the QUIC version of a connection (#3620) ([lucas-clemente/quic-go#3620](https://github.com/lucas-clemente/quic-go/pull/3620)) + - expose function to convert byte slice to a connection ID (#3614) ([lucas-clemente/quic-go#3614](https://github.com/lucas-clemente/quic-go/pull/3614)) + - use `go run` for mockgen, goimports and ginkgo (#3616) ([lucas-clemente/quic-go#3616](https://github.com/lucas-clemente/quic-go/pull/3616)) + - fix client SNI handling (#3613) ([lucas-clemente/quic-go#3613](https://github.com/lucas-clemente/quic-go/pull/3613)) + - chore: fix multiple typos in comments (#3612) ([lucas-clemente/quic-go#3612](https://github.com/lucas-clemente/quic-go/pull/3612)) + - use the new zero-allocation control message parsing function from x/sys (#3609) ([lucas-clemente/quic-go#3609](https://github.com/lucas-clemente/quic-go/pull/3609)) + - http3: add support for parsing and writing HTTP/3 capsules (#3607) ([lucas-clemente/quic-go#3607](https://github.com/lucas-clemente/quic-go/pull/3607)) + - http3: add request to response (#3608) ([lucas-clemente/quic-go#3608](https://github.com/lucas-clemente/quic-go/pull/3608)) + - fix availability signaling of the send queue (#3597) ([lucas-clemente/quic-go#3597](https://github.com/lucas-clemente/quic-go/pull/3597)) + - http3: add a ConnectionState method to the StreamCreator interface (#3600) ([lucas-clemente/quic-go#3600](https://github.com/lucas-clemente/quic-go/pull/3600)) + - http3: add a Context method to the StreamCreator interface (#3601) ([lucas-clemente/quic-go#3601](https://github.com/lucas-clemente/quic-go/pull/3601)) + - rename the variable in quic.Config.AllowConnectionWindowIncrease (#3602) ([lucas-clemente/quic-go#3602](https://github.com/lucas-clemente/quic-go/pull/3602)) + - migrate to Ginkgo v2, remove benchmark test ([lucas-clemente/quic-go#3589](https://github.com/lucas-clemente/quic-go/pull/3589)) + - don't drop more than 10 consecutive packets in drop test (#3584) ([lucas-clemente/quic-go#3584](https://github.com/lucas-clemente/quic-go/pull/3584)) + - use a monotonous timer for the connection (#3570) ([lucas-clemente/quic-go#3570](https://github.com/lucas-clemente/quic-go/pull/3570)) + - http3: add http3.Server.ServeQUICConn to serve a single QUIC connection (#3587) ([lucas-clemente/quic-go#3587](https://github.com/lucas-clemente/quic-go/pull/3587)) + - http3: expose ALPN values (#3580) ([lucas-clemente/quic-go#3580](https://github.com/lucas-clemente/quic-go/pull/3580)) + - log the size of buffered packets (#3571) ([lucas-clemente/quic-go#3571](https://github.com/lucas-clemente/quic-go/pull/3571)) + - ackhandler: reject duplicate packets in ReceivedPacket (#3568) ([lucas-clemente/quic-go#3568](https://github.com/lucas-clemente/quic-go/pull/3568)) + - reduce max DATAGRAM frame size, so that DATAGRAMs fit in IPv6 packets (#3581) ([lucas-clemente/quic-go#3581](https://github.com/lucas-clemente/quic-go/pull/3581)) + - use a Peek / Pop API for the datagram queue (#3582) ([lucas-clemente/quic-go#3582](https://github.com/lucas-clemente/quic-go/pull/3582)) + - http3: handle ErrAbortHandler when the handler panics (#3575) ([lucas-clemente/quic-go#3575](https://github.com/lucas-clemente/quic-go/pull/3575)) + - http3: fix double close of chan when using DontCloseRequestStream (#3561) ([lucas-clemente/quic-go#3561](https://github.com/lucas-clemente/quic-go/pull/3561)) + - qlog: rename key_retired to key_discarded (#3463) ([lucas-clemente/quic-go#3463](https://github.com/lucas-clemente/quic-go/pull/3463)) + - simplify packing of ACK-only packets ([lucas-clemente/quic-go#3545](https://github.com/lucas-clemente/quic-go/pull/3545)) + - use a sync.Pool for ACK frames ([lucas-clemente/quic-go#3547](https://github.com/lucas-clemente/quic-go/pull/3547)) + - prioritize sending ACKs over sending new DATAGRAM frames (#3544) ([lucas-clemente/quic-go#3544](https://github.com/lucas-clemente/quic-go/pull/3544)) + - http3: reduce usage of bytes.Buffer (#3539) ([lucas-clemente/quic-go#3539](https://github.com/lucas-clemente/quic-go/pull/3539)) + - use a single bytes.Reader for frame parsing (#3536) ([lucas-clemente/quic-go#3536](https://github.com/lucas-clemente/quic-go/pull/3536)) + - split code paths for packing 0-RTT and 1-RTT packets in packet packer (#3540) ([lucas-clemente/quic-go#3540](https://github.com/lucas-clemente/quic-go/pull/3540)) + - remove the wire.ShortHeader in favor of more return values (#3535) ([lucas-clemente/quic-go#3535](https://github.com/lucas-clemente/quic-go/pull/3535)) + - preallocate the message buffers of the ipv4.Message passed to ReadBatch (#3541) ([lucas-clemente/quic-go#3541](https://github.com/lucas-clemente/quic-go/pull/3541)) + - introduce a separate code paths for Short Header packet handling ([lucas-clemente/quic-go#3534](https://github.com/lucas-clemente/quic-go/pull/3534)) + - fix usage of ackhandler.Packet pool for non-ack-eliciting packets (#3538) ([lucas-clemente/quic-go#3538](https://github.com/lucas-clemente/quic-go/pull/3538)) + - return an error when parsing a too long connection ID from a header (#3533) ([lucas-clemente/quic-go#3533](https://github.com/lucas-clemente/quic-go/pull/3533)) + - speed up marshaling of transport parameters (#3531) ([lucas-clemente/quic-go#3531](https://github.com/lucas-clemente/quic-go/pull/3531)) + - use a struct containing an array to represent Connection IDs ([lucas-clemente/quic-go#3529](https://github.com/lucas-clemente/quic-go/pull/3529)) + - reduce allocations of ackhandler.Packet ([lucas-clemente/quic-go#3525](https://github.com/lucas-clemente/quic-go/pull/3525)) + - serialize frames by appending to a byte slice, not to a bytes.Buffer ([lucas-clemente/quic-go#3530](https://github.com/lucas-clemente/quic-go/pull/3530)) + - fix datagram RFC number in documentation for quic.Config (#3523) ([lucas-clemente/quic-go#3523](https://github.com/lucas-clemente/quic-go/pull/3523)) + - add DPLPMTUD (RFC 8899) to list of supported RFCs in README (#3520) ([lucas-clemente/quic-go#3520](https://github.com/lucas-clemente/quic-go/pull/3520)) + - use the null tracers in the tracer integration tests (#3528) ([lucas-clemente/quic-go#3528](https://github.com/lucas-clemente/quic-go/pull/3528)) +- github.com/marten-seemann/webtransport-go (v0.1.1 -> v0.4.3): + - release v0.4.3 (#57) ([marten-seemann/webtransport-go#57](https://github.com/marten-seemann/webtransport-go/pull/57)) + - return the correct error from OpenStreamSync when context is canceled (#55) ([marten-seemann/webtransport-go#55](https://github.com/marten-seemann/webtransport-go/pull/55)) + - release v0.4.2 (#54) ([marten-seemann/webtransport-go#54](https://github.com/marten-seemann/webtransport-go/pull/54)) + - use a buffered channel in the acceptQueue (#53) ([marten-seemann/webtransport-go#53](https://github.com/marten-seemann/webtransport-go/pull/53)) + - add a comment why using (a blocking) Read in the StreamHijacker is fine + - release v0.4.1 (#52) ([marten-seemann/webtransport-go#52](https://github.com/marten-seemann/webtransport-go/pull/52)) + - release session mutex when an error occurs when closing (#51) ([marten-seemann/webtransport-go#51](https://github.com/marten-seemann/webtransport-go/pull/51)) + - release v0.4.0 (#48) ([marten-seemann/webtransport-go#48](https://github.com/marten-seemann/webtransport-go/pull/48)) + - add a Server.ServeQUICConn method (#47) ([marten-seemann/webtransport-go#47](https://github.com/marten-seemann/webtransport-go/pull/47)) + - release v0.3.0 (#46) ([marten-seemann/webtransport-go#46](https://github.com/marten-seemann/webtransport-go/pull/46)) + - read and write CLOSE_WEBTRANSPORT_SESSION capsules ([marten-seemann/webtransport-go#40](https://github.com/marten-seemann/webtransport-go/pull/40)) + - implement the SetDeadline method on the stream (#44) ([marten-seemann/webtransport-go#44](https://github.com/marten-seemann/webtransport-go/pull/44)) + - expose the QUIC stream ID on the stream interfaces (#43) ([marten-seemann/webtransport-go#43](https://github.com/marten-seemann/webtransport-go/pull/43)) + - release v0.2.0 (#38) ([marten-seemann/webtransport-go#38](https://github.com/marten-seemann/webtransport-go/pull/38)) + - expose quic-go's connection tracing ID on the Session.Context (#35) ([marten-seemann/webtransport-go#35](https://github.com/marten-seemann/webtransport-go/pull/35)) + - add a ConnectionState method to the Session (#33) ([marten-seemann/webtransport-go#33](https://github.com/marten-seemann/webtransport-go/pull/33)) + - chore: update quic-go to v0.30.0 (#36) ([marten-seemann/webtransport-go#36](https://github.com/marten-seemann/webtransport-go/pull/36)) + - fix interop build (#37) ([marten-seemann/webtransport-go#37](https://github.com/marten-seemann/webtransport-go/pull/37)) + - rename session receiver variable (#34) ([marten-seemann/webtransport-go#34](https://github.com/marten-seemann/webtransport-go/pull/34)) + - fix double close of chan when using DontCloseRequestStream (#30) ([marten-seemann/webtransport-go#30](https://github.com/marten-seemann/webtransport-go/pull/30)) + - add a simple integration test using Selenium and a headless Chrome (#28) ([marten-seemann/webtransport-go#28](https://github.com/marten-seemann/webtransport-go/pull/28)) + - use a generic accept queue for uni- and bidirectional streams (#26) ([marten-seemann/webtransport-go#26](https://github.com/marten-seemann/webtransport-go/pull/26)) +- github.com/multiformats/go-base36 (v0.1.0 -> v0.2.0): + - v0.2.0 + - sync: update CI config files (#11) ([multiformats/go-base36#11](https://github.com/multiformats/go-base36/pull/11)) + - fix link to documentation (#9) ([multiformats/go-base36#9](https://github.com/multiformats/go-base36/pull/9)) + - sync: update CI config files (#8) ([multiformats/go-base36#8](https://github.com/multiformats/go-base36/pull/8)) + - Address `staticcheck` issue ([multiformats/go-base36#5](https://github.com/multiformats/go-base36/pull/5)) + - Feat/fasterer ([multiformats/go-base36#4](https://github.com/multiformats/go-base36/pull/4)) +- github.com/multiformats/go-multiaddr (v0.7.0 -> v0.8.0): + - release v0.8.0 ([multiformats/go-multiaddr#187](https://github.com/multiformats/go-multiaddr/pull/187)) + - Add quic-v1 component ([multiformats/go-multiaddr#186](https://github.com/multiformats/go-multiaddr/pull/186)) +- github.com/multiformats/go-varint (v0.0.6 -> v0.0.7): + - v0.0.7 ([multiformats/go-varint#18](https://github.com/multiformats/go-varint/pull/18)) + - feat: optimize decoding (#15) ([multiformats/go-varint#15](https://github.com/multiformats/go-varint/pull/15)) + - sync: update CI config files (#13) ([multiformats/go-varint#13](https://github.com/multiformats/go-varint/pull/13)) + - fix staticcheck ([multiformats/go-varint#9](https://github.com/multiformats/go-varint/pull/9)) + - tests for unbounded uvarint streams. ([multiformats/go-varint#7](https://github.com/multiformats/go-varint/pull/7)) +- github.com/whyrusleeping/cbor-gen (v0.0.0-20210219115102-f37d292932f2 -> v0.0.0-20221220214510-0333c149dec0): + - fix bug in consts code + - Allow 'const' fields to be declared ([whyrusleeping/cbor-gen#78](https://github.com/whyrusleeping/cbor-gen/pull/78)) + - support omitting empty fields on map encoders ([whyrusleeping/cbor-gen#77](https://github.com/whyrusleeping/cbor-gen/pull/77)) + - support string pointers + - feat: add the ability to encode a byte array (#76) ([whyrusleeping/cbor-gen#76](https://github.com/whyrusleeping/cbor-gen/pull/76)) + - support string slices (#73) ([whyrusleeping/cbor-gen#73](https://github.com/whyrusleeping/cbor-gen/pull/73)) + - Feat/size types ([whyrusleeping/cbor-gen#69](https://github.com/whyrusleeping/cbor-gen/pull/69)) + - Add CborReader and CborWriter (#67) ([whyrusleeping/cbor-gen#67](https://github.com/whyrusleeping/cbor-gen/pull/67)) + - Fix broken TestTimeIsh (#66) ([whyrusleeping/cbor-gen#66](https://github.com/whyrusleeping/cbor-gen/pull/66)) + - Return EOF and ErrUnexpectedEOF correctly (#64) ([whyrusleeping/cbor-gen#64](https://github.com/whyrusleeping/cbor-gen/pull/64)) + - fix: don't fail if we try to discard nothing at the end of an object ([whyrusleeping/cbor-gen#63](https://github.com/whyrusleeping/cbor-gen/pull/63)) + - Make peeker.ReadByte follow buffer.ReadByte semantics ([whyrusleeping/cbor-gen#61](https://github.com/whyrusleeping/cbor-gen/pull/61)) + - Fix read bug in readByteBuf ([whyrusleeping/cbor-gen#60](https://github.com/whyrusleeping/cbor-gen/pull/60)) + - support for cborgen struct field tags ([whyrusleeping/cbor-gen#58](https://github.com/whyrusleeping/cbor-gen/pull/58)) + - feat: take cbor adapters by-value when encoding ([whyrusleeping/cbor-gen#55](https://github.com/whyrusleeping/cbor-gen/pull/55)) + - fix: import "math" in generated code for uint8 unmarshalling ([whyrusleeping/cbor-gen#53](https://github.com/whyrusleeping/cbor-gen/pull/53)) + - doc: document Write*EncodersToFile functions ([whyrusleeping/cbor-gen#52](https://github.com/whyrusleeping/cbor-gen/pull/52)) + +
+ ### 👨‍👩‍👧‍👦 Contributors + +| Contributor | Commits | Lines ± | Files Changed | +|-------------|---------|---------|---------------| +| Marten Seemann | 154 | +8826/-6369 | 911 | +| Gus Eggert | 7 | +2792/-1444 | 40 | +| Marco Munizaga | 26 | +2324/-752 | 101 | +| hannahhoward | 7 | +695/-1587 | 50 | +| Rod Vagg | 30 | +1508/-668 | 106 | +| Henrique Dias | 13 | +1321/-431 | 85 | +| Yahya Hassanzadeh | 4 | +984/-158 | 9 | +| galargh | 17 | +519/-520 | 20 | +| Steve Loeppky | 11 | +612/-418 | 25 | +| Antonio Navarro Perez | 30 | +742/-88 | 47 | +| Marcin Rataj | 19 | +377/-407 | 52 | +| Ian Davis | 2 | +419/-307 | 7 | +| whyrusleeping | 5 | +670/-28 | 17 | +| Piotr Galar | 8 | +211/-417 | 25 | +| web3-bot | 28 | +282/-264 | 75 | +| Will Scott | 10 | +428/-103 | 19 | +| julian88110 | 2 | +367/-55 | 27 | +| Will | 5 | +282/-131 | 65 | +| Jorropo | 25 | +263/-94 | 38 | +| Wondertan | 10 | +203/-87 | 24 | +| Mohsin Zaidi | 1 | +269/-0 | 4 | +| Dennis Trautwein | 3 | +230/-21 | 7 | +| Prithvi Shahi | 1 | +116/-77 | 1 | +| Masih H. Derkani | 5 | +130/-37 | 11 | +| Iulian Pascalau | 1 | +151/-16 | 2 | +| Scott Martin | 1 | +166/-0 | 3 | +| Daniel Vernall | 1 | +92/-45 | 2 | +| Steven Allen | 7 | +114/-15 | 11 | +| Hlib Kanunnikov | 4 | +100/-28 | 6 | +| Peter Rabbitson | 4 | +59/-65 | 5 | +| Lucas Molas | 1 | +60/-57 | 7 | +| nisdas | 3 | +107/-6 | 5 | +| why | 2 | +80/-20 | 5 | +| ShengTao | 2 | +46/-45 | 16 | +| nisainan | 2 | +40/-50 | 12 | +| Mikel Cortes | 3 | +44/-36 | 10 | +| Chinmay Kousik | 1 | +64/-14 | 6 | +| ZenGround0 | 2 | +62/-15 | 6 | +| Antonio Navarro | 3 | +58/-3 | 8 | +| Michael Muré | 2 | +49/-2 | 2 | +| Dirk McCormick | 1 | +3/-42 | 1 | +| kixelated | 1 | +20/-20 | 4 | +| Russell Dempsey | 1 | +19/-17 | 3 | +| Karthik Nallabolu | 1 | +17/-17 | 1 | +| protolambda | 1 | +26/-4 | 4 | +| cliffc-spirent | 1 | +25/-5 | 2 | +| Raúl Kripalani | 1 | +29/-0 | 1 | +| Håvard Anda Estensen | 1 | +9/-19 | 6 | +| vyzo | 1 | +11/-12 | 1 | +| anorth | 1 | +15/-8 | 3 | +| shade34321 | 1 | +21/-1 | 2 | +| Toby | 2 | +9/-13 | 6 | +| Nishant Das | 1 | +9/-9 | 5 | +| Jeromy Johnson | 1 | +17/-0 | 3 | +| Oleg | 1 | +14/-1 | 1 | +| Hector Sanjuan | 6 | +4/-11 | 6 | +| Łukasz Magiera | 2 | +10/-4 | 2 | +| Aayush Rajasekaran | 1 | +7/-7 | 1 | +| Adin Schmahmann | 1 | +4/-3 | 1 | +| Stojan Dimitrovski | 1 | +6/-0 | 1 | +| Mathew Jacob | 1 | +3/-3 | 1 | +| Vladimir Ivanov | 1 | +2/-2 | 1 | +| Nex Zhu | 1 | +4/-0 | 2 | +| Michele Mastrogiovanni | 1 | +2/-2 | 1 | +| Louis Thibault | 1 | +4/-0 | 1 | +| Eric Myhre | 1 | +3/-1 | 1 | +| Kubo Mage | 2 | +2/-1 | 2 | +| tabcat | 1 | +1/-1 | 1 | +| Viacheslav | 1 | +1/-1 | 1 | +| Max Inden | 1 | +1/-1 | 1 | +| Manic Security | 1 | +1/-1 | 1 | +| Jc0803kevin | 1 | +1/-1 | 1 | +| David Brouwer | 1 | +2/-0 | 2 | +| Rong Zhou | 1 | +1/-0 | 1 | +| Neel Virdy | 1 | +1/-0 | 1 | diff --git a/docs/changelogs/v0.19.md b/docs/changelogs/v0.19.md new file mode 100644 index 000000000..f7e190a7e --- /dev/null +++ b/docs/changelogs/v0.19.md @@ -0,0 +1,611 @@ + +# Kubo changelog v0.19 + +## v0.19.2 + +### Highlights + +#### FullRT DHT HTTP Routers + +The default HTTP routers are now used when the FullRT DHT client is used. This fixes +the issue where cid.contact is not being queried by default when the accelerated +DHT client was enabled. Read more in ([ipfs/kubo#9841](https://github.com/ipfs/kubo/pull/9841)). + +### Changelog + +
Full Changelog + +- github.com/ipfs/kubo: + - fix: use default HTTP routers when FullRT DHT client is used (#9841) ([ipfs/kubo#9841](https://github.com/ipfs/kubo/pull/9841)) + - chore: update version + +
+ +### Contributors + +| Contributor | Commits | Lines ± | Files Changed | +|-------------|---------|---------|---------------| +| Gus Eggert | 1 | +65/-53 | 4 | +| Henrique Dias | 1 | +1/-1 | 1 | + +## v0.19.1 + +### 🔦 Highlights + +#### DHT Timeouts +In v0.16.0, Kubo added the ability to configure custom content routers and DHTs with the `custom` router type, and as part of this added a default 5 minute timeout to all DHT operations. In some cases with large repos ([example](https://github.com/ipfs/kubo/issues/9722)), this can cause provide and reprovide operations to fail because the timeout is reached. This release removes these timeouts on DHT operations. If users desire these timeouts, they can be added back using [the `custom` router type](https://github.com/ipfs/kubo/blob/master/docs/config.md#routingrouters-parameters). + +### Changelog + +
Full Changelog + +- github.com/ipfs/kubo: + - chore: update version + - fix: remove timeout on default DHT operations (#9783) ([ipfs/kubo#9783](https://github.com/ipfs/kubo/pull/9783)) + - chore: update version +- github.com/ipfs/go-blockservice (v0.5.0 -> v0.5.1): + - chore: release v0.5.1 + - fix: remove busyloop in getBlocks by removing batching +- github.com/libp2p/go-libp2p (v0.26.3 -> v0.26.4): + - release v0.26.4 + - autorelay: fix busy loop bug and flaky tests in relay finder (#2208) ([libp2p/go-libp2p#2208](https://github.com/libp2p/go-libp2p/pull/2208)) +- github.com/libp2p/go-libp2p-routing-helpers (v0.6.1 -> v0.6.2): + - Release v0.6.2 (#73) ([libp2p/go-libp2p-routing-helpers#73](https://github.com/libp2p/go-libp2p-routing-helpers/pull/73)) + - feat: zero timeout on composed routers should disable timeout (#72) ([libp2p/go-libp2p-routing-helpers#72](https://github.com/libp2p/go-libp2p-routing-helpers/pull/72)) + +
+ +### Contributors + +| Contributor | Commits | Lines ± | Files Changed | +|-------------|---------|---------|---------------| +| Marco Munizaga | 1 | +347/-46 | 5 | +| Gus Eggert | 3 | +119/-93 | 8 | +| Jorropo | 2 | +20/-32 | 2 | +| galargh | 2 | +2/-2 | 2 | +| Marten Seemann | 1 | +2/-2 | 1 | + + +## v0.19.0 + +- [Overview](#overview) +- [🔦 Highlights](#-highlights) + - [Improving the libp2p resource management integration](#improving-the-libp2p-resource-management-integration) + - [Gateways](#gateways) + - [Signed IPNS Record response format](#signed-ipns-record-response-format) + - [Example fetch and inspect IPNS record](#example-fetch-and-inspect-ipns-record) + - [Addition of "autoclient" router type](#addition-of-autoclient-router-type) + - [Deprecation of the `ipfs pubsub` commands and matching HTTP endpoints](#deprecation-of-the-ipfs-pubsub-commands-and-matching-http-endpoints) +- [📝 Changelog](#-changelog) +- [👨‍👩‍👧‍👦 Contributors](#-contributors) + +### Overview + +### 🔦 Highlights + +#### Improving the libp2p resource management integration + +There are further followups up on libp2p resource manager improvements in Kubo [0.18.0](https://github.com/ipfs/kubo/blob/master/docs/changelogs/v0.18.md#improving-libp2p-resource-management-integration-1) +and [0.18.1](https://github.com/ipfs/kubo/blob/master/docs/changelogs/v0.18.md#improving-libp2p-resource-management-integration): +1. `ipfs swarm limits` and `ipfs swarm stats` have been replaced by `ipfs swarm resources` to provide a single/combined view for limits and their current usage in a more intuitive ordering. +1. Removal of `Swarm.ResourceMgr.Limits` config. Instead [the power user can specify limits in a .json file that are fed directly to go-libp2p](https://github.com/ipfs/kubo/blob/master/docs/libp2p-resource-management.md#user-supplied-override-limits). This allows the power user to take advantage of the [new resource manager types introduced in go-libp2p 0.25](https://github.com/libp2p/go-libp2p/blob/master/CHANGELOG.md#new-resource-manager-types-) including "use default", "unlimited", "block all". + - Note: we don't expect most users to need these capablities, but they are there if so. +1. [Doc updates](https://github.com/ipfs/kubo/blob/master/docs/libp2p-resource-management.md). + +#### Gateways + +##### Signed IPNS Record response format + +This release implements [IPIP-351](https://github.com/ipfs/specs/pull/351) and +adds Gateway support for returning signed (verifiable) `ipns-record` (0x0300) +when `/ipns/{libp2p-key}` is requested with either +`Accept: application/vnd.ipfs.ipns-record` HTTP header +or `?format=ipns-record` URL query parameter. + + +The Gateway in Kubo already supported [trustless, verifiable retrieval](https://docs.ipfs.tech/reference/http/gateway/#trustless-verifiable-retrieval) of immutable `/ipfs/` namespace. +With `?format=ipns-record`, light HTTP clients are now able to get the same level of verifiability for IPNS websites. + +Tooling is limited at the moment, but we are working on [go-libipfs](https://github.com/ipfs/go-libipfs/) examples that illustrate the verifiable HTTP client pattern. + +##### Example: fetch IPNS record over HTTP and inspect it with `ipfs name inspect --verify` + +```console +$ FILE_CID=$(echo "Hello IPFS" | ipfs add --cid-version 1 -q) +$ IPNS_KEY=$(ipfs key gen test) +$ ipfs name publish /ipfs/$FILE_CID --key=test --ttl=30m +Published to k51q..dvf1: /ipfs/bafk..z244 +$ curl "http://127.0.0.1:8080/ipns/$IPNS_KEY?format=ipns-record" > signed.ipns-record +$ ipfs name inspect --verify $IPNS_KEY < signed.ipns-record +Value: "/ipfs/bafk..." +Validity Type: "EOL" +Validity: 2023-03-09T23:13:34.032977468Z +Sequence: 0 +TTL: 1800000000000 +PublicKey: "" +Signature V1: "m..." +Signature V2: "m..." +Data: {...} + +Validation results: + Valid: true + PublicKey: 12D3... +``` + +#### Addition of "autoclient" router type +A new routing type "autoclient" has been added. This mode is similar to "auto", in that it is a hybrid of content routers (including Kademlia and HTTP routers), but it does not run a DHT server. This is similar to the difference between "dhtclient" and "dht" router types. + +See the [Routing.Type documentation](https://github.com/ipfs/kubo/blob/master/docs/config.md#routingtype) for more information. + +#### Deprecation of the `ipfs pubsub` commands and matching HTTP endpoints + +We are deprecating `ipfs pubsub` and all `/api/v0/pubsub/` RPC endpoints and will remove them in the next release. + +For more information and rational see [#9717](https://github.com/ipfs/kubo/issues/9717). + +### 📝 Changelog + +
Full Changelog + +- github.com/ipfs/kubo: + - chore: update version + - docs: 0.19 changelog ([ipfs/kubo#9707](https://github.com/ipfs/kubo/pull/9707)) + - fix: canonicalize user defined headers + - fix: apply API.HTTPHeaders to /webui redirect + - feat: add heap allocs to 'ipfs diag profile' + - fix: future proof with > rcmgr.DefaultLimit for new enum rcmgr values + - test: add test for presarvation of unlimited configs for inbound systems + - fix: preserve Unlimited StreamsInbound in connmgr reconciliation + - test: fix flaky rcmgr test + - chore: deprecate the pubsub api + - test: port peering test from sharness to Go + - test: use `T.TempDir` to create temporary test directory + - fix: --verify forgets the verified key + - test: name --verify forgets the verified key + - feat: add "autoclient" routing type + - test: parallelize more of rcmgr Go tests + - test: port legacy DHT tests to Go + - fix: t0116-gateway-cache.sh ([ipfs/kubo#9696](https://github.com/ipfs/kubo/pull/9696)) + - docs: add bifrost to early testers ([ipfs/kubo#9699](https://github.com/ipfs/kubo/pull/9699)) + - fix: typo in documentation for install path + - chore: update version + - feat: Reduce RM code footprint + - Doc updates/additions + - ci: replace junit html generation with gh action + - test: port rcmgr sharness tests to Go + - test(gateway): use deterministic CAR fixtures ([ipfs/kubo#9657](https://github.com/ipfs/kubo/pull/9657)) + - feat(gateway): error handling improvements (500, 502, 504) (#9660) ([ipfs/kubo#9660](https://github.com/ipfs/kubo/pull/9660)) + - docs: be clear about swarm.addrfilters (#9661) ([ipfs/kubo#9661](https://github.com/ipfs/kubo/pull/9661)) + - chore: update go-libp2p to v0.26 (#9656) ([ipfs/kubo#9656](https://github.com/ipfs/kubo/pull/9656)) + - feat(pinning): connect some missing go context (#9557) ([ipfs/kubo#9557](https://github.com/ipfs/kubo/pull/9557)) + - fix(gateway): return HTTP 500 on ErrResolveFailed (#9589) ([ipfs/kubo#9589](https://github.com/ipfs/kubo/pull/9589)) + - docs: bulk spelling edits (#9544) ([ipfs/kubo#9544](https://github.com/ipfs/kubo/pull/9544)) + - docs: "remote" errors from resource manager (#9653) ([ipfs/kubo#9653](https://github.com/ipfs/kubo/pull/9653)) + - test: remove gateway tests migrated to go-libipfs + - fix: update rcmgr for go-libp2p v0.25 + - chore: update go-libp2p to v0.25.1 + - docs(0.18.1): guide users to clean up limits (#9644) ([ipfs/kubo#9644](https://github.com/ipfs/kubo/pull/9644)) + - feat: add NewOptionalInteger function + - fix: dereference int64 pointer in OptionalInteger.String() (#9640) ([ipfs/kubo#9640](https://github.com/ipfs/kubo/pull/9640)) + - fix: restore wire format for /api/v0/routing/get|put (#9639) ([ipfs/kubo#9639](https://github.com/ipfs/kubo/pull/9639)) + - refactor(gw): move Host (DNSLink and subdomain) handling to go-libipfs (#9624) ([ipfs/kubo#9624](https://github.com/ipfs/kubo/pull/9624)) + - refactor: new go-libipfs/gateway API, deprecate Gateway.Writable (#9616) ([ipfs/kubo#9616](https://github.com/ipfs/kubo/pull/9616)) + - Create Changelog: v0.19 ([ipfs/kubo#9617](https://github.com/ipfs/kubo/pull/9617)) + - refactor: use gateway from go-libipfs (#9588) ([ipfs/kubo#9588](https://github.com/ipfs/kubo/pull/9588)) + - Merge Release: v0.18.1 ([ipfs/kubo#9613](https://github.com/ipfs/kubo/pull/9613)) + - Add overview section + - Adjust inbound connection limits depending on memory. + - feat: ipfs-webui 2.22.0 + - chore: bump go-libipfs remove go-bitswap + - docs: DefaultResourceMgrMinInboundConns + - feat(gateway): IPNS record response format (IPIP-351) (#9399) ([ipfs/kubo#9399](https://github.com/ipfs/kubo/pull/9399)) + - fix(ipns): honour --ttl flag in 'ipfs name publish' (#9471) ([ipfs/kubo#9471](https://github.com/ipfs/kubo/pull/9471)) + - feat: Pubsub.SeenMessagesStrategy (#9543) ([ipfs/kubo#9543](https://github.com/ipfs/kubo/pull/9543)) + - chore: bump go-libipfs to replace go-block-format + - Merge Kubo: v0.18 ([ipfs/kubo#9581](https://github.com/ipfs/kubo/pull/9581)) + - fix: clarity: no user supplied rcmgr limits of 0 (#9563) ([ipfs/kubo#9563](https://github.com/ipfs/kubo/pull/9563)) + - fix(gateway): undesired conversions to dag-json and friends (#9566) ([ipfs/kubo#9566](https://github.com/ipfs/kubo/pull/9566)) + - fix: ensure connmgr is smaller then autoscalled ressource limits + - fix: typo in ensureConnMgrMakeSenseVsResourcesMgr + - docs: clarify browser descriptions for webtransport + - fix: update saxon download path + - fix: refuse to start if connmgr is smaller than ressource limits and not using none connmgr + - fix: User-Agent sent to HTTP routers + - test: port gateway sharness tests to Go tests + - fix: do not download saxon in parallel + - docs: improve docs/README (#9539) ([ipfs/kubo#9539](https://github.com/ipfs/kubo/pull/9539)) + - test: port CircleCI to GH Actions and improve sharness reporting (#9355) ([ipfs/kubo#9355](https://github.com/ipfs/kubo/pull/9355)) + - chore: migrate from go-ipfs-files to go-libipfs/files (#9535) ([ipfs/kubo#9535](https://github.com/ipfs/kubo/pull/9535)) + - fix: stats dht command when Routing.Type=auto (#9538) ([ipfs/kubo#9538](https://github.com/ipfs/kubo/pull/9538)) + - fix: hint people to changing from RSA peer ids + - fix(gateway): JSON when Accept is a list + - fix(test): retry flaky t0125-twonode.sh + - docs: fix Router config Godoc (#9528) ([ipfs/kubo#9528](https://github.com/ipfs/kubo/pull/9528)) + - fix(ci): flaky sharness test + - docs(config): ProviderSearchDelay (#9526) ([ipfs/kubo#9526](https://github.com/ipfs/kubo/pull/9526)) + - docs: clarify debug environment variables + - fix: disable provide over HTTP with Routing.Type=auto (#9511) ([ipfs/kubo#9511](https://github.com/ipfs/kubo/pull/9511)) + - fix(test): stabilize flaky provider tests + - feat: port pins CLI test + - Removing QRI from early tester ([ipfs/kubo#9503](https://github.com/ipfs/kubo/pull/9503)) + - Update Version (dev): v0.18 ([ipfs/kubo#9500](https://github.com/ipfs/kubo/pull/9500)) +- github.com/ipfs/go-bitfield (v1.0.0 -> v1.1.0): + - Merge pull request from GHSA-2h6c-j3gf-xp9r + - sync: update CI config files (#3) ([ipfs/go-bitfield#3](https://github.com/ipfs/go-bitfield/pull/3)) +- github.com/ipfs/go-block-format (v0.0.3 -> v0.1.1): + - chore: release v0.1.1 + - docs: fix wrong copy paste in docs + - chore: release v0.1.0 + - refactor: deprecate and add stub types to go-libipfs/blocks + - sync: update CI config files (#34) ([ipfs/go-block-format#34](https://github.com/ipfs/go-block-format/pull/34)) + - remove Makefile ([ipfs/go-block-format#31](https://github.com/ipfs/go-block-format/pull/31)) +- github.com/ipfs/go-ipfs-files (v0.0.8 -> v0.3.0): + - ([ipfs/go-ipfs-files#59](https://github.com/ipfs/go-ipfs-files/pull/59)) + - docs: add moved noticed [ci skip] + - Release v0.2.0 + - fix: error when TAR has files outside of root (#56) ([ipfs/go-ipfs-files#56](https://github.com/ipfs/go-ipfs-files/pull/56)) + - sync: update CI config files ([ipfs/go-ipfs-files#55](https://github.com/ipfs/go-ipfs-files/pull/55)) + - chore(Directory): add DirIterator API restriction: iterate only once + - Release v0.1.1 + - fix: add dragonfly build option for filewriter flags + - fix: add freebsd build option for filewriter flags + - Release v0.1.0 + - docs: fix community CONTRIBUTING.md link (#45) ([ipfs/go-ipfs-files#45](https://github.com/ipfs/go-ipfs-files/pull/45)) + - chore(filewriter): cleanup writes (#43) ([ipfs/go-ipfs-files#43](https://github.com/ipfs/go-ipfs-files/pull/43)) + - sync: update CI config files (#44) ([ipfs/go-ipfs-files#44](https://github.com/ipfs/go-ipfs-files/pull/44)) + - sync: update CI config files ([ipfs/go-ipfs-files#40](https://github.com/ipfs/go-ipfs-files/pull/40)) + - fix: manually parse the content disposition to preserve directories ([ipfs/go-ipfs-files#42](https://github.com/ipfs/go-ipfs-files/pull/42)) + - fix: round timestamps down by truncating them to seconds ([ipfs/go-ipfs-files#41](https://github.com/ipfs/go-ipfs-files/pull/41)) + - sync: update CI config files ([ipfs/go-ipfs-files#34](https://github.com/ipfs/go-ipfs-files/pull/34)) + - Fix test failure on Windows caused by nil `sys` in mock `FileInfo` ([ipfs/go-ipfs-files#39](https://github.com/ipfs/go-ipfs-files/pull/39)) + - fix staticcheck ([ipfs/go-ipfs-files#35](https://github.com/ipfs/go-ipfs-files/pull/35)) + - fix linters ([ipfs/go-ipfs-files#33](https://github.com/ipfs/go-ipfs-files/pull/33)) +- github.com/ipfs/go-ipfs-pinner (v0.2.1 -> v0.3.0): + - chore: release v0.3.0 (#27) ([ipfs/go-ipfs-pinner#27](https://github.com/ipfs/go-ipfs-pinner/pull/27)) + - feat!: add and connect missing context, remove RemovePinWithMode (#23) ([ipfs/go-ipfs-pinner#23](https://github.com/ipfs/go-ipfs-pinner/pull/23)) + - sync: update CI config files ([ipfs/go-ipfs-pinner#16](https://github.com/ipfs/go-ipfs-pinner/pull/16)) +- github.com/ipfs/go-ipfs-pq (v0.0.2 -> v0.0.3): + - chore: release v0.0.3 + - fix: enable early GC + - sync: update CI config files (#10) ([ipfs/go-ipfs-pq#10](https://github.com/ipfs/go-ipfs-pq/pull/10)) + - sync: update CI config files ([ipfs/go-ipfs-pq#8](https://github.com/ipfs/go-ipfs-pq/pull/8)) + - remove Makefile ([ipfs/go-ipfs-pq#7](https://github.com/ipfs/go-ipfs-pq/pull/7)) +- github.com/ipfs/go-libipfs (v0.2.0 -> v0.6.2): + - chore: release 0.6.2 (#211) ([ipfs/go-libipfs#211](https://github.com/ipfs/go-libipfs/pull/211)) + - fix(gateway): 500 on panic, recover on WithHostname + - refactor: use assert in remaining gateway tests + - chore: release 0.6.1 + - feat: support HTTP 429 with Retry-After (#194) ([ipfs/go-libipfs#194](https://github.com/ipfs/go-libipfs/pull/194)) + - docs: fix typo in README.md + - fix(gateway): return 500 for all /ip[nf]s/id failures + - chore: make gocritic happier + - feat(gateway): improved error handling, support for 502 and 504 ([ipfs/go-libipfs#182](https://github.com/ipfs/go-libipfs/pull/182)) + - feat: add content path in request context (#184) ([ipfs/go-libipfs#184](https://github.com/ipfs/go-libipfs/pull/184)) + - sync: update CI config files ([ipfs/go-libipfs#159](https://github.com/ipfs/go-libipfs/pull/159)) + - fix(gateway): return HTTP 500 on namesys.ErrResolveFailed (#150) ([ipfs/go-libipfs#150](https://github.com/ipfs/go-libipfs/pull/150)) + - docs(examples): add UnixFS file download over Bitswap (#143) ([ipfs/go-libipfs#143](https://github.com/ipfs/go-libipfs/pull/143)) + - bitswap/server/internal/decision: fix: remove unused private type + - chore: release v0.6.0 + - bitswap/server/internal/decision: add more non flaky tests + - bitswap/server/internal/decision: add filtering on CIDs - Ignore cids that are too big. - Kill connection for peers that are using inline CIDs. + - bitswap/server/internal/decision: rewrite ledger inversion + - docs(readme): various updates for clarity (#171) ([ipfs/go-libipfs#171](https://github.com/ipfs/go-libipfs/pull/171)) + - feat: metric for implicit index.html in dirs + - fix(gateway): ensure ipfs_http_gw_get_duration_seconds gets updated + - test(gateway): migrate Go tests from Kubo ([ipfs/go-libipfs#156](https://github.com/ipfs/go-libipfs/pull/156)) + - docs: fix link (#165) ([ipfs/go-libipfs#165](https://github.com/ipfs/go-libipfs/pull/165)) + - fix: GetIPNSRecord example gateway implementation (#158) ([ipfs/go-libipfs#158](https://github.com/ipfs/go-libipfs/pull/158)) + - chore: release v0.5.0 + - chore: update go-libp2p to v0.25.1 + - fix(gateway): display correct error with 500 (#160) ([ipfs/go-libipfs#160](https://github.com/ipfs/go-libipfs/pull/160)) + - fix: gateway car example dnslink + - feat(gateway): add TAR, IPNS Record, DAG-* histograms and spans (#155) ([ipfs/go-libipfs#155](https://github.com/ipfs/go-libipfs/pull/155)) + - feat(gateway): migrate subdomain and dnslink code (#153) ([ipfs/go-libipfs#153](https://github.com/ipfs/go-libipfs/pull/153)) + - docs: add example of gateway that proxies to ?format=raw (#151) ([ipfs/go-libipfs#151](https://github.com/ipfs/go-libipfs/pull/151)) + - docs: add example of gateway backed by CAR file (#147) ([ipfs/go-libipfs#147](https://github.com/ipfs/go-libipfs/pull/147)) + - undefined ([ipfs/go-libipfs#145](https://github.com/ipfs/go-libipfs/pull/145)) + - Extract Gateway Code From Kubo + ([ipfs/go-libipfs#65](https://github.com/ipfs/go-libipfs/pull/65)) + - Migrate go-bitswap ([ipfs/go-libipfs#63](https://github.com/ipfs/go-libipfs/pull/63)) + - Use `PUT` as method to insert provider records + - Migrate `go-block-format` ([ipfs/go-libipfs#58](https://github.com/ipfs/go-libipfs/pull/58)) + - chore: add codecov PR comment + - chore: add a logo and some basics in the README (#37) ([ipfs/go-libipfs#37](https://github.com/ipfs/go-libipfs/pull/37)) +- github.com/ipfs/go-namesys (v0.6.0 -> v0.7.0): + - chore: release 0.7.0 (#36) ([ipfs/go-namesys#36](https://github.com/ipfs/go-namesys/pull/36)) + - feat: use PublishOptions for publishing IPNS records (#35) ([ipfs/go-namesys#35](https://github.com/ipfs/go-namesys/pull/35)) +- github.com/ipfs/go-path (v0.3.0 -> v0.3.1): + - chore: release v0.3.1 (#67) ([ipfs/go-path#67](https://github.com/ipfs/go-path/pull/67)) + - feat: expose ErrInvalidPath and implement .Is function (#66) ([ipfs/go-path#66](https://github.com/ipfs/go-path/pull/66)) + - sync: update CI config files (#60) ([ipfs/go-path#60](https://github.com/ipfs/go-path/pull/60)) + - feat: add basic tracing ([ipfs/go-path#59](https://github.com/ipfs/go-path/pull/59)) +- github.com/ipfs/go-peertaskqueue (v0.8.0 -> v0.8.1): + - chore: release v0.8.1 + - feat: add PushTasksTruncated which only push a limited amount of tasks + - feat: add (*PeerTaskQueue).Clear which fully removes a peer + - sync: update CI config files (#26) ([ipfs/go-peertaskqueue#26](https://github.com/ipfs/go-peertaskqueue/pull/26)) +- github.com/ipfs/go-unixfs (v0.4.2 -> v0.4.4): + - chore: release v0.4.4 + - fix: correctly handle return errors + - fix: correctly handle errors in balancedbuilder's Layout + - test: fix tests after hamt issues fixes + - Merge pull request from GHSA-q264-w97q-q778 +- github.com/ipfs/go-unixfsnode (v1.5.1 -> v1.5.2): + - Merge pull request from GHSA-4gj3-6r43-3wfc +- github.com/ipfs/interface-go-ipfs-core (v0.8.2 -> v0.11.0): + - chore: release v0.11.0 + - test: basic routing interface test + - chore: release v0.10.0 (#102) ([ipfs/interface-go-ipfs-core#102](https://github.com/ipfs/interface-go-ipfs-core/pull/102)) + - feat: add RoutingAPI to CoreAPI + - chore: release 0.9.0 (#101) ([ipfs/interface-go-ipfs-core#101](https://github.com/ipfs/interface-go-ipfs-core/pull/101)) + - feat: add namesys publish options (#94) ([ipfs/interface-go-ipfs-core#94](https://github.com/ipfs/interface-go-ipfs-core/pull/94)) +- github.com/ipld/go-car (v0.4.0 -> v0.5.0): + - chore: bump version to 0.5.0 + - fix: remove use of ioutil + - run gofmt -s + - bump go.mod to Go 1.18 and run go fix + - bump go.mod to Go 1.18 and run go fix + - OpenReadWriteFile: add test + - blockstore: allow to pass a file to write in (#323) ([ipld/go-car#323](https://github.com/ipld/go-car/pull/323)) + - feat: add `car inspect` command to cmd pkg (#320) ([ipld/go-car#320](https://github.com/ipld/go-car/pull/320)) + - Separate `index.ReadFrom` tests + - Only read index codec during inspection + - Upgrade to the latest `go-car/v2` + - Empty identity CID should be indexed when options are set +- github.com/libp2p/go-libp2p (v0.24.2 -> v0.26.3): + - Release v0.26.3 (#2197) ([libp2p/go-libp2p#2197](https://github.com/libp2p/go-libp2p/pull/2197)) + - retract v0.26.1, release v0.26.2 (#2153) ([libp2p/go-libp2p#2153](https://github.com/libp2p/go-libp2p/pull/2153)) + - rcmgr: fix JSON marshalling of ResourceManagerStat peer map (#2156) ([libp2p/go-libp2p#2156](https://github.com/libp2p/go-libp2p/pull/2156)) + - release v0.26.1 ([libp2p/go-libp2p#2146](https://github.com/libp2p/go-libp2p/pull/2146)) + - release v0.26.0 (#2133) ([libp2p/go-libp2p#2133](https://github.com/libp2p/go-libp2p/pull/2133)) + - identify: add more detailed metrics (#2126) ([libp2p/go-libp2p#2126](https://github.com/libp2p/go-libp2p/pull/2126)) + - autorelay: refactor relay finder and start autorelay after identify (#2120) ([libp2p/go-libp2p#2120](https://github.com/libp2p/go-libp2p/pull/2120)) + - don't use the time value from the time.Ticker channel (#2127) ([libp2p/go-libp2p#2127](https://github.com/libp2p/go-libp2p/pull/2127)) + - Wrap conn with metrics (#2131) ([libp2p/go-libp2p#2131](https://github.com/libp2p/go-libp2p/pull/2131)) + - chore: update changelog for 0.26.0 (#2132) ([libp2p/go-libp2p#2132](https://github.com/libp2p/go-libp2p/pull/2132)) + - circuitv2: Update proto files to proto3 (#2121) ([libp2p/go-libp2p#2121](https://github.com/libp2p/go-libp2p/pull/2121)) + - swarm: remove parallel tests from swarm tests (#2130) ([libp2p/go-libp2p#2130](https://github.com/libp2p/go-libp2p/pull/2130)) + - circuitv2: add a relay option to disable limits (#2125) ([libp2p/go-libp2p#2125](https://github.com/libp2p/go-libp2p/pull/2125)) + - quic: fix stalled virtual listener (#2122) ([libp2p/go-libp2p#2122](https://github.com/libp2p/go-libp2p/pull/2122)) + - swarm: add early muxer selection to swarm metrics (#2119) ([libp2p/go-libp2p#2119](https://github.com/libp2p/go-libp2p/pull/2119)) + - metrics: add options to disable metrics and to set Prometheus registerer (#2116) ([libp2p/go-libp2p#2116](https://github.com/libp2p/go-libp2p/pull/2116)) + - swarm: add ip_version to metrics (#2114) ([libp2p/go-libp2p#2114](https://github.com/libp2p/go-libp2p/pull/2114)) + - Revert mistaken "Bump timeout" + - Bump timeout + - remove all circuit v1 related code (#2107) ([libp2p/go-libp2p#2107](https://github.com/libp2p/go-libp2p/pull/2107)) + - quic: don't send detailed error messages when closing connections (#2112) ([libp2p/go-libp2p#2112](https://github.com/libp2p/go-libp2p/pull/2112)) + - metrics: add no alloc metrics for eventbus, swarm, identify (#2108) ([libp2p/go-libp2p#2108](https://github.com/libp2p/go-libp2p/pull/2108)) + - chore: fix typo in Changelog (#2111) ([libp2p/go-libp2p#2111](https://github.com/libp2p/go-libp2p/pull/2111)) + - chore: update changelog (#2109) ([libp2p/go-libp2p#2109](https://github.com/libp2p/go-libp2p/pull/2109)) + - chore: unify dashboard location (#2110) ([libp2p/go-libp2p#2110](https://github.com/libp2p/go-libp2p/pull/2110)) + - autonat: add metrics (#2086) ([libp2p/go-libp2p#2086](https://github.com/libp2p/go-libp2p/pull/2086)) + - relaymanager: do not start new relay if one already exists (#2093) ([libp2p/go-libp2p#2093](https://github.com/libp2p/go-libp2p/pull/2093)) + - autonat: don't emit reachability changed events on address change (#2092) ([libp2p/go-libp2p#2092](https://github.com/libp2p/go-libp2p/pull/2092)) + - chore: modify changelog entries (#2101) ([libp2p/go-libp2p#2101](https://github.com/libp2p/go-libp2p/pull/2101)) + - Introduce a changelog (#2084) ([libp2p/go-libp2p#2084](https://github.com/libp2p/go-libp2p/pull/2084)) + - use atomic.Int32 and atomic.Int64 (#2096) ([libp2p/go-libp2p#2096](https://github.com/libp2p/go-libp2p/pull/2096)) + - change atomic.Value to atomic.Pointer (#2088) ([libp2p/go-libp2p#2088](https://github.com/libp2p/go-libp2p/pull/2088)) + - use atomic.Bool instead of int32 operations (#2089) ([libp2p/go-libp2p#2089](https://github.com/libp2p/go-libp2p/pull/2089)) + - sync: update CI config files (#2073) ([libp2p/go-libp2p#2073](https://github.com/libp2p/go-libp2p/pull/2073)) + - chore: update examples to v0.25.1 (#2080) ([libp2p/go-libp2p#2080](https://github.com/libp2p/go-libp2p/pull/2080)) + - v0.25.1 (#2082) ([libp2p/go-libp2p#2082](https://github.com/libp2p/go-libp2p/pull/2082)) + - Start host in mocknet (#2078) ([libp2p/go-libp2p#2078](https://github.com/libp2p/go-libp2p/pull/2078)) + - Release v0.25.0 (#2077) ([libp2p/go-libp2p#2077](https://github.com/libp2p/go-libp2p/pull/2077)) + - identify: add some basic metrics (#2069) ([libp2p/go-libp2p#2069](https://github.com/libp2p/go-libp2p/pull/2069)) + - p2p/test/quic: use contexts with a timeout for Connect calls (#2070) ([libp2p/go-libp2p#2070](https://github.com/libp2p/go-libp2p/pull/2070)) + - feat!: rcmgr: Change LimitConfig to use LimitVal type (#2000) ([libp2p/go-libp2p#2000](https://github.com/libp2p/go-libp2p/pull/2000)) + - identify: refactor sending of Identify pushes (#1984) ([libp2p/go-libp2p#1984](https://github.com/libp2p/go-libp2p/pull/1984)) + - Update interop to match spec (#2049) ([libp2p/go-libp2p#2049](https://github.com/libp2p/go-libp2p/pull/2049)) + - chore: git-ignore various flavors of qlog files (#2064) ([libp2p/go-libp2p#2064](https://github.com/libp2p/go-libp2p/pull/2064)) + - rcmgr: add libp2p prefix to all metrics (#2063) ([libp2p/go-libp2p#2063](https://github.com/libp2p/go-libp2p/pull/2063)) + - websocket: Replace gorilla websocket transport with nhooyr websocket transport (#1982) ([libp2p/go-libp2p#1982](https://github.com/libp2p/go-libp2p/pull/1982)) + - rcmgr: Use prometheus SDK for rcmgr metrics (#2044) ([libp2p/go-libp2p#2044](https://github.com/libp2p/go-libp2p/pull/2044)) + - autorelay: Split libp2p.EnableAutoRelay into 2 functions (#2022) ([libp2p/go-libp2p#2022](https://github.com/libp2p/go-libp2p/pull/2022)) + - set names for eventbus event subscriptions (#2057) ([libp2p/go-libp2p#2057](https://github.com/libp2p/go-libp2p/pull/2057)) + - Test cleanup (#2053) ([libp2p/go-libp2p#2053](https://github.com/libp2p/go-libp2p/pull/2053)) + - metrics: use a single slice pool for all metrics tracer (#2054) ([libp2p/go-libp2p#2054](https://github.com/libp2p/go-libp2p/pull/2054)) + - eventbus: add metrics (#2038) ([libp2p/go-libp2p#2038](https://github.com/libp2p/go-libp2p/pull/2038)) + - quic: disable sending of Version Negotiation packets (#2015) ([libp2p/go-libp2p#2015](https://github.com/libp2p/go-libp2p/pull/2015)) + - p2p/test: fix flaky notification test (#2051) ([libp2p/go-libp2p#2051](https://github.com/libp2p/go-libp2p/pull/2051)) + - quic, tcp: only register Prometheus counters when metrics are enabled ([libp2p/go-libp2p#1971](https://github.com/libp2p/go-libp2p/pull/1971)) + - p2p/test: add test for EvtLocalAddressesUpdated event (#2016) ([libp2p/go-libp2p#2016](https://github.com/libp2p/go-libp2p/pull/2016)) + - quic / webtransport: extend test to test dialing draft-29 and v1 (#1957) ([libp2p/go-libp2p#1957](https://github.com/libp2p/go-libp2p/pull/1957)) + - holepunch: fix flaky by not remove holepunch protocol handler (#1948) ([libp2p/go-libp2p#1948](https://github.com/libp2p/go-libp2p/pull/1948)) + - use quic-go and webtransport-go from quic-go organization (#2040) ([libp2p/go-libp2p#2040](https://github.com/libp2p/go-libp2p/pull/2040)) + - Migrate to test-plan composite action (#2039) ([libp2p/go-libp2p#2039](https://github.com/libp2p/go-libp2p/pull/2039)) + - chore: remove license files from the eventbus package (#2042) ([libp2p/go-libp2p#2042](https://github.com/libp2p/go-libp2p/pull/2042)) + - rcmgr: *: Always close connscope (#2037) ([libp2p/go-libp2p#2037](https://github.com/libp2p/go-libp2p/pull/2037)) + - chore: remove textual roadmap in favor for Starmap (#2036) ([libp2p/go-libp2p#2036](https://github.com/libp2p/go-libp2p/pull/2036)) + - swarm metrics: fix datasource for dashboard (#2024) ([libp2p/go-libp2p#2024](https://github.com/libp2p/go-libp2p/pull/2024)) + - consistently use protocol.ID instead of strings (#2004) ([libp2p/go-libp2p#2004](https://github.com/libp2p/go-libp2p/pull/2004)) + - swarm: add a basic metrics tracer (#1973) ([libp2p/go-libp2p#1973](https://github.com/libp2p/go-libp2p/pull/1973)) + - Expose muxer ids (#2012) ([libp2p/go-libp2p#2012](https://github.com/libp2p/go-libp2p/pull/2012)) + - Clean addresses with peer id before adding to addrbook (#2007) ([libp2p/go-libp2p#2007](https://github.com/libp2p/go-libp2p/pull/2007)) + - feat: ci test-plans: Parse test timeout parameter for interop test (#2014) ([libp2p/go-libp2p#2014](https://github.com/libp2p/go-libp2p/pull/2014)) + - Export resource manager errors (#2008) ([libp2p/go-libp2p#2008](https://github.com/libp2p/go-libp2p/pull/2008)) + - peerstore: make it possible to use an empty peer ID (#2006) ([libp2p/go-libp2p#2006](https://github.com/libp2p/go-libp2p/pull/2006)) + - Add ci flakiness score to readme (#2002) ([libp2p/go-libp2p#2002](https://github.com/libp2p/go-libp2p/pull/2002)) + - rcmgr: fix: Ignore zero values when marshalling Limits. (#1998) ([libp2p/go-libp2p#1998](https://github.com/libp2p/go-libp2p/pull/1998)) + - CI: Fast multidimensional Interop tests (#1991) ([libp2p/go-libp2p#1991](https://github.com/libp2p/go-libp2p/pull/1991)) + - feat: add some users to the readme (#1981) ([libp2p/go-libp2p#1981](https://github.com/libp2p/go-libp2p/pull/1981)) + - ci: run go generate as part of the go-check workflow (#1986) ([libp2p/go-libp2p#1986](https://github.com/libp2p/go-libp2p/pull/1986)) + - switch to Google's Protobuf library, make protobufs compile with go generate ([libp2p/go-libp2p#1979](https://github.com/libp2p/go-libp2p/pull/1979)) + - circuitv2: correctly set the transport in the ConnectionState (#1972) ([libp2p/go-libp2p#1972](https://github.com/libp2p/go-libp2p/pull/1972)) + - roadmap: remove optimizations of the TCP-based handshake (#1959) ([libp2p/go-libp2p#1959](https://github.com/libp2p/go-libp2p/pull/1959)) + - identify: remove support for Identify Delta ([libp2p/go-libp2p#1975](https://github.com/libp2p/go-libp2p/pull/1975)) + - core: remove introspection package (#1978) ([libp2p/go-libp2p#1978](https://github.com/libp2p/go-libp2p/pull/1978)) + - identify: remove old code targeting Go 1.17 (#1964) ([libp2p/go-libp2p#1964](https://github.com/libp2p/go-libp2p/pull/1964)) + - add WebTransport to the list of default transports (#1915) ([libp2p/go-libp2p#1915](https://github.com/libp2p/go-libp2p/pull/1915)) + - core/crypto: drop all OpenSSL code paths (#1953) ([libp2p/go-libp2p#1953](https://github.com/libp2p/go-libp2p/pull/1953)) + - chore: use generic LRU cache (#1980) ([libp2p/go-libp2p#1980](https://github.com/libp2p/go-libp2p/pull/1980)) +- github.com/libp2p/go-libp2p-kad-dht (v0.20.0 -> v0.21.1): + - chore: bump to v0.21.1 (#821) ([libp2p/go-libp2p-kad-dht#821](https://github.com/libp2p/go-libp2p-kad-dht/pull/821)) + - feat: send FIND_NODE request to peers on routing table refresh (#810) ([libp2p/go-libp2p-kad-dht#810](https://github.com/libp2p/go-libp2p-kad-dht/pull/810)) + - chore: release v0.21. + - chore: Update to go libp2p v0.25 ([libp2p/go-libp2p-kad-dht#815](https://github.com/libp2p/go-libp2p-kad-dht/pull/815)) +- github.com/libp2p/go-libp2p-pubsub (v0.8.3 -> v0.9.0): + - chore: update to go-libp2p v0.25 (#517) ([libp2p/go-libp2p-pubsub#517](https://github.com/libp2p/go-libp2p-pubsub/pull/517)) +- github.com/libp2p/go-libp2p-routing-helpers (v0.6.0 -> v0.6.1): + - chore: release v0.6.1 + - fix: cancel parallel routers +- github.com/libp2p/go-msgio (v0.2.0 -> v0.3.0): + - release v0.3.0 (#39) ([libp2p/go-msgio#39](https://github.com/libp2p/go-msgio/pull/39)) + - switch from deprecated gogo to google.golang.org/protobuf ([libp2p/go-msgio#38](https://github.com/libp2p/go-msgio/pull/38)) + - sync: update CI config files (#36) ([libp2p/go-msgio#36](https://github.com/libp2p/go-msgio/pull/36)) +- github.com/lucas-clemente/quic-go (v0.31.1 -> v0.29.1): + - http3: fix double close of chan when using DontCloseRequestStream +- github.com/multiformats/go-multistream (v0.3.3 -> v0.4.1): + - release v0.4.1 ([multiformats/go-multistream#101](https://github.com/multiformats/go-multistream/pull/101)) + - Fix errors Is checking ([multiformats/go-multistream#100](https://github.com/multiformats/go-multistream/pull/100)) + - release v0.4.0 (#93) ([multiformats/go-multistream#93](https://github.com/multiformats/go-multistream/pull/93)) + - switch to Go's native fuzzing (#96) ([multiformats/go-multistream#96](https://github.com/multiformats/go-multistream/pull/96)) + - Add not supported protocols to returned errors (#97) ([multiformats/go-multistream#97](https://github.com/multiformats/go-multistream/pull/97)) + - Make MultistreamMuxer and Client APIs generic (#95) ([multiformats/go-multistream#95](https://github.com/multiformats/go-multistream/pull/95)) + - remove MultistreamMuxer.NegotiateLazy (#92) ([multiformats/go-multistream#92](https://github.com/multiformats/go-multistream/pull/92)) + - sync: update CI config files (#91) ([multiformats/go-multistream#91](https://github.com/multiformats/go-multistream/pull/91)) +- github.com/warpfork/go-wish (v0.0.0-20200122115046-b9ea61034e4a -> v0.0.0-20220906213052-39a1cc7a02d0): + - Update readme with deprecation info +- github.com/whyrusleeping/cbor-gen (v0.0.0-20221220214510-0333c149dec0 -> v0.0.0-20230126041949-52956bd4c9aa): + - add setter to allow reuse of cborreader struct + - fix typo + - allow fields to be ignored ([whyrusleeping/cbor-gen#79](https://github.com/whyrusleeping/cbor-gen/pull/79)) + +
+ +### 👨‍👩‍👧‍👦 Contributors + +| Contributor | Commits | Lines ± | Files Changed | +|-------------|---------|---------|---------------| +| Dirk McCormick | 128 | +16757/-7211 | 387 | +| Henrique Dias | 69 | +7599/-10016 | 316 | +| hannahhoward | 88 | +8503/-4397 | 271 | +| Jeromy Johnson | 244 | +6544/-4034 | 774 | +| Marten Seemann | 64 | +4870/-5628 | 266 | +| Steven Allen | 296 | +4769/-3517 | 972 | +| Brian Tiger Chow | 250 | +5520/-2579 | 435 | +| Jorropo | 64 | +4237/-3548 | 302 | +| Sukun | 18 | +4327/-1093 | 132 | +| Marco Munizaga | 35 | +2809/-1294 | 94 | +| Gus Eggert | 20 | +2523/-1476 | 99 | +| Adin Schmahmann | 15 | +683/-2625 | 69 | +| Marcin Rataj | 73 | +2348/-882 | 133 | +| whyrusleeping | 12 | +1683/-1338 | 23 | +| Jeromy | 99 | +1754/-1181 | 453 | +| Juan Batiz-Benet | 69 | +1182/-678 | 149 | +| Lars Gierth | 31 | +827/-358 | 92 | +| Paul Wolneykien | 2 | +670/-338 | 9 | +| Laurent Senta | 16 | +806/-134 | 53 | +| Henry | 19 | +438/-372 | 36 | +| Michael Muré | 8 | +400/-387 | 19 | +| Łukasz Magiera | 56 | +413/-354 | 117 | +| Jakub Sztandera | 40 | +413/-251 | 100 | +| Justin Johnson | 2 | +479/-165 | 5 | +| Piotr Galar | 7 | +227/-378 | 24 | +| Kevin Atkinson | 11 | +252/-232 | 49 | +| web3-bot | 17 | +236/-240 | 59 | +| Petar Maymounkov | 2 | +348/-84 | 11 | +| Hector Sanjuan | 38 | +206/-223 | 85 | +| Antonio Navarro Perez | 9 | +259/-95 | 17 | +| keks | 22 | +233/-118 | 24 | +| Ho-Sheng Hsiao | 3 | +170/-170 | 30 | +| Lucas Molas | 6 | +266/-54 | 16 | +| Mildred Ki'Lya | 4 | +280/-35 | 7 | +| Steve Loeppky | 5 | +147/-156 | 9 | +| rht | 14 | +97/-188 | 20 | +| Prithvi Shahi | 6 | +89/-193 | 11 | +| Ian Davis | 6 | +198/-75 | 11 | +| taylor | 1 | +180/-89 | 8 | +| ᴍᴀᴛᴛ ʙᴇʟʟ | 14 | +158/-104 | 18 | +| Chris Boddy | 6 | +190/-45 | 8 | +| Rod Vagg | 3 | +203/-28 | 15 | +| Masih H. Derkani | 8 | +165/-61 | 16 | +| Kevin Wallace | 4 | +194/-27 | 7 | +| Mohsin Zaidi | 1 | +179/-41 | 5 | +| ElPaisano | 1 | +110/-110 | 22 | +| Simon Zhu | 6 | +177/-32 | 8 | +| galargh | 9 | +80/-120 | 14 | +| Tomasz Zdybał | 1 | +180/-1 | 4 | +| dgrisham | 3 | +176/-2 | 4 | +| Michael Avila | 3 | +116/-59 | 8 | +| Raúl Kripalani | 2 | +85/-77 | 34 | +| Dr Ian Preston | 11 | +101/-48 | 11 | +| JP Hastings-Spital | 1 | +145/-0 | 2 | +| George Antoniadis | 6 | +59/-58 | 43 | +| Kevin Neaton | 2 | +97/-16 | 4 | +| Adrian Lanzafame | 6 | +81/-25 | 7 | +| Dennis Trautwein | 3 | +89/-9 | 5 | +| mathew-cf | 2 | +82/-9 | 5 | +| tg | 1 | +41/-33 | 1 | +| Eng Zer Jun | 1 | +15/-54 | 5 | +| zramsay | 4 | +15/-53 | 12 | +| muXxer | 1 | +28/-33 | 4 | +| Thomas Eizinger | 1 | +24/-37 | 4 | +| Remco Bloemen | 2 | +28/-18 | 3 | +| Manuel Alonso | 1 | +36/-9 | 1 | +| vyzo | 4 | +26/-12 | 13 | +| Djalil Dreamski | 3 | +27/-9 | 3 | +| Thomas Gardner | 2 | +32/-3 | 4 | +| Jan Winkelmann | 2 | +23/-12 | 8 | +| Artem Andreenko | 1 | +16/-19 | 1 | +| James Stanley | 1 | +34/-0 | 1 | +| Brendan McMillion | 1 | +10/-17 | 3 | +| Jack Loughran | 1 | +22/-0 | 3 | +| Peter Wu | 2 | +12/-9 | 2 | +| Gowtham G | 4 | +14/-7 | 4 | +| Tor Arne Vestbø | 3 | +19/-1 | 3 | +| Cory Schwartz | 1 | +8/-12 | 5 | +| Peter Rabbitson | 1 | +15/-4 | 1 | +| David Dias | 1 | +9/-9 | 1 | +| Will Scott | 1 | +13/-4 | 2 | +| Eric Myhre | 1 | +15/-2 | 1 | +| Stephen Whitmore | 1 | +8/-8 | 1 | +| Rafael Ramalho | 5 | +11/-5 | 5 | +| Christian Couder | 1 | +14/-2 | 1 | +| W. Trevor King | 2 | +9/-6 | 3 | +| Steven Vandevelde | 1 | +11/-3 | 1 | +| Knut Ahlers | 3 | +9/-5 | 3 | +| Bob Potter | 1 | +3/-10 | 1 | +| Russell Dempsey | 4 | +8/-4 | 4 | +| Diogo Silva | 4 | +8/-4 | 4 | +| Dave Justice | 1 | +8/-4 | 1 | +| Andy Leap | 2 | +2/-10 | 2 | +| divingpetrel | 1 | +7/-4 | 2 | +| Iaroslav Gridin | 1 | +9/-2 | 1 | +| Dominic Della Valle | 3 | +5/-5 | 3 | +| Vijayee Kulkaa | 1 | +3/-6 | 1 | +| Friedel Ziegelmayer | 3 | +6/-3 | 3 | +| Stephen Solka | 1 | +1/-7 | 1 | +| Richard Littauer | 3 | +4/-4 | 3 | +| Franky W | 2 | +4/-4 | 2 | +| Dimitris Apostolou | 2 | +4/-4 | 3 | +| Adrian Ulrich | 1 | +8/-0 | 1 | +| Masashi Salvador Mitsuzawa | 1 | +5/-1 | 1 | +| Gabe | 1 | +3/-3 | 1 | +| zuuluuz | 1 | +4/-1 | 1 | +| myml | 1 | +5/-0 | 1 | +| swedneck | 1 | +3/-1 | 1 | +| Wayback Archiver | 1 | +2/-2 | 1 | +| Vladimir Ivanov | 1 | +2/-2 | 1 | +| Péter Szilágyi | 1 | +2/-2 | 1 | +| Karthik Bala | 1 | +2/-2 | 1 | +| Etienne Laurin | 1 | +1/-3 | 1 | +| Shotaro Yamada | 1 | +2/-1 | 1 | +| Robert Carlsen | 1 | +2/-1 | 1 | +| Oli Evans | 1 | +2/-1 | 1 | +| Dan McQuillan | 1 | +2/-1 | 1 | +| susarlanikhilesh | 1 | +1/-1 | 1 | +| mateon1 | 1 | +1/-1 | 1 | +| kpcyrd | 1 | +1/-1 | 1 | +| bbenshoof | 1 | +1/-1 | 1 | +| ZenGround0 | 1 | +1/-1 | 1 | +| Will Hawkins | 1 | +1/-1 | 1 | +| Tommi Virtanen | 1 | +1/-1 | 1 | +| Seungbae Yu | 1 | +1/-1 | 1 | +| Riishab Joshi | 1 | +1/-1 | 1 | +| Kubo Mage | 1 | +1/-1 | 1 | +| Ivan | 1 | +1/-1 | 1 | +| Guillaume Renault | 1 | +1/-1 | 1 | +| Anjor Kanekar | 1 | +1/-1 | 1 | +| Andrew Chin | 1 | +1/-1 | 1 | +| Abdul Rauf | 1 | +1/-1 | 1 | +| makeworld | 1 | +1/-0 | 1 | diff --git a/docs/changelogs/v0.20.md b/docs/changelogs/v0.20.md new file mode 100644 index 000000000..3a6ce8f64 --- /dev/null +++ b/docs/changelogs/v0.20.md @@ -0,0 +1,593 @@ +# Kubo changelog v0.20 + +- [v0.20.0](#v0200) + +## v0.20.0 + +- [Overview](#overview) +- [🔦 Highlights](#-highlights) + - [Boxo under the covers](#boxo-under-the-covers) + - [HTTP Gateway](#http-gateway) + - [Switch to `boxo/gateway` library](#switch-to-boxogateway-library) + - [Improved testing](#improved-testing) + - [Trace Context support](#trace-context-support) + - [Removed legacy features](#removed-legacy-features) + - [`--empty-repo` is now the default](#--empty-repo-is-now-the-default) + - [Reminder: `ipfs pubsub` commands and matching HTTP endpoints are deprecated and will be removed](#reminder-ipfs-pubsub-commands-and-matching-http-endpoints-are-deprecated-and-will-be-removed) +- [📝 Changelog](#-changelog) +- [👨‍👩‍👧‍👦 Contributors](#-contributors) + +### Overview + +### 🔦 Highlights + +#### Boxo under the covers +We have consolidated many IPFS repos into [Boxo](https://github.com/ipfs/boxo), and this release switches Kubo over to use Boxo instead of those repos, resulting in the removal of 27 dependencies from Kubo: + +- github.com/ipfs/go-bitswap +- github.com/ipfs/go-ipfs-files +- github.com/ipfs/tar-utils +- gihtub.com/ipfs/go-block-format +- github.com/ipfs/interface-go-ipfs-core +- github.com/ipfs/go-unixfs +- github.com/ipfs/go-pinning-service-http-client +- github.com/ipfs/go-path +- github.com/ipfs/go-namesys +- github.com/ipfs/go-mfs +- github.com/ipfs/go-ipfs-provider +- github.com/ipfs/go-ipfs-pinner +- github.com/ipfs/go-ipfs-keystore +- github.com/ipfs/go-filestore +- github.com/ipfs/go-ipns +- github.com/ipfs/go-blockservice +- github.com/ipfs/go-ipfs-chunker +- github.com/ipfs/go-fetcher +- github.com/ipfs/go-ipfs-blockstore +- github.com/ipfs/go-ipfs-posinfo +- github.com/ipfs/go-ipfs-util +- github.com/ipfs/go-ipfs-ds-help +- github.com/ipfs/go-verifcid +- github.com/ipfs/go-ipfs-exchange-offline +- github.com/ipfs/go-ipfs-routing +- github.com/ipfs/go-ipfs-exchange-interface +- github.com/ipfs/go-libipfs + +Note: if you consume these in your own code, we recommend migrating to Boxo. To ease this process, there's a [tool which will help migrate your code to Boxo](https://github.com/ipfs/boxo#migrating-to-box). + +You can learn more about the [Boxo 0.8 release](https://github.com/ipfs/boxo/releases/tag/v0.8.0) that Kubo now depends and the general effort to get Boxo to be a stable foundation [here](https://github.com/ipfs/boxo/issues/196). + +#### HTTP Gateway + +##### Switch to `boxo/gateway` library + +Gateway code was extracted and refactored into a standalone library that now +lives in [boxo/gateway](https://github.com/ipfs/boxo/tree/main/gateway). This +enabled us to clean up some legacy code and remove dependency on Kubo +internals. + +The GO API is still being refined, but now operates on higher level abstraction +defined by `gateway.IPFSBackend` interface. It is now possible to embed +gateway functionality without the rest of Kubo. + +See the [car](https://github.com/ipfs/boxo/tree/main/examples/gateway/car) +and [proxy](https://github.com/ipfs/boxo/tree/main/examples/gateway/proxy) +examples, or more advanced +[bifrost-gateway](https://github.com/ipfs/bifrost-gateway). + +##### Improved testing + +We are also in the progress of moving away from gateway testing being based on +Kubo sharness tests, and are working on +[ipfs/gateway-conformance](https://github.com/ipfs/gateway-conformance) test +suite that is vendor agnostic and can be run against arbitrary HTTP endpoint to +test specific subset of [HTTP Gateways specifications](https://specs.ipfs.tech/http-gateways/). + +##### Trace Context support + +We've introduced initial support for `traceparent` header from [W3C's Trace +Context spec](https://w3c.github.io/trace-context/). + +If `traceparent` header is +present in the gateway request, one can use its `trace-id` part to inspect +trace spans via selected exporter such as Jaeger UI +([docs](https://github.com/ipfs/boxo/blob/main/docs/tracing.md#using-jaeger-ui), +[demo](https://user-images.githubusercontent.com/157609/231312374-bafc2035-1fc6-4d6b-901b-9e4af039807c.png)). + +To learn more, see [tracing docs](https://github.com/ipfs/boxo/blob/main/docs/tracing.md). + +##### Removed legacy features + +- Some Kubo-specific prometheus metrics are no longer available. + - An up-to-date list of gateway metrics can be found in [boxo/gateway/metrics.go](https://github.com/ipfs/boxo/blob/main/gateway/metrics.go). +- The legacy opt-in `Gateway.Writable` is no longer available as of Kubo 0.20. + - We are working on developing a modern replacement. + To support our efforts, please leave a comment describing your use case in + [ipfs/specs#375](https://github.com/ipfs/specs/issues/375). + +#### `--empty-repo` is now the default + +When creating a repository with `ipfs init`, `--empty-repo=true` is now the default. This means +that your repository will be empty by default instead of containing the introduction files. +You can read more about the rationale behind this decision on the [tracking issue](https://github.com/ipfs/kubo/issues/9757). + +#### Reminder: `ipfs pubsub` commands and matching HTTP endpoints are deprecated and will be removed + +`ipfs pubsub` commands and all `/api/v0/pubsub/` RPC endpoints and will be removed in the next release. For more information and rational see [#9717](https://github.com/ipfs/kubo/issues/9717). + +### 📝 Changelog + +
Full Changelog + +- github.com/ipfs/kubo: + - fix: deadlock on retrieving WebTransport addresses (#9857) ([ipfs/kubo#9857](https://github.com/ipfs/kubo/pull/9857)) + - docs(config): remove mentions of relay v1 (#9860) ([ipfs/kubo#9860](https://github.com/ipfs/kubo/pull/9860)) + - Merge branch 'master' into merge-release-v0.19.2 + - docs: add changelog for v0.19.2 + - feat: webui@3.0.0 (#9835) ([ipfs/kubo#9835](https://github.com/ipfs/kubo/pull/9835)) + - fix: use default HTTP routers when FullRT DHT client is used (#9841) ([ipfs/kubo#9841](https://github.com/ipfs/kubo/pull/9841)) + - chore: update version + - docs: add `ipfs pubsub` deprecation reminder to changelog (#9827) ([ipfs/kubo#9827](https://github.com/ipfs/kubo/pull/9827)) + - docs: preparing 0.20 changelog for release (#9799) ([ipfs/kubo#9799](https://github.com/ipfs/kubo/pull/9799)) + - feat: boxo tracing and traceparent support (#9811) ([ipfs/kubo#9811](https://github.com/ipfs/kubo/pull/9811)) + - chore: update version + - chore: update version + - update go-libp2p to v0.27.0 + - docs: add optimistic provide feature description + - feat: add experimental optimistic provide + - fix(ci): speed up docker build (#9800) ([ipfs/kubo#9800](https://github.com/ipfs/kubo/pull/9800)) + - feat(tracing): use OTEL_PROPAGATORS as per OTel spec (#9801) ([ipfs/kubo#9801](https://github.com/ipfs/kubo/pull/9801)) + - docs: fix jaeger command (#9797) ([ipfs/kubo#9797](https://github.com/ipfs/kubo/pull/9797)) + - Merge Release: v0.19.1 (#9794) ([ipfs/kubo#9794](https://github.com/ipfs/kubo/pull/9794)) + - chore: upgrade OpenTelemetry dependencies (#9736) ([ipfs/kubo#9736](https://github.com/ipfs/kubo/pull/9736)) + - test: fix flaky content routing over HTTP test (#9772) ([ipfs/kubo#9772](https://github.com/ipfs/kubo/pull/9772)) + - feat: allow injecting custom path resolvers (#9750) ([ipfs/kubo#9750](https://github.com/ipfs/kubo/pull/9750)) + - feat: add changelog entry for router timeouts for v0.19.1 (#9784) ([ipfs/kubo#9784](https://github.com/ipfs/kubo/pull/9784)) + - feat(gw): new metrics and HTTP range support (#9786) ([ipfs/kubo#9786](https://github.com/ipfs/kubo/pull/9786)) + - feat!: make --empty-repo default (#9758) ([ipfs/kubo#9758](https://github.com/ipfs/kubo/pull/9758)) + - fix: remove timeout on default DHT operations (#9783) ([ipfs/kubo#9783](https://github.com/ipfs/kubo/pull/9783)) + - refactor: switch gateway code to new API from go-libipfs (#9681) ([ipfs/kubo#9681](https://github.com/ipfs/kubo/pull/9681)) + - test: port remote pinning tests to Go (#9720) ([ipfs/kubo#9720](https://github.com/ipfs/kubo/pull/9720)) + - feat: add identify option to swarm peers command + - test: port routing DHT tests to Go (#9709) ([ipfs/kubo#9709](https://github.com/ipfs/kubo/pull/9709)) + - test: fix autoclient flakiness (#9769) ([ipfs/kubo#9769](https://github.com/ipfs/kubo/pull/9769)) + - test: skip flaky pubsub test (#9770) ([ipfs/kubo#9770](https://github.com/ipfs/kubo/pull/9770)) + - chore: migrate go-libipfs to boxo + - feat: add tracing to the commands client + - feat: add client-side metrics for routing-v1 client + - test: increase max wait time for peering assertion + - feat: remove writable gateway (#9743) ([ipfs/kubo#9743](https://github.com/ipfs/kubo/pull/9743)) + - Process Improvement: v0.18.0 ([ipfs/kubo#9484](https://github.com/ipfs/kubo/pull/9484)) + - fix: deadlock while racing `ipfs dag import` and `ipfs repo gc` + - feat: improve dag/import (#9721) ([ipfs/kubo#9721](https://github.com/ipfs/kubo/pull/9721)) + - ci: remove circleci config ([ipfs/kubo#9687](https://github.com/ipfs/kubo/pull/9687)) + - docs: use fx.Decorate instead of fx.Replace in examples (#9725) ([ipfs/kubo#9725](https://github.com/ipfs/kubo/pull/9725)) + - Create Changelog: v0.20 ([ipfs/kubo#9742](https://github.com/ipfs/kubo/pull/9742)) + - Merge Release: v0.19.0 ([ipfs/kubo#9741](https://github.com/ipfs/kubo/pull/9741)) + - feat(gateway): invalid CID returns 400 Bad Request (#9726) ([ipfs/kubo#9726](https://github.com/ipfs/kubo/pull/9726)) + - fix: remove outdated changelog part ([ipfs/kubo#9739](https://github.com/ipfs/kubo/pull/9739)) + - docs: 0.19 changelog ([ipfs/kubo#9707](https://github.com/ipfs/kubo/pull/9707)) + - fix: canonicalize user defined headers + - fix: apply API.HTTPHeaders to /webui redirect + - feat: add heap allocs to 'ipfs diag profile' + - fix: future proof with > rcmgr.DefaultLimit for new enum rcmgr values + - test: add test for presarvation of unlimited configs for inbound systems + - fix: preserve Unlimited StreamsInbound in connmgr reconciliation + - test: fix flaky rcmgr test + - chore: deprecate the pubsub api + - Revert "chore: add hamt directory sharding test" + - chore: add hamt directory sharding test + - test: port peering test from sharness to Go + - test: use `T.TempDir` to create temporary test directory + - fix: --verify forgets the verified key + - test: name --verify forgets the verified key + - chore: fix toc in changelog for 0.18 + - feat: add "autoclient" routing type + - test: parallelize more of rcmgr Go tests + - test: port legacy DHT tests to Go + - fix: t0116-gateway-cache.sh ([ipfs/kubo#9696](https://github.com/ipfs/kubo/pull/9696)) + - docs: add bifrost to early testers ([ipfs/kubo#9699](https://github.com/ipfs/kubo/pull/9699)) + - fix: typo in documentation for install path + - docs: fix typos + - Update Version: v0.19 ([ipfs/kubo#9698](https://github.com/ipfs/kubo/pull/9698)) +- github.com/ipfs/go-block-format (v0.1.1 -> v0.1.2): + - chore: release v0.1.2 + - Revert deprecation and go-libipfs/blocks stub types + - docs: deprecation notice [ci skip] +- github.com/ipfs/go-cid (v0.3.2 -> v0.4.1): + - v0.4.1 + - Add unit test for unexpected eof + - Update cid.go + - CidFromReader should not wrap valid EOF return. + - chore: version 0.4.0 + - feat: wrap parsing errors into ErrInvalidCid + - fix: use crypto/rand.Read + - Fix README.md example error (#146) ([ipfs/go-cid#146](https://github.com/ipfs/go-cid/pull/146)) +- github.com/ipfs/go-delegated-routing (v0.7.0 -> v0.8.0): + - chore: release v0.8.0 + - chore: migrate from go-ipns to boxo + - docs: add deprecation notice [ci skip] +- github.com/ipfs/go-graphsync (v0.14.1 -> v0.14.4): + - Update version to cover latest fixes (#419) ([ipfs/go-graphsync#419](https://github.com/ipfs/go-graphsync/pull/419)) + - Bring changes from #412 + - Bring changes from #391 + - fix: calling message queue Shutdown twice causes panic (because close is called twice on done channel) (#414) ([ipfs/go-graphsync#414](https://github.com/ipfs/go-graphsync/pull/414)) + - docs(CHANGELOG): update for v0.14.3 + - fix: wire up proper linksystem to traverser (#411) ([ipfs/go-graphsync#411](https://github.com/ipfs/go-graphsync/pull/411)) + - sync: update CI config files (#378) ([ipfs/go-graphsync#378](https://github.com/ipfs/go-graphsync/pull/378)) + - chore: remove social links (#398) ([ipfs/go-graphsync#398](https://github.com/ipfs/go-graphsync/pull/398)) + - Removes `main` branch callout. + - release v0.14.2 +- github.com/ipfs/go-ipfs-blockstore (v1.2.0 -> v1.3.0): + - chore: release v1.3.0 + - feat: stub and deprecate NewBlockstoreNoPrefix + - Accept options for blockstore: start with WriteThrough and NoPrefix + - Allow using a NewWriteThrough() blockstore. + - sync: update CI config files (#105) ([ipfs/go-ipfs-blockstore#105](https://github.com/ipfs/go-ipfs-blockstore/pull/105)) + - feat: fast-path for PutMany, falling back to Put for single block call (#97) ([ipfs/go-ipfs-blockstore#97](https://github.com/ipfs/go-ipfs-blockstore/pull/97)) +- github.com/ipfs/go-ipfs-cmds (v0.8.2 -> v0.9.0): + - chore: release v0.9.0 + - chore: change go-libipfs to boxo +- github.com/ipfs/go-libipfs (v0.6.2 -> v0.7.0): + - chore: bump to 0.7.0 (#213) ([ipfs/go-libipfs#213](https://github.com/ipfs/go-libipfs/pull/213)) + - feat: return 400 on /ipfs/invalid-cid (#205) ([ipfs/go-libipfs#205](https://github.com/ipfs/go-libipfs/pull/205)) + - docs: add note in README that go-libipfs is not comprehensive (#163) ([ipfs/go-libipfs#163](https://github.com/ipfs/go-libipfs/pull/163)) +- github.com/ipfs/go-merkledag (v0.9.0 -> v0.10.0): + - chore: bump version to 0.10.0 + - fix: switch to crypto/rand.Read + - stop using the deprecated io/ioutil package +- github.com/ipfs/go-unixfs (v0.4.4 -> v0.4.5): + - chore: release v0.4.5 + - chore: remove go-libipfs dependency +- github.com/ipfs/go-unixfsnode (v1.5.2 -> v1.6.0): + - chore: bump v1.6.0 + - feat: add UnixFSPathSelectorBuilder ([ipfs/go-unixfsnode#45](https://github.com/ipfs/go-unixfsnode/pull/45)) + - fix: update state to allow iter continuance on NotFound errors + - chore!: make PBLinkItr private - not intended for public use + - fix: propagate iteration errors +- github.com/ipld/go-car/v2 (v2.5.1 -> v2.9.1-0.20230325062757-fff0e4397a3d): + - chore: unmigrate from go-libipfs + - Create CODEOWNERS + - blockstore: give a direct access to the index for read operations + - blockstore: only close the file on error in OpenReadWrite, not OpenReadWriteFile + - fix: handle (and test) WholeCID vs not; fast Has() path for storage + - ReadWrite: faster Has() by using the in-memory index instead of reading on disk + - fix: let `extract` skip missing unixfs shard links + - fix: error when no files extracted + - fix: make -f optional, read from stdin if omitted + - fix: update cmd/car/README with latest description + - chore: add test cases for extract modes + - feat: extract accepts '-' as an output path for stdout + - feat: extract specific path, accept stdin as streaming input + - fix: if we don't read the full block data, don't error on !EOF + - blockstore: try to close during Finalize(), even in case of previous error + - ReadWrite: add an alternative FinalizeReadOnly+Close flow + - feat: add WithTrustedCar() reader option (#381) ([ipld/go-car#381](https://github.com/ipld/go-car/pull/381)) + - blockstore: fast path for AllKeysChan using the index + - fix: switch to crypto/rand.Read + - stop using the deprecated io/ioutil package + - fix(doc): fix storage package doc formatting + - fix: return errors for unsupported operations + - chore: move insertionindex into store pkg + - chore: add experimental note + - fix: minor lint & windows fd test problems + - feat: docs for StorageCar interfaces + - feat: ReadableWritable; dedupe shared code + - feat: add Writable functionality to StorageCar + - feat: StorageCar as a Readable storage, separate from blockstore + - feat(blockstore): implement a streaming read only storage + - feat(cmd): add index create subcommand to create an external carv2 index ([ipld/go-car#350](https://github.com/ipld/go-car/pull/350)) + - chore: bump version to 0.6.0 + - fix: use goreleaser instead + - Allow using WalkOption in WriteCar function ([ipld/go-car#357](https://github.com/ipld/go-car/pull/357)) + - fix: update go-block-format to the version that includes the stubs + - feat: upgrade from go-block-format to go-libipfs/blocks + - cleanup readme a bit to make the cli more discoverable (#353) ([ipld/go-car#353](https://github.com/ipld/go-car/pull/353)) + - Update install instructions in README.md + - Add a debugging form for car files. (#341) ([ipld/go-car#341](https://github.com/ipld/go-car/pull/341)) + - ([ipld/go-car#340](https://github.com/ipld/go-car/pull/340)) +- github.com/ipld/go-codec-dagpb (v1.5.0 -> v1.6.0): + - Update version.json +- github.com/ipld/go-ipld-prime (v0.19.0 -> v0.20.0): + - Prepare v0.20.0 + - fix(datamodel): add tests to Copy, make it complain on nil + - feat(dagcbor): mode to allow parsing undelimited streamed objects + - Fix mispatched package declaration. + - Add several pieces of docs to schema/dmt. + - Additional access to schema/dmt package; schema concatenation feature ([ipld/go-ipld-prime#483](https://github.com/ipld/go-ipld-prime/pull/483)) + - Fix hash mismatch error on matching link pointer + - feat: support errors.Is for schema errors +- github.com/ipld/go-ipld-prime/storage/bsadapter (v0.0.0-20211210234204-ce2a1c70cd73 -> v0.0.0-20230102063945-1a409dc236dd): + - build(deps): bump github.com/ipfs/go-blockservice + - Fix mispatched package declaration. + - Add several pieces of docs to schema/dmt. + - Additional access to schema/dmt package; schema concatenation feature ([ipld/go-ipld-prime/storage/bsadapter#483](https://github.com/ipld/go-ipld-prime/storage/bsadapter/pull/483)) + - fix: go mod tidy + - build(deps): bump github.com/frankban/quicktest from 1.14.3 to 1.14.4 + - Fix hash mismatch error on matching link pointer + - build(deps): bump github.com/warpfork/go-testmark from 0.10.0 to 0.11.0 + - feat: support errors.Is for schema errors + - build(deps): bump github.com/multiformats/go-multicodec + - Prepare v0.19.0 + - fix: correct json codec links & bytes handling + - build(deps): bump github.com/google/go-cmp from 0.5.8 to 0.5.9 (#468) ([ipld/go-ipld-prime/storage/bsadapter#468](https://github.com/ipld/go-ipld-prime/storage/bsadapter/pull/468)) + - build(deps): bump github.com/ipfs/go-cid from 0.3.0 to 0.3.2 (#466) ([ipld/go-ipld-prime/storage/bsadapter#466](https://github.com/ipld/go-ipld-prime/storage/bsadapter/pull/466)) + - build(deps): bump github.com/ipfs/go-cid in /storage/bsrvadapter (#464) ([ipld/go-ipld-prime/storage/bsadapter#464](https://github.com/ipld/go-ipld-prime/storage/bsadapter/pull/464)) + - test(basicnode): increase test coverage for int and map types (#454) ([ipld/go-ipld-prime/storage/bsadapter#454](https://github.com/ipld/go-ipld-prime/storage/bsadapter/pull/454)) + - build(deps): bump github.com/ipfs/go-cid in /storage/bsrvadapter + - build(deps): bump github.com/ipfs/go-cid from 0.2.0 to 0.3.0 + - build(deps): bump github.com/multiformats/go-multicodec + - fix: remove reliance on ioutil + - fix: update sub-package modules + - build(deps): bump github.com/multiformats/go-multihash + - build(deps): bump github.com/ipfs/go-datastore in /storage/dsadapter + - update .github/workflows/go-check.yml + - update .github/workflows/go-test.yml + - run gofmt -s + - bump go.mod to Go 1.18 and run go fix + - bump go.mod to Go 1.18 and run go fix + - bump go.mod to Go 1.18 and run go fix + - bump go.mod to Go 1.18 and run go fix + - feat: add kinded union to gendemo + - fix: go mod 1.17 compat problems + - build(deps): bump github.com/ipfs/go-blockservice + - Prepare v0.18.0 + - fix(deps): update benchmarks go.sum + - build(deps): bump github.com/multiformats/go-multihash + - feat(bindnode): add a BindnodeRegistry utility (#437) ([ipld/go-ipld-prime/storage/bsadapter#437](https://github.com/ipld/go-ipld-prime/storage/bsadapter/pull/437)) + - feat(bindnode): support full uint64 range + - chore(bindnode): remove typed functions for options + - chore(bindnode): docs and minor tweaks + - feat(bindnode): make Any converters work for List and Map values + - fix(bindnode): shorten converter option names, minor perf improvements + - fix(bindnode): only custom convert AssignNull for Any converter + - feat(bindnode): pass Null on to nullable custom converters + - chore(bindnode): config helper refactor w/ short-circuit + - feat(bindnode): add AddCustomTypeAnyConverter() to handle `Any` fields + - feat(bindnode): add AddCustomTypeXConverter() options for most scalar kinds + - chore(bindnode): back out of reflection for converters + - feat(bindnode): switch to converter functions instead of type + - feat(bindnode): allow custom type conversions with options + - feat: add release checklist (#442) ([ipld/go-ipld-prime/storage/bsadapter#442](https://github.com/ipld/go-ipld-prime/storage/bsadapter/pull/442)) + - Prepare v0.17.0 + - feat: introduce UIntNode interface, used within DAG-CBOR codec + - add option to not parse beyond end of structure (#435) ([ipld/go-ipld-prime/storage/bsadapter#435](https://github.com/ipld/go-ipld-prime/storage/bsadapter/pull/435)) + - sync benchmarks go.sum + - build(deps): bump github.com/multiformats/go-multicodec + - patch: first draft. ([ipld/go-ipld-prime/storage/bsadapter#350](https://github.com/ipld/go-ipld-prime/storage/bsadapter/pull/350)) + - feat(bindnode): infer links and Any from Go types (#432) ([ipld/go-ipld-prime/storage/bsadapter#432](https://github.com/ipld/go-ipld-prime/storage/bsadapter/pull/432)) + - fix(codecs): error on cid.Undef links in dag{json,cbor} encoding (#433) ([ipld/go-ipld-prime/storage/bsadapter#433](https://github.com/ipld/go-ipld-prime/storage/bsadapter/pull/433)) + - chore(bindnode): add test for sub-node unwrapping + - fix(bindnode): more helpful error message for enum value footgun + - fix(bindnode): panic early if API has been passed ptr-to-ptr + - fix(deps): mod tidy for dependencies + - build(deps): bump github.com/warpfork/go-testmark from 0.3.0 to 0.10.0 + - build(deps): bump github.com/multiformats/go-multicodec + - build(deps): bump github.com/ipfs/go-cid from 0.0.4 to 0.2.0 + - build(deps): bump github.com/google/go-cmp from 0.5.7 to 0.5.8 + - build(deps): bump github.com/frankban/quicktest from 1.14.2 to 1.14.3 + - build(deps): bump github.com/ipfs/go-cid in /storage/bsrvadapter + - chore(deps): expand dependabot to sub-modules + - chore(deps): add dependabot config + - printer: fix printing of floats + - add version.json file (#411) ([ipld/go-ipld-prime/storage/bsadapter#411](https://github.com/ipld/go-ipld-prime/storage/bsadapter/pull/411)) + - ci: use GOFLAGS to control test tags + - ci: disable coverpkg using custom workflow insertion + - ci: add initial web3 unified-ci files + - fix: make 32-bit safe and stable & add to CI + - ci: add go-check.yml workflow from unified-ci + - ci: go mod tidy + - fix: staticcheck and govet fixes + - test: make tests work on Windows, add Windows to CI (#405) ([ipld/go-ipld-prime/storage/bsadapter#405](https://github.com/ipld/go-ipld-prime/storage/bsadapter/pull/405)) + - schema: enable inline types through dsl parser & compiler (#404) ([ipld/go-ipld-prime/storage/bsadapter#404](https://github.com/ipld/go-ipld-prime/storage/bsadapter/pull/404)) + - node/bindnode: allow nilable types for IPLD optional/nullable + - test(ci): enable macos in GitHub Actions + - test(gen-go): disable parallelism when testing on macos + - storage: update deps + - dsl support for stringjoin struct repr and stringprefix union repr ([ipld/go-ipld-prime/storage/bsadapter#397](https://github.com/ipld/go-ipld-prime/storage/bsadapter/pull/397)) + - codec/dagcbor: add DecodeOptions.ExperimentalDeterminism + - node/bindnode: add some more docs + - start testing on Go 1.18.x, drop Go 1.16.x + - readme: getting started pointers. + - readme: bindnode definitely needs a mention! + - Readme updates! + - datamodel: document that repr prototypes produce type nodes + - node/bindnode: minor fuzz improvements + - gengo: update readme. + - fix(dagcbor): don't accept trailing bytes + - schema/dmt: reject duplicate or missing union repr members + - node/bindnode: actually check schemadmt.Compile errors when fuzzing + - node/bindnode: avoid OOM when inferring from cyclic IPLD schemas + - schema/dmt: require enum reprs to refer valid members + - skip NaN/Inf errors for dag-json + - node/bindnode: refuse to decode empty union values + - schema/dmt: error in Compile if union reprs refer to unknown members + - node/bindnode: start fuzzing with schema/dmt and codec/dagcbor + - mark v0.16.0 + - node/bindnode: enforce pointer requirement for nullable maps + - Implement WalkTransforming traversal (#376) ([ipld/go-ipld-prime/storage/bsadapter#376](https://github.com/ipld/go-ipld-prime/storage/bsadapter/pull/376)) + - docs(datamodel): add comment to LargeBytesNode + - Add partial-match traversal of large bytes (#375) ([ipld/go-ipld-prime/storage/bsadapter#375](https://github.com/ipld/go-ipld-prime/storage/bsadapter/pull/375)) + - Implement option to start traversals at a path ([ipld/go-ipld-prime/storage/bsadapter#358](https://github.com/ipld/go-ipld-prime/storage/bsadapter/pull/358)) + - add top-level "go value with schema" example + - Support optional `LargeBytesNode` interface (#372) ([ipld/go-ipld-prime/storage/bsadapter#372](https://github.com/ipld/go-ipld-prime/storage/bsadapter/pull/372)) + - node/bindnode: support pointers to datamodel.Node to bind with Any + - fix(bindnode): tuple struct iterator should handle absent fields properly + - node/bindnode: make AssignNode work at the repr level + - node/bindnode: add support for unsigned integers + - node/bindnode: cover even more edge case panics + - node/bindnode: polish some more AsT panics + - schema/dmt: stop using a fake test to generate code ([ipld/go-ipld-prime/storage/bsadapter#356](https://github.com/ipld/go-ipld-prime/storage/bsadapter/pull/356)) + - schema: remove one review note; add another. + - fix: minor EncodedLength fixes, add tests to fully exercise + - feat: add dagcbor.EncodedLength(Node) to calculate length without encoding + - chore: rename Garbage() to Generate() + - fix: minor garbage nits + - fix: Garbage() takes rand parameter, tweak algorithms, improve docs + - feat: add Garbage() Node generator + - node/bindnode: introduce an assembler that always errors + - node/bindnode: polish panics on invalid AssignT calls + - datamodel: don't panic when stringifying an empty KindSet + - node/bindnode: start using ipld.LoadSchema APIs + - selectors: fix for edge case around recursion clauses with an immediate edge. ([ipld/go-ipld-prime/storage/bsadapter#334](https://github.com/ipld/go-ipld-prime/storage/bsadapter/pull/334)) + - node/bindnode: improve support for pointer types + - node/bindnode: subtract all absents in Length at the repr level + - fix(codecs): error when encoding maps whose lengths don't match entry count + - schema: avoid alloc and copy in Struct and Enum methods + - node/bindnode: allow mapping int-repr enums with Go integers + - schema,node/bindnode: add support for Any + - signaling ADLs in selectors (#301) ([ipld/go-ipld-prime/storage/bsadapter#301](https://github.com/ipld/go-ipld-prime/storage/bsadapter/pull/301)) + - node/bindnode: add support for enums + - schema/...: add support for enum int representations + - node/bindnode: allow binding cidlink.Link to links +- github.com/libp2p/go-libp2p (v0.26.4 -> v0.27.3): + - release v0.27.3 + - quic virtual listener: don't panic when quic-go's accept call errors (#2276) ([libp2p/go-libp2p#2276](https://github.com/libp2p/go-libp2p/pull/2276)) + - Release v0.27.2 (#2270) ([libp2p/go-libp2p#2270](https://github.com/libp2p/go-libp2p/pull/2270)) + - release v0.27.1 (#2252) ([libp2p/go-libp2p#2252](https://github.com/libp2p/go-libp2p/pull/2252)) + - Infer public webtransport addrs from quic-v1 addrs. (#2251) ([libp2p/go-libp2p#2251](https://github.com/libp2p/go-libp2p/pull/2251)) + - basichost: don't allocate when deduplicating multiaddrs (#2206) ([libp2p/go-libp2p#2206](https://github.com/libp2p/go-libp2p/pull/2206)) + - identify: fix normalization of interface listen addresses (#2250) ([libp2p/go-libp2p#2250](https://github.com/libp2p/go-libp2p/pull/2250)) + - autonat: fix flaky TestAutoNATDialRefused (#2245) ([libp2p/go-libp2p#2245](https://github.com/libp2p/go-libp2p/pull/2245)) + - basichost: remove stray print statement in test (#2249) ([libp2p/go-libp2p#2249](https://github.com/libp2p/go-libp2p/pull/2249)) + - swarm: fix multiaddr comparison in ListenClose (#2247) ([libp2p/go-libp2p#2247](https://github.com/libp2p/go-libp2p/pull/2247)) + - release v0.27.0 (#2242) ([libp2p/go-libp2p#2242](https://github.com/libp2p/go-libp2p/pull/2242)) + - add a security policy (#2238) ([libp2p/go-libp2p#2238](https://github.com/libp2p/go-libp2p/pull/2238)) + - chore: 0.27.0 changelog entries (#2241) ([libp2p/go-libp2p#2241](https://github.com/libp2p/go-libp2p/pull/2241)) + - correctly handle WebTransport addresses without certhashes (#2239) ([libp2p/go-libp2p#2239](https://github.com/libp2p/go-libp2p/pull/2239)) + - autorelay: add metrics (#2185) ([libp2p/go-libp2p#2185](https://github.com/libp2p/go-libp2p/pull/2185)) + - autonat: don't change status on dial request refused (#2225) ([libp2p/go-libp2p#2225](https://github.com/libp2p/go-libp2p/pull/2225)) + - autonat: fix closing of listeners in dialPolicy tests (#2226) ([libp2p/go-libp2p#2226](https://github.com/libp2p/go-libp2p/pull/2226)) + - discovery (backoff): fix typo in comment (#2214) ([libp2p/go-libp2p#2214](https://github.com/libp2p/go-libp2p/pull/2214)) + - relaysvc: flaky TestReachabilityChangeEvent (#2215) ([libp2p/go-libp2p#2215](https://github.com/libp2p/go-libp2p/pull/2215)) + - Add wss transport to interop tester impl (#2178) ([libp2p/go-libp2p#2178](https://github.com/libp2p/go-libp2p/pull/2178)) + - tests: add a stream read deadline transport test (#2210) ([libp2p/go-libp2p#2210](https://github.com/libp2p/go-libp2p/pull/2210)) + - autorelay: fix busy loop bug and flaky tests in relay finder (#2208) ([libp2p/go-libp2p#2208](https://github.com/libp2p/go-libp2p/pull/2208)) + - tests: test mplex and Yamux, Noise and TLS in transport tests (#2209) ([libp2p/go-libp2p#2209](https://github.com/libp2p/go-libp2p/pull/2209)) + - tests: add some basic transport integration tests (#2207) ([libp2p/go-libp2p#2207](https://github.com/libp2p/go-libp2p/pull/2207)) + - autorelay: remove unused semaphore (#2184) ([libp2p/go-libp2p#2184](https://github.com/libp2p/go-libp2p/pull/2184)) + - basichost: prevent duplicate dials (#2196) ([libp2p/go-libp2p#2196](https://github.com/libp2p/go-libp2p/pull/2196)) + - websocket: don't set a WSS multiaddr for accepted unencrypted conns (#2199) ([libp2p/go-libp2p#2199](https://github.com/libp2p/go-libp2p/pull/2199)) + - websocket: Don't limit message sizes in the websocket reader (#2193) ([libp2p/go-libp2p#2193](https://github.com/libp2p/go-libp2p/pull/2193)) + - identify: fix stale comment (#2179) ([libp2p/go-libp2p#2179](https://github.com/libp2p/go-libp2p/pull/2179)) + - relay service: add metrics (#2154) ([libp2p/go-libp2p#2154](https://github.com/libp2p/go-libp2p/pull/2154)) + - identify: Fix IdentifyWait when Connected events happen out of order (#2173) ([libp2p/go-libp2p#2173](https://github.com/libp2p/go-libp2p/pull/2173)) + - chore: fix ressource manager's README (#2168) ([libp2p/go-libp2p#2168](https://github.com/libp2p/go-libp2p/pull/2168)) + - relay: fix deadlock when closing (#2171) ([libp2p/go-libp2p#2171](https://github.com/libp2p/go-libp2p/pull/2171)) + - core: remove LocalPrivateKey method from network.Conn interface (#2144) ([libp2p/go-libp2p#2144](https://github.com/libp2p/go-libp2p/pull/2144)) + - routed host: return connection error instead of routing error (#2169) ([libp2p/go-libp2p#2169](https://github.com/libp2p/go-libp2p/pull/2169)) + - connmgr: reduce log level for closing connections (#2165) ([libp2p/go-libp2p#2165](https://github.com/libp2p/go-libp2p/pull/2165)) + - circuitv2: cleanup relay service properly (#2164) ([libp2p/go-libp2p#2164](https://github.com/libp2p/go-libp2p/pull/2164)) + - chore: add patch release to changelog (#2151) ([libp2p/go-libp2p#2151](https://github.com/libp2p/go-libp2p/pull/2151)) + - chore: remove superfluous testing section from README (#2150) ([libp2p/go-libp2p#2150](https://github.com/libp2p/go-libp2p/pull/2150)) + - autonat: don't use autonat for address discovery (#2148) ([libp2p/go-libp2p#2148](https://github.com/libp2p/go-libp2p/pull/2148)) + - swarm metrics: fix connection direction (#2147) ([libp2p/go-libp2p#2147](https://github.com/libp2p/go-libp2p/pull/2147)) + - connmgr: Use eventually equal helper in connmgr tests (#2128) ([libp2p/go-libp2p#2128](https://github.com/libp2p/go-libp2p/pull/2128)) + - swarm: emit PeerConnectedness event from swarm instead of from hosts (#1574) ([libp2p/go-libp2p#1574](https://github.com/libp2p/go-libp2p/pull/1574)) + - relay: initialize the ASN util when starting the service (#2143) ([libp2p/go-libp2p#2143](https://github.com/libp2p/go-libp2p/pull/2143)) + - Fix flaky TestMetricsNoAllocNoCover test (#2142) ([libp2p/go-libp2p#2142](https://github.com/libp2p/go-libp2p/pull/2142)) + - identify: Bump timeouts/sleep in tests (#2135) ([libp2p/go-libp2p#2135](https://github.com/libp2p/go-libp2p/pull/2135)) + - Add sleep to fix flaky test (#2129) ([libp2p/go-libp2p#2129](https://github.com/libp2p/go-libp2p/pull/2129)) + - basic_host: Fix flaky tests (#2136) ([libp2p/go-libp2p#2136](https://github.com/libp2p/go-libp2p/pull/2136)) + - swarm: Check context once more before dialing (#2139) ([libp2p/go-libp2p#2139](https://github.com/libp2p/go-libp2p/pull/2139)) +- github.com/libp2p/go-libp2p-asn-util (v0.2.0 -> v0.3.0): + - release v0.3.0 (#26) ([libp2p/go-libp2p-asn-util#26](https://github.com/libp2p/go-libp2p-asn-util/pull/26)) + - initialize the store lazily (#25) ([libp2p/go-libp2p-asn-util#25](https://github.com/libp2p/go-libp2p-asn-util/pull/25)) +- github.com/libp2p/go-libp2p-gostream (v0.5.0 -> v0.6.0): + - Update libp2p ([libp2p/go-libp2p-gostream#80](https://github.com/libp2p/go-libp2p-gostream/pull/80)) + - fix typo in README (#75) ([libp2p/go-libp2p-gostream#75](https://github.com/libp2p/go-libp2p-gostream/pull/75)) +- github.com/libp2p/go-libp2p-http (v0.4.0 -> v0.5.0): + - sync: update CI config files ([libp2p/go-libp2p-http#82](https://github.com/libp2p/go-libp2p-http/pull/82)) +- github.com/libp2p/go-libp2p-kad-dht (v0.21.1 -> v0.23.0): + - Release v0.23.0 + - Specified CODEOWNERS ([libp2p/go-libp2p-kad-dht#828](https://github.com/libp2p/go-libp2p-kad-dht/pull/828)) + - fix: optimistic provide ci checks in tests ([libp2p/go-libp2p-kad-dht#833](https://github.com/libp2p/go-libp2p-kad-dht/pull/833)) + - feat: add experimental optimistic provide (#783) ([libp2p/go-libp2p-kad-dht#783](https://github.com/libp2p/go-libp2p-kad-dht/pull/783)) + - feat: rework tracing a bit + - feat: add basic tracing + - chore: release v0.22.0 + - chore: migrate go-libipfs to boxo + - Fix multiple ProviderAddrTTL definitions #795 ([libp2p/go-libp2p-kad-dht#831](https://github.com/libp2p/go-libp2p-kad-dht/pull/831)) + - Increase provider Multiaddress TTL ([libp2p/go-libp2p-kad-dht#795](https://github.com/libp2p/go-libp2p-kad-dht/pull/795)) + - Make provider manager options configurable in `fullrt` ([libp2p/go-libp2p-kad-dht#829](https://github.com/libp2p/go-libp2p-kad-dht/pull/829)) + - Adjust PeerSet logic in the DHT lookup process ([libp2p/go-libp2p-kad-dht#802](https://github.com/libp2p/go-libp2p-kad-dht/pull/802)) + - added maintainers in the README ([libp2p/go-libp2p-kad-dht#826](https://github.com/libp2p/go-libp2p-kad-dht/pull/826)) + - Allow DHT crawler to be swappable + - Introduce options to parameterize config of the accelerated DHT client ([libp2p/go-libp2p-kad-dht#822](https://github.com/libp2p/go-libp2p-kad-dht/pull/822)) +- github.com/libp2p/go-libp2p-pubsub (v0.9.0 -> v0.9.3): + - Fix Memory Leak In New Timecache Implementations (#528) ([libp2p/go-libp2p-pubsub#528](https://github.com/libp2p/go-libp2p-pubsub/pull/528)) + - Default validator support (#525) ([libp2p/go-libp2p-pubsub#525](https://github.com/libp2p/go-libp2p-pubsub/pull/525)) + - Refactor timecache implementations (#523) ([libp2p/go-libp2p-pubsub#523](https://github.com/libp2p/go-libp2p-pubsub/pull/523)) + - fix(timecache): remove panic in first seen cache on Add (#522) ([libp2p/go-libp2p-pubsub#522](https://github.com/libp2p/go-libp2p-pubsub/pull/522)) + - chore: update go version and dependencies (#516) ([libp2p/go-libp2p-pubsub#516](https://github.com/libp2p/go-libp2p-pubsub/pull/516)) +- github.com/multiformats/go-multiaddr (v0.8.0 -> v0.9.0): + - Release v0.9.0 ([multiformats/go-multiaddr#196](https://github.com/multiformats/go-multiaddr/pull/196)) + - Update webrtc protocols after rename ([multiformats/go-multiaddr#195](https://github.com/multiformats/go-multiaddr/pull/195)) +- github.com/multiformats/go-multibase (v0.1.1 -> v0.2.0): + - chore: bump v0.2.0 + - fix: math/rand -> crypto/rand + - fuzz: add Decoder fuzzing +- github.com/multiformats/go-multicodec (v0.7.0 -> v0.8.1): + - Bump version to release `ipns-record` code + - chore: update submodules and go generate + - deps: upgrade stringer to compatible version + - v0.8.0 + - chore: update submodules and go generate +- github.com/warpfork/go-testmark (v0.10.0 -> v0.11.0): + - Quick changelog to note we have an API update. + - Index fix ([warpfork/go-testmark#13](https://github.com/warpfork/go-testmark/pull/13)) + - Link to python implementation in the readme! + +
+ +### 👨‍👩‍👧‍👦 Contributors + +| Contributor | Commits | Lines ± | Files Changed | +|-------------|---------|---------|---------------| +| Rod Vagg | 40 | +4214/-1400 | 102 | +| Sukun | 12 | +3541/-267 | 34 | +| Gus Eggert | 22 | +2387/-1160 | 81 | +| galargh | 23 | +1331/-1734 | 34 | +| Henrique Dias | 23 | +681/-1167 | 79 | +| Marco Munizaga | 19 | +1500/-187 | 55 | +| Jorropo | 25 | +897/-597 | 180 | +| Dennis Trautwein | 4 | +990/-60 | 14 | +| Marten Seemann | 18 | +443/-450 | 53 | +| vyzo | 2 | +595/-152 | 11 | +| Michael Muré | 8 | +427/-182 | 18 | +| Will | 2 | +536/-15 | 5 | +| Adin Schmahmann | 3 | +327/-125 | 11 | +| hannahhoward | 2 | +344/-1 | 4 | +| Arthur Gavazza | 1 | +210/-50 | 4 | +| Hector Sanjuan | 6 | +181/-77 | 13 | +| Masih H. Derkani | 5 | +214/-42 | 12 | +| Calvin Behling | 4 | +158/-58 | 11 | +| Eric Myhre | 7 | +113/-27 | 15 | +| Marcin Rataj | 5 | +72/-30 | 5 | +| Steve Loeppky | 2 | +99/-0 | 2 | +| Piotr Galar | 9 | +60/-18 | 9 | +| gammazero | 4 | +69/-0 | 8 | +| Prithvi Shahi | 2 | +55/-14 | 2 | +| Eng Zer Jun | 1 | +15/-54 | 5 | +| Laurent Senta | 3 | +44/-2 | 3 | +| Ian Davis | 1 | +35/-0 | 1 | +| web3-bot | 4 | +19/-13 | 7 | +| guillaumemichel | 2 | +18/-14 | 3 | +| Guillaume Michel - guissou | 4 | +24/-8 | 4 | +| omahs | 1 | +9/-9 | 3 | +| cortze | 3 | +9/-9 | 3 | +| Nishant Das | 1 | +9/-5 | 3 | +| Hlib Kanunnikov | 2 | +11/-3 | 3 | +| Andrew Gillis | 3 | +6/-8 | 3 | +| Johnny | 1 | +0/-10 | 1 | +| Rafał Leszko | 1 | +4/-4 | 1 | +| Dirk McCormick | 1 | +4/-1 | 1 | +| Antonio Navarro Perez | 1 | +4/-1 | 1 | +| RichΛrd | 2 | +2/-2 | 2 | +| Russell Dempsey | 1 | +2/-1 | 1 | +| Winterhuman | 1 | +1/-1 | 1 | +| Will Hawkins | 1 | +1/-1 | 1 | +| Nikhilesh Susarla | 1 | +1/-1 | 1 | +| Kubo Mage | 1 | +1/-1 | 1 | +| Bryan White | 1 | +1/-1 | 1 | + + diff --git a/docs/changelogs/v0.21.md b/docs/changelogs/v0.21.md new file mode 100644 index 000000000..4dd29c5ed --- /dev/null +++ b/docs/changelogs/v0.21.md @@ -0,0 +1,351 @@ +# Kubo changelog v0.21 + +- [v0.21.1](#v0211) +- [v0.21.0](#v0210) + +## v0.21.1 + +- Update go-libp2p: + - [v0.27.8](https://github.com/libp2p/go-libp2p/releases/tag/v0.27.8) + - [v0.27.9](https://github.com/libp2p/go-libp2p/releases/tag/v0.27.9) +- Update Boxo to v0.10.3 ([ipfs/boxo#412](https://github.com/ipfs/boxo/pull/412)). + +## v0.21.0 + +- [Overview](#overview) +- [🔦 Highlights](#-highlights) + - [Saving previously seen nodes for later bootstrapping](#saving-previously-seen-nodes-for-later-bootstrapping) + - [Gateway: `DeserializedResponses` config flag](#gateway-deserializedresponses-config-flag) + - [`client/rpc` migration of `go-ipfs-http-client`](#clientrpc-migration-of-go-ipfs-http-client) + - [Gateway: DAG-CBOR/-JSON previews and improved error pages](#gateway-dag-cbor-json-previews-and-improved-error-pages) + - [Gateway: subdomain redirects are now `text/html`](#gateway-subdomain-redirects-are-now-texthtml) + - [Gateway: support for partial CAR export parameters (IPIP-402)](#gateway-support-for-partial-car-export-parameters-ipip-402) + - [`ipfs dag stat` deduping statistics](#ipfs-dag-stat-deduping-statistics) + - [Accelerated DHT Client is no longer experimental](#accelerated-dht-client-is-no-longer-experimental) +- [📝 Changelog](#-changelog) +- [👨‍👩‍👧‍👦 Contributors](#-contributors) + +### Overview + +### 🔦 Highlights + +#### Saving previously seen nodes for later bootstrapping + +Kubo now stores a subset of connected peers as backup bootstrap nodes ([kubo#8856](https://github.com/ipfs/kubo/pull/8856)). +These nodes are used in addition to the explicitly defined bootstrappers in the +[`Bootstrap`](https://github.com/ipfs/kubo/blob/master/docs/config.md#bootstrap) configuration. + +This enhancement improves the resiliency of the system, as it eliminates the +necessity of relying solely on the default bootstrappers operated by Protocol +Labs for joining the public IPFS swarm. Previously, this level of robustness +was only available in LAN contexts with [mDNS peer discovery](https://github.com/ipfs/kubo/blob/master/docs/config.md#discoverymdns) +enabled. + +With this update, the same level of robustness is applied to peers that lack +mDNS peers and solely rely on the public DHT. + +#### Gateway: `DeserializedResponses` config flag + +This release introduces the +[`Gateway.DeserializedResponses`](https://github.com/ipfs/kubo/blob/master/docs/config.md#gatewaydeserializedresponses) +configuration flag. + +With this flag, one can explicitly configure whether the gateway responds to +deserialized requests or not. By default, this flag is enabled. + +Disabling deserialized responses allows the +gateway to operate +as a [Trustless Gateway](https://specs.ipfs.tech/http-gateways/trustless-gateway/) +limited to three [verifiable](https://docs.ipfs.tech/reference/http/gateway/#trustless-verifiable-retrieval) +response types: +[application/vnd.ipld.raw](https://www.iana.org/assignments/media-types/application/vnd.ipld.raw), +[application/vnd.ipld.car](https://www.iana.org/assignments/media-types/application/vnd.ipld.car), +and [application/vnd.ipfs.ipns-record](https://www.iana.org/assignments/media-types/application/vnd.ipfs.ipns-record). + +With deserialized responses disabled, the Kubo gateway can serve as a block +backend for other software (like +[bifrost-gateway](https://github.com/ipfs/bifrost-gateway#readme), +[IPFS in Chromium](https://github.com/little-bear-labs/ipfs-chromium/blob/main/README.md) +etc) without the usual risks associated with hosting deserialized data behind +third-party CIDs. + +#### `client/rpc` migration of `go-ipfs-http-client` + +The [`go-ipfs-http-client`](https://github.com/ipfs/go-ipfs-http-client) RPC has +been migrated into [`kubo/client/rpc`](../../client/rpc). + +With this change the two will be kept in sync, in some previous releases we +updated the CoreAPI with new Kubo features but forgot to port thoses to the +http-client, making it impossible to use them together with the same coreapi +version. + +For smooth transition `v0.7.0` of `go-ipfs-http-client` provides updated stubs +for Kubo `v0.21`. + +#### Gateway: DAG-CBOR/-JSON previews and improved error pages + +In this release, we improved the HTML templates of our HTTP gateway: + +1. You can now preview the contents of a DAG-CBOR and DAG-JSON document from your browser, as well as follow any IPLD Links ([CBOR Tag 42](https://github.com/ipld/cid-cbor/)) contained within them. +2. The HTML directory listings now contain [updated, higher-definition icons](https://user-images.githubusercontent.com/5447088/241224419-5385793a-d3bb-40aa-8cb0-0382b5bc56a0.png). +3. On gateway error, instead of a plain text error message, web browsers will now get a friendly HTML response with more details regarding the problem. + +HTML responses are returned when request's `Accept` header includes `text/html`. + +| DAG-CBOR Preview | Error Page | +| ---- | ---- | +| ![DAG-CBOR Preview](https://github.com/ipfs/boxo/assets/5447088/973f05d1-5731-4469-9da5-d1d776891899) | ![Error Page](https://github.com/ipfs/boxo/assets/5447088/14c453df-adbc-4634-b038-133121914550) | + +#### Gateway: subdomain redirects are now `text/html` + +HTTP 301 redirects [from path to subdomain](https://specs.ipfs.tech/http-gateways/subdomain-gateway/#migrating-from-path-to-subdomain-gateway) +no longer include the target data in the body. +The data is returned only once, with the final HTTP 200 returned from the +target subdomain. + +The HTTP 301 body now includes human-readable `text/html` message +for clients that do not follow redirects by default: + +```console +$ curl "https://subdomain-gw.example.net/ipfs/${cid}/" +
Moved Permanently. +``` + +Rationale can be found in [kubo#9913](https://github.com/ipfs/kubo/pull/9913). + +#### Gateway: support for partial CAR export parameters (IPIP-402) + +The gateway now supports optional CAR export parameters +`dag-scope=block|entity|all` and `entity-bytes=from:to` as specified in +[IPIP-402](https://specs.ipfs.tech/ipips/ipip-0402/). + +Batch block retrieval minimizes round trips, catering to the requirements of +light HTTP clients for directory enumeration, range requests, and content path +resolution. + +#### `ipfs dag stat` deduping statistics + +`ipfs dat stat` now accept multiple CIDs and will dump advanced statistics +on the number of shared blocks and size of each CID. + +```console +$ ipfs dag stat --progress=false QmfXuRxzyVy5H2LssLgtXrKCrNvDY8UBvMp2aoW8LS8AYA QmfZDyu2UFfUhL4VdHaw7Hofivmn5D4DdQj38Lwo86RsnB + +CID Blocks Size +QmfXuRxzyVy5H2LssLgtXrKCrNvDY8UBvMp2aoW8LS8AYA 3 2151 +QmfZDyu2UFfUhL4VdHaw7Hofivmn5D4DdQj38Lwo86RsnB 4 3223 + +Summary +Total Size: 3326 +Unique Blocks: 5 +Shared Size: 2048 +Ratio: 1.615755 +``` + +`ipfs --enc=json dag stat`'s keys are a non breaking change, new keys have been added but old keys with previous sementics are still here. + +#### Accelerated DHT Client is no longer experimental + +The [accelerated DHT client](docs/config.md#routingaccelerateddhtclient) is now +the main recommended solution for users who are hosting lots of data. +By trading some upfront DHT caching and increased memory usage, +one gets provider throughput improvements up to 6 millions times bigger dataset. +See [the docs](docs/config.md#routingaccelerateddhtclient) for more info. + +The `Experimental.AcceleratedDHTClient` flag moved to [`Routing.AcceleratedDHTClient`](/docs/config.md#routingaccelerateddhtclient). +A config migration has been added to handle this automatically. + +A new tracker estimates the providing speed and warns users if they +should be using AcceleratedDHTClient because they are falling behind. + +### 📝 Changelog + +
Full Changelog + +- github.com/ipfs/kubo: + - fix: correctly handle migration of configs + - fix(gateway): include CORS on subdomain redirects (#9994) ([ipfs/kubo#9994](https://github.com/ipfs/kubo/pull/9994)) + - fix: docker repository initialization race condition + - chore: update version + - ([ipfs/kubo#9981](https://github.com/ipfs/kubo/pull/9981)) + - ([ipfs/kubo#9960](https://github.com/ipfs/kubo/pull/9960)) + - ([ipfs/kubo#9936](https://github.com/ipfs/kubo/pull/9936)) +- github.com/ipfs/boxo (v0.8.1 -> v0.10.2-0.20230629143123-2d3edc552442): + - chore: version 0.10.2 + - fix(gateway): include CORS on subdomain redirects (#395) ([ipfs/boxo#395](https://github.com/ipfs/boxo/pull/395)) + - fix(gateway): ensure 'X-Ipfs-Root' header is valid (#337) ([ipfs/boxo#337](https://github.com/ipfs/boxo/pull/337)) + - docs: prepare changelog for next release [ci skip] + - chore: version 0.10.1 (#359) ([ipfs/boxo#359](https://github.com/ipfs/boxo/pull/359)) + - fix(gateway): allow CAR trustless requests with path + - blockstore: replace go.uber.org/atomic with sync/atomic + - fix(gateway): remove handleUnsupportedHeaders after go-ipfs 0.13 (#350) ([ipfs/boxo#350](https://github.com/ipfs/boxo/pull/350)) + - docs: update RELEASE.md based on 0.9 release (#343) ([ipfs/boxo#343](https://github.com/ipfs/boxo/pull/343)) + - chore: v0.10.0 (#345) ([ipfs/boxo#345](https://github.com/ipfs/boxo/pull/345)) + - docs(changelog): car params from ipip-402 + - docs(changelog): add gateway deserialized responses (#341) ([ipfs/boxo#341](https://github.com/ipfs/boxo/pull/341)) + - feat(gateway): implement IPIP-402 extensions for gateway CAR requests (#303) ([ipfs/boxo#303](https://github.com/ipfs/boxo/pull/303)) + - chore: release v0.9.0 + - changelog: update for 0.8.1 and 0.9.0 + - provider: second round of reprovider refactor + - feat(unixfs): change protobuf package name to unixfs.v1.pb to prevent collisions with go-unixfs. Also regenerate protobufs with latest gogo + - feat(ipld/merkledag): remove use of go-ipld-format global registry + - feat(ipld/merkledag): updated to use its own global go-ipld-legacy registry instead of a shared global registry + - chore: do not rely on deprecated logger + - changelog: add changelog for async pin listing (#336) ([ipfs/boxo#336](https://github.com/ipfs/boxo/pull/336)) + - pinner: change the interface to have async pin listing + - provider: revert throughput callback and related refactor + - fix(gateway): question marks in url.Path when redirecting (#313) ([ipfs/boxo#313](https://github.com/ipfs/boxo/pull/313)) + - fix(gateway)!: no duplicate payload during subdomain redirects (#326) ([ipfs/boxo#326](https://github.com/ipfs/boxo/pull/326)) + - provider: add breaking changes to the changelog (#330) ([ipfs/boxo#330](https://github.com/ipfs/boxo/pull/330)) + - relocated magic numbers, updated Reprovide Interval from 24h to 22h + - provider: refactor to only maintain one batched implementation and add throughput callback + - feat(gateway): HTML preview for dag-cbor and dag-json (#315) ([ipfs/boxo#315](https://github.com/ipfs/boxo/pull/315)) + - coreiface: add a testing.T argument to the provider + - feat(gateway): improved templates, user friendly errors (#298) ([ipfs/boxo#298](https://github.com/ipfs/boxo/pull/298)) + - feat(gateway)!: deserialised responses turned off by default (#252) ([ipfs/boxo#252](https://github.com/ipfs/boxo/pull/252)) + - fix(gw): missing return in error case ([ipfs/boxo#319](https://github.com/ipfs/boxo/pull/319)) + - feat(routing/http): pass records limit on routing.FindProviders (#299) ([ipfs/boxo#299](https://github.com/ipfs/boxo/pull/299)) + - bitswap/client: fix PeerResponseTrackerProbabilityOneKnownOneUnknownPeer + - feat(gw): add ipfs_http_gw_car_stream_fail_duration_seconds (#312) ([ipfs/boxo#312](https://github.com/ipfs/boxo/pull/312)) + - feat(gw): add ipfs_http_gw_request_types metric (#311) ([ipfs/boxo#311](https://github.com/ipfs/boxo/pull/311)) + - refactor: simplify ipns validation in example + - feat: add deprecator + - fix(routing/v1): add newline in NDJSON responses (#300) ([ipfs/boxo#300](https://github.com/ipfs/boxo/pull/300)) + - feat(gateway): redirect ipns b58mh to cid (#236) ([ipfs/boxo#236](https://github.com/ipfs/boxo/pull/236)) + - refactor: replace assert.Nil for assert.NoError + - tar: add test cases for validatePlatformPath + - feat(ipns): helper ValidateWithPeerID and UnmarshalIpnsEntry (#294) ([ipfs/boxo#294](https://github.com/ipfs/boxo/pull/294)) + - Revert "feat: reusable ipns verify (#292)" + - feat: reusable ipns verify (#292) ([ipfs/boxo#292](https://github.com/ipfs/boxo/pull/292)) + - refactor: remove badger, leveldb dependencies (#286) ([ipfs/boxo#286](https://github.com/ipfs/boxo/pull/286)) + - feat(routing/http): add streaming support (#18) ([ipfs/boxo#18](https://github.com/ipfs/boxo/pull/18)) + - feat(routing): allow-offline with routing put (#278) ([ipfs/boxo#278](https://github.com/ipfs/boxo/pull/278)) + - refactor(gateway): switch to xxhash/v2 (#285) ([ipfs/boxo#285](https://github.com/ipfs/boxo/pull/285)) +- github.com/ipfs/go-ipfs-util (v0.0.2 -> v0.0.3): + - docs: remove contribution section + - chore: bump version + - chore: deprecate types and readme + - sync: update CI config files (#12) ([ipfs/go-ipfs-util#12](https://github.com/ipfs/go-ipfs-util/pull/12)) + - fix staticcheck ([ipfs/go-ipfs-util#9](https://github.com/ipfs/go-ipfs-util/pull/9)) +- github.com/ipfs/go-ipld-format (v0.4.0 -> v0.5.0): + - chore: release version v0.5.0 + - feat: remove block decoding global registry + - sync: update CI config files (#75) ([ipfs/go-ipld-format#75](https://github.com/ipfs/go-ipld-format/pull/75)) + - sync: update CI config files (#74) ([ipfs/go-ipld-format#74](https://github.com/ipfs/go-ipld-format/pull/74)) +- github.com/ipfs/go-ipld-legacy (v0.1.1 -> v0.2.1): + - v0.2.1 ([ipfs/go-ipld-legacy#15](https://github.com/ipfs/go-ipld-legacy/pull/15)) + - Expose a constructor for making a decoder with an existing link system ([ipfs/go-ipld-legacy#14](https://github.com/ipfs/go-ipld-legacy/pull/14)) + - Update to v0.2.0 ([ipfs/go-ipld-legacy#13](https://github.com/ipfs/go-ipld-legacy/pull/13)) + - Remove global variable ([ipfs/go-ipld-legacy#12](https://github.com/ipfs/go-ipld-legacy/pull/12)) + - sync: update CI config files (#8) ([ipfs/go-ipld-legacy#8](https://github.com/ipfs/go-ipld-legacy/pull/8)) +- github.com/ipfs/go-unixfsnode (v1.6.0 -> v1.7.1): + - chore: bump to v1.7.1 + - test: remove unnecessary t.Log + - test: check if reader reads only necessary blocks + - fix: do not read extra block if offset = at+childSize + - doc: added simple doc for testutil package + - bump v1.7.0 + - feat(testutil): add test data generation utils (extracted from Lassie) +- github.com/libp2p/go-libp2p (v0.27.3 -> v0.27.7): + - Release v0.27.7 (#2374) ([libp2p/go-libp2p#2374](https://github.com/libp2p/go-libp2p/pull/2374)) + - Release v0.27.6 (#2359) ([libp2p/go-libp2p#2359](https://github.com/libp2p/go-libp2p/pull/2359)) + - Release v0.27.5 (#2324) ([libp2p/go-libp2p#2324](https://github.com/libp2p/go-libp2p/pull/2324)) + - Bump version to v0.27.4 + - identify: reject signed peer records on peer ID mismatch + - swarm: change maps with multiaddress keys to use strings (#2284) ([libp2p/go-libp2p#2284](https://github.com/libp2p/go-libp2p/pull/2284)) + - identify: avoid spuriously triggering pushes (#2299) ([libp2p/go-libp2p#2299](https://github.com/libp2p/go-libp2p/pull/2299)) +- github.com/libp2p/go-libp2p-kad-dht (v0.23.0 -> v0.24.2): + - chore: release v0.24.2 + - chore: release v0.24.1 + - fix: decrease tests noise, update kbucket and fix fixRTIUfNeeded + - refactor: remove goprocess + - fix: leaking go routines + - chore: release v0.24.0 + - fix: don't add unresponsive DHT servers to the Routing Table (#820) ([libp2p/go-libp2p-kad-dht#820](https://github.com/libp2p/go-libp2p-kad-dht/pull/820)) +- github.com/libp2p/go-libp2p-kbucket (v0.5.0 -> v0.6.3): + - fix: fix abba bug in UsefullNewPeer ([libp2p/go-libp2p-kbucket#122](https://github.com/libp2p/go-libp2p-kbucket/pull/122)) + - chore: release v0.6.2 ([libp2p/go-libp2p-kbucket#121](https://github.com/libp2p/go-libp2p-kbucket/pull/121)) + - Replacing UsefulPeer() with UsefulNewPeer() ([libp2p/go-libp2p-kbucket#120](https://github.com/libp2p/go-libp2p-kbucket/pull/120)) + - chore: release 0.6.1 ([libp2p/go-libp2p-kbucket#119](https://github.com/libp2p/go-libp2p-kbucket/pull/119)) + - UsefulPeer function ([libp2p/go-libp2p-kbucket#113](https://github.com/libp2p/go-libp2p-kbucket/pull/113)) + - Fixed peer replacement with bucket size of 1. ([libp2p/go-libp2p-kbucket#117](https://github.com/libp2p/go-libp2p-kbucket/pull/117)) + - GenRandomKey function ([libp2p/go-libp2p-kbucket#116](https://github.com/libp2p/go-libp2p-kbucket/pull/116)) + - Removed maintainers from readme ([libp2p/go-libp2p-kbucket#115](https://github.com/libp2p/go-libp2p-kbucket/pull/115)) + - Add maintainers ([libp2p/go-libp2p-kbucket#114](https://github.com/libp2p/go-libp2p-kbucket/pull/114)) + - sync: update CI config files (#112) ([libp2p/go-libp2p-kbucket#112](https://github.com/libp2p/go-libp2p-kbucket/pull/112)) +- github.com/libp2p/go-libp2p-routing-helpers (v0.6.2 -> v0.7.0): + - chore: release v0.7.0 + - fix: iterate over keys manually in ProvideMany +- github.com/libp2p/go-reuseport (v0.2.0 -> v0.3.0): + - release v0.3.0 (#103) ([libp2p/go-reuseport#103](https://github.com/libp2p/go-reuseport/pull/103)) + - fix error handling when setting socket options (#102) ([libp2p/go-reuseport#102](https://github.com/libp2p/go-reuseport/pull/102)) + - minor README updates (#96) ([libp2p/go-reuseport#96](https://github.com/libp2p/go-reuseport/pull/96)) + - sync: update CI config files (#94) ([libp2p/go-reuseport#94](https://github.com/libp2p/go-reuseport/pull/94)) + - feat: add a DialTimeout function ([libp2p/go-reuseport#92](https://github.com/libp2p/go-reuseport/pull/92)) +- github.com/multiformats/go-multicodec (v0.8.1 -> v0.9.0): + - Bump v0.9.0 + - Bump v0.8.2 + - chore: update submodules and go generate + - chore: update submodules and go generate + - chore: update submodules and go generate + - chore: update submodules and go generate + - chore: update submodules and go generate + - chore: update submodules and go generate +- github.com/multiformats/go-multihash (v0.2.1 -> v0.2.3): + - chore: release v0.2.3 + - perf: outline logic in Decode to allow for stack allocations + - chore: release v0.2.2 + - sha256: drop minio in favor of crypto/sha256 for go1.21 and above + - sync: update CI config files (#169) ([multiformats/go-multihash#169](https://github.com/multiformats/go-multihash/pull/169)) + - add handler for hasher.Write returned error ([multiformats/go-multihash#167](https://github.com/multiformats/go-multihash/pull/167)) + - sync: update CI config files (#165) ([multiformats/go-multihash#165](https://github.com/multiformats/go-multihash/pull/165)) + - test: add benchmark for all hash functions Sum + +
+ +### 👨‍👩‍👧‍👦 Contributors + +| Contributor | Commits | Lines ± | Files Changed | +|-------------|---------|---------|---------------| +| Jorropo | 47 | +4394/-4458 | 202 | +| Henrique Dias | 48 | +4344/-3962 | 205 | +| Łukasz Magiera | 68 | +3604/-886 | 172 | +| Adin Schmahmann | 8 | +1754/-1057 | 37 | +| galargh | 7 | +1355/-1302 | 15 | +| Gus Eggert | 7 | +1566/-655 | 33 | +| rvagg | 1 | +396/-389 | 3 | +| Michael Muré | 3 | +547/-202 | 14 | +| Guillaume Michel - guissou | 5 | +153/-494 | 17 | +| guillaumemichel | 15 | +446/-189 | 28 | +| Laurent Senta | 4 | +472/-152 | 29 | +| Rod Vagg | 6 | +554/-37 | 23 | +| Marcin Rataj | 11 | +330/-82 | 21 | +| Arthur Gavazza | 1 | +296/-87 | 7 | +| Lucas Molas | 1 | +323/-56 | 6 | +| Marco Munizaga | 5 | +227/-97 | 17 | +| Alex | 8 | +163/-116 | 10 | +| Steven Allen | 11 | +154/-114 | 14 | +| Marten Seemann | 6 | +214/-41 | 12 | +| web3-bot | 9 | +76/-75 | 28 | +| Hector Sanjuan | 2 | +5/-96 | 4 | +| Sukun | 1 | +83/-17 | 3 | +| Steve Loeppky | 2 | +100/-0 | 2 | +| Edgar Lee | 1 | +46/-46 | 12 | +| Ivan Schasny | 1 | +67/-5 | 4 | +| imthe1 | 1 | +65/-3 | 5 | +| godcong | 2 | +30/-31 | 5 | +| Will Scott | 4 | +36/-23 | 6 | +| Petar Maymounkov | 1 | +45/-9 | 1 | +| Ross Jones | 1 | +43/-1 | 2 | +| William Entriken | 1 | +38/-0 | 1 | +| João Pedro | 1 | +35/-0 | 1 | +| jhertz | 1 | +21/-0 | 2 | +| Nikhilesh Susarla | 1 | +21/-0 | 3 | +| Matt Joiner | 1 | +11/-9 | 2 | +| Vlad | 2 | +4/-2 | 2 | +| Russell Dempsey | 2 | +4/-2 | 2 | +| Will | 2 | +2/-2 | 2 | +| Piotr Galar | 1 | +1/-1 | 1 | +| Joel Gustafson | 1 | +1/-1 | 1 | +| Dennis Trautwein | 1 | +1/-1 | 1 | +| Bryan Stenson | 1 | +1/-1 | 1 | diff --git a/docs/changelogs/v0.22.md b/docs/changelogs/v0.22.md new file mode 100644 index 000000000..3aa55f30e --- /dev/null +++ b/docs/changelogs/v0.22.md @@ -0,0 +1,292 @@ +# Kubo changelog v0.22 + +- [v0.22.0](#v0220) + +## v0.22.0 + +- [Overview](#overview) +- [🔦 Highlights](#-highlights) + - [Gateway: support for `order=` and `dups=` parameters (IPIP-412)](#gateway-support-for-order-and-dups-parameters-ipip-412) + - [`ipfs name publish` now supports V2 only IPNS records](#ipfs-name-publish-now-supports-v2-only-ipns-records) + - [IPNS name resolution has been fixed](#ipns-name-resolution-has-been-fixed) + - [go-libp2p v0.29.0 update with smart dialing](#go-libp2p-v0290-update-with-smart-dialing) +- [📝 Changelog](#-changelog) +- [👨‍👩‍👧‍👦 Contributors](#-contributors) + +### Overview + +### 🔦 Highlights + +#### Gateway: support for `order=` and `dups=` parameters (IPIP-412) + +The updated [`boxo/gateway` library](https://github.com/ipfs/boxo/tree/main/gateway) +introduces support for ordered CAR responses through the inclusion of optional +CAR content type parameters: `order=dfs` and `dups=y|n` from +[IPIP-412](https://github.com/ipfs/specs/pull/412). + +Previously, Kubo already provided CARs in DFS order without duplicate blocks. +With the implementation of IPIP-412, this behavior is now explicitly defined +rather than implied. + +In the absence of `dups` or `order` in `Accept` request reader, the default CAR +response will have the `Content-Type: application/vnd.ipld.car; version=1; order=dfs; dups=n` +and the same blocks as Kubo 0.21. + +Kubo 0.22 still only supports DFS block ordering (`order=dfs`). However, it is +now possible to request a DFS CAR stream with duplicate blocks by opting in via +`Accept: application/vnd.ipld.car; order=dfs; dups=y`. This opt-in feature can be +beneficial for memory-constrained clients and IoT devices, as it allows for +streaming large DAGs without the need to store all previously encountered +blocks in memory. + +#### `ipfs name publish` now supports V2 only IPNS records + +When publishing an IPNS record, you are now able to create v2 only records +by passing `--v1compat=false`. By default, we still create V1+V2 records, such +that there is the highest chance of backwards compatibility. The goal is to move +to V2 only in the future. + +For more details, see [IPIP-428](https://specs.ipfs.tech/ipips/ipip-0428/) +and the updated [IPNS Record Verification](https://specs.ipfs.tech/ipns/ipns-record/#record-verification) logic. + +#### IPNS name resolution has been fixed + +IPNS name resolution had a regression where if IPNS over PubSub was enabled, but the name was not also available via IPNS over PubSub it would take 1 minute to for the lookup to complete (if the record was not yet cached). + +This has been fixed and as before will give the best record from either the DHT subsystem or IPNS over PubSub, whichever comes back first. + +For details see [#9927](https://github.com/ipfs/kubo/issues/9927) and [#10020](https://github.com/ipfs/kubo/pull/10020). + +# go-libp2p v0.29.0 update with smart dialing + +We updated from [go-libp2p](https://github.com/libp2p/go-libp2p) [v0.27.7](https://github.com/libp2p/go-libp2p/releases/tag/v0.27.7) to [v0.29.0](https://github.com/libp2p/go-libp2p/releases/tag/v0.29.0). This release includes smart dialing, which is a prioritization algorithm that will try to rank addresses and protocols rather than attempting all options in parallel. Anecdotally, we have observed [Kubo nodes make 30% less dials](https://github.com/libp2p/go-libp2p/issues/2326#issuecomment-1644332863) with no to low latency impact. + +This includes a breaking change to `ipfs id` and some of the `ipfs swarm` commands. We no longer report `ProtocolVersion`. This used to be hardcoded as `ipfs/0.1.0` and sent to other peers but was not providing any distinguishing value. See [libp2p/go-libp2p#2294](https://github.com/libp2p/go-libp2p/issues/2294) for more information. + +### 📝 Changelog + +
Full Changelog + +- github.com/ipfs/kubo: + - chore: change version to v0.22.0 + - chore(misc/README.md): trim duplicated content + - Merge branch 'release-v0.21' back into master + - docs(readme): unofficial packages badge + - chore: remove sharness tests ported to conformance testing (#9999) ([ipfs/kubo#9999](https://github.com/ipfs/kubo/pull/9999)) + - ci: switch from testing against js-ipfs to helia (#10042) ([ipfs/kubo#10042](https://github.com/ipfs/kubo/pull/10042)) + - chore: merge release back into master + - chore: change orbitdb to haydenyoung EARLY_TESTERS + - Fix usage numbers + - chore: update early testers list (#9218) ([ipfs/kubo#9218](https://github.com/ipfs/kubo/pull/9218)) + - docs: changelog v0.21 fixes (#10037) ([ipfs/kubo#10037](https://github.com/ipfs/kubo/pull/10037)) + - refactor(ci): simplify Dockerfile and add docker image testing (#10021) ([ipfs/kubo#10021](https://github.com/ipfs/kubo/pull/10021)) + - chore: update version + - fix(relay): apply user provider options + - libp2p: stop reporting ProtocolVersion + - chore: update go-libp2p to v0.29.0 + - chore: update go-libp2p to v0.28.1 + - fix: mark all routers DoNotWaitForSearchValue (#10020) ([ipfs/kubo#10020](https://github.com/ipfs/kubo/pull/10020)) + - feat(gateway): support for ipip-412 parameters + - docs(commands): explain that swarm connect can reuse existing connections or known addresses (#10015) ([ipfs/kubo#10015](https://github.com/ipfs/kubo/pull/10015)) + - docs: add Brave to RELEASE_ISSUE_TEMPLATE.md (#10012) ([ipfs/kubo#10012](https://github.com/ipfs/kubo/pull/10012)) + - feat: webui@4.0.2 + - ([ipfs/kubo#10008](https://github.com/ipfs/kubo/pull/10008)) + - docs: skip check before prepare branch in RELEASE_ISSUE_TEMPLATE.md + - docs: update RELEASE_ISSUE_TEMPLATE.md with a warning about npm publish + - docs: update refs to kuboreleaser in RELEASE_ISSUE_TEMPLATE.md + - docs: Gateway.HTTPHeaders + - refactor: replace boxo/ipld/car by ipld/go-car + - chore: bump to boxo master + - fix: correctly handle migration of configs + - fix(gateway): include CORS on subdomain redirects (#9994) ([ipfs/kubo#9994](https://github.com/ipfs/kubo/pull/9994)) + - fix: docker repository initialization race condition + - feat(ipns): records with V2-only signatures (#9932) ([ipfs/kubo#9932](https://github.com/ipfs/kubo/pull/9932)) + - cmds/dag/import: pin roots by default (#9966) ([ipfs/kubo#9966](https://github.com/ipfs/kubo/pull/9966)) + - docs: fix 0.21 changelog + - feat!: dag import - don't pin roots by default (#9926) ([ipfs/kubo#9926](https://github.com/ipfs/kubo/pull/9926)) + - fix(cmd): useful errors in dag import (#9945) ([ipfs/kubo#9945](https://github.com/ipfs/kubo/pull/9945)) + - feat: webui@4.0.1 (#9940) ([ipfs/kubo#9940](https://github.com/ipfs/kubo/pull/9940)) + - chore(docs): typo http→https + - fix: more stable prometheus test (#9944) ([ipfs/kubo#9944](https://github.com/ipfs/kubo/pull/9944)) + - ([ipfs/kubo#9937](https://github.com/ipfs/kubo/pull/9937)) +- github.com/ipfs/boxo (v0.10.3 -> v0.11.0): + - Release v0.11.0 ([ipfs/boxo#417](https://github.com/ipfs/boxo/pull/417)) +- github.com/ipfs/go-bitswap (null -> v0.11.0): + - chore: release v0.11.0 + - chore: release v0.10.2 + - fix: create a copy of the protocol slice in network.processSettings + - chore: release v0.10.1 + - fix: incorrect type in the WithTracer polyfill option + - chore: fix incorrect log message when a bad option is passed + - chore: release v0.10.0 + - chore: update go-libp2p v0.22.0 + - chore: release v0.9.0 + - feat: split client and server ([ipfs/go-bitswap#570](https://github.com/ipfs/go-bitswap/pull/570)) + - chore: remove goprocess from blockstoremanager + - Don't add blocks to the datastore ([ipfs/go-bitswap#571](https://github.com/ipfs/go-bitswap/pull/571)) + - Remove dependency on travis package from go-libp2p-testing ([ipfs/go-bitswap#569](https://github.com/ipfs/go-bitswap/pull/569)) + - feat: add basic tracing (#562) ([ipfs/go-bitswap#562](https://github.com/ipfs/go-bitswap/pull/562)) + - chore: release v0.7.0 (#566) ([ipfs/go-bitswap#566](https://github.com/ipfs/go-bitswap/pull/566)) + - feat: coalesce and queue connection event handling (#565) ([ipfs/go-bitswap#565](https://github.com/ipfs/go-bitswap/pull/565)) +- github.com/ipfs/go-merkledag (v0.10.0 -> v0.11.0): + - chore: update v0.11.0 (#106) ([ipfs/go-merkledag#106](https://github.com/ipfs/go-merkledag/pull/106)) + - update merkeldag to use the explicit decoder registry (#104) ([ipfs/go-merkledag#104](https://github.com/ipfs/go-merkledag/pull/104)) + - Update status in README.md and added CODEOWNERS (#101) ([ipfs/go-merkledag#101](https://github.com/ipfs/go-merkledag/pull/101)) +- github.com/ipld/go-car/v2 (v2.9.1-0.20230325062757-fff0e4397a3d -> v2.10.2-0.20230622090957-499d0c909d33): + - feat: add inverse and version to filter cmd ([ipld/go-car#457](https://github.com/ipld/go-car/pull/457)) + - v0.6.1 bump + - chore: update usage of merkledag by go-car (#437) ([ipld/go-car#437](https://github.com/ipld/go-car/pull/437)) + - feat(cmd/car): add '--no-wrap' option to 'create' command ([ipld/go-car#432](https://github.com/ipld/go-car/pull/432)) + - fix: remove github.com/ipfs/go-ipfs-blockstore dependency + - feat: expose index for StorageCar + - perf: reduce NewCarReader allocations + - fix(deps): update deps for cmd (use master go-car and go-car/v2 for now) + - fix: new error strings from go-cid + - fix: tests should match stderr for verbose output + - fix: reading from stdin should broadcast EOF to block loaders + - refactor insertion index to be publicly accessible ([ipld/go-car#408](https://github.com/ipld/go-car/pull/408)) +- github.com/libp2p/go-libp2p (v0.27.9 -> v0.29.2): + - release v0.29.2 + - release v0.29.1 + - swarm: don't open new streams over transient connections (#2450) ([libp2p/go-libp2p#2450](https://github.com/libp2p/go-libp2p/pull/2450)) + - core/crypto: restrict RSA keys to <= 8192 bits (#2454) ([libp2p/go-libp2p#2454](https://github.com/libp2p/go-libp2p/pull/2454)) + - Release version v0.29.0 (#2431) ([libp2p/go-libp2p#2431](https://github.com/libp2p/go-libp2p/pull/2431)) + - webtransport: reject listening on a multiaddr with a certhash (#2426) ([libp2p/go-libp2p#2426](https://github.com/libp2p/go-libp2p/pull/2426)) + - swarm: deprecate libp2p.DialRanker option (#2430) ([libp2p/go-libp2p#2430](https://github.com/libp2p/go-libp2p/pull/2430)) + - quic: Update to quic-go v0.36.2 (#2424) ([libp2p/go-libp2p#2424](https://github.com/libp2p/go-libp2p/pull/2424)) + - autonat: fix typo in WithSchedule option comment (#2425) ([libp2p/go-libp2p#2425](https://github.com/libp2p/go-libp2p/pull/2425)) + - identify: filter nat64 well-known prefix ipv6 addresses (#2392) ([libp2p/go-libp2p#2392](https://github.com/libp2p/go-libp2p/pull/2392)) + - update go-multiaddr to v0.10.1, use Unique function from there (#2407) ([libp2p/go-libp2p#2407](https://github.com/libp2p/go-libp2p/pull/2407)) + - swarm: enable smart dialing by default (#2420) ([libp2p/go-libp2p#2420](https://github.com/libp2p/go-libp2p/pull/2420)) + - transport integration tests: make TestMoreStreamsThanOurLimits less flaky (#2410) ([libp2p/go-libp2p#2410](https://github.com/libp2p/go-libp2p/pull/2410)) + - holepunch: skip racy TestDirectDialWorks (#2419) ([libp2p/go-libp2p#2419](https://github.com/libp2p/go-libp2p/pull/2419)) + - swarm: change relay dial delay to 500ms (#2421) ([libp2p/go-libp2p#2421](https://github.com/libp2p/go-libp2p/pull/2421)) + - identify: disable racy TestLargeIdentifyMessage with race detector (#2401) ([libp2p/go-libp2p#2401](https://github.com/libp2p/go-libp2p/pull/2401)) + - swarm: make black hole detection configurable (#2403) ([libp2p/go-libp2p#2403](https://github.com/libp2p/go-libp2p/pull/2403)) + - net/mock: support ConnectionGater in MockNet (#2297) ([libp2p/go-libp2p#2297](https://github.com/libp2p/go-libp2p/pull/2297)) + - docs: Add a Github workflow for checking dead links (#2406) ([libp2p/go-libp2p#2406](https://github.com/libp2p/go-libp2p/pull/2406)) + - rcmgr: enable metrics by default (#2389) (#2409) ([libp2p/go-libp2p#2409](https://github.com/libp2p/go-libp2p/pull/2409)) + - chore: remove outdated info in README and link to libp2p-implementers slack (#2405) ([libp2p/go-libp2p#2405](https://github.com/libp2p/go-libp2p/pull/2405)) + - metrics: deduplicate code in examples (#2404) ([libp2p/go-libp2p#2404](https://github.com/libp2p/go-libp2p/pull/2404)) + - transport tests: remove mplex tests (#2402) ([libp2p/go-libp2p#2402](https://github.com/libp2p/go-libp2p/pull/2402)) + - swarm: implement Happy Eyeballs ranking (#2365) ([libp2p/go-libp2p#2365](https://github.com/libp2p/go-libp2p/pull/2365)) + - docs: fix some comments (#2391) ([libp2p/go-libp2p#2391](https://github.com/libp2p/go-libp2p/pull/2391)) + - metrics: provide separate docker-compose files for OSX and Linux (#2397) ([libp2p/go-libp2p#2397](https://github.com/libp2p/go-libp2p/pull/2397)) + - identify: use zero-alloc slice sorting function (#2396) ([libp2p/go-libp2p#2396](https://github.com/libp2p/go-libp2p/pull/2396)) + - rcmgr: move StatsTraceReporter to rcmgr package (#2388) ([libp2p/go-libp2p#2388](https://github.com/libp2p/go-libp2p/pull/2388)) + - swarm: implement blackhole detection (#2320) ([libp2p/go-libp2p#2320](https://github.com/libp2p/go-libp2p/pull/2320)) + - basichost / blankhost: wrap errors (#2331) ([libp2p/go-libp2p#2331](https://github.com/libp2p/go-libp2p/pull/2331)) + - network: don't allocate in DedupAddrs (#2395) ([libp2p/go-libp2p#2395](https://github.com/libp2p/go-libp2p/pull/2395)) + - rcmgr: test snapshot defaults and that we keep consistent defaults (#2315) ([libp2p/go-libp2p#2315](https://github.com/libp2p/go-libp2p/pull/2315)) + - rcmgr: register prometheus metrics with the libp2p registerer (#2370) ([libp2p/go-libp2p#2370](https://github.com/libp2p/go-libp2p/pull/2370)) + - metrics: make it possible to spin up Grafana using docker-compose (#2383) ([libp2p/go-libp2p#2383](https://github.com/libp2p/go-libp2p/pull/2383)) + - identify: set stream deadlines for Identify and Identify Push streams (#2382) ([libp2p/go-libp2p#2382](https://github.com/libp2p/go-libp2p/pull/2382)) + - fix: in the swarm move Connectedness emit after releasing conns (#2373) ([libp2p/go-libp2p#2373](https://github.com/libp2p/go-libp2p/pull/2373)) + - metrics: add example for metrics and dashboard (#2232) ([libp2p/go-libp2p#2232](https://github.com/libp2p/go-libp2p/pull/2232)) + - dashboards: finish metrics effort (#2362) ([libp2p/go-libp2p#2362](https://github.com/libp2p/go-libp2p/pull/2362)) + - transport tests: many streams and lots of data (#2296) ([libp2p/go-libp2p#2296](https://github.com/libp2p/go-libp2p/pull/2296)) + - webtransport: close the challenge stream after the Noise handshake (#2305) ([libp2p/go-libp2p#2305](https://github.com/libp2p/go-libp2p/pull/2305)) + - test: document why InstantTimer is required (#2351) ([libp2p/go-libp2p#2351](https://github.com/libp2p/go-libp2p/pull/2351)) + - rcmgr: fix link to dashboards in README (#2363) ([libp2p/go-libp2p#2363](https://github.com/libp2p/go-libp2p/pull/2363)) + - docs: fix some comments errors (#2356) ([libp2p/go-libp2p#2356](https://github.com/libp2p/go-libp2p/pull/2356)) + - release v0.28.0 (#2344) ([libp2p/go-libp2p#2344](https://github.com/libp2p/go-libp2p/pull/2344)) + - nat: add HasDiscoveredNAT method for checking NAT environments (#2358) ([libp2p/go-libp2p#2358](https://github.com/libp2p/go-libp2p/pull/2358)) + - swarm: fix stale DialBackoff comment (#2353) ([libp2p/go-libp2p#2353](https://github.com/libp2p/go-libp2p/pull/2353)) + - swarm: use RLock for DialBackoff reads (#2354) ([libp2p/go-libp2p#2354](https://github.com/libp2p/go-libp2p/pull/2354)) + - Clear stream scope if we error (#2345) ([libp2p/go-libp2p#2345](https://github.com/libp2p/go-libp2p/pull/2345)) + - changelog: improve description of smart dialing (#2342) ([libp2p/go-libp2p#2342](https://github.com/libp2p/go-libp2p/pull/2342)) + - swarm: make smart-dialing opt in (#2340) ([libp2p/go-libp2p#2340](https://github.com/libp2p/go-libp2p/pull/2340)) + - swarm: cleanup address filtering logic (#2333) ([libp2p/go-libp2p#2333](https://github.com/libp2p/go-libp2p/pull/2333)) + - chore: add 0.28.0 changelog (#2335) ([libp2p/go-libp2p#2335](https://github.com/libp2p/go-libp2p/pull/2335)) + - swarm: improve documentation for the DefaultDialRanker (#2336) ([libp2p/go-libp2p#2336](https://github.com/libp2p/go-libp2p/pull/2336)) + - holepunch: add metrics (#2246) ([libp2p/go-libp2p#2246](https://github.com/libp2p/go-libp2p/pull/2246)) + - swarm: implement smart dialing logic (#2260) ([libp2p/go-libp2p#2260](https://github.com/libp2p/go-libp2p/pull/2260)) + - revert "feat:add contexts to all peerstore methods (#2312)" (#2328) ([libp2p/go-libp2p#2328](https://github.com/libp2p/go-libp2p/pull/2328)) + - identify: don't save signed peer records (#2325) ([libp2p/go-libp2p#2325](https://github.com/libp2p/go-libp2p/pull/2325)) + - feat:add contexts to all peerstore methods (#2312) ([libp2p/go-libp2p#2312](https://github.com/libp2p/go-libp2p/pull/2312)) + - swarm: Dedup addresses to dial (#2322) ([libp2p/go-libp2p#2322](https://github.com/libp2p/go-libp2p/pull/2322)) + - identify: filter received addresses based on the node's remote address (#2300) ([libp2p/go-libp2p#2300](https://github.com/libp2p/go-libp2p/pull/2300)) + - update go-nat to v0.2.0, use context on AddMapping and RemoveMapping (#2319) ([libp2p/go-libp2p#2319](https://github.com/libp2p/go-libp2p/pull/2319)) + - transport integration tests: add tests for resource manager (#2285) ([libp2p/go-libp2p#2285](https://github.com/libp2p/go-libp2p/pull/2285)) + - identify: reject signed peer records on peer ID mismatch + - identify: don't send default protocol version (#2303) ([libp2p/go-libp2p#2303](https://github.com/libp2p/go-libp2p/pull/2303)) + - metrics: add instance filter to all dashboards (#2301) ([libp2p/go-libp2p#2301](https://github.com/libp2p/go-libp2p/pull/2301)) + - identify: avoid spuriously triggering pushes (#2299) ([libp2p/go-libp2p#2299](https://github.com/libp2p/go-libp2p/pull/2299)) + - net/mock: mimic Swarm's event and notification behavior in MockNet (#2287) ([libp2p/go-libp2p#2287](https://github.com/libp2p/go-libp2p/pull/2287)) + - examples: fix flaky multipro TestMain (#2289) ([libp2p/go-libp2p#2289](https://github.com/libp2p/go-libp2p/pull/2289)) + - swarm: change maps with multiaddress keys to use strings (#2284) ([libp2p/go-libp2p#2284](https://github.com/libp2p/go-libp2p/pull/2284)) + - tests: add comprehensive end-to-end tests for connection gating (#2200) ([libp2p/go-libp2p#2200](https://github.com/libp2p/go-libp2p/pull/2200)) + - swarm: log unexpected listener errors (#2277) ([libp2p/go-libp2p#2277](https://github.com/libp2p/go-libp2p/pull/2277)) + - websocket: switch back to the gorilla library (#2280) ([libp2p/go-libp2p#2280](https://github.com/libp2p/go-libp2p/pull/2280)) + - quic: prioritise listen connections for reuse (#2262) ([libp2p/go-libp2p#2262](https://github.com/libp2p/go-libp2p/pull/2262)) + - quic virtual listener: don't panic when quic-go's accept call errors (#2276) ([libp2p/go-libp2p#2276](https://github.com/libp2p/go-libp2p/pull/2276)) + - tests: add docks for debugging flaky tests (#2216) ([libp2p/go-libp2p#2216](https://github.com/libp2p/go-libp2p/pull/2216)) + - webtransport: only add cert hashes if we already started listening (#2271) ([libp2p/go-libp2p#2271](https://github.com/libp2p/go-libp2p/pull/2271)) + - Revert "webtransport: initialize the certmanager when creating the transport (#2268)" (#2273) ([libp2p/go-libp2p#2273](https://github.com/libp2p/go-libp2p/pull/2273)) + - webtransport: initialize the certmanager when creating the transport (#2268) ([libp2p/go-libp2p#2268](https://github.com/libp2p/go-libp2p/pull/2268)) + - move NAT mapping logic out of the host, add tests for NAT handling ([libp2p/go-libp2p#2248](https://github.com/libp2p/go-libp2p/pull/2248)) + - githooks: add a githook to check that the test-plans go.mod is tidied (#2256) ([libp2p/go-libp2p#2256](https://github.com/libp2p/go-libp2p/pull/2256)) + - quic: fix race condition when generating random holepunch packet (#2263) ([libp2p/go-libp2p#2263](https://github.com/libp2p/go-libp2p/pull/2263)) + - swarm: remove unused variable in addrDial (#2257) ([libp2p/go-libp2p#2257](https://github.com/libp2p/go-libp2p/pull/2257)) +- github.com/libp2p/go-libp2p-routing-helpers (v0.7.0 -> v0.7.1): + - chore: release v0.7.1 + - fix: for comparallel never return nil channel for FindProvidersAsync + - chore: rename DoNotWaitForStreamingResponses to DoNotWaitForSearchValue + - feat: add DoNotWaitForStreamingResponses to ParallelRouter + - chore: cleanup error handling in compparallel + - fix: correctly handle errors in compparallel + - fix: make the ProvideMany docs clearer + - perf: remove goroutine that just waits before closing with a synchrous waitgroup +- github.com/libp2p/go-nat (v0.1.0 -> v0.2.0): + - release v0.2.0 (#30) ([libp2p/go-nat#30](https://github.com/libp2p/go-nat/pull/30)) + - update deps, use contexts on UPnP functions (#29) ([libp2p/go-nat#29](https://github.com/libp2p/go-nat/pull/29)) + - sync: update CI config files (#28) ([libp2p/go-nat#28](https://github.com/libp2p/go-nat/pull/28)) + - sync: update CI config files (#24) ([libp2p/go-nat#24](https://github.com/libp2p/go-nat/pull/24)) +- github.com/libp2p/go-yamux/v4 (v4.0.0 -> v4.0.1): + - Release v4.0.1 ([libp2p/go-yamux#106](https://github.com/libp2p/go-yamux/pull/106)) + - fix: sendWindowUpdate respects deadlines (#105) ([libp2p/go-yamux#105](https://github.com/libp2p/go-yamux/pull/105)) +- github.com/multiformats/go-multiaddr (v0.9.0 -> v0.10.1): + - release v0.10.1 (#206) ([multiformats/go-multiaddr#206](https://github.com/multiformats/go-multiaddr/pull/206)) + - fix nat64 well-known prefix check (#205) ([multiformats/go-multiaddr#205](https://github.com/multiformats/go-multiaddr/pull/205)) + - release v0.10.0 (#204) ([multiformats/go-multiaddr#204](https://github.com/multiformats/go-multiaddr/pull/204)) + - add a Unique function (#203) ([multiformats/go-multiaddr#203](https://github.com/multiformats/go-multiaddr/pull/203)) + - manet: add function to test if address is NAT64 IPv4 converted IPv6 address (#202) ([multiformats/go-multiaddr#202](https://github.com/multiformats/go-multiaddr/pull/202)) + - sync: update CI config files (#190) ([multiformats/go-multiaddr#190](https://github.com/multiformats/go-multiaddr/pull/190)) + +
+ +### 👨‍👩‍👧‍👦 Contributors + +| Contributor | Commits | Lines ± | Files Changed | +|-------------|---------|---------|---------------| +| Henrique Dias | 14 | +3735/-17889 | 185 | +| Sukun | 28 | +5910/-957 | 100 | +| Jorropo | 40 | +2913/-2112 | 205 | +| Marten Seemann | 41 | +2926/-1833 | 163 | +| Marco Munizaga | 20 | +1559/-586 | 81 | +| Prem Chaitanya Prathi | 1 | +757/-740 | 61 | +| Laurent Senta | 2 | +69/-1094 | 32 | +| Marcin Rataj | 11 | +339/-198 | 22 | +| Steven Allen | 2 | +313/-161 | 9 | +| Will | 2 | +118/-211 | 9 | +| Adin Schmahmann | 4 | +275/-41 | 8 | +| Michael Muré | 1 | +113/-164 | 6 | +| Rod Vagg | 8 | +228/-46 | 28 | +| Gus Eggert | 5 | +156/-93 | 21 | +| Adrian Sutton | 1 | +190/-17 | 4 | +| Hlib Kanunnikov | 3 | +139/-40 | 9 | +| VM | 2 | +80/-79 | 49 | +| UnkwUsr | 1 | +0/-124 | 1 | +| Piotr Galar | 4 | +51/-59 | 5 | +| web3-bot | 3 | +22/-46 | 4 | +| Will Scott | 2 | +29/-28 | 6 | +| Prithvi Shahi | 2 | +40/-7 | 2 | +| Brad Fitzpatrick | 1 | +42/-2 | 2 | +| Steve Loeppky | 1 | +6/-23 | 2 | +| Sahib Yar | 1 | +4/-4 | 3 | +| Russell Dempsey | 2 | +4/-2 | 2 | +| Mohamed MHAMDI | 1 | +3/-3 | 1 | +| Bryan White | 1 | +2/-2 | 1 | +| Dennis Trautwein | 1 | +1/-1 | 1 | +| Antonio Navarro Perez | 1 | +0/-1 | 1 | + diff --git a/docs/changelogs/v0.23.md b/docs/changelogs/v0.23.md new file mode 100644 index 000000000..70c1d460a --- /dev/null +++ b/docs/changelogs/v0.23.md @@ -0,0 +1,387 @@ +# Kubo changelog v0.23 + +- [v0.23.0](#v0230) + +## v0.23.0 + +- [Overview](#overview) +- [🔦 Highlights](#-highlights) + - [Mplex deprecation](#mplex-deprecation) + - [Gateway: meaningful CAR responses on Not Found errors](#gateway-meaningful-car-responses-on-not-found-errors) + - [Gateway: added `Gateway.DisableHTMLErrors` configuration option](#gateway-added-gatewaydisablehtmlerrors-configuration-option) + - [Binary characters in file names: no longer works with old clients and new Kubo servers](#binary-characters-in-file-names-no-longer-works-with-old-clients-and-new-kubo-servers) + - [Self-hosting `/routing/v1` endpoint for delegated routing needs](#self-hosting-routingv1-endpoint-for-delegated-routing-needs) + - [Trustless Gateway Over Libp2p Experiment](#trustless-gateway-over-libp2p-experiment) + - [Removal of `/quic` (Draft 29) support](#removal-of-quic-draft-29-support) + - [Better Caching of multiaddresses for providers in DHT servers](#better-caching-of-multiaddresses-for-providers-in-dht-servers) + - [Fixed FUSE multiblock structures](#fixed-fuse-multiblock-structures) +- [📝 Changelog](#-changelog) +- [👨‍👩‍👧‍👦 Contributors](#-contributors) + +### Overview + +### 🔦 Highlights + +#### Mplex deprecation + +Mplex is being deprecated, this is because it is unreliable and +randomly drop streams when sending data *too fast*. + +New pieces of code rely on backpressure, that means the stream will dynamicaly +slow down the sending rate if data is getting backed up. +Backpressure is provided by **Yamux** and **QUIC**. + +In case you need compatibility with older implementations that do not ship with +Yamux (like default's JS-IPFS) you can turned it back ON in the config with: +```console +$ ipfs config --json Swarm.Transports.Multiplexers.Mplex 200 +``` + +We will completely remove Mplex in v0.24 as it makes protocols very bad to implement, +if you are in this situation you need to add yamux support to your other implementation. + +#### Gateway: meaningful CAR responses on Not Found errors + +When requesting a CAR from the gateway, the root of the CAR might no longer be +meaningful. By default, the CAR root will be the last resolvable segment of the +path. However, in situations where the path cannot be resolved, such as when +the path does not exist, a CAR will be sent with a root of `bafkqaaa` (empty CID). + +This CAR will contain all blocks necessary to validate that the path does not +exist without having to trust the gateway. + +#### Gateway: added `Gateway.DisableHTMLErrors` configuration option + +The `Gateway.DisableHTMLErrors` configuration option forces errors to be +displayed in browsers as plain text (`text/plain`) rather than HTML error +pages. It's especially beneficial for whitelabel or middleware deployments that +wish to avoid IPFS branding and links on error pages in browsers. + +#### Binary characters in file names: no longer works with old clients and new Kubo servers + +In this version, we updated Kubo to support Go 1.20+. In Go 1.20, a regression +regarding multipart headers was [introduced](https://github.com/golang/go/issues/60674). +This only affects `ipfs add` when a file name has binary characters in its name. +As a consequence, we had to update the encoding of the file name headers. This is +the compatibility table: + +| | New Client | Old Client | +|------------|------------|-------------| +| New Server | ✅ | 🟡* | +| Old Server | ✅ | ✅ | + +*Old clients can only send Unicode file paths to the server. + +#### Self-hosting `/routing/v1` endpoint for delegated routing needs + +The `Routing` system configured in Kubo can be now exposed on the gateway port as a standard +HTTP [Routing V1](https://specs.ipfs.tech/routing/http-routing-v1/) API endpoint. This allows +self-hosting and experimentation with custom delegated routers. This is disabled by default, +but can be enabled by setting [`Gateway.ExposeRoutingAPI`](https://github.com/ipfs/kubo/blob/master/docs/config.md#gatewayexposeroutingapi) to `true` . + +#### Trustless Gateway Over Libp2p Experiment + +In this update, we've introduced an experimental opt-in feature allowing users to +serve a subset of [Trustless Gateway](https://specs.ipfs.tech/http-gateways/trustless-gateway/) responses, +such as blocks and CARs, over libp2p. This enhancement leverages the ongoing +[`/http/1.1` specification work in libp2p](https://github.com/libp2p/specs/pull/508) +to make it easier to support HTTP semantics over libp2p streams. + +This development means that if users wish to utilize the Trustless Gateway API +for data transport, they can now do so even in scenarios where standard HTTP +might be problematic, such as when the endpoint is behind a firewall or when +attempting to serve data to a browser without a CA certificate. + +See [HTTP Gateway over Libp2p](https://github.com/ipfs/kubo/blob/master/docs/experimental-features.md#http-gateway-over-libp2p) for details about this experiment. + +#### Removal of `/quic` (Draft 29) support + +Kubo no longer supports QUIC Draft 29. This means that older nodes aren't able to connect +to newer nodes using QUIC Draft 29. However, they are still able to connect through any other +transport that both nodes talk (such as QUIC RFC 9000, or TCP). QUIC Draft 29 was a preliminary implementation of QUIC before +the official RFC 9000 was published, and it has now been dropped by [`go-libp2p`](https://github.com/libp2p/go-libp2p/releases/tag/v0.30.0) +and therefore Kubo. + +In [Kubo 0.18](https://github.com/ipfs/kubo/releases/tag/v0.18.0), we shipped a migration +to have listeners for both `/quic` (Draft 29) and `/quic-v1` (RFC 9000). Similarly, in this +version we are shipping a migration to remove the current `/quic` addresses, maintaining +the `/quic-v1` addresses only. For more background information, check [issue #9496](https://github.com/ipfs/kubo/issues/9496). + +#### Better Caching of multiaddresses for providers in DHT servers + +Thanks to [probelab.io's RFM17.1](https://github.com/plprobelab/network-measurements/blob/master/results/rfm17.1-sharing-prs-with-multiaddresses.md) DHT servers will [now cache the addresses of content hosts for the lifetime of the provider record](https://github.com/libp2p/go-libp2p-kad-dht/commit/777160f164b8c187c534debd293157031e9f3a02). + +This means clients who resolve content from theses servers get a responses which include both peer id and multiaddresses. +In most cases this enables skipping a second query which resolves the peer id to multiaddresses for stable enough peers. + +This will improve content fetching lantency in the network overtime as servers updates. + +#### Fixed FUSE multiblock structures + +`ls`ing directories and reading dag-pb files on a fuse volume have been fixed. [#9044](https://github.com/ipfs/kubo/issues/9044) +Thx a lot @bmwiedemann for debugging this issue. + +### 📝 Changelog + +
Full Changelog + +- github.com/ipfs/kubo: + - fix: align systemd unit file with default IPFS installation path (#10163) ([ipfs/kubo#10163](https://github.com/ipfs/kubo/pull/10163)) + - docs: capitalize headers for consistency + - Merge commit '695bf66674931a138862b6fa2cb0b16dc2f6ddd8' into release-v0.23.0 + - chore: update version + - changelog: generalize fuse 9044's entry + - changelog: update fuse 9044's entry + - Update go-unixfsnode to 1.8.0 to fix FUSE + - docs(readme): header improvements (#10144) ([ipfs/kubo#10144](https://github.com/ipfs/kubo/pull/10144)) + - fix(docker): allow nofuse builds for MacOS (#10135) ([ipfs/kubo#10135](https://github.com/ipfs/kubo/pull/10135)) + - docs: fix typos + - docs: s/ipfs dht/amino dht/ + - changelog: mention probelab RFM17.1 dht improvement + - tests: remove sharness ping tests + - perf: make bootstrap saves O(N) + - chore: update go-libp2p-kad-dht + - chore: webui v4.1.1 (#10120) ([ipfs/kubo#10120](https://github.com/ipfs/kubo/pull/10120)) + - core/bootstrap: fix panic without backup bootstrap peer functions (#10029) ([ipfs/kubo#10029](https://github.com/ipfs/kubo/pull/10029)) + - feat: add Gateway.DisableHTMLErrors option (#10137) ([ipfs/kubo#10137](https://github.com/ipfs/kubo/pull/10137)) + - fix(migrations): use dweb.link (#10133) ([ipfs/kubo#10133](https://github.com/ipfs/kubo/pull/10133)) + - docs: add changelog info for QUIC Draft 29 (#10132) ([ipfs/kubo#10132](https://github.com/ipfs/kubo/pull/10132)) + - feat: add gateway to http over libp2p ([ipfs/kubo#10108](https://github.com/ipfs/kubo/pull/10108)) + - migration: update 14-to-15 to v1.0.1 + - chore: update to build with Go 1.21 + - refactor: stop using go-libp2p deprecated peer.ID.Pretty + - docs(readonly): fix typo + - docs(changelog): link to relevant IPIP + - fix: hamt traversal in ipld-explorer (webui@4.1.0) (#10025) ([ipfs/kubo#10025](https://github.com/ipfs/kubo/pull/10025)) + - refactor: if statement (#10105) ([ipfs/kubo#10105](https://github.com/ipfs/kubo/pull/10105)) + - chore: bump repo version to 15 + - docs: remove link to deleted #accelerated-dht-client + - feat(gateway): expose /routing/v1 server (opt-in) (#9877) ([ipfs/kubo#9877](https://github.com/ipfs/kubo/pull/9877)) + - improve error in fuse node failures + - chore: update boxo, go-libp2p, and internalize mplex (#10095) ([ipfs/kubo#10095](https://github.com/ipfs/kubo/pull/10095)) + - dockerfile: reorder copy order for better layer caching + - refactor: using error is instead of == (#10093) ([ipfs/kubo#10093](https://github.com/ipfs/kubo/pull/10093)) + - fix: use %-encoded headers in most compatible way + - fix: open /dev/null with read write permissions + - chore: bump to go 1.20 + - docs(readme): new logo and header + - docker: change to releases that follow debian's updates + - docker: bump debian version to bookworm + - chore: restore exec perms for t0116-gateway-cache.sh and fixtures (#10085) ([ipfs/kubo#10085](https://github.com/ipfs/kubo/pull/10085)) + - fix(gw): useful IPIP-402 CARs on not found errors (#10084) ([ipfs/kubo#10084](https://github.com/ipfs/kubo/pull/10084)) + - feat: add zsh completions (#10040) ([ipfs/kubo#10040](https://github.com/ipfs/kubo/pull/10040)) + - style: remove commented imports [skip changelog] + - style: gofumpt and godot [skip changelog] (#10081) ([ipfs/kubo#10081](https://github.com/ipfs/kubo/pull/10081)) + - chore: bump boxo for verifcid breaking changes + - chore: remove outdated comment (#10077) ([ipfs/kubo#10077](https://github.com/ipfs/kubo/pull/10077)) + - chore: remove deprecated testground plans + - feat: allow users to optin again into mplex + - feat: remove Mplex + - docs(readme): minimal reqs (#10066) ([ipfs/kubo#10066](https://github.com/ipfs/kubo/pull/10066)) + - docs: add v0.23.md + - docs: get ready for v0.23 + - chore: fix link in v0.22 changelog +- github.com/ipfs/boxo (v0.11.0 -> v0.13.1): + - Release v0.13.1 ([ipfs/boxo#469](https://github.com/ipfs/boxo/pull/469)) + - Release v0.13.0 ([ipfs/boxo#465](https://github.com/ipfs/boxo/pull/465)) + - Release v0.12 ([ipfs/boxo#446](https://github.com/ipfs/boxo/pull/446)) +- github.com/ipfs/go-graphsync (v0.14.4 -> v0.15.1): + - v0.15.1 bump + - fix: partial revert of 1be7c1a20; make traverser process identity CIDs + - v0.15.0 bump + - chore: add identity CID parse tests + - fix: traverser should skip over identity CIDs + - fix(ipld): update ipld deps, only slurp LargeBytesNode when matching + - docs(version): update for v0.14.7 + - Handle context cancellation properly (#428) ([ipfs/go-graphsync#428](https://github.com/ipfs/go-graphsync/pull/428)) + - chore(version.json): update for v0.14.6 + - feat: MaxLinks for requests (#420) ([ipfs/go-graphsync#420](https://github.com/ipfs/go-graphsync/pull/420)) + - fix(responsemanager): network disconnect reliability (#425) ([ipfs/go-graphsync#425](https://github.com/ipfs/go-graphsync/pull/425)) + - Update version to reflect latest fixes (#424) ([ipfs/go-graphsync#424](https://github.com/ipfs/go-graphsync/pull/424)) + - Fix shutdown bug in #412 (#422) ([ipfs/go-graphsync#422](https://github.com/ipfs/go-graphsync/pull/422)) +- github.com/ipfs/go-ipfs-cmds (v0.9.0 -> v0.10.0): + - chore: version 0.10.0 + - fix: panic when calling .SetLength for writerResponseEmitter + - fix!: client with raw abs path option + - doc: clarify flag inheritance explanation + - ci: uci/copy-templates ([ipfs/go-ipfs-cmds#242](https://github.com/ipfs/go-ipfs-cmds/pull/242)) + - chore: remove dep on github.com/Kubuxu/go-os-helper +- github.com/ipfs/go-unixfsnode (v1.7.1 -> v1.8.1): + - v1.8.1 bump + - testutil: relax DirEntry usage for non-dag-pb + - v1.8.0 bump + - fix: add cross-impl shard test + - files returned from unixfsnode should be traversable back to their substrate + - fix: better import name + - chore: refactor and add tests with fixtures + - fix: proper tsize encoding in sharded files + - rel 1.7.4 + - Provide path for getting sizes on directory iteration ([ipfs/go-unixfsnode#60](https://github.com/ipfs/go-unixfsnode/pull/60)) + - tag 1.7.3 ([ipfs/go-unixfsnode#57](https://github.com/ipfs/go-unixfsnode/pull/57)) + - Fail to construct preload hamt shards when traversal fails ([ipfs/go-unixfsnode#55](https://github.com/ipfs/go-unixfsnode/pull/55)) + - fix: large files support io.SeekCurrent ([ipfs/go-unixfsnode#56](https://github.com/ipfs/go-unixfsnode/pull/56)) + - chore(version): update version number + - feat: add entity matcher w/o preload, add matcher fn for consuming bytes ([ipfs/go-unixfsnode#52](https://github.com/ipfs/go-unixfsnode/pull/52)) +- github.com/ipld/go-ipld-prime (v0.20.0 -> v0.21.0): + - v0.21.0 release + - fix(selectors): document ranges in slice matcher + - fix(selectors): update ipld/ipld submodule with latest fixtures + - fix(selectors): more permissive with slice "from" underflow + - chore: extract simpleBytes to testutil package + - feat(selectors): negative values for slice matcher's From and To + - chore: extract MultiByteNote to testutil package + - feat(test): add matcher/slice selector test cases + - feat: remove hard-error when slice matcher reaches non-string/bytes node + - fix: cache offsets for sequential reads + - feat: add inline union representation to schema parser + - fix: basic.NewInt returns pointer (like others) + - fix(bindnode): listpairs value assembly handles complex reprs + - fix(bindnode): listpairs repr assembler handles AssignNode + - fix(schema): handle parsing of "listpairs" in the DSL + - fix: remove _skipAbsent labels + - fix: make listpairs repr [[k1,v1],[k2,v2]...] + - feat(bindnode): support listpairs struct representation + - fix(windows,test): avoid "already exists" error on codegen tests for Windows + - Make traversal.WalkTransforming() work + - doc: clean up and expand on traversal pkg docs + - doc: add lots of notes about using the preloader and the budget + - doc: expand on preloader docs + - fix: inline initialPhase() logic for clarity + - feat: preload walk using phase state, call preloader once per link + - fix: handle Budget & SeenLinks + - chore: remove BufferedLoader + - fix: recurse preloader at block level + - fix: Context->PreloadContext for clarity and consistency with LinkContext + - fix: replace ioutil.ReadAll + - fix: fix tooling complaints + - feat: add BufferedLoader + - feat(traversal): allow preloading functionality + - fix: address dodgy test case variable capture + - stop using the deprecated io/ioutil package + - stop using the deprecated io/ioutil package + - stop using the deprecated io/ioutil package + - fix: make StartAtPath work properly for matching walks +- github.com/libp2p/go-libp2p (v0.29.2 -> v0.31.0): + - release v0.31.0 (#2543) ([libp2p/go-libp2p#2543](https://github.com/libp2p/go-libp2p/pull/2543)) + - dashboards: improve naming for black hole panel (#2539) ([libp2p/go-libp2p#2539](https://github.com/libp2p/go-libp2p/pull/2539)) + - reuseport: use DialContext instead of Dial to fail quickly (#2541) ([libp2p/go-libp2p#2541](https://github.com/libp2p/go-libp2p/pull/2541)) + - swarm: track dial cancellation reason (#2532) ([libp2p/go-libp2p#2532](https://github.com/libp2p/go-libp2p/pull/2532)) + - p2p/http: cache json wellknown mappings in the .well-known handler (#2537) ([libp2p/go-libp2p#2537](https://github.com/libp2p/go-libp2p/pull/2537)) + - feat: Implement HTTP spec (#2438) ([libp2p/go-libp2p#2438](https://github.com/libp2p/go-libp2p/pull/2438)) + - move libp2p/go-libp2p-gostream to p2p/net/gostream ([libp2p/go-libp2p#2535](https://github.com/libp2p/go-libp2p/pull/2535)) + - host: disable black hole detection on autonat dialer (#2529) ([libp2p/go-libp2p#2529](https://github.com/libp2p/go-libp2p/pull/2529)) + - identify: disable racy test when running with race detector (#2526) ([libp2p/go-libp2p#2526](https://github.com/libp2p/go-libp2p/pull/2526)) + - swarm: return a more meaningful error when dialing QUIC draft-29 (#2524) ([libp2p/go-libp2p#2524](https://github.com/libp2p/go-libp2p/pull/2524)) + - swarm: fix Unwrap for DialError, implement Unwrap for TransportError (#2437) ([libp2p/go-libp2p#2437](https://github.com/libp2p/go-libp2p/pull/2437)) + - swarm: return errors on filtered addresses when dialing (#2461) ([libp2p/go-libp2p#2461](https://github.com/libp2p/go-libp2p/pull/2461)) + - core: add ErrPeerIDMismatch error type to replace ad-hoc errors (#2451) ([libp2p/go-libp2p#2451](https://github.com/libp2p/go-libp2p/pull/2451)) + - update quic-go to v0.38.1 (#2506) ([libp2p/go-libp2p#2506](https://github.com/libp2p/go-libp2p/pull/2506)) + - quic: don't claim to be able to dial draft-29 in CanDial (#2520) ([libp2p/go-libp2p#2520](https://github.com/libp2p/go-libp2p/pull/2520)) + - examples: update go-libp2p to v0.30.0 (#2507) ([libp2p/go-libp2p#2507](https://github.com/libp2p/go-libp2p/pull/2507)) + - metrics: update dashboard names from libp2p to go-libp2p (#2512) ([libp2p/go-libp2p#2512](https://github.com/libp2p/go-libp2p/pull/2512)) + - chore: be more descriptive about where public dashboards come from (#2508) ([libp2p/go-libp2p#2508](https://github.com/libp2p/go-libp2p/pull/2508)) + - release v0.30.0 (#2505) ([libp2p/go-libp2p#2505](https://github.com/libp2p/go-libp2p/pull/2505)) + - transport tests: add deadline tests (#2286) ([libp2p/go-libp2p#2286](https://github.com/libp2p/go-libp2p/pull/2286)) + - chore: remove unused and outdated package-list.json (#2499) ([libp2p/go-libp2p#2499](https://github.com/libp2p/go-libp2p/pull/2499)) + - muxer: remove support for mplex (#2498) ([libp2p/go-libp2p#2498](https://github.com/libp2p/go-libp2p/pull/2498)) + - transport tests: refactor workers in TestMoreStreamsThanOurLimits (#2472) ([libp2p/go-libp2p#2472](https://github.com/libp2p/go-libp2p/pull/2472)) + - use standard library sha256 implementation for Go 1.21 (#2309) ([libp2p/go-libp2p#2309](https://github.com/libp2p/go-libp2p/pull/2309)) + - quic: update quic-go to v0.37.5 (#2497) ([libp2p/go-libp2p#2497](https://github.com/libp2p/go-libp2p/pull/2497)) + - cleanup: add continue in case of failure in the (*BasicHost).Addrs certhash loop (#2492) ([libp2p/go-libp2p#2492](https://github.com/libp2p/go-libp2p/pull/2492)) + - tests: add a CertHashes testcase in TestInferWebtransportAddrsFromQuic (#2495) ([libp2p/go-libp2p#2495](https://github.com/libp2p/go-libp2p/pull/2495)) + - basichost: use byte representation of WebTransport multiaddr as map key (#2494) ([libp2p/go-libp2p#2494](https://github.com/libp2p/go-libp2p/pull/2494)) + - webtransport: check for UDP multiaddr component in address matcher (#2491) ([libp2p/go-libp2p#2491](https://github.com/libp2p/go-libp2p/pull/2491)) + - swarm: remove unnecessary reqno for pending request tracking (#2460) ([libp2p/go-libp2p#2460](https://github.com/libp2p/go-libp2p/pull/2460)) + - quic: drop support for QUIC draft-29 (#2487) ([libp2p/go-libp2p#2487](https://github.com/libp2p/go-libp2p/pull/2487)) + - metrics: add links to public dashboards (#2486) ([libp2p/go-libp2p#2486](https://github.com/libp2p/go-libp2p/pull/2486)) + - swarm: remove leftover TODO (#2474) ([libp2p/go-libp2p#2474](https://github.com/libp2p/go-libp2p/pull/2474)) + - peerstore: deprecate the database-backed peerstore (#2475) ([libp2p/go-libp2p#2475](https://github.com/libp2p/go-libp2p/pull/2475)) + - identify: fix sorting of observed addresses (#2476) ([libp2p/go-libp2p#2476](https://github.com/libp2p/go-libp2p/pull/2476)) + - update go-multiaddr to v0.11.0 (#2467) ([libp2p/go-libp2p#2467](https://github.com/libp2p/go-libp2p/pull/2467)) + - chore: update golang-lru to v2.0.4, fixing semver violation (#2448) ([libp2p/go-libp2p#2448](https://github.com/libp2p/go-libp2p/pull/2448)) + - swarm: don't open new streams over transient connections (#2450) ([libp2p/go-libp2p#2450](https://github.com/libp2p/go-libp2p/pull/2450)) + - core/crypto: restrict RSA keys to <= 8192 bits (#2454) ([libp2p/go-libp2p#2454](https://github.com/libp2p/go-libp2p/pull/2454)) + - chore: add notable project requirement (#2453) ([libp2p/go-libp2p#2453](https://github.com/libp2p/go-libp2p/pull/2453)) + - examples: update go-libp2p to v0.29.0 (#2432) ([libp2p/go-libp2p#2432](https://github.com/libp2p/go-libp2p/pull/2432)) + - examples: fix description of command line flags for pubsub (#2400) ([libp2p/go-libp2p#2400](https://github.com/libp2p/go-libp2p/pull/2400)) + - basichost: remove invalid comment (#2435) ([libp2p/go-libp2p#2435](https://github.com/libp2p/go-libp2p/pull/2435)) +- github.com/libp2p/go-libp2p-kad-dht (v0.24.2 -> v0.24.4): + - Make v0.24.4 ([libp2p/go-libp2p-kad-dht#931](https://github.com/libp2p/go-libp2p-kad-dht/pull/931)) +- github.com/libp2p/go-libp2p-routing-helpers (v0.7.1 -> v0.7.3): + - chore: release v0.7.3 + - nit: invert if + - fix: for getValueOrErrorParallel do not return values if they come with errors + - test: add test to make sure we return not found when we get errors back with values + - chore: release v0.7.2 + - tracing: do not leak goroutines when the context is canceled + - tracing: allow for reuse of the tracing + - tracing: add tracing to compose parallel's worker + - tests: add more tests + - tests: mark all tests Parallel + - tracing: add highlevel APIs records on the composable routers +- github.com/libp2p/go-reuseport (v0.3.0 -> v0.4.0): + - release v0.4.0 (#111) ([libp2p/go-reuseport#111](https://github.com/libp2p/go-reuseport/pull/111)) + - use SO_REUSEPORT_LB on FreeBSD (#106) ([libp2p/go-reuseport#106](https://github.com/libp2p/go-reuseport/pull/106)) +- github.com/multiformats/go-multiaddr (v0.10.1 -> v0.11.0): + - release v0.11.0 (#214) ([multiformats/go-multiaddr#214](https://github.com/multiformats/go-multiaddr/pull/214)) + - update golang.org/x/exp slice comparison to match standard library version (#210) ([multiformats/go-multiaddr#210](https://github.com/multiformats/go-multiaddr/pull/210)) +- github.com/warpfork/go-testmark (v0.11.0 -> v0.12.1): + - suite: allow disabling file parallelism. + - Suite feature ([warpfork/go-testmark#16](https://github.com/warpfork/go-testmark/pull/16)) + - fix unchecked error in a test + - accept a simplification suggestion from linters + - Trailing whitespace error ([warpfork/go-testmark#15](https://github.com/warpfork/go-testmark/pull/15)) + - FS implementation (#11) ([warpfork/go-testmark#11](https://github.com/warpfork/go-testmark/pull/11)) + - Add a readme for the testexec extension and its conventions. ([warpfork/go-testmark#14](https://github.com/warpfork/go-testmark/pull/14)) + - Strict mode for testexec structure ([warpfork/go-testmark#12](https://github.com/warpfork/go-testmark/pull/12)) + +
+ +### 👨‍👩‍👧‍👦 Contributors + +| Contributor | Commits | Lines ± | Files Changed | +|-------------|---------|---------|---------------| +| Rod Vagg | 48 | +3578/-1789 | 110 | +| Henrique Dias | 24 | +3173/-1128 | 104 | +| Jorropo | 51 | +1721/-1297 | 252 | +| Marco Munizaga | 6 | +1989/-505 | 39 | +| Kay | 3 | +487/-474 | 163 | +| hannahhoward | 8 | +626/-136 | 23 | +| Calvin Behling | 6 | +496/-259 | 20 | +| Eric Myhre | 9 | +610/-121 | 16 | +| Adin Schmahmann | 17 | +659/-45 | 35 | +| Marten Seemann | 17 | +218/-477 | 119 | +| Sukun | 11 | +481/-174 | 29 | +| CJB | 1 | +639/-2 | 5 | +| Hector Sanjuan | 10 | +450/-127 | 21 | +| Wondertan | 2 | +203/-127 | 8 | +| Marcin Rataj | 11 | +148/-86 | 18 | +| Andrew Gillis | 2 | +163/-14 | 5 | +| P. Reis | 3 | +120/-4 | 4 | +| Will Scott | 4 | +107/-12 | 6 | +| Amir Mohammad Fakhimi | 1 | +97/-2 | 5 | +| Ed Schouten | 1 | +55/-7 | 2 | +| Icarus9913 | 1 | +30/-30 | 18 | +| Dirk McCormick | 1 | +3/-42 | 1 | +| Raúl Kripalani | 1 | +20/-18 | 4 | +| Michael Muré | 1 | +26/-7 | 5 | +| Prem Chaitanya Prathi | 1 | +28/-1 | 2 | +| ShengTao | 1 | +13/-14 | 4 | +| Prithvi Shahi | 3 | +14/-13 | 3 | +| web3-bot | 5 | +12/-10 | 9 | +| Alejandro Criado-Pérez | 1 | +11/-11 | 6 | +| Steven Allen | 2 | +6/-10 | 2 | +| Andrej Manduch | 1 | +5/-5 | 3 | +| Russell Dempsey | 2 | +4/-2 | 2 | +| Johannes Maria Frank | 1 | +4/-1 | 1 | +| downIoads | 1 | +2/-2 | 1 | +| Will | 2 | +2/-2 | 2 | +| Marin Kirkov | 1 | +2/-2 | 2 | +| Gus Eggert | 1 | +2/-2 | 1 | +| Bernhard M. Wiedemann | 1 | +4/-0 | 1 | +| Dennis Trautwein | 1 | +1/-2 | 1 | +| “GheisMohammadi” | 1 | +1/-1 | 1 | +| cce | 1 | +1/-1 | 1 | +| Joao Andrade | 1 | +1/-1 | 1 | +| guillaumemichel | 1 | +1/-0 | 1 | +| Santiago Botto | 1 | +0/-1 | 1 | diff --git a/docs/changelogs/v0.24.md b/docs/changelogs/v0.24.md new file mode 100644 index 000000000..9ca7fa84e --- /dev/null +++ b/docs/changelogs/v0.24.md @@ -0,0 +1,209 @@ +# Kubo changelog v0.24 + +- [v0.24.0](#v0240) + +## v0.24.0 + +- [Overview](#overview) +- [🔦 Highlights](#-highlights) + - [Support for content blocking](#support-for-content-blocking) + - [Gateway: the root of the CARs are no longer meaningful](#gateway-the-root-of-the-cars-are-no-longer-meaningful) + - [IPNS: improved publishing defaults](#ipns-improved-publishing-defaults) + - [IPNS: record TTL is used for caching](#ipns-record-ttl-is-used-for-caching) + - [Experimental Transport: WebRTC Direct](#experimental-transport-webrtc-direct) +- [📝 Changelog](#-changelog) +- [👨‍👩‍👧‍👦 Contributors](#-contributors) + +### Overview + +### 🔦 Highlights + +#### Support for content blocking + +This Kubo release ships with built-in content-blocking subsystem [announced earlier this year](https://blog.ipfs.tech/2023-content-blocking-for-the-ipfs-stack/). +Content blocking is an opt-in decision made by the operator of `ipfs daemon`. +The official build does not ship with any denylists. + +Learn more at [`/docs/content-blocking.md`](https://github.com/ipfs/kubo/blob/master/docs/content-blocking.md) + +#### Gateway: the root of the CARs are no longer meaningful + +When requesting a CAR from the gateway, the root of the CAR might no longer be +meaningful. By default, the CAR root will be the last resolvable segment of the +path. However, in situations where the path cannot be resolved, such as when +the path does not exist, a CAR will be sent with a root of `bafkqaaa` (empty CID). +This CAR will contain all blocks necessary to validate that the path does not exist. + +#### IPNS: improved publishing defaults + +This release changes the default values used when publishing IPNS record +via `ipfs name publish` command: + +- Default `--lifetime` increased from `24h` to `48h` to take full advantage of + the increased expiration window of Amino DHT + ([go-libp2p-kad-dht#793](https://github.com/libp2p/go-libp2p-kad-dht/pull/793)) +- Default `--ttl` increased from `1m` to `1h` to improve website caching and follow + saner defaults present in similar systems like DNS + ([specs#371](https://github.com/ipfs/specs/pull/371)) + +This change only impacts the implicit defaults, when mentioned parameters are omitted +during publishing. Users are free to override the default if different value +makes more sense for their use case. + +#### IPNS: record TTL is used for caching + +In this release, we've made significant improvements to IPNS caching. + +Previously, the TTL value in IPNS records was not utilized, and the +`boxo/namesys` library maintained a static one-minute resolution cache. + +With this update, IPNS publishers gain more control over how long a valid IPNS +record remains cached before checking an upstream routing system, such as Amino +DHT, for updates. The TTL value in the IPNS record now serves as a hint for: + +- `boxo/namesys`: the internal cache, determining how long the IPNS resolution + result is cached before asking upsteam routing systems for updates. +- `boxo/gateway`: the `Cache-Control` HTTP header in responses to requests made + for `/ipns/name` content paths. + +These changes make it easier for rarely updated IPNS-hosted websites to be +cached more efficiently and load faster in browser contexts. + +#### Experimental Transport: WebRTC Direct + +This Kubo release includes the initial work towards WebRTC Direct +introduced in [`go-libp2p`](https://github.com/libp2p/go-libp2p/releases/tag/v0.32.0) v0.32: + +> [WebRTC Direct](https://github.com/libp2p/specs/blob/master/webrtc/webrtc-direct.md) +> allows browser nodes to connect to go-libp2p nodes directly, +> without any configuration (e.g. TLS certificates) needed on the go-libp2p +> side. This is useful for browser nodes that aren’t able to use +> [WebTransport](https://blog.libp2p.io/2022-12-19-libp2p-webtransport/). + +The `/webrtc-direct` transport is disabled by default in Kubo 0.24, +and not ready for production use yet, but we plan to enable it in a future release. + +See [`Swarm.Transports.Network.WebRTCDirect`](https://github.com/ipfs/kubo/blob/master/docs/config.md#swarmtransportsnetworkwebrtcdirect) +to learn how to enable it manually, and what current limitations are. + +### 📝 Changelog + +
Full Changelog + +- github.com/ipfs/kubo: + - chore: update version + - fix: allow event emitting to happen in parallel with getting the query channel + - fixes to routing put command (#10205) ([ipfs/kubo#10205](https://github.com/ipfs/kubo/pull/10205)) + - docs: fix accelerated-dht-client + - docs/config: remove extra commas in PublicGateways example entries + - chore: update version + - docs: make it clear Web RTC Direct is experimental + - feat: add WebRTC Direct support + - docs: update EARLY_TESTERS.md (#10194) ([ipfs/kubo#10194](https://github.com/ipfs/kubo/pull/10194)) + - Release: v0.24.0-1 ([ipfs/kubo#10190](https://github.com/ipfs/kubo/pull/10190)) +- github.com/ipfs/boxo (v0.13.1 -> v0.15.0): + - Release v0.15.0 ([ipfs/boxo#505](https://github.com/ipfs/boxo/pull/505)) + - Release v0.14.0 ([ipfs/boxo#500](https://github.com/ipfs/boxo/pull/500)) +- github.com/ipfs/go-block-format (v0.1.2 -> v0.2.0): + - v0.2.0 bump +- github.com/ipfs/go-graphsync (v0.15.1 -> v0.16.0): + - chore: release 0.16.0 + - chore: bump go-libp2p to 0.32.0 +- github.com/ipfs/go-ipld-format (v0.5.0 -> v0.6.0): + - v0.6.0 bump + - chore: update deps + - fix: stop using the deprecated io/ioutil package +- github.com/libp2p/go-libp2p (v0.31.0 -> v0.32.1): + - release v0.32.1 (#2637) ([libp2p/go-libp2p#2637](https://github.com/libp2p/go-libp2p/pull/2637)) + - swarm: fix timer Leak in the dial loop (#2636) ([libp2p/go-libp2p#2636](https://github.com/libp2p/go-libp2p/pull/2636)) + - release v0.32.0 (#2625) ([libp2p/go-libp2p#2625](https://github.com/libp2p/go-libp2p/pull/2625)) + - chore: update js-libp2p examples repo (#2624) ([libp2p/go-libp2p#2624](https://github.com/libp2p/go-libp2p/pull/2624)) + - identify: don't filter dns addresses based on remote addr type (#2553) ([libp2p/go-libp2p#2553](https://github.com/libp2p/go-libp2p/pull/2553)) + - webrtc: fix race in TestRemoveConnByUfrag (#2620) ([libp2p/go-libp2p#2620](https://github.com/libp2p/go-libp2p/pull/2620)) + - swarm: fix recursive resolving of DNS multiaddrs (#2564) ([libp2p/go-libp2p#2564](https://github.com/libp2p/go-libp2p/pull/2564)) + - ci: migrate to renamed interop test action (#2617) ([libp2p/go-libp2p#2617](https://github.com/libp2p/go-libp2p/pull/2617)) + - quic: update quic-go to v0.39.1, set a static resumption token generator key (#2572) ([libp2p/go-libp2p#2572](https://github.com/libp2p/go-libp2p/pull/2572)) + - test/basichost: fix flaky test due to rcmgr (#2613) ([libp2p/go-libp2p#2613](https://github.com/libp2p/go-libp2p/pull/2613)) + - swarm: use typed atomics (#2612) ([libp2p/go-libp2p#2612](https://github.com/libp2p/go-libp2p/pull/2612)) + - swarm: cleanup stream handler goroutine (#2610) ([libp2p/go-libp2p#2610](https://github.com/libp2p/go-libp2p/pull/2610)) + - circuitv2: don't check ASN for private addrs (#2611) ([libp2p/go-libp2p#2611](https://github.com/libp2p/go-libp2p/pull/2611)) + - swarm: use happy eyeballs ranking for TCP dials (#2573) ([libp2p/go-libp2p#2573](https://github.com/libp2p/go-libp2p/pull/2573)) + - webrtc: fix race in TestMuxedConnection (#2607) ([libp2p/go-libp2p#2607](https://github.com/libp2p/go-libp2p/pull/2607)) + - tcp: fix build on riscv64 (#2590) ([libp2p/go-libp2p#2590](https://github.com/libp2p/go-libp2p/pull/2590)) + - Fix missing deprecation tag (#2605) ([libp2p/go-libp2p#2605](https://github.com/libp2p/go-libp2p/pull/2605)) + - swarm: wait for transient connections to upgrade for NewStream (#2542) ([libp2p/go-libp2p#2542](https://github.com/libp2p/go-libp2p/pull/2542)) + - docs: fix typos (#2604) ([libp2p/go-libp2p#2604](https://github.com/libp2p/go-libp2p/pull/2604)) + - webrtc: correctly report incoming packet address on muxed connection (#2586) ([libp2p/go-libp2p#2586](https://github.com/libp2p/go-libp2p/pull/2586)) + - swarm: add loopback to low timeout filter (#2595) ([libp2p/go-libp2p#2595](https://github.com/libp2p/go-libp2p/pull/2595)) + - Fix typos in comments and a test failure message (#2600) ([libp2p/go-libp2p#2600](https://github.com/libp2p/go-libp2p/pull/2600)) + - libp2phttp: don't strip `/` suffix when mounting handler (#2552) ([libp2p/go-libp2p#2552](https://github.com/libp2p/go-libp2p/pull/2552)) + - interop: fix redis env var (#2585) ([libp2p/go-libp2p#2585](https://github.com/libp2p/go-libp2p/pull/2585)) + - quicreuse: remove QUIC metrics tracer (#2582) ([libp2p/go-libp2p#2582](https://github.com/libp2p/go-libp2p/pull/2582)) + - config: warn if connmgr limits conflict with rcmgr (#2527) ([libp2p/go-libp2p#2527](https://github.com/libp2p/go-libp2p/pull/2527)) + - update gomock to v0.3.0 (#2581) ([libp2p/go-libp2p#2581](https://github.com/libp2p/go-libp2p/pull/2581)) + - webrtc: fix deadlock on connection close (#2580) ([libp2p/go-libp2p#2580](https://github.com/libp2p/go-libp2p/pull/2580)) + - webrtc: put buffer back to pool (#2574) ([libp2p/go-libp2p#2574](https://github.com/libp2p/go-libp2p/pull/2574)) + - webrtc: fail Write early if deadline has exceeded before the call (#2578) ([libp2p/go-libp2p#2578](https://github.com/libp2p/go-libp2p/pull/2578)) + - swarm: fix DialPeer behaviour for transient connections (#2547) ([libp2p/go-libp2p#2547](https://github.com/libp2p/go-libp2p/pull/2547)) + - websocket: don't resolve /dnsaddr addresses (#2571) ([libp2p/go-libp2p#2571](https://github.com/libp2p/go-libp2p/pull/2571)) + - core/peer: remove deprecated ID.Pretty method (#2565) ([libp2p/go-libp2p#2565](https://github.com/libp2p/go-libp2p/pull/2565)) + - core/peer: remove deprecated Encode function (#2566) ([libp2p/go-libp2p#2566](https://github.com/libp2p/go-libp2p/pull/2566)) + - mock: use go.uber.org/mock (#2540) ([libp2p/go-libp2p#2540](https://github.com/libp2p/go-libp2p/pull/2540)) + - add WebRTC Direct transport implementation (#2337) ([libp2p/go-libp2p#2337](https://github.com/libp2p/go-libp2p/pull/2337)) + - upgrader: drop support for multistream simultaneous open (#2557) ([libp2p/go-libp2p#2557](https://github.com/libp2p/go-libp2p/pull/2557)) + - examples: stop using deprecated peer.ID.Pretty (#2563) ([libp2p/go-libp2p#2563](https://github.com/libp2p/go-libp2p/pull/2563)) + - swarm: don't dial unspecified addresses (#2560) ([libp2p/go-libp2p#2560](https://github.com/libp2p/go-libp2p/pull/2560)) + - basichost: handle the SetProtocol error in NewStream (#2555) ([libp2p/go-libp2p#2555](https://github.com/libp2p/go-libp2p/pull/2555)) + - libp2phttp: don't initialise ServeMux if not nil (#2548) ([libp2p/go-libp2p#2548](https://github.com/libp2p/go-libp2p/pull/2548)) +- github.com/libp2p/go-libp2p-pubsub (v0.9.3 -> v0.10.0): + - chore: update go-libp2p to v0.32 (#548) ([libp2p/go-libp2p-pubsub#548](https://github.com/libp2p/go-libp2p-pubsub/pull/548)) + - remove usage of deprecated peerid.Pretty method (#542) ([libp2p/go-libp2p-pubsub#542](https://github.com/libp2p/go-libp2p-pubsub/pull/542)) + - Revert "fix: topicscore params can't be set for dynamically subscribed topic (#540)" (#541) ([libp2p/go-libp2p-pubsub#541](https://github.com/libp2p/go-libp2p-pubsub/pull/541)) + - fix: topicscore params can't be set for dynamically subscribed topic (#540) ([libp2p/go-libp2p-pubsub#540](https://github.com/libp2p/go-libp2p-pubsub/pull/540)) +- github.com/multiformats/go-multiaddr (v0.11.0 -> v0.12.0): + - release v0.12.0 (#223) ([multiformats/go-multiaddr#223](https://github.com/multiformats/go-multiaddr/pull/223)) + - net: consider /dns/localhost as private address (#221) ([multiformats/go-multiaddr#221](https://github.com/multiformats/go-multiaddr/pull/221)) + - net: consider dns addresses as public (#220) ([multiformats/go-multiaddr#220](https://github.com/multiformats/go-multiaddr/pull/220)) +- github.com/multiformats/go-multistream (v0.4.1 -> v0.5.0): + - remove support for the simultaneous open extension (#107) ([multiformats/go-multistream#107](https://github.com/multiformats/go-multistream/pull/107)) + +
+ +### 👨‍👩‍👧‍👦 Contributors + +| Contributor | Commits | Lines ± | Files Changed | +|-------------|---------|---------|---------------| +| Henrique Dias | 27 | +4505/-3853 | 244 | +| Marten Seemann | 18 | +4260/-1173 | 101 | +| Sukun | 24 | +1499/-340 | 79 | +| Andrew Gillis | 4 | +169/-1025 | 16 | +| Adin Schmahmann | 4 | +788/-184 | 19 | +| Hector Sanjuan | 6 | +619/-72 | 19 | +| Steven Allen | 11 | +489/-101 | 14 | +| Jorropo | 10 | +221/-192 | 28 | +| Łukasz Magiera | 2 | +306/-9 | 3 | +| Lucas Molas | 1 | +183/-52 | 2 | +| Marcin Rataj | 5 | +160/-25 | 6 | +| piersy | 1 | +57/-0 | 6 | +| Raúl Kripalani | 1 | +25/-25 | 2 | +| Alvin Reyes | 1 | +34/-14 | 1 | +| Dennis Trautwein | 1 | +1/-40 | 2 | +| Icarus9913 | 1 | +14/-14 | 10 | +| Takashi Matsuda | 2 | +18/-1 | 3 | +| gammazero | 4 | +8/-5 | 7 | +| xiaolou86 | 1 | +6/-6 | 5 | +| Daniel Martí | 1 | +9/-2 | 1 | +| Rod Vagg | 3 | +5/-5 | 4 | +| Andrej Manduch | 1 | +5/-5 | 3 | +| vuittont60 | 1 | +4/-4 | 3 | +| vyzo | 1 | +5/-1 | 1 | +| tkzktk | 1 | +3/-3 | 3 | +| tk | 1 | +3/-3 | 2 | +| Prem Chaitanya Prathi | 1 | +1/-5 | 1 | +| Kay | 2 | +2/-3 | 2 | +| Thomas Eizinger | 1 | +2/-2 | 1 | +| Steve Loeppky | 1 | +2/-2 | 1 | +| Jonas Keunecke | 1 | +2/-2 | 1 | +| Alejandro Criado-Pérez | 1 | +1/-1 | 1 | +| web3-bot | 1 | +1/-0 | 1 | +| Eric | 1 | +1/-0 | 1 | diff --git a/docs/changelogs/v0.25.md b/docs/changelogs/v0.25.md new file mode 100644 index 000000000..db610044a --- /dev/null +++ b/docs/changelogs/v0.25.md @@ -0,0 +1,137 @@ +# Kubo changelog v0.25 + +- [v0.25.0](#v0250) + +## v0.25.0 + +- [Overview](#overview) +- [🔦 Highlights](#-highlights) + - [WebUI: Updated Peers View](#webui-updated-peers-view) + - [RPC `API.Authorizations`](#rpc-apiauthorizations) + - [MPLEX Removal](#mplex-removal) + - [Graphsync Experiment Removal](#graphsync-experiment-removal) + - [Commands `ipfs key sign` and `ipfs key verify`](#commands-ipfs-key-sign-and-ipfs-key-verify) +- [📝 Changelog](#-changelog) +- [👨‍👩‍👧‍👦 Contributors](#-contributors) + +### Overview + +### 🔦 Highlights + +#### WebUI: Updated Peers View + +WebUI [v4.2.0](https://github.com/ipfs/ipfs-webui/releases/tag/v4.2.0) shipped +with updated [ipfs-geoip](https://www.npmjs.com/package/ipfs-geoip) dataset +and [ability to filter the peers table](https://github.com/ipfs/ipfs-webui/pull/2181). + +#### RPC `API.Authorizations` + +Kubo RPC API now supports optional HTTP Authorization. + +Granular control over user access to the RPC can be defined in the +[`API.Authorizations`](https://github.com/ipfs/kubo/blob/master/docs/config.md#apiauthorizations) +map in the configuration file, allowing different users or apps to have unique +access secrets and allowed paths. + +This feature is opt-in. By default, no authorization is set up. +For configuration instructions, +refer to the [documentation](https://github.com/ipfs/kubo/blob/master/docs/config.md#apiauthorizations). + +#### MPLEX Removal + +After deprecating and removing mplex support by default in [v0.23.0](https://github.com/ipfs/kubo/blob/master/docs/changelogs/v0.23.md#mplex-deprecation). + +We now fully removed it. If you still need mplex support to talk with other pieces of software, +please try updating them, and if they don't support yamux or QUIC [talk to us about it](https://github.com/ipfs/kubo/issues/new/choose). + +Mplex is unreliable by design, it will drop data and generete errors when sending data *too fast*, +yamux and QUIC support backpressure, that means if we send data faster than the remote machine can process it, we slows down to match the remote's speed. + +#### Graphsync Experiment Removal + +Currently the Graphsync server is to our knowledge not used +due to lack of compatible software. +And we are left to have to maintain the go-graphsync implementation when trying +to update Kubo because some dependency changed and it fails to build anymore. + +For more information see https://github.com/ipfs/kubo/pull/9747. + +##### Commands `ipfs key sign` and `ipfs key verify` + +This allows the Kubo node to sign arbitrary bytes to prove ownership of a PeerID or an IPNS Name. To avoid signature reuse, the signed payload is always prefixed with `libp2p-key signed message:`. + +These commands are also both available through the RPC client and implemented in `client/rpc`. + +For more information see https://github.com/ipfs/kubo/issues/10230. + +### 📝 Changelog + +
Full Changelog + +- github.com/ipfs/kubo: + - chore: update version + - fix: allow daemon to start correctly if the API is null (#10062) ([ipfs/kubo#10062](https://github.com/ipfs/kubo/pull/10062)) + - chore: update version + - feat: ipfs key sign|verify (#10235) ([ipfs/kubo#10235](https://github.com/ipfs/kubo/pull/10235)) + - docs(cli): fix spelling + - feat: webui v4.2.0 (#10241) ([ipfs/kubo#10241](https://github.com/ipfs/kubo/pull/10241)) + - Migrate coreiface ([ipfs/kubo#10237](https://github.com/ipfs/kubo/pull/10237)) + - docs: clarify WebRTCDirect cannot reuse the same port as QUIC + - libp2p: remove mplex + - graphsync: remove support for the server + - docs: move kubo-specific docs (#10226) ([ipfs/kubo#10226](https://github.com/ipfs/kubo/pull/10226)) + - feat(rpc): Opt-in HTTP RPC API Authorization (#10218) ([ipfs/kubo#10218](https://github.com/ipfs/kubo/pull/10218)) + - docs: clarify ipfs id agent version + - fix: regression in 'ipfs dns' + - docs(changelog): clarify webrtc in v0.24 + - chore: create next changelog + - Merge Release: v0.24.0 ([ipfs/kubo#10209](https://github.com/ipfs/kubo/pull/10209)) + - fix: allow event emitting to happen in parallel with getting the query channel + - fixes to routing put command (#10205) ([ipfs/kubo#10205](https://github.com/ipfs/kubo/pull/10205)) + - docs: fix accelerated-dht-client + - docs/config: remove extra commas in PublicGateways example entries + - docs: make it clear Web RTC Direct is experimental + - feat: add WebRTC Direct support + - docs: update EARLY_TESTERS.md (#10194) ([ipfs/kubo#10194](https://github.com/ipfs/kubo/pull/10194)) + - Update Version: v0.24 ([ipfs/kubo#10191](https://github.com/ipfs/kubo/pull/10191)) +- github.com/ipfs/boxo (v0.15.0 -> v0.16.0): + - Release 0.16.0 ([ipfs/boxo#518](https://github.com/ipfs/boxo/pull/518)) +- github.com/libp2p/go-libp2p (v0.32.1 -> v0.32.2): + - release v0.32.2 + +
+ +### 👨‍👩‍👧‍👦 Contributors + +| Contributor | Commits | Lines ± | Files Changed | +|-------------|---------|---------|---------------| +| Łukasz Magiera | 149 | +7833/-2505 | 375 | +| Henrique Dias | 26 | +2498/-7535 | 210 | +| Steven Allen | 48 | +497/-373 | 129 | +| Jorropo | 9 | +247/-604 | 49 | +| Michael Muré | 6 | +306/-79 | 14 | +| Adin Schmahmann | 3 | +275/-8 | 5 | +| Lucas Molas | 1 | +181/-56 | 2 | +| Laurent Senta | 1 | +109/-24 | 7 | +| Lars Gierth | 6 | +82/-18 | 8 | +| Petar Maymounkov | 1 | +66/-32 | 3 | +| web3-bot | 1 | +47/-42 | 17 | +| Marcin Rataj | 6 | +57/-23 | 8 | +| Kevin Atkinson | 5 | +31/-31 | 17 | +| Marten Seemann | 3 | +27/-28 | 16 | +| Hector Sanjuan | 3 | +28/-14 | 10 | +| Overbool | 2 | +36/-3 | 3 | +| Raúl Kripalani | 1 | +11/-12 | 4 | +| hannahhoward | 2 | +11/-7 | 6 | +| Jeromy Johnson | 5 | +9/-9 | 5 | +| ForrestWeston | 1 | +14/-1 | 1 | +| Russell Dempsey | 1 | +10/-2 | 2 | +| Will Scott | 1 | +8/-1 | 1 | +| Jeromy | 2 | +4/-4 | 2 | +| sukun | 1 | +2/-2 | 1 | +| Steve Loeppky | 1 | +2/-2 | 1 | +| Jonas Keunecke | 1 | +2/-2 | 1 | +| Edgar Lee | 1 | +3/-1 | 1 | +| Dreamacro | 1 | +2/-2 | 2 | +| godcong | 1 | +1/-1 | 1 | +| Cole Brown | 1 | +1/-1 | 1 | diff --git a/docs/changelogs/v0.26.md b/docs/changelogs/v0.26.md new file mode 100644 index 000000000..bb38a6849 --- /dev/null +++ b/docs/changelogs/v0.26.md @@ -0,0 +1,124 @@ +# Kubo changelog v0.26 + +- [v0.26.0](#v0260) + +## v0.26.0 + +- [Overview](#overview) +- [🔦 Highlights](#-highlights) + - [Several deprecated commands have been removed](#several-deprecated-commands-have-been-removed) + - [Support optional pin names](#support-optional-pin-names) + - [`jaeger` trace exporter has been removed](#jaeger-trace-exporter-has-been-removed) +- [📝 Changelog](#-changelog) +- [👨‍👩‍👧‍👦 Contributors](#-contributors) + +### Overview + +### 🔦 Highlights + +#### Kubo binary imports + +For users of [Kubo preloaded plugins](https://github.com/ipfs/kubo/blob/master/docs/plugins.md#preloaded-plugins) there is now a way to create a kubo instance with your plugins by depending on the `cmd/ipfs/kubo` package rather than rebuilding kubo with the included plugins. + +See the [customization docs](https://github.com/ipfs/kubo/blob/master/docs/customizing.md) for more information. + +#### Several deprecated commands have been removed + +Several deprecated commands have been removed: + +- `ipfs urlstore` deprecated in [April 2019, Kubo 0.4.21](https://github.com/ipfs/kubo/commit/8beaee63b3fa634c59b85179286ad3873921a535), use `ipfs add -q --nocopy --cid-version=1 {url}` instead. +- `ipfs repo fsck` deprecated in [July 2019, Kubo 0.5.0](https://github.com/ipfs/kubo/commit/288a83ce7dcbf4a2498e06e4a95245bbb5e30f45) +- `ipfs file` (and `ipfs file ls`) deprecated in [November 2020, Kubo 0.8.0](https://github.com/ipfs/kubo/commit/ec64dc5c396e7114590e15909384fabce0035482), use `ipfs ls` and `ipfs files ls` instead. +- `ipfs dns` deprecated in [April 2022, Kubo 0.13](https://github.com/ipfs/kubo/commit/76ae33a9f3f9abd166d1f6f23d6a8a0511510e3c), use `ipfs resolve /ipns/{name}` instead. +- `ipfs tar` deprecated [April 2022, Kubo 0.13](https://github.com/ipfs/kubo/pull/8849) + +#### Support optional pin names + +You can now add a name to a pin when pinning a CID. To do so, use `ipfs pin add --name "Some Name" bafy...`. You can list your pins, including their names, with `ipfs pin ls --names`. + +#### `jaeger` trace exporter has been removed + +`jaeger` exporter has been removed from upstream, you should use `otlp` exporter instead. +See the [boxo tracing docs](https://github.com/ipfs/boxo/blob/a391d02102875ee7075a692076154bec1fa871f3/docs/tracing.md) for an example. + +### 📝 Changelog + +
Full Changelog + +- github.com/ipfs/kubo: + - chore: update version + - chore: update version + - feat(pinning): allow for overwriting pin name + - chore: update otlp + - Revert "build,docker: add support for riscv64" + - feat: support optional pin names (#10261) ([ipfs/kubo#10261](https://github.com/ipfs/kubo/pull/10261)) + - build,docker: add support for riscv64 + - feat(cmd/ipfs): Make it possible to depend on cmd/ipfs/kubo for easier preloaded plugin management ([ipfs/kubo#10219](https://github.com/ipfs/kubo/pull/10219)) + - docs: fix broken link in HTTP RPC client doc (#10267) ([ipfs/kubo#10267](https://github.com/ipfs/kubo/pull/10267)) + - Merge Release: v0.25.0 [skip changelog] ([ipfs/kubo#10260](https://github.com/ipfs/kubo/pull/10260)) + - docs: add detail to NOpfs instructions in content-blocking.md + - commands: remove several deprecated commands + - fix: allow daemon to start correctly if the API is null (#10062) ([ipfs/kubo#10062](https://github.com/ipfs/kubo/pull/10062)) + - chore: update version +- github.com/ipfs/boxo (v0.16.0 -> v0.17.0): + - Release v0.17.0 ([ipfs/boxo#542](https://github.com/ipfs/boxo/pull/542)) +- github.com/ipfs/go-ipld-cbor (v0.0.6 -> v0.1.0): + - v0.1.0 bump + - chore: add or force update version.json + - allow configuration of ipldStores default hash function ([ipfs/go-ipld-cbor#86](https://github.com/ipfs/go-ipld-cbor/pull/86)) + - sync: update CI config files (#85) ([ipfs/go-ipld-cbor#85](https://github.com/ipfs/go-ipld-cbor/pull/85)) +- github.com/ipfs/go-unixfsnode (v1.8.1 -> v1.9.0): + - v1.9.0 bump + - feat: expose ToDirEntryFrom to allow sub-dag representation + - feat: new UnixFS{File,Directory} with options pattern + - feat: testutil generator enhancements +- github.com/ipld/go-car/v2 (v2.10.2-0.20230622090957-499d0c909d33 -> v2.13.1): + - fix: BlockMetadata#Offset should be for section, not block data + - fix: add closed check, expose storage.ErrClosed + - fix: switch constructor args to match storage.New*, make roots plural + - feat: add DeferredCarWriter + - feat: fix BlockReader#SkipNext & add SourceOffset property + - v0.6.2 ([ipld/go-car#464](https://github.com/ipld/go-car/pull/464)) + - fix: opt-in way to allow empty list of roots in CAR headers ([ipld/go-car#461](https://github.com/ipld/go-car/pull/461)) +- github.com/libp2p/go-libp2p-asn-util (v0.3.0 -> v0.4.1): + - chore: release v0.4.1 + - fix: add Init method on backward compat + - chore: release v0.4.0 + - rewrite representation to a sorted binary list and embed it + - docs: fix incorrect markdown === in README + - ci: run go generate on CI (#27) ([libp2p/go-libp2p-asn-util#27](https://github.com/libp2p/go-libp2p-asn-util/pull/27)) +- github.com/multiformats/go-multiaddr (v0.12.0 -> v0.12.1): + - v0.12.1 bump + - manet: reduce allocations in resolve unspecified address +- github.com/whyrusleeping/cbor-gen (v0.0.0-20230126041949-52956bd4c9aa -> v0.0.0-20240109153615-66e95c3e8a87): + - Add a feature to preserve nil slices (#88) ([whyrusleeping/cbor-gen#88](https://github.com/whyrusleeping/cbor-gen/pull/88)) + - some cleanup for easier reading ([whyrusleeping/cbor-gen#89](https://github.com/whyrusleeping/cbor-gen/pull/89)) + - Support gen for map with value type `string` (#83) ([whyrusleeping/cbor-gen#83](https://github.com/whyrusleeping/cbor-gen/pull/83)) + - feat: add support for pointers to CIDs in slices (#86) ([whyrusleeping/cbor-gen#86](https://github.com/whyrusleeping/cbor-gen/pull/86)) + - optimize anything using WriteString ([whyrusleeping/cbor-gen#85](https://github.com/whyrusleeping/cbor-gen/pull/85)) + - Implement *bool support and support omitempty for slices ([whyrusleeping/cbor-gen#81](https://github.com/whyrusleeping/cbor-gen/pull/81)) + +
+ +### 👨‍👩‍👧‍👦 Contributors + +| Contributor | Commits | Lines ± | Files Changed | +|-------------|---------|---------|---------------| +| Henrique Dias | 11 | +493/-1184 | 48 | +| Łukasz Magiera | 3 | +610/-582 | 16 | +| Rod Vagg | 11 | +1030/-151 | 18 | +| whyrusleeping | 6 | +553/-388 | 14 | +| Jorropo | 13 | +561/-348 | 84 | +| Jeromy Johnson | 1 | +771/-48 | 6 | +| Steven Allen | 2 | +264/-135 | 4 | +| Forrest | 1 | +214/-0 | 5 | +| Marcin Rataj | 1 | +89/-24 | 2 | +| sukun | 1 | +31/-11 | 5 | +| Will Scott | 3 | +25/-10 | 3 | +| Adin Schmahmann | 3 | +21/-5 | 3 | +| web3-bot | 2 | +8/-8 | 3 | +| Marten Seemann | 1 | +13/-1 | 1 | +| Bumblefudge | 1 | +5/-2 | 1 | +| Will | 1 | +1/-1 | 1 | +| Nicholas Ericksen | 1 | +1/-1 | 1 | +| 0xbasar | 1 | +1/-1 | 1 | diff --git a/docs/changelogs/v0.27.md b/docs/changelogs/v0.27.md new file mode 100644 index 000000000..e5bd895ca --- /dev/null +++ b/docs/changelogs/v0.27.md @@ -0,0 +1,144 @@ +# Kubo changelog v0.27 + +- [v0.27.0](#v0270) + +## v0.27.0 + +- [Overview](#overview) +- [🔦 Highlights](#-highlights) + - [Gateway: support for `/api/v0` is deprecated](#gateway-support-for-apiv0-is-deprecated) + - [IPNS resolver cache's TTL can now be configured](#ipns-resolver-caches-ttl-can-now-be-configured) + - [RPC client: deprecated DHT API, added Routing API](#rpc-client-deprecated-dht-api-added-routing-api) + - [Deprecated DHT commands removed from `/api/v0/dht`](#deprecated-dht-commands-removed-from-apiv0dht) + - [Repository migrations are now trustless](#repository-migrations-are-now-trustless) +- [📝 Changelog](#-changelog) +- [👨‍👩‍👧‍👦 Contributors](#-contributors) + +### Overview + +### 🔦 Highlights + +#### Gateway: support for `/api/v0` is deprecated + +Support for exposing the legacy subset of Kubo RPC via the Gateway port is deprecated and should not be used. It will be removed in the next version. You can read more in . + +If you have a legacy software that relies on this behavior, and want to expose parts of `/api/v0` next to `/ipfs`, use reverse-proxy in front of Kubo to mount both Gateway and RPC on the same port. NOTE: exposing RPC to the internet comes with security risk: make sure to specify access control via [API.Authorizations](https://github.com/ipfs/kubo/blob/master/docs/config.md#apiauthorizations). + +#### IPNS resolver cache's TTL can now be configured + +You can now configure the upper-bound of a cached IPNS entry's Time-To-Live via [`Ipns.MaxCacheTTL`](https://github.com/ipfs/kubo/blob/master/docs/config.md#ipnsmaxcachettl). + +#### RPC client: deprecated DHT API, added Routing API + +The RPC client for GO (`kubo/client/rpc`) now includes a Routing API to match the available commands in `/api/v0/routing`. In addition, the DHT API has been marked as deprecated. + +In the next version, all DHT deprecated methods will be removed from the Go RPC client. + +#### Deprecated DHT commands removed from `/api/v0/dht` + +All the DHT commands that were deprecated for over a year were finally removed from `/api/v0/dht`. Users should switch to modern `/api/v0/routing` which works with [both Amino DHT and Delegated Routers](https://github.com/ipfs/kubo/blob/master/docs/config.md#routing). + +#### Repository migrations are now trustless + +Kubo now only uses [trustless requests](https://specs.ipfs.tech/http-gateways/trustless-gateway/) (e.g., CAR files) when downloading repository migrations via HTTP. This further strengthens Kubo by not delegating trust to public gateways. The migration binaries are locally verified before being executed. + +### 📝 Changelog + +
Full Changelog + +- github.com/ipfs/kubo: + - chore: update version + - chore: update version + - test: cleanup content blocking tests (#10360) ([ipfs/kubo#10360](https://github.com/ipfs/kubo/pull/10360)) + - docs: improve release issue template + - chore: update version + - repo/fsrepo/migrations: verified HTTP migrations (#10324) ([ipfs/kubo#10324](https://github.com/ipfs/kubo/pull/10324)) + - chore: fix link + - docs: clarify Gateway.ExposeRoutingAPI (#10337) ([ipfs/kubo#10337](https://github.com/ipfs/kubo/pull/10337)) + - commands/add: return an error when using --only-hash and --to-files + - docs(config): mention routing v1 spec + - core/commands: remove 'ipfs dht' commands, except 'query' (#10328) ([ipfs/kubo#10328](https://github.com/ipfs/kubo/pull/10328)) + - core: deprecate CoreAPI.Dht, introduce CoreAPI.Routing + - refactor: superfluous namespace test redirects (#10322) ([ipfs/kubo#10322](https://github.com/ipfs/kubo/pull/10322)) + - feat: add Ipns.MaxCacheTTL + - fix(gw): negative entity-bytes beyond file size (#10320) ([ipfs/kubo#10320](https://github.com/ipfs/kubo/pull/10320)) + - core/corehttp: wrap gateway with headers, deprecate gateway /api/v0 + - docs: add changelog link to release issue template + - docs: remove whizzzkid + - chore: create next changelog + - Merge Release: v0.26.0 [skip changelog] ([ipfs/kubo#10313](https://github.com/ipfs/kubo/pull/10313)) + - config: remove all options that are marked as REMOVED + - chore: remove Gateway.APICommands + - docs(cli): name inspect --verify (#10308) ([ipfs/kubo#10308](https://github.com/ipfs/kubo/pull/10308)) + - docs: improve release issue template (#10305) ([ipfs/kubo#10305](https://github.com/ipfs/kubo/pull/10305)) + - core/corehttp: wrap hostname option with otelhttp + - fix: profiling tests + - profile: add trace + - docs(config): clarify ReproviderStrategy roots + - chore: update version + - docs: in RELEASE_ISSUE_TEMPLATE ask releaser to ensure we are using the latest go release on the major branch +- github.com/ipfs/boxo (v0.17.0 -> v0.18.0): + - Release v0.18.0 ([ipfs/boxo#581](https://github.com/ipfs/boxo/pull/581)) +- github.com/libp2p/go-libp2p (v0.32.2 -> v0.33.0): + - release v0.33.0 (#2715) ([libp2p/go-libp2p#2715](https://github.com/libp2p/go-libp2p/pull/2715)) + - chore: update deps for v0.33 (#2713) ([libp2p/go-libp2p#2713](https://github.com/libp2p/go-libp2p/pull/2713)) + - webrtc: wait for FIN_ACK before closing data channels (#2615) ([libp2p/go-libp2p#2615](https://github.com/libp2p/go-libp2p/pull/2615)) + - quic: upgrade quic-go to v0.41.0 (#2710) ([libp2p/go-libp2p#2710](https://github.com/libp2p/go-libp2p/pull/2710)) + - chore: remove unused GenerateEKeyPair function (#2711) ([libp2p/go-libp2p#2711](https://github.com/libp2p/go-libp2p/pull/2711)) + - chore: drop support for go1.20 (#2708) ([libp2p/go-libp2p#2708](https://github.com/libp2p/go-libp2p/pull/2708)) + - chore: testify fix got, expected transpositions (#2666) ([libp2p/go-libp2p#2666](https://github.com/libp2p/go-libp2p/pull/2666)) + - docs: fix broken link in README + - chore: fix typos (#2694) ([libp2p/go-libp2p#2694](https://github.com/libp2p/go-libp2p/pull/2694)) + - libp2phttp: fix flaky ExampleHost_listenOnHTTPTransportAndStreams (#2697) ([libp2p/go-libp2p#2697](https://github.com/libp2p/go-libp2p/pull/2697)) + - chore(p2p/host): fix typos (#2683) ([libp2p/go-libp2p#2683](https://github.com/libp2p/go-libp2p/pull/2683)) + - chore: fix typos (#2689) ([libp2p/go-libp2p#2689](https://github.com/libp2p/go-libp2p/pull/2689)) + - defaults: do TLS by default for encryption (#2650) ([libp2p/go-libp2p#2650](https://github.com/libp2p/go-libp2p/pull/2650)) + - webrtc: fix flaky TestMaxInFlightRequests (#2682) ([libp2p/go-libp2p#2682](https://github.com/libp2p/go-libp2p/pull/2682)) + - chore: remove unnecessary conversions (#2680) ([libp2p/go-libp2p#2680](https://github.com/libp2p/go-libp2p/pull/2680)) + - chore: update chat-with-mdns example readme (#2678) ([libp2p/go-libp2p#2678](https://github.com/libp2p/go-libp2p/pull/2678)) + - examples: call NewStream from only one side (#2677) ([libp2p/go-libp2p#2677](https://github.com/libp2p/go-libp2p/pull/2677)) + - chore: fix typos in comment (#2674) ([libp2p/go-libp2p#2674](https://github.com/libp2p/go-libp2p/pull/2674)) + - chore: update go-libp2p-asn-util (#2673) ([libp2p/go-libp2p#2673](https://github.com/libp2p/go-libp2p/pull/2673)) + - chore: update go security policy url (#2665) ([libp2p/go-libp2p#2665](https://github.com/libp2p/go-libp2p/pull/2665)) + - security: remove separate licenses for Noise and TLS (#2663) ([libp2p/go-libp2p#2663](https://github.com/libp2p/go-libp2p/pull/2663)) + - webrtc: clarify that there is no reuseport functionality (#2652) ([libp2p/go-libp2p#2652](https://github.com/libp2p/go-libp2p/pull/2652)) + - rcmgr: fix connmgr connection limit conflict warning (#2648) ([libp2p/go-libp2p#2648](https://github.com/libp2p/go-libp2p/pull/2648)) + - tcp: fix build on loong64 (#2655) ([libp2p/go-libp2p#2655](https://github.com/libp2p/go-libp2p/pull/2655)) + - swarm: fix grafana dashboard templating (#2640) ([libp2p/go-libp2p#2640](https://github.com/libp2p/go-libp2p/pull/2640)) + - chore: fix typos (#2608) ([libp2p/go-libp2p#2608](https://github.com/libp2p/go-libp2p/pull/2608)) + - chore: add resource manager dashboard to docker-compose (#2641) ([libp2p/go-libp2p#2641](https://github.com/libp2p/go-libp2p/pull/2641)) + - pstoremanager: fix race condition when removing peers from peer store (#2644) ([libp2p/go-libp2p#2644](https://github.com/libp2p/go-libp2p/pull/2644)) + - examples: remove unused 'SetStreamHandler' (#2598) ([libp2p/go-libp2p#2598](https://github.com/libp2p/go-libp2p/pull/2598)) + - Update docs from RSA to Ed25519 (#2606) ([libp2p/go-libp2p#2606](https://github.com/libp2p/go-libp2p/pull/2606)) +- github.com/multiformats/go-multiaddr (v0.12.1 -> v0.12.2): + - chore: release v0.12.2 + - tests: add round trip equality check to fuzz (#232) ([multiformats/go-multiaddr#232](https://github.com/multiformats/go-multiaddr/pull/232)) + - fix: correctly parse ports as uint16 and explicitely fail on overflows (#228) ([multiformats/go-multiaddr#228](https://github.com/multiformats/go-multiaddr/pull/228)) + - replace custom random tests with testing.F (#227) ([multiformats/go-multiaddr#227](https://github.com/multiformats/go-multiaddr/pull/227)) + +
+ +### 👨‍👩‍👧‍👦 Contributors + +| Contributor | Commits | Lines ± | Files Changed | +|-------------|---------|---------|---------------| +| Henrique Dias | 26 | +1668/-1484 | 96 | +| Sukun | 13 | +983/-618 | 68 | +| Jorropo | 18 | +501/-222 | 32 | +| Marten Seemann | 2 | +17/-244 | 5 | +| dozyio | 1 | +117/-132 | 31 | +| Marcin Rataj | 7 | +100/-20 | 8 | +| Alexandr Burdiyan | 2 | +29/-54 | 2 | +| Tyler | 1 | +17/-19 | 2 | +| KeienWang | 2 | +14/-14 | 12 | +| Håvard Anda Estensen | 1 | +14/-14 | 11 | +| Halimao | 2 | +17/-4 | 2 | +| hannahhoward | 1 | +14/-6 | 2 | +| alex | 1 | +8/-8 | 4 | +| shuoer86 | 1 | +7/-7 | 5 | +| John Chase | 1 | +0/-12 | 1 | +| GoodDaisy | 1 | +5/-5 | 4 | +| Michael Muré | 1 | +6/-2 | 1 | +| 吴小白 | 1 | +3/-3 | 3 | +| Vehorny | 1 | +3/-3 | 2 | +| Eric | 1 | +1/-1 | 1 | diff --git a/docs/changelogs/v0.28.md b/docs/changelogs/v0.28.md new file mode 100644 index 000000000..6dfd33386 --- /dev/null +++ b/docs/changelogs/v0.28.md @@ -0,0 +1,130 @@ +# Kubo changelog v0.28 + +- [v0.28.0](#v0280) + +## v0.28.0 + +- [Overview](#overview) +- [🔦 Highlights](#-highlights) + - [RPC client: removed deprecated DHT API](#rpc-client-removed-deprecated-dht-api) + - [Gateway: `/api/v0` is removed](#gateway-apiv0-is-removed) + - [Removed deprecated Object API commands](#removed-deprecated-object-api-commands) + - [No longer publishes loopback and private addresses on DHT](#no-longer-publishes-loopback-and-private-addresses-on-dht) + - [Pin roots are now prioritized when announcing](#pin-roots-are-now-prioritized-when-announcing) +- [📝 Changelog](#-changelog) +- [👨‍👩‍👧‍👦 Contributors](#-contributors) + +### Overview + +#### RPC client: removed deprecated DHT API + +The deprecated DHT API commands in the RPC client have been removed. Instead, use the Routing API. + +#### Gateway: `/api/v0` is removed + +The legacy subset of the Kubo RPC that was available via the Gateway port and was deprecated is now completely removed. You can read more in . + +If you have a legacy software that relies on this behavior, and want to expose parts of `/api/v0` next to `/ipfs`, use reverse-proxy in front of Kubo to mount both Gateway and RPC on the same port. NOTE: exposing RPC to the internet comes with security risk: make sure to specify access control via [API.Authorizations](https://github.com/ipfs/kubo/blob/master/docs/config.md#apiauthorizations). + +#### Removed deprecated Object API commands + +The Object API commands deprecated back in [2021](https://github.com/ipfs/kubo/issues/7936) have been removed, except for `object diff`, `object patch add-link` and `object patch rm-link`, whose alternatives have not yet been built (see issues [4801](https://github.com/ipfs/kubo/issues/4801) and [4782](https://github.com/ipfs/kubo/issues/4782)). + +##### Kubo ignores loopback addresses on LAN DHT and private addresses on WAN DHT + +Kubo no longer keeps track of loopback and private addresses on the LAN and WAN DHTs, respectively. This means that other nodes will not try to dial likely undialable addresses. + +To support testing scenarios where multiple Kubo instances run on the same machine, [`Routing.LoopbackAddressesOnLanDHT`](https://github.com/ipfs/kubo/blob/master/docs/config.md#routingloopbackaddressesonlandht) is set to `true` when the `test` profile is applied. + +#### Pin roots are now prioritized when announcing + +The root CIDs of pinned content are now prioritized when announcing to the Amino DHT with [`Reprovider.Strategy`](https://github.com/ipfs/kubo/blob/master/docs/config.md#reproviderstrategy) set to `all` (default) or `pinned`, making the important CIDs accessible faster. + +### 📝 Changelog + +
Full Changelog + +- github.com/ipfs/kubo: + - chore: update version + - chore: update version + - core/node: prioritize announcing pin roots, and flat strategy (#10376) ([ipfs/kubo#10376](https://github.com/ipfs/kubo/pull/10376)) + - chore: webui v4.2.1 (#10391) ([ipfs/kubo#10391](https://github.com/ipfs/kubo/pull/10391)) + - docs(config): clarify RPC vs Gateway + - chore: upgrade go-libp2p-kad-dht (#10378) ([ipfs/kubo#10378](https://github.com/ipfs/kubo/pull/10378)) + - chore(config): make Routing.AcceleratedDHTClient a Flag (#10384) ([ipfs/kubo#10384](https://github.com/ipfs/kubo/pull/10384)) + - fix: switch lowpower profile to autoclient + - core: fix some typos (#10382) ([ipfs/kubo#10382](https://github.com/ipfs/kubo/pull/10382)) + - docs: fix some typos (#10377) ([ipfs/kubo#10377](https://github.com/ipfs/kubo/pull/10377)) + - core/commands!: remove deprecated object APIs (#10375) ([ipfs/kubo#10375](https://github.com/ipfs/kubo/pull/10375)) + - docs: update default ipns lifetime + - coreapi/unixfs: don't create an additional IpfsNode for --only-hash + - chore: cleanup old workaround (#10369) ([ipfs/kubo#10369](https://github.com/ipfs/kubo/pull/10369)) + - chore: finish reframe removal + - docs: remove repetitive words (#10370) ([ipfs/kubo#10370](https://github.com/ipfs/kubo/pull/10370)) + - docs: updated links and refs to external resources (#10368) ([ipfs/kubo#10368](https://github.com/ipfs/kubo/pull/10368)) + - core/corehttp!: remove /api/v0 from gateway port + - client/rpc!: remove deprecated DHT commands + - ci: upgrade to go 1.22 (#10355) ([ipfs/kubo#10355](https://github.com/ipfs/kubo/pull/10355)) + - chore: create next changelog + - Merge Release: v0.27.0 [skip changelog] ([ipfs/kubo#10362](https://github.com/ipfs/kubo/pull/10362)) + - test: cleanup content blocking tests (#10360) ([ipfs/kubo#10360](https://github.com/ipfs/kubo/pull/10360)) + - docs: improve release issue template + - chore: update version +- github.com/ipfs/boxo (v0.18.0 -> v0.19.0): + - Release v0.19.0 ([ipfs/boxo#598](https://github.com/ipfs/boxo/pull/598)) +- github.com/libp2p/go-libp2p (v0.33.0 -> v0.33.2): + - chore: release v0.33.2 (#2755) ([libp2p/go-libp2p#2755](https://github.com/libp2p/go-libp2p/pull/2755)) + - Update quic-go to v0.42.0. Release v0.33.1 (#2741) ([libp2p/go-libp2p#2741](https://github.com/libp2p/go-libp2p/pull/2741)) +- github.com/libp2p/go-libp2p-kad-dht (v0.24.4 -> v0.25.2): + - chore: release v0.25.2 ([libp2p/go-libp2p-kad-dht#961](https://github.com/libp2p/go-libp2p-kad-dht/pull/961)) + - add ctx canceled err check ([libp2p/go-libp2p-kad-dht#960](https://github.com/libp2p/go-libp2p-kad-dht/pull/960)) + - chore: release v0.25.1 + - perf: don't buffer the output of FindProvidersAsync + - chore: use go-libp2p-routing-helpers for tracing needs + - fix: properly iterate in tracing for protocol messenger + - fix: apply addrFilters in the dht (#872) ([libp2p/go-libp2p-kad-dht#872](https://github.com/libp2p/go-libp2p-kad-dht/pull/872)) + - Add provider record addresses to peerstore ([libp2p/go-libp2p-kad-dht#870](https://github.com/libp2p/go-libp2p-kad-dht/pull/870)) + - chore: release v0.25.0 + - tracing: add protocol messages client tracing + - Enhance handleNewMessage Server Mode Logging: Convert Error Logs to Debug Level ([libp2p/go-libp2p-kad-dht#860](https://github.com/libp2p/go-libp2p-kad-dht/pull/860)) + - tracing: fix DHT keys as string attribute not being valid utf-8 ([libp2p/go-libp2p-kad-dht#859](https://github.com/libp2p/go-libp2p-kad-dht/pull/859)) + - merge: fix: issues discovered in kubo v0.21.0-rc2 (#853) ([libp2p/go-libp2p-kad-dht#853](https://github.com/libp2p/go-libp2p-kad-dht/pull/853)) + - merge: fix: issues discovered in kubo v0.21.0-rc1 (#851) ([libp2p/go-libp2p-kad-dht#851](https://github.com/libp2p/go-libp2p-kad-dht/pull/851)) + - Release v0.24.0 ([libp2p/go-libp2p-kad-dht#844](https://github.com/libp2p/go-libp2p-kad-dht/pull/844)) + - fix: don't add unresponsive DHT servers to the Routing Table (#820) ([libp2p/go-libp2p-kad-dht#820](https://github.com/libp2p/go-libp2p-kad-dht/pull/820)) + - filter local addresses (for WAN) and localhost addresses (for LAN) ([libp2p/go-libp2p-kad-dht#839](https://github.com/libp2p/go-libp2p-kad-dht/pull/839)) +- github.com/multiformats/go-multiaddr (v0.12.2 -> v0.12.3): + - chore: release v0.12.3 ([multiformats/go-multiaddr#240](https://github.com/multiformats/go-multiaddr/pull/240)) + - chore: Expand comment ForEach ([multiformats/go-multiaddr#238](https://github.com/multiformats/go-multiaddr/pull/238)) + - .Decapsulate by Components ([multiformats/go-multiaddr#239](https://github.com/multiformats/go-multiaddr/pull/239)) +- github.com/whyrusleeping/cbor-gen (v0.0.0-20240109153615-66e95c3e8a87 -> v0.1.0): + - Nullable ints (#93) ([whyrusleeping/cbor-gen#93](https://github.com/whyrusleeping/cbor-gen/pull/93)) + - Introduce Gen{} struct for configurability ([whyrusleeping/cbor-gen#94](https://github.com/whyrusleeping/cbor-gen/pull/94)) + - Transparent encoding ([whyrusleeping/cbor-gen#91](https://github.com/whyrusleeping/cbor-gen/pull/91)) + - turn max length consts into global vars ([whyrusleeping/cbor-gen#92](https://github.com/whyrusleeping/cbor-gen/pull/92)) + +
+ +### 👨‍👩‍👧‍👦 Contributors + +| Contributor | Commits | Lines ± | Files Changed | +|-------------|---------|---------|---------------| +| Henrique Dias | 19 | +867/-2806 | 96 | +| Rod Vagg | 7 | +921/-475 | 25 | +| Marcin Rataj | 8 | +358/-344 | 18 | +| Guillaume Michel - guissou | 1 | +145/-485 | 13 | +| Jorropo | 8 | +429/-136 | 22 | +| Łukasz Magiera | 4 | +284/-48 | 11 | +| whyrusleeping | 1 | +90/-90 | 2 | +| Michael Muré | 2 | +48/-73 | 9 | +| Marco Munizaga | 6 | +86/-29 | 10 | +| guillaumemichel | 3 | +93/-1 | 3 | +| Marten Seemann | 1 | +31/-4 | 4 | +| godeamon | 3 | +11/-8 | 3 | +| shuangcui | 1 | +6/-6 | 5 | +| occupyhabit | 1 | +3/-3 | 3 | +| crazehang | 1 | +2/-2 | 1 | +| Dennis Trautwein | 1 | +1/-2 | 1 | +| “GheisMohammadi” | 1 | +1/-1 | 1 | +| web3-bot | 1 | +2/-0 | 1 | +| Daniel Norman | 1 | +1/-1 | 1 | diff --git a/docs/changelogs/v0.29.md b/docs/changelogs/v0.29.md new file mode 100644 index 000000000..8c45bbfca --- /dev/null +++ b/docs/changelogs/v0.29.md @@ -0,0 +1,241 @@ +# Kubo changelog v0.29 + +- [v0.29.0](#v0290) + +## v0.29.0 + +- [Overview](#overview) +- [🔦 Highlights](#-highlights) + - [Add search functionality for pin names](#add-search-functionality-for-pin-names) + - [Customizing `ipfs add` defaults](#customizing-ipfs-add-defaults) +- [📝 Changelog](#-changelog) +- [👨‍👩‍👧‍👦 Contributors](#-contributors) + +### Overview + +### 🔦 Highlights + +#### Add search functionality for pin names + +It is now possible to search for pins by name via `ipfs pin ls --name "SomeName"`. +The search is case-sensitive and will return all pins that contain the specified substring in their name. + +> [!TIP] +> The `ipfs pin ls -n` is now a shorthand for `ipfs pin ls --name`, mirroring the behavior of `ipfs pin add`. +> See `ipfs pin ls --help` for more information. + +#### Customizing `ipfs add` defaults + +This release supports overriding global data ingestion defaults used by commands like `ipfs add` via user-defined [`Import.*` configuration options](../config.md#import). +The hash function, CID version, or UnixFS raw leaves and chunker behaviors can be set once, and used as the new implicit default for `ipfs add`. + +> [!TIP] +> As a convenience, two CID [profiles](../config.md#profile) are provided: `legacy-cid-v0` and `test-cid-v1`. +> A test profile that defaults to modern CIDv1 can be applied via `ipfs config profile apply test-cid-v1`. +> We encourage users to try it and report any issues in [kubo#4143](https://github.com/ipfs/kubo/issues/4143). + +### 📝 Changelog + +
Full Changelog + +- github.com/ipfs/kubo: + - fix(cli): unify --name param in ls and add (#10439) ([ipfs/kubo#10439](https://github.com/ipfs/kubo/pull/10439)) + - chore: set version to 0.29.0-rc2 + - fix(libp2p): streams config validation in resource manager (#10435) ([ipfs/kubo#10435](https://github.com/ipfs/kubo/pull/10435)) + - chore: update version + - chore: libp2p 0.34.1 (#10429) ([ipfs/kubo#10429](https://github.com/ipfs/kubo/pull/10429)) + - refactor: stop using github.com/pkg/errors (#10431) ([ipfs/kubo#10431](https://github.com/ipfs/kubo/pull/10431)) + - chore: fix --help text + - config: introduce Import section (#10421) ([ipfs/kubo#10421](https://github.com/ipfs/kubo/pull/10421)) + - feat: enables searching pins by name (#10412) ([ipfs/kubo#10412](https://github.com/ipfs/kubo/pull/10412)) + - fix(fuse): ipfs path parsing (#10243) ([ipfs/kubo#10243](https://github.com/ipfs/kubo/pull/10243)) + - core/node: fix divide by zero fatal crash for reprovide rate check (#10411) ([ipfs/kubo#10411](https://github.com/ipfs/kubo/pull/10411)) + - chore: bump to go-ipfs-cmds @ v0.11 + - chore: create next changelog + - Merge Release: v0.28.0 [skip changelog] ([ipfs/kubo#10402](https://github.com/ipfs/kubo/pull/10402)) + - docs: update release checklist (#10401) ([ipfs/kubo#10401](https://github.com/ipfs/kubo/pull/10401)) + - chore: update version +- github.com/ipfs/boxo (v0.19.0 -> v0.20.0): + - Release v0.20.0 ([ipfs/boxo#613](https://github.com/ipfs/boxo/pull/613)) +- github.com/ipfs/go-blockservice (v0.5.0 -> v0.5.2): + - docs: remove contribution section + - chore: bump version + - chore: deprecate types and readme + - chore: release v0.5.1 + - fix: remove busyloop in getBlocks by removing batching +- github.com/ipfs/go-ipfs-blockstore (v1.3.0 -> v1.3.1): + - docs: remove contribution section + - chore: bump version + - chore: deprecate types and readme +- github.com/ipfs/go-ipfs-cmds (v0.10.0 -> v0.11.0): + - chore: release v0.11.0 (#253) ([ipfs/go-ipfs-cmds#253](https://github.com/ipfs/go-ipfs-cmds/pull/253)) + - chore: update deps (#252) ([ipfs/go-ipfs-cmds#252](https://github.com/ipfs/go-ipfs-cmds/pull/252)) + - chore: release 0.10.2 (#251) ([ipfs/go-ipfs-cmds#251](https://github.com/ipfs/go-ipfs-cmds/pull/251)) + - fix(http): return error in case of panic (#250) ([ipfs/go-ipfs-cmds#250](https://github.com/ipfs/go-ipfs-cmds/pull/250)) + - chore: release v0.10.1 +- github.com/ipfs/go-ipfs-ds-help (v1.1.0 -> v1.1.1): + - docs: remove contribution section + - chore: bump version + - chore: deprecate types and readme +- github.com/ipfs/go-ipfs-exchange-interface (v0.2.0 -> v0.2.1): + - chore: bump version + - Deprecate types and readme (#29) ([ipfs/go-ipfs-exchange-interface#29](https://github.com/ipfs/go-ipfs-exchange-interface/pull/29)) + - docs: Add proper documenation to the interface. +- github.com/ipfs/go-verifcid (v0.0.2 -> v0.0.3): + - chore: bump version + - chore: deprecate types and readme + - Make poseidon hashes good hashes ([ipfs/go-verifcid#19](https://github.com/ipfs/go-verifcid/pull/19)) + - sync: update CI config files (#18) ([ipfs/go-verifcid#18](https://github.com/ipfs/go-verifcid/pull/18)) +- github.com/ipld/go-car (v0.5.0 -> v0.6.2): + - v0.6.2 ([ipld/go-car#464](https://github.com/ipld/go-car/pull/464)) + - fix: opt-in way to allow empty list of roots in CAR headers ([ipld/go-car#461](https://github.com/ipld/go-car/pull/461)) + - feat: add inverse and version to filter cmd ([ipld/go-car#457](https://github.com/ipld/go-car/pull/457)) + - v0.6.1 bump + - chore: update usage of merkledag by go-car (#437) ([ipld/go-car#437](https://github.com/ipld/go-car/pull/437)) + - feat(cmd/car): add '--no-wrap' option to 'create' command ([ipld/go-car#432](https://github.com/ipld/go-car/pull/432)) + - fix: remove github.com/ipfs/go-ipfs-blockstore dependency + - feat: expose index for StorageCar + - perf: reduce NewCarReader allocations + - fix(deps): update deps for cmd (use master go-car and go-car/v2 for now) + - fix: new error strings from go-cid + - fix: tests should match stderr for verbose output + - fix: reading from stdin should broadcast EOF to block loaders + - refactor insertion index to be publicly accessible ([ipld/go-car#408](https://github.com/ipld/go-car/pull/408)) + - chore: unmigrate from go-libipfs + - Create CODEOWNERS + - blockstore: give a direct access to the index for read operations + - blockstore: only close the file on error in OpenReadWrite, not OpenReadWriteFile + - fix: handle (and test) WholeCID vs not; fast Has() path for storage + - ReadWrite: faster Has() by using the in-memory index instead of reading on disk + - fix: let `extract` skip missing unixfs shard links + - fix: error when no files extracted + - fix: make -f optional, read from stdin if omitted + - fix: update cmd/car/README with latest description + - chore: add test cases for extract modes + - feat: extract accepts '-' as an output path for stdout + - feat: extract specific path, accept stdin as streaming input + - fix: if we don't read the full block data, don't error on !EOF + - blockstore: try to close during Finalize(), even in case of previous error + - ReadWrite: add an alternative FinalizeReadOnly+Close flow + - feat: add WithTrustedCar() reader option (#381) ([ipld/go-car#381](https://github.com/ipld/go-car/pull/381)) + - blockstore: fast path for AllKeysChan using the index + - fix: switch to crypto/rand.Read + - stop using the deprecated io/ioutil package + - fix(doc): fix storage package doc formatting + - fix: return errors for unsupported operations + - chore: move insertionindex into store pkg + - chore: add experimental note + - fix: minor lint & windows fd test problems + - feat: docs for StorageCar interfaces + - feat: ReadableWritable; dedupe shared code + - feat: add Writable functionality to StorageCar + - feat: StorageCar as a Readable storage, separate from blockstore + - feat(blockstore): implement a streaming read only storage + - feat(cmd): add index create subcommand to create an external carv2 index ([ipld/go-car#350](https://github.com/ipld/go-car/pull/350)) + - chore: bump version to 0.6.0 + - fix: use goreleaser instead + - Allow using WalkOption in WriteCar function ([ipld/go-car#357](https://github.com/ipld/go-car/pull/357)) + - fix: update go-block-format to the version that includes the stubs + - feat: upgrade from go-block-format to go-libipfs/blocks + - cleanup readme a bit to make the cli more discoverable (#353) ([ipld/go-car#353](https://github.com/ipld/go-car/pull/353)) + - Update install instructions in README.md + - Add a debugging form for car files. (#341) ([ipld/go-car#341](https://github.com/ipld/go-car/pull/341)) + - ([ipld/go-car#340](https://github.com/ipld/go-car/pull/340)) + - add a `SkipNext` method on block reader (#338) ([ipld/go-car#338](https://github.com/ipld/go-car/pull/338)) + - feat: Has() and Get() will respect StoreIdentityCIDs option +- github.com/libp2p/go-libp2p (v0.33.2 -> v0.34.1): + - release v0.34.1 (#2811) ([libp2p/go-libp2p#2811](https://github.com/libp2p/go-libp2p/pull/2811)) + - config: fix Insecure security constructor (#2810) ([libp2p/go-libp2p#2810](https://github.com/libp2p/go-libp2p/pull/2810)) + - rcmgr: Backwards compatibility if you wrap default impl (#2805) ([libp2p/go-libp2p#2805](https://github.com/libp2p/go-libp2p/pull/2805)) + - v0.34.0 (#2795) ([libp2p/go-libp2p#2795](https://github.com/libp2p/go-libp2p/pull/2795)) + - swarm: fix addr for TestBlackHoledAddrBlocked (#2803) ([libp2p/go-libp2p#2803](https://github.com/libp2p/go-libp2p/pull/2803)) + - Add backwards compatibility with old well-known resource (#2798) ([libp2p/go-libp2p#2798](https://github.com/libp2p/go-libp2p/pull/2798)) + - rcmgr: remove a connection only once from the limiter (#2800) ([libp2p/go-libp2p#2800](https://github.com/libp2p/go-libp2p/pull/2800)) + - Adhere to request.Context when roundtripping on a stream (#2796) ([libp2p/go-libp2p#2796](https://github.com/libp2p/go-libp2p/pull/2796)) + - fix: Set missing deadlines (#2794) ([libp2p/go-libp2p#2794](https://github.com/libp2p/go-libp2p/pull/2794)) + - rcmgr: Add conn_limiter to limit number of conns per ip cidr (#2788) ([libp2p/go-libp2p#2788](https://github.com/libp2p/go-libp2p/pull/2788)) + - identify: refactor observed address manager to do address mapping at thin waist(IP+TCP/UDP) layer (#2793) ([libp2p/go-libp2p#2793](https://github.com/libp2p/go-libp2p/pull/2793)) + - fix: DNS protocol address is not reserved (#2792) ([libp2p/go-libp2p#2792](https://github.com/libp2p/go-libp2p/pull/2792)) + - Update github.com/quic-go/quic-go dependency (#2780) ([libp2p/go-libp2p#2780](https://github.com/libp2p/go-libp2p/pull/2780)) + - webrtc: add webrtc addresses to host normalizer (#2784) ([libp2p/go-libp2p#2784](https://github.com/libp2p/go-libp2p/pull/2784)) + - Add a "Limited" network connectivity state (#2696) ([libp2p/go-libp2p#2696](https://github.com/libp2p/go-libp2p/pull/2696)) + - basichost: append certhash for webrtc addresses provided via address factory (#2774) ([libp2p/go-libp2p#2774](https://github.com/libp2p/go-libp2p/pull/2774)) + - Fix comment (#2775) ([libp2p/go-libp2p#2775](https://github.com/libp2p/go-libp2p/pull/2775)) + - Update: update incomplete readmes (#2767) ([libp2p/go-libp2p#2767](https://github.com/libp2p/go-libp2p/pull/2767)) + - libp2phttp: Return connection: close when doing http over streams (#2756) ([libp2p/go-libp2p#2756](https://github.com/libp2p/go-libp2p/pull/2756)) + - Identify: emit useful events after identification (#2759) ([libp2p/go-libp2p#2759](https://github.com/libp2p/go-libp2p/pull/2759)) + - Update chat with rendezvous example (#2769) ([libp2p/go-libp2p#2769](https://github.com/libp2p/go-libp2p/pull/2769)) + - Rename well-known resource (#2757) ([libp2p/go-libp2p#2757](https://github.com/libp2p/go-libp2p/pull/2757)) + - quic: make server cmd use RFC 9000 instead of draft-29 (#2753) ([libp2p/go-libp2p#2753](https://github.com/libp2p/go-libp2p/pull/2753)) + - autonat: Clean up after close (#2749) ([libp2p/go-libp2p#2749](https://github.com/libp2p/go-libp2p/pull/2749)) + - webrtc: run onDone callback immediately on close (#2729) ([libp2p/go-libp2p#2729](https://github.com/libp2p/go-libp2p/pull/2729)) + - fix: add NullResourceManager to webrtc, fixes panic (#2752) ([libp2p/go-libp2p#2752](https://github.com/libp2p/go-libp2p/pull/2752)) + - feat: add tls KeyLogWriter option (#2750) ([libp2p/go-libp2p#2750](https://github.com/libp2p/go-libp2p/pull/2750)) + - Use any port, not a specific one for examples (#2748) ([libp2p/go-libp2p#2748](https://github.com/libp2p/go-libp2p/pull/2748)) + - quicreuse: remove workaround for quic-go listener close deadlock (#2746) ([libp2p/go-libp2p#2746](https://github.com/libp2p/go-libp2p/pull/2746)) + - use Fx to start and stop the host, swarm, autorelay and quicreuse (#2118) ([libp2p/go-libp2p#2118](https://github.com/libp2p/go-libp2p/pull/2118)) + - webrtc: set sctp receive buffer size to 100kB (#2745) ([libp2p/go-libp2p#2745](https://github.com/libp2p/go-libp2p/pull/2745)) + - basichost: log more info when protocol selection fails (#2734) ([libp2p/go-libp2p#2734](https://github.com/libp2p/go-libp2p/pull/2734)) + - chore: bump quic-go (#2742) ([libp2p/go-libp2p#2742](https://github.com/libp2p/go-libp2p/pull/2742)) + - security: remove unnecessary noise code (#2738) ([libp2p/go-libp2p#2738](https://github.com/libp2p/go-libp2p/pull/2738)) + - webrtc: increase receive buffer size on listener (#2730) ([libp2p/go-libp2p#2730](https://github.com/libp2p/go-libp2p/pull/2730)) + - webrtc: fix bug with logger wrapper (#2727) ([libp2p/go-libp2p#2727](https://github.com/libp2p/go-libp2p/pull/2727)) + - dcutr: fix log format to actually print error (#2725) ([libp2p/go-libp2p#2725](https://github.com/libp2p/go-libp2p/pull/2725)) + - webrtc: use a common logger for all pion logging (#2718) ([libp2p/go-libp2p#2718](https://github.com/libp2p/go-libp2p/pull/2718)) + - chore: remove unreadable code, move a test function to test code, better locking in webrtc control reader + - ping: use context.Afterfunc to avoid a lingering goroutine (#2723) ([libp2p/go-libp2p#2723](https://github.com/libp2p/go-libp2p/pull/2723)) + - webrtc: close mux when closing listener (#2717) ([libp2p/go-libp2p#2717](https://github.com/libp2p/go-libp2p/pull/2717)) + - webrtc: setup datachannel handlers before connecting to a peer (#2716) ([libp2p/go-libp2p#2716](https://github.com/libp2p/go-libp2p/pull/2716)) +- github.com/libp2p/go-libp2p-pubsub (v0.10.0 -> v0.11.0): + - Fix: Own our CertifiedAddrBook (#555) ([libp2p/go-libp2p-pubsub#555](https://github.com/libp2p/go-libp2p-pubsub/pull/555)) + - chores: bump go-libp2p (#558) ([libp2p/go-libp2p-pubsub#558](https://github.com/libp2p/go-libp2p-pubsub/pull/558)) + - fix: Don't bother parsing an empty slice (#556) ([libp2p/go-libp2p-pubsub#556](https://github.com/libp2p/go-libp2p-pubsub/pull/556)) + - Replace fragmentRPC with appendOrMergeRPC (#557) ([libp2p/go-libp2p-pubsub#557](https://github.com/libp2p/go-libp2p-pubsub/pull/557)) +- github.com/multiformats/go-multiaddr (v0.12.3 -> v0.12.4): + - Release v0.12.4 ([multiformats/go-multiaddr#245](https://github.com/multiformats/go-multiaddr/pull/245)) + - net: restrict unicast ip6 public address space (#235) ([multiformats/go-multiaddr#235](https://github.com/multiformats/go-multiaddr/pull/235)) +- github.com/whyrusleeping/cbor-gen (v0.1.0 -> v0.1.1): + - fix: reduce memory held by deferred objects (#96) ([whyrusleeping/cbor-gen#96](https://github.com/whyrusleeping/cbor-gen/pull/96)) + +
+ +### 👨‍👩‍👧‍👦 Contributors + +| Contributor | Commits | Lines ± | Files Changed | +|-------------|---------|---------|---------------| +| Henrique Dias | 33 | +4994/-579 | 115 | +| Rod Vagg | 29 | +3781/-1367 | 90 | +| sukun | 12 | +2026/-1215 | 39 | +| Marco Munizaga | 18 | +1482/-382 | 47 | +| Will | 5 | +769/-213 | 17 | +| Steven Allen | 5 | +540/-115 | 24 | +| Sukun | 4 | +274/-194 | 11 | +| Michael Muré | 7 | +372/-55 | 16 | +| Marten Seemann | 1 | +243/-141 | 10 | +| Marcin Rataj | 7 | +244/-134 | 13 | +| hannahhoward | 1 | +277/-0 | 2 | +| Will Scott | 5 | +54/-38 | 9 | +| Hector Sanjuan | 3 | +68/-20 | 5 | +| Jorropo | 5 | +34/-47 | 15 | +| Andrew Gillis | 2 | +67/-7 | 3 | +| IGP | 1 | +59/-8 | 5 | +| Adin Schmahmann | 2 | +50/-0 | 3 | +| Laurent Senta | 1 | +40/-4 | 2 | +| Brad Fitzpatrick | 1 | +42/-2 | 2 | +| Fabio Bozzo | 1 | +36/-1 | 3 | +| Yolan Romailler | 1 | +15/-19 | 4 | +| Hlib Kanunnikov | 2 | +14/-14 | 6 | +| Andreas Penzkofer | 1 | +22/-2 | 3 | +| Matthias Fasching | 1 | +8/-10 | 1 | +| gopherfarm | 2 | +16/-1 | 2 | +| Dreamacro | 1 | +1/-10 | 1 | +| web3-bot | 2 | +7/-3 | 4 | +| Rafał Leszko | 1 | +4/-4 | 1 | +| Oleg Kovalov | 1 | +4/-4 | 3 | +| dbeal | 1 | +5/-1 | 1 | +| Antonio Navarro Perez | 1 | +4/-1 | 1 | +| dozyio | 1 | +3/-0 | 1 | +| zhiqiangxu | 1 | +1/-1 | 1 | +| the harder the luckier | 1 | +1/-1 | 1 | +| Lukáš Lukáč | 1 | +1/-1 | 1 | +| Steve Loeppky | 1 | +1/-0 | 1 | diff --git a/docs/changelogs/v0.3.md b/docs/changelogs/v0.3.md index 5d37a1b52..61c2f431e 100644 --- a/docs/changelogs/v0.3.md +++ b/docs/changelogs/v0.3.md @@ -46,7 +46,7 @@ test coverage. ## v0.3.10 - 2015-12-07 This patch update introduces the 'ipfs update' command which will be used for -future ipfs updates along with a few other bugfixes and documentation +future ipfs updates along with a few other bug fixes and documentation improvements. @@ -156,7 +156,7 @@ NOTICE: Version 0.3.8 also requires golang version 1.5.1 or higher. * use go1.5's built in trailers, no more failures (@whyrusleeping) * fix random bitswap hangs (@whyrusleeping) * rate limit fd usage (@whyrusleeping) - * fix panic in bitswap ratelimiting (@whyrusleeping) + * fix panic in bitswap rate limiting (@whyrusleeping) * Tool Changes * --empty-repo option for init (@prusnak) @@ -264,7 +264,7 @@ fixes (yet again) windows builds. * remove deadcode @lgierth @whyrusleeping * reduced number of logging libs to 2 (soon to be 1) @rht * dial address filtering @whyrusleeping - * prometheus metrics @lgierth + * Prometheus metrics @lgierth * new index page for gateway @krl @cryptix * move ping to separate protocol @whyrusleeping * add events to bitswap for a dashboard @whyrusleeping @@ -340,9 +340,9 @@ This patch improves overall stability and performance This patch update fixes various issues, in particular: - windows support (0.3.0 had broken it) -- commandline parses spaces correctly. +- command line parses spaces correctly. -* much improved commandline parsing by @AtnNn +* much improved command line parsing by @AtnNn * improved dockerfile by @luzifer * add cmd cleanup by @wking * fix flatfs windows support by @tv42 and @gatesvp @@ -354,7 +354,7 @@ This patch update fixes various issues, in particular: ## v0.3.2 - 2015-04-22 -This patch update implements multicast dns as well as fxing a few test issues. +This patch update implements multicast dns as well as fixing a few test issues. * implement mdns peer discovery @whyrusleeping * fix mounting issues in sharness tests @chriscool @@ -404,6 +404,6 @@ What to expect: * other * bash completion is now available - * ipfs stats bw -- bandwidth meetering + * ipfs stats bw -- bandwidth metering And many more things. diff --git a/docs/changelogs/v0.30.md b/docs/changelogs/v0.30.md new file mode 100644 index 000000000..36c3a5c75 --- /dev/null +++ b/docs/changelogs/v0.30.md @@ -0,0 +1,341 @@ +# Kubo changelog v0.30 + +- [v0.30.0](#v0300) + +## v0.30.0 + +- [Overview](#overview) +- [🔦 Highlights](#-highlights) + - [Improved P2P connectivity](#improved-p2p-connectivity) + - [Refactored Bitswap and dag-pb chunker](#refactored-bitswap-and-dag-pb-chunker) + - [WebRTC-Direct Transport enabled by default](#webrtc-direct-transport-enabled-by-default) + - [UnixFS 1.5: Mode and Modification Time Support](#unixfs-15-mode-and-modification-time-support) + - [AutoNAT V2 Service Introduced Alongside V1](#autonat-v2-service-introduced-alongside-v1) + - [Automated `ipfs version check`](#automated-ipfs-version-check) + - [Version Suffix Configuration](#version-suffix-configuration) + - [`/unix/` socket support in `Addresses.API`](#unix-socket-support-in-addressesapi) + - [Cleaned Up `ipfs daemon` Startup Log](#cleaned-up-ipfs-daemon-startup-log) + - [Commands Preserve Specified Hostname](#commands-preserve-specified-hostname) +- [📝 Changelog](#-changelog) +- [👨‍👩‍👧‍👦 Contributors](#-contributors) + +### Overview + +### 🔦 Highlights + +This release took longer and is more packed with fixes and features than usual. + +> [!IMPORTANT] +> TLDR: update, it contains many, many fixes. + +#### Improved P2P connectivity + +This release comes with significant go-libp2p update from v0.34.1 to v0.36.3 ([release notes](https://github.com/libp2p/go-libp2p/releases/)). + +It includes multiple fixes to key protocols: [QUIC](https://github.com/libp2p/specs/tree/master/quic)/[Webtransport](https://github.com/libp2p/specs/tree/master/webtransport)/[WebRTC](https://github.com/libp2p/specs/tree/master/webrtc), Connection Upgrades through Relay ([DCUtR](https://github.com/libp2p/specs/blob/master/relay/DCUtR.md)), and [Secure WebSockets](https://github.com/libp2p/specs/pull/624). + +Also, peers that are behind certain types of NAT will now be more reachable. For this alone, Kubo users are highly encouraged to upgrade. + +#### Refactored Bitswap and dag-pb chunker + +Some workloads may experience improved memory profile thanks to optimizations from Boxo SDK [v0.23.0](https://github.com/ipfs/boxo/releases/tag/v0.23.0). + +> [!IMPORTANT] +> Storage providers should upgrade to take advantage of the Bitswap server fix, which resolves the issue of greedy peers depleting available wantlist slots for their PeerID, resulting in stalled downloads. + +#### WebRTC-Direct Transport enabled by default + +Kubo now ships with [WebRTC Direct](https://github.com/libp2p/specs/blob/master/webrtc/webrtc-direct.md) listener enabled by default: `/udp/4001/webrtc-direct`. + +WebRTC Direct complements existing `/wss` (Secure WebSockets) and `/webtransport` transports. Unlike `/wss`, which requires a domain name and a CA-issued TLS certificate, WebRTC Direct works with IPs and can be enabled by default on all Kubo nodes. + +Learn more: [`Swarm.Transports.Network.WebRTCDirect`](https://github.com/ipfs/kubo/blob/master/docs/config.md#swarmtransportsnetworkwebrtcdirect) + +> [!NOTE] +> Kubo 0.30 includes a migration for existing users that adds `/webrtc-direct` listener on the same UDP port as `/udp/{port}/quic-v1`. This supports the WebRTC-Direct rollout by reusing preexisting UDP firewall settings and port mappings created for QUIC. + +#### UnixFS 1.5: Mode and Modification Time Support + +Kubo now allows users to opt-in to store mode and modification time for files, directories, and symbolic links. +By default, if you do not opt-in, the old behavior remains unchanged, and the same CIDs will be generated as before. + +The `ipfs add` CLI options `--preserve-mode` and `--preserve-mtime` can be used to store the original mode and last modified time of the file being added, and `ipfs files stat /ipfs/CID` can be used for inspecting these optional attributes: + +```console +$ touch ./file +$ chmod 654 ./file +$ ipfs add --preserve-mode --preserve-mtime -Q ./file +QmczQr4XS1rRnWVopyg5Chr9EQ7JKpbhgnrjpb5kTQ1DKQ + +$ ipfs files stat /ipfs/QmczQr4XS1rRnWVopyg5Chr9EQ7JKpbhgnrjpb5kTQ1DKQ +QmczQr4XS1rRnWVopyg5Chr9EQ7JKpbhgnrjpb5kTQ1DKQ +Size: 0 +CumulativeSize: 22 +ChildBlocks: 0 +Type: file +Mode: -rw-r-xr-- (0654) +Mtime: 13 Aug 2024, 21:15:31 UTC +``` + +The CLI and HTTP RPC options `--mode`, `--mtime` and `--mtime-nsecs` can be used to set them to arbitrary values. + +Opt-in support for `mode` and `mtime` was also added to MFS (`ipfs files --help`). For more information see `--help` text of `ipfs files touch|stat|chmod` commands. + +Modification time support was also added to the Gateway. If present, value from file's dag-pb is returned in `Last-Modified` HTTP header and requests made with `If-Modified-Since` can produce HTTP 304 not modified response. + +> [!NOTE] +> Storing `mode` and `mtime` requires root block to be `dag-pb` and disabled `raw-leaves` setting to create envelope for storing the metadata. + +#### AutoNAT V2 Service Introduced Alongside V1 + +The AutoNAT service enables nodes to determine their public reachability on the internet. [AutoNAT V2](https://github.com/libp2p/specs/pull/538) enhances this protocol with improved features. In this release, Kubo will offer both V1 and V2 services to other peers, although it will continue to use only V1 when acting as a client. Future releases will phase out V1, transitioning clients to utilize V2 exclusively. + +For more details, see the [Deployment Plan for AutoNAT V2](https://github.com/ipfs/kubo/issues/10091) and [`AutoNAT`](https://github.com/ipfs/kubo/blob/master/docs/config.md#autonat) configuration options. + +#### Automated `ipfs version check` + +Kubo now performs privacy-preserving version checks using the [libp2p identify protocol](https://github.com/libp2p/specs/blob/master/identify/README.md) on peers detected by the Amino DHT client. +If more than 5% of Kubo peers seen by your node are running a newer version, you will receive a log message notification. + +- For manual checks, refer to `ipfs version check --help` for details. +- To disable automated checks, set [`Version.SwarmCheckEnabled`](https://github.com/ipfs/kubo/blob/master/docs/config.md#versionswarmcheckenabled) to `false`. + +#### Version Suffix Configuration + +Defining the optional agent version suffix is now simpler. The [`Version.AgentSuffix`](https://github.com/ipfs/kubo/blob/master/docs/config.md#agentsuffix) value from the Kubo config takes precedence over any value provided via `ipfs daemon --agent-version-suffix` (which is still supported). + +> [!NOTE] +> Setting a custom version suffix helps with ecosystem analysis, such as Amino DHT reports published at https://stats.ipfs.network + +#### `/unix/` socket support in `Addresses.API` + +This release fixes a bug which blocked users from using Unix domain sockets for [Kubo's RPC](https://docs.ipfs.tech/reference/kubo/rpc/) (instead of a local HTTP port). + +```console +$ ipfs config Addresses.API "/unix/tmp/kubo.socket" +$ ipfs daemon # start with rpc socket +... +RPC API server listening on /unix/tmp/kubo.socket + +$ # cli client, in different terminal can find socket via /api file +$ cat $IPFS_PATH/api +/unix/tmp/kubo.socket + +$ # or have it pased via --api +$ ipfs --api=/unix/tmp/kubo.socket id +``` + +#### Cleaned Up `ipfs daemon` Startup Log + +The `ipfs daemon` startup output has been streamlined to enhance clarity and usability: + +```console +$ ipfs daemon +Initializing daemon... +Kubo version: 0.30.0 +Repo version: 16 +System version: amd64/linux +Golang version: go1.22.5 +PeerID: 12D3KooWQ73s1CQsm4jWwQvdCAtc5w8LatyQt7QLQARk5xdhK9CE +Swarm listening on 127.0.0.1:4001 (TCP+UDP) +Swarm listening on 192.0.2.10:4001 (TCP+UDP) +Swarm listening on [::1]:4001 (TCP+UDP) +Swarm listening on [2001:0db8::10]:4001 (TCP+UDP) +Run 'ipfs id' to inspect announced and discovered multiaddrs of this node. +RPC API server listening on /ip4/127.0.0.1/tcp/5001 +WebUI: http://127.0.0.1:5001/webui +Gateway server listening on /ip4/127.0.0.1/tcp/8080 +Daemon is ready +``` + +The previous lengthy listing of all listener and announced multiaddrs has been removed due to its complexity, especially with modern libp2p nodes sharing multiple transports and long lists of `/webtransport` and `/webrtc-direct` certhashes. +The output now features a simplified list of swarm listeners, displayed in the format `host:port (TCP+UDP)`, which provides essential information for debugging connectivity issues, particularly related to port forwarding. +Announced libp2p addresses are no longer printed on startup, because libp2p may change or augument them based on AutoNAT, relay, and UPnP state. Instead, users are prompted to run `ipfs id` to obtain up-to-date list of listeners and announced multiaddrs in libp2p format. + +#### Commands Preserve Specified Hostname + +When executing a [CLI command](https://docs.ipfs.tech/reference/kubo/cli/) over [Kubo RPC API](https://docs.ipfs.tech/reference/kubo/rpc/), if a hostname is specified by `--api=/dns4//` the resulting HTTP request now contains the hostname, instead of the the IP address that the hostname resolved to, as was the previous behavior. This makes it easier for those trying to run Kubo behind a reverse proxy using hostname-based rules. + +#### Commands Preserve Specified Hostname + +When executing a [CLI command](https://docs.ipfs.tech/reference/kubo/cli/) over [Kubo RPC API](https://docs.ipfs.tech/reference/kubo/rpc/), if a hostname is specified by `--api=/dns4//` the resulting HTTP request now contains the hostname, instead of the the IP address that the hostname resolved to, as was the previous behavior. This makes it easier for those trying to run Kubo behind a reverse proxy using hostname-based rules. + +### 📝 Changelog + +
Full Changelog + +- github.com/ipfs/kubo: + - chore: set version to 0.30.0 + - chore: bump CurrentVersionNumber + - chore: boxo v0.23.0 and go-libp2p v0.36.3 (#10507) ([ipfs/kubo#10507](https://github.com/ipfs/kubo/pull/10507)) + - fix: switch back to go 1.22 (#10502) ([ipfs/kubo#10502](https://github.com/ipfs/kubo/pull/10502)) + - chore: update go-unixfsnode, cmds, and boxo (#10494) ([ipfs/kubo#10494](https://github.com/ipfs/kubo/pull/10494)) + - fix(cli): preserve hostname specified with --api in http request headers (#10497) ([ipfs/kubo#10497](https://github.com/ipfs/kubo/pull/10497)) + - chore: upgrade to go 1.23 (#10486) ([ipfs/kubo#10486](https://github.com/ipfs/kubo/pull/10486)) + - fix: error during config when running benchmarks (#10495) ([ipfs/kubo#10495](https://github.com/ipfs/kubo/pull/10495)) + - chore: update version to rc-2 + - chore: update version + - chore: fix function name (#10481) ([ipfs/kubo#10481](https://github.com/ipfs/kubo/pull/10481)) + - feat: Support storing UnixFS 1.5 Mode and ModTime (#10478) ([ipfs/kubo#10478](https://github.com/ipfs/kubo/pull/10478)) + - fix(rpc): cross-platform support for /unix/ socket maddrs in Addresses.API ([ipfs/kubo#10019](https://github.com/ipfs/kubo/pull/10019)) + - chore(daemon): sort listeners (#10480) ([ipfs/kubo#10480](https://github.com/ipfs/kubo/pull/10480)) + - feat(daemon): improve stdout on startup (#10472) ([ipfs/kubo#10472](https://github.com/ipfs/kubo/pull/10472)) + - fix(daemon): panic in kubo/daemon.go:595 (#10473) ([ipfs/kubo#10473](https://github.com/ipfs/kubo/pull/10473)) + - feat: webui v4.3.0 (#10477) ([ipfs/kubo#10477](https://github.com/ipfs/kubo/pull/10477)) + - docs(readme): add Gentoo Linux (#10474) ([ipfs/kubo#10474](https://github.com/ipfs/kubo/pull/10474)) + - libp2p: default to prefering TLS ([ipfs/kubo#10227](https://github.com/ipfs/kubo/pull/10227)) + - docs: document unofficial Ubuntu PPA ([ipfs/kubo#10467](https://github.com/ipfs/kubo/pull/10467)) + - feat: run AutoNAT V2 service in addition to V1 (#10468) ([ipfs/kubo#10468](https://github.com/ipfs/kubo/pull/10468)) + - feat: go-libp2p 0.36 and /webrtc-direct listener (#10463) ([ipfs/kubo#10463](https://github.com/ipfs/kubo/pull/10463)) + - chore: update dependencies (#10462)(#10466) ([ipfs/kubo#10466](https://github.com/ipfs/kubo/pull/10466)) + - feat: periodic version check and json config (#10438) ([ipfs/kubo#10438](https://github.com/ipfs/kubo/pull/10438)) + - docs: clarify pnet limitations + - docs: "error mounting: could not resolve name" (#10449) ([ipfs/kubo#10449](https://github.com/ipfs/kubo/pull/10449)) + - docs: update ipfs-swarm-key-gen example (#10453) ([ipfs/kubo#10453](https://github.com/ipfs/kubo/pull/10453)) + - chore: update deps incl. boxo v0.21.0 (#10444) ([ipfs/kubo#10444](https://github.com/ipfs/kubo/pull/10444)) + - chore: go-libp2p 0.35.1 (#10430) ([ipfs/kubo#10430](https://github.com/ipfs/kubo/pull/10430)) + - docsa: update RELEASE_CHECKLIST.md + - chore: create next changelog (#10443) ([ipfs/kubo#10443](https://github.com/ipfs/kubo/pull/10443)) + - Merge Release: v0.29.0 [skip changelog] ([ipfs/kubo#10442](https://github.com/ipfs/kubo/pull/10442)) + - fix(cli): unify --name param in ls and add (#10439) ([ipfs/kubo#10439](https://github.com/ipfs/kubo/pull/10439)) + - fix(libp2p): streams config validation in resource manager (#10435) ([ipfs/kubo#10435](https://github.com/ipfs/kubo/pull/10435)) + - chore: fix some typos (#10396) ([ipfs/kubo#10396](https://github.com/ipfs/kubo/pull/10396)) + - chore: update version +- github.com/ipfs/boxo (v0.20.0 -> v0.23.0): + - Release v0.23.0 ([ipfs/boxo#669](https://github.com/ipfs/boxo/pull/669)) + - docs(changelog): move entry to correct release + - Release v0.22.0 ([ipfs/boxo#654](https://github.com/ipfs/boxo/pull/654)) + - Release v0.21.0 ([ipfs/boxo#622](https://github.com/ipfs/boxo/pull/622)) +- github.com/ipfs/go-ipfs-cmds (v0.11.0 -> v0.13.0): + - chore: release v0.13.0 (#261) ([ipfs/go-ipfs-cmds#261](https://github.com/ipfs/go-ipfs-cmds/pull/261)) + - chore: release v0.12.0 (#259) ([ipfs/go-ipfs-cmds#259](https://github.com/ipfs/go-ipfs-cmds/pull/259)) +- github.com/ipfs/go-unixfsnode (v1.9.0 -> v1.9.1): + - Update release version ([ipfs/go-unixfsnode#76](https://github.com/ipfs/go-unixfsnode/pull/76)) + - chore: update dependencies ([ipfs/go-unixfsnode#75](https://github.com/ipfs/go-unixfsnode/pull/75)) +- github.com/libp2p/go-libp2p (v0.34.1 -> v0.36.3): + - Release v0.36.3 + - Fix: WebSocket: Clone TLS config before creating a new listener + - fix: enable dctur when interface address is public (#2931) ([libp2p/go-libp2p#2931](https://github.com/libp2p/go-libp2p/pull/2931)) + - fix: QUIC/Webtransport Transports now will prefer their owned listeners for dialing out (#2936) ([libp2p/go-libp2p#2936](https://github.com/libp2p/go-libp2p/pull/2936)) + - ci: uci/update-go (#2937) ([libp2p/go-libp2p#2937](https://github.com/libp2p/go-libp2p/pull/2937)) + - fix: slice append value (#2938) ([libp2p/go-libp2p#2938](https://github.com/libp2p/go-libp2p/pull/2938)) + - webrtc: wait for listener context before dropping connection (#2932) ([libp2p/go-libp2p#2932](https://github.com/libp2p/go-libp2p/pull/2932)) + - ci: use go1.23, drop go1.21 (#2933) ([libp2p/go-libp2p#2933](https://github.com/libp2p/go-libp2p/pull/2933)) + - Fail on any test timeout (#2929) ([libp2p/go-libp2p#2929](https://github.com/libp2p/go-libp2p/pull/2929)) + - test: Try to fix test timeout (#2930) ([libp2p/go-libp2p#2930](https://github.com/libp2p/go-libp2p/pull/2930)) + - ci: Out of the tarpit (#2923) ([libp2p/go-libp2p#2923](https://github.com/libp2p/go-libp2p/pull/2923)) + - Fix proto import paths (#2920) ([libp2p/go-libp2p#2920](https://github.com/libp2p/go-libp2p/pull/2920)) + - Release v0.36.2 + - webrtc: reduce loglevel for pion logs (#2915) ([libp2p/go-libp2p#2915](https://github.com/libp2p/go-libp2p/pull/2915)) + - webrtc: close connection when remote closes (#2914) ([libp2p/go-libp2p#2914](https://github.com/libp2p/go-libp2p/pull/2914)) + - basic_host: close swarm on Close (#2916) ([libp2p/go-libp2p#2916](https://github.com/libp2p/go-libp2p/pull/2916)) + - Revert "Create funding.json" (#2919) ([libp2p/go-libp2p#2919](https://github.com/libp2p/go-libp2p/pull/2919)) + - Create funding.json + - Release v0.36.1 + - Release v0.36.0 (#2905) ([libp2p/go-libp2p#2905](https://github.com/libp2p/go-libp2p/pull/2905)) + - swarm: add a default timeout to conn.NewStream (#2907) ([libp2p/go-libp2p#2907](https://github.com/libp2p/go-libp2p/pull/2907)) + - udpmux: Don't log an error if canceled because of shutdown (#2903) ([libp2p/go-libp2p#2903](https://github.com/libp2p/go-libp2p/pull/2903)) + - ObsAddrManager: Infer external addresses for transports that share the same listening address. (#2892) ([libp2p/go-libp2p#2892](https://github.com/libp2p/go-libp2p/pull/2892)) + - feat: WebRTC reuse QUIC conn (#2889) ([libp2p/go-libp2p#2889](https://github.com/libp2p/go-libp2p/pull/2889)) + - examples/chat-with-mdns: default to a random port (#2896) ([libp2p/go-libp2p#2896](https://github.com/libp2p/go-libp2p/pull/2896)) + - allow findpeers limit to be 0 (#2894) ([libp2p/go-libp2p#2894](https://github.com/libp2p/go-libp2p/pull/2894)) + - quic: add support for quic-go metrics (#2823) ([libp2p/go-libp2p#2823](https://github.com/libp2p/go-libp2p/pull/2823)) + - webrtc: remove experimental tag, enable by default (#2887) ([libp2p/go-libp2p#2887](https://github.com/libp2p/go-libp2p/pull/2887)) + - config: fix AddrFactory for AutoNAT (#2868) ([libp2p/go-libp2p#2868](https://github.com/libp2p/go-libp2p/pull/2868)) + - chore: /quic → /quic-v1 (#2888) ([libp2p/go-libp2p#2888](https://github.com/libp2p/go-libp2p/pull/2888)) + - basichost: reset stream if SetProtocol fails (#2875) ([libp2p/go-libp2p#2875](https://github.com/libp2p/go-libp2p/pull/2875)) + - feat: libp2phttp `/http-path` (#2850) ([libp2p/go-libp2p#2850](https://github.com/libp2p/go-libp2p/pull/2850)) + - readme: update per latest multiversx rename (#2874) ([libp2p/go-libp2p#2874](https://github.com/libp2p/go-libp2p/pull/2874)) + - websocket: don't return transport.ErrListenerClosed on closing listener (#2867) ([libp2p/go-libp2p#2867](https://github.com/libp2p/go-libp2p/pull/2867)) + - Added tau to README.md (#2870) ([libp2p/go-libp2p#2870](https://github.com/libp2p/go-libp2p/pull/2870)) + - basichost: reset new stream if rcmgr blocks (#2869) ([libp2p/go-libp2p#2869](https://github.com/libp2p/go-libp2p/pull/2869)) + - transport integration tests: test conn attempt is dropped when the rcmgr blocks for WebRTC (#2856) ([libp2p/go-libp2p#2856](https://github.com/libp2p/go-libp2p/pull/2856)) + - webtransport: close underlying h3 connection (#2862) ([libp2p/go-libp2p#2862](https://github.com/libp2p/go-libp2p/pull/2862)) + - peerstore: don't intern protocols (#2860) ([libp2p/go-libp2p#2860](https://github.com/libp2p/go-libp2p/pull/2860)) + - autonatv2: add server metrics for dial requests (#2848) ([libp2p/go-libp2p#2848](https://github.com/libp2p/go-libp2p/pull/2848)) + - PR Comments + - Add a transport level test to ensure we close conns after rejecting them by the rcmgr + - Close quic conns when wrapping conn fails + - libp2p: use rcmgr for autonat dials (#2842) ([libp2p/go-libp2p#2842](https://github.com/libp2p/go-libp2p/pull/2842)) + - metricshelper: improve checks for ip and transport (#2849) ([libp2p/go-libp2p#2849](https://github.com/libp2p/go-libp2p/pull/2849)) + - Don't reuse the URL, make a new one + - Use default transport to make using the Host cheaper + - cleanup + - Add future test + - HTTP Host implements RoundTripper + - swarm: improve dial worker performance for common case + - pstoremanager: fix connectedness check + - autonatv2: implement autonatv2 spec (#2469) ([libp2p/go-libp2p#2469](https://github.com/libp2p/go-libp2p/pull/2469)) + - webrtc: add a test for establishing many connections (#2801) ([libp2p/go-libp2p#2801](https://github.com/libp2p/go-libp2p/pull/2801)) + - webrtc: fix ufrag prefix for dialing (#2832) ([libp2p/go-libp2p#2832](https://github.com/libp2p/go-libp2p/pull/2832)) + - circuitv2: improve voucher validation (#2826) ([libp2p/go-libp2p#2826](https://github.com/libp2p/go-libp2p/pull/2826)) + - libp2phttp: workaround for ResponseWriter's CloseNotifier (#2821) ([libp2p/go-libp2p#2821](https://github.com/libp2p/go-libp2p/pull/2821)) + - Update README.md (#2830) ([libp2p/go-libp2p#2830](https://github.com/libp2p/go-libp2p/pull/2830)) + - identify: add test for observed address handling (#2828) ([libp2p/go-libp2p#2828](https://github.com/libp2p/go-libp2p/pull/2828)) + - identify: fix bug in observed address handling (#2825) ([libp2p/go-libp2p#2825](https://github.com/libp2p/go-libp2p/pull/2825)) + - identify: Don't filter addr if remote is neither public nor private (#2820) ([libp2p/go-libp2p#2820](https://github.com/libp2p/go-libp2p/pull/2820)) + - limit ping duration to 30s (#1358) ([libp2p/go-libp2p#1358](https://github.com/libp2p/go-libp2p/pull/1358)) + - Remove out-dated code in example readme (#2818) ([libp2p/go-libp2p#2818](https://github.com/libp2p/go-libp2p/pull/2818)) + - v0.35.0 (#2812) ([libp2p/go-libp2p#2812](https://github.com/libp2p/go-libp2p/pull/2812)) + - rcmgr: Support specific network prefix in conn limiter (#2807) ([libp2p/go-libp2p#2807](https://github.com/libp2p/go-libp2p/pull/2807)) +- github.com/libp2p/go-libp2p-kad-dht (v0.25.2 -> v0.26.1): + - Release v0.26.1 ([libp2p/go-libp2p-kad-dht#983](https://github.com/libp2p/go-libp2p-kad-dht/pull/983)) + - fix: Unexport hasValidConnectedness to make a patch release ([libp2p/go-libp2p-kad-dht#982](https://github.com/libp2p/go-libp2p-kad-dht/pull/982)) + - correctly merging fix from https://github.com/libp2p/go-libp2p-kad-dht/pull/976 ([libp2p/go-libp2p-kad-dht#980](https://github.com/libp2p/go-libp2p-kad-dht/pull/980)) + - Release v0.26.0 ([libp2p/go-libp2p-kad-dht#979](https://github.com/libp2p/go-libp2p-kad-dht/pull/979)) + - chore: update deps ([libp2p/go-libp2p-kad-dht#974](https://github.com/libp2p/go-libp2p-kad-dht/pull/974)) + - Upgrade to go-log v2.5.1 ([libp2p/go-libp2p-kad-dht#971](https://github.com/libp2p/go-libp2p-kad-dht/pull/971)) + - Fix: don't perform lookupCheck if not enough peers in routing table ([libp2p/go-libp2p-kad-dht#970](https://github.com/libp2p/go-libp2p-kad-dht/pull/970)) + - findnode(self) should return multiple peers ([libp2p/go-libp2p-kad-dht#968](https://github.com/libp2p/go-libp2p-kad-dht/pull/968)) +- github.com/libp2p/go-libp2p-routing-helpers (v0.7.3 -> v0.7.4): + - chore: release v0.7.4 (#85) ([libp2p/go-libp2p-routing-helpers#85](https://github.com/libp2p/go-libp2p-routing-helpers/pull/85)) + - fix: composable parallel router tracing by index (#84) ([libp2p/go-libp2p-routing-helpers#84](https://github.com/libp2p/go-libp2p-routing-helpers/pull/84)) +- github.com/multiformats/go-multiaddr (v0.12.4 -> v0.13.0): + - Release v0.13.0 ([multiformats/go-multiaddr#248](https://github.com/multiformats/go-multiaddr/pull/248)) + - Add support for http-path ([multiformats/go-multiaddr#246](https://github.com/multiformats/go-multiaddr/pull/246)) +- github.com/whyrusleeping/cbor-gen (v0.1.1 -> v0.1.2): + - properly extend strings (#95) ([whyrusleeping/cbor-gen#95](https://github.com/whyrusleeping/cbor-gen/pull/95)) + - ioutil to io (#98) ([whyrusleeping/cbor-gen#98](https://github.com/whyrusleeping/cbor-gen/pull/98)) + +
+ +### 👨‍👩‍👧‍👦 Contributors + +| Contributor | Commits | Lines ± | Files Changed | +|-------------|---------|---------|---------------| +| Andrew Gillis | 14 | +4920/-1714 | 145 | +| sukun | 26 | +4402/-448 | 79 | +| Marco Munizaga | 32 | +2287/-536 | 73 | +| Marcin Rataj | 41 | +685/-193 | 86 | +| Patryk | 1 | +312/-10 | 8 | +| guillaumemichel | 7 | +134/-105 | 14 | +| Adin Schmahmann | 5 | +145/-80 | 9 | +| Henrique Dias | 2 | +190/-1 | 6 | +| Josh Klopfenstein | 1 | +90/-35 | 27 | +| gammazero | 5 | +90/-28 | 11 | +| Jeromy Johnson | 1 | +116/-0 | 5 | +| Daniel N | 3 | +27/-25 | 9 | +| Daniel Norman | 2 | +28/-19 | 4 | +| Ivan Shvedunov | 2 | +25/-10 | 2 | +| Michael Muré | 2 | +22/-9 | 4 | +| Dominic Della Valle | 1 | +23/-4 | 1 | +| Andrei Vukolov | 1 | +27/-0 | 1 | +| chris erway | 1 | +9/-9 | 9 | +| Vitaly Zdanevich | 1 | +12/-0 | 1 | +| Guillaume Michel | 1 | +4/-7 | 1 | +| swedneck | 1 | +10/-0 | 1 | +| Jorropo | 2 | +5/-5 | 3 | +| omahs | 1 | +4/-4 | 4 | +| THAT ONE GUY | 1 | +3/-5 | 2 | +| vyzo | 1 | +5/-2 | 1 | +| looklose | 1 | +3/-3 | 2 | +| web3-bot | 2 | +2/-3 | 4 | +| Dave Huseby | 1 | +5/-0 | 1 | +| shenpengfeng | 1 | +1/-1 | 1 | +| bytetigers | 1 | +1/-1 | 1 | +| Sorin Stanculeanu | 1 | +1/-1 | 1 | +| Lukáš Lukáč | 1 | +1/-1 | 1 | +| Gabe | 1 | +1/-1 | 1 | +| Bryan Stenson | 1 | +1/-1 | 1 | +| Samy Fodil | 1 | +1/-0 | 1 | +| Lane Rettig | 1 | +1/-0 | 1 | diff --git a/docs/changelogs/v0.31.md b/docs/changelogs/v0.31.md new file mode 100644 index 000000000..b20b15862 --- /dev/null +++ b/docs/changelogs/v0.31.md @@ -0,0 +1,154 @@ +# Kubo changelog v0.31 + +- [v0.31.0](#v0310) + +## v0.31.0 + +- [Overview](#overview) +- [🔦 Highlights](#-highlights) + - [Experimental Pebble Datastore](#experimental-pebble-datastore) + - [New metrics](#new-metrics) + - [`lowpower` profile no longer breaks DHT announcements](#lowpower-profile-no-longer-breaks-dht-announcements) + - [go 1.23, boxo 0.24 and go-libp2p 0.36.5](#go-123-boxo-024-and-go-libp2p-0365) +- [📝 Changelog](#-changelog) +- [👨‍👩‍👧‍👦 Contributors](#-contributors) + +### Overview + +### 🔦 Highlights + +#### Experimental Pebble Datastore + +[Pebble](https://github.com/ipfs/kubo/blob/master/docs/config.md#pebbleds-profile) provides a high-performance alternative to leveldb as the datastore, and provides a modern replacement for [legacy badgerv1](https://github.com/ipfs/kubo/blob/master/docs/config.md#badgerds-profile). + +A fresh Kubo node can be initialized with [`pebbleds` profile](https://github.com/ipfs/kubo/blob/master/docs/config.md#pebbleds-profile) via `ipfs init --profile pebbleds`. + +There are a number of parameters available for tuning pebble's performance to your specific needs. Default values are used for any parameters that are not configured or are set to their zero-value. +For a description of the available tuning parameters, see [kubo/docs/datastores.md#pebbleds](https://github.com/ipfs/kubo/blob/master/docs/datastores.md#pebbleds). + +#### New metrics + +- Added 3 new go metrics: `go_gc_gogc_percent`, `go_gc_gomemlimit_bytes` and `go_sched_gomaxprocs_threads` as those are [recommended by the Go team](https://github.com/prometheus/client_golang/pull/1559) +- Added [network usage metrics](https://github.com/prometheus/client_golang/pull/1555): `process_network_receive_bytes_total` and `process_network_transmit_bytes_total` +- Removed `go_memstat_lookups_total` metric [which was always 0](https://github.com/prometheus/client_golang/pull/1577) + +#### `lowpower` profile no longer breaks DHT announcements + +We've notices users were applying `lowpower` profile, and then reporting content routing issues. This was because `lowpower` disabled reprovider system and locally hosted data was no longer announced on Amino DHT. + +This release changes [`lowpower` profile](https://github.com/ipfs/kubo/blob/master/docs/config.md#lowpower-profile) to not change reprovider settings, ensuring the new users are not sabotaging themselves. It also adds [`annouce-on`](https://github.com/ipfs/kubo/blob/master/docs/config.md#announce-on-profile) and [`announce-off`](https://github.com/ipfs/kubo/blob/master/docs/config.md#announce-off-profile) profiles for controlling announcement settings separately. + +> [!IMPORTANT] +> If you've ever applied the `lowpower` profile before, there is a high chance your node is not announcing to DHT anymore. +> If you have `Reprovider.Interval` set to `0` you may want to set it to `22h` (or run `ipfs config profile apply announce-on`) to fix your system. +> +> As a convenience, `ipfs daemon` will warn if reprovide system is disabled, creating oportinity to fix configuration if it was not intentional. + +#### go 1.23, boxo 0.24 and go-libp2p 0.36.5 + +Various bugfixes. Please update. + +### 📝 Changelog + +
Full Changelog + +- github.com/ipfs/kubo: + - fix: go 1.23(.2) (#10540) ([ipfs/kubo#10540](https://github.com/ipfs/kubo/pull/10540)) + - chore: bump version to 0.32.0-dev + - feat(routing/http): support IPIP-484 and streaming (#10534) ([ipfs/kubo#10534](https://github.com/ipfs/kubo/pull/10534)) + - fix(daemon): webui URL when rpc is catch-all (#10520) ([ipfs/kubo#10520](https://github.com/ipfs/kubo/pull/10520)) + - chore: update changelog and config doc with more info about pebble (#10533) ([ipfs/kubo#10533](https://github.com/ipfs/kubo/pull/10533)) + - feat: pebbleds profile and plugin (#10530) ([ipfs/kubo#10530](https://github.com/ipfs/kubo/pull/10530)) + - chore: dependency updates for 0.31 (#10511) ([ipfs/kubo#10511](https://github.com/ipfs/kubo/pull/10511)) + - feat: explicit announce-on/off profiles (#10524) ([ipfs/kubo#10524](https://github.com/ipfs/kubo/pull/10524)) + - fix(core): look for MFS root in local repo only (#8661) ([ipfs/kubo#8661](https://github.com/ipfs/kubo/pull/8661)) + - Fix issue in ResourceManager and nopfsPlugin about repo path (#10492) ([ipfs/kubo#10492](https://github.com/ipfs/kubo/pull/10492)) + - feat(bitswap): allow configuring WithWantHaveReplaceSize (#10512) ([ipfs/kubo#10512](https://github.com/ipfs/kubo/pull/10512)) + - refactor: simplify logic for MFS pinning (#10506) ([ipfs/kubo#10506](https://github.com/ipfs/kubo/pull/10506)) + - docs: clarify Gateway.PublicGateways (#10525) ([ipfs/kubo#10525](https://github.com/ipfs/kubo/pull/10525)) + - chore: clarify dep update in RELEASE_CHECKLIST.md (#10518) ([ipfs/kubo#10518](https://github.com/ipfs/kubo/pull/10518)) + - feat: ipfs-webui v4.3.2 (#10523) ([ipfs/kubo#10523](https://github.com/ipfs/kubo/pull/10523)) + - docs(config): add useful references + - docs(config): improve profile descriptions (#10517) ([ipfs/kubo#10517](https://github.com/ipfs/kubo/pull/10517)) + - docs: update RELEASE_CHECKLIST.md (#10496) ([ipfs/kubo#10496](https://github.com/ipfs/kubo/pull/10496)) + - chore: create next changelog (#10510) ([ipfs/kubo#10510](https://github.com/ipfs/kubo/pull/10510)) + - Merge Release: v0.30.0 [skip changelog] ([ipfs/kubo#10508](https://github.com/ipfs/kubo/pull/10508)) + - chore: boxo v0.23.0 and go-libp2p v0.36.3 (#10507) ([ipfs/kubo#10507](https://github.com/ipfs/kubo/pull/10507)) + - docs: replace outdated package paths described in rpc README (#10505) ([ipfs/kubo#10505](https://github.com/ipfs/kubo/pull/10505)) + - fix: switch back to go 1.22 (#10502) ([ipfs/kubo#10502](https://github.com/ipfs/kubo/pull/10502)) + - fix(cli): preserve hostname specified with --api in http request headers (#10497) ([ipfs/kubo#10497](https://github.com/ipfs/kubo/pull/10497)) + - chore: upgrade to go 1.23 (#10486) ([ipfs/kubo#10486](https://github.com/ipfs/kubo/pull/10486)) + - fix: error during config when running benchmarks (#10495) ([ipfs/kubo#10495](https://github.com/ipfs/kubo/pull/10495)) + - chore: update go-unixfsnode, cmds, and boxo (#10494) ([ipfs/kubo#10494](https://github.com/ipfs/kubo/pull/10494)) + - Docs fix spelling issues (#10493) ([ipfs/kubo#10493](https://github.com/ipfs/kubo/pull/10493)) + - chore: update version (#10491) ([ipfs/kubo#10491](https://github.com/ipfs/kubo/pull/10491)) +- github.com/ipfs/boxo (v0.23.0 -> v0.24.0): + - Release v0.24.0 ([ipfs/boxo#683](https://github.com/ipfs/boxo/pull/683)) +- github.com/ipfs/go-ipld-cbor (v0.1.0 -> v0.2.0): + - v0.2.0 + - deprecate DumpObject() in favor of better named Encode() + - add an EncodeWriter method, using the pooled marshallers + - fix expCid vs actualCid guard +- github.com/ipld/go-car/v2 (v2.13.1 -> v2.14.2): + - v2.14.2 bump + - fix: goreleaser v2 compat, trigger release-binaries with workflow_run + - v2.14.1 bump + - chore: update fuzz to Go 1.22 + - v2.14.0 bump + - fix(cmd): properly pick up --inverse and --cid-file args ([ipld/go-car#531](https://github.com/ipld/go-car/pull/531)) + - Re-factor cmd functions to library ([ipld/go-car#524](https://github.com/ipld/go-car/pull/524)) + - ci: uci/copy-templates ([ipld/go-car#521](https://github.com/ipld/go-car/pull/521)) + - Add a `car ls --unixfs-blocks` to render two-column output ([ipld/go-car#514](https://github.com/ipld/go-car/pull/514)) +- github.com/libp2p/go-libp2p (v0.36.3 -> v0.36.5): + - chore: remove Roadmap file (#2954) ([libp2p/go-libp2p#2954](https://github.com/libp2p/go-libp2p/pull/2954)) + - fix: Release v0.36.5 + - autonatv2: recover from panics (#2992) ([libp2p/go-libp2p#2992](https://github.com/libp2p/go-libp2p/pull/2992)) + - basichost: ensure no duplicates in Addrs output (#2980) ([libp2p/go-libp2p#2980](https://github.com/libp2p/go-libp2p/pull/2980)) + - Release v0.36.4 + - peerstore: better GC in membacked peerstore (#2960) ([libp2p/go-libp2p#2960](https://github.com/libp2p/go-libp2p/pull/2960)) + - fix: use quic.Version instead of the deprecated quic.VersionNumber (#2955) ([libp2p/go-libp2p#2955](https://github.com/libp2p/go-libp2p/pull/2955)) + - tcp: fix metrics for multiple calls to Close (#2953) ([libp2p/go-libp2p#2953](https://github.com/libp2p/go-libp2p/pull/2953)) +- github.com/libp2p/go-libp2p-kbucket (v0.6.3 -> v0.6.4): + - release v0.6.4 ([libp2p/go-libp2p-kbucket#135](https://github.com/libp2p/go-libp2p-kbucket/pull/135)) + - feat: add log printing when peer added and removed table ([libp2p/go-libp2p-kbucket#134](https://github.com/libp2p/go-libp2p-kbucket/pull/134)) + - Upgrade to go-log v2.5.1 ([libp2p/go-libp2p-kbucket#132](https://github.com/libp2p/go-libp2p-kbucket/pull/132)) + - chore: update go-libp2p-asn-util +- github.com/multiformats/go-multiaddr-dns (v0.3.1 -> v0.4.0): + - Release v0.4.0 (#64) ([multiformats/go-multiaddr-dns#64](https://github.com/multiformats/go-multiaddr-dns/pull/64)) + - Limit total number of resolved addresses from DNS response (#63) ([multiformats/go-multiaddr-dns#63](https://github.com/multiformats/go-multiaddr-dns/pull/63)) + - fix!: Only resolve the first DNS-like component (#61) ([multiformats/go-multiaddr-dns#61](https://github.com/multiformats/go-multiaddr-dns/pull/61)) + - sync: update CI config files (#43) ([multiformats/go-multiaddr-dns#43](https://github.com/multiformats/go-multiaddr-dns/pull/43)) + - remove deprecated types ([multiformats/go-multiaddr-dns#37](https://github.com/multiformats/go-multiaddr-dns/pull/37)) + - remove Jenkinsfile ([multiformats/go-multiaddr-dns#40](https://github.com/multiformats/go-multiaddr-dns/pull/40)) + - sync: update CI config files (#29) ([multiformats/go-multiaddr-dns#29](https://github.com/multiformats/go-multiaddr-dns/pull/29)) + - use net.IP.Equal to compare IP addresses ([multiformats/go-multiaddr-dns#30](https://github.com/multiformats/go-multiaddr-dns/pull/30)) + +
+ +### 👨‍👩‍👧‍👦 Contributors + +| Contributor | Commits | Lines ± | Files Changed | +|-------------|---------|---------|---------------| +| Will Scott | 3 | +731/-581 | 14 | +| Daniel N | 17 | +1034/-191 | 33 | +| Marco Munizaga | 5 | +721/-404 | 12 | +| Andrew Gillis | 9 | +765/-266 | 35 | +| Marcin Rataj | 17 | +568/-323 | 41 | +| Daniel Norman | 3 | +232/-111 | 10 | +| sukun | 4 | +93/-8 | 8 | +| Jorropo | 2 | +48/-45 | 5 | +| Marten Seemann | 3 | +19/-47 | 5 | +| fengzie | 1 | +29/-26 | 5 | +| Rod Vagg | 7 | +27/-11 | 9 | +| gopherfarm | 1 | +14/-14 | 6 | +| web3-bot | 3 | +13/-10 | 3 | +| Michael Muré | 2 | +16/-5 | 4 | +| i-norden | 1 | +9/-9 | 1 | +| Elias Rad | 1 | +7/-7 | 4 | +| Prithvi Shahi | 1 | +0/-11 | 2 | +| Lucas Molas | 1 | +5/-4 | 1 | +| elecbug | 1 | +6/-2 | 1 | +| gammazero | 2 | +2/-2 | 2 | +| chris erway | 1 | +2/-2 | 2 | +| Russell Dempsey | 1 | +2/-1 | 1 | +| guillaumemichel | 1 | +1/-1 | 1 | diff --git a/docs/changelogs/v0.32.md b/docs/changelogs/v0.32.md new file mode 100644 index 000000000..58ed16030 --- /dev/null +++ b/docs/changelogs/v0.32.md @@ -0,0 +1,43 @@ +# Kubo changelog v0.32 + +- [v0.32.0](#v0310) + +## v0.32.0 + +- [Overview](#overview) +- [🔦 Highlights](#-highlights) + - [🎯 AutoTLS: Automatic Certificates for libp2p WebSockets via `libp2p.direct`](#-autotls-automatic-certificates-for-libp2p-websockets-via-libp2pdirect) + - [📦️ Dependency updates](#-dependency-updates) +- [📝 Changelog](#-changelog) +- [👨‍👩‍👧‍👦 Contributors](#-contributors) + +### Overview + +### 🔦 Highlights + +#### 🎯 AutoTLS: Automatic Certificates for libp2p WebSockets via `libp2p.direct` + +This release introduces an experimental feature that significantly improves how browsers can connect to Kubo node. +Opt-in configuration allows Kubo nodes to obtain CA-signed TLS certificates for [libp2p Secure WebSocket (WSS)](https://github.com/libp2p/specs/blob/master/websockets/README.md) connections automatically. + +See [`AutoTLS`](https://github.com/ipfs/kubo/blob/master/docs/config.md#autotls) configuration for details how to enable it. We appreciate you testing and providing an early feedback in [kubo#10560](https://github.com/ipfs/kubo/issues/10560). + +#### 📦️ Dependency updates + +- update `ipfs-webui` to [v4.4.0](https://github.com/ipfs/ipfs-webui/releases/tag/v4.4.0) +- update `boxo` to [v0.24.1](https://github.com/ipfs/boxo/releases/tag/v0.24.1) + [v0.24.2](https://github.com/ipfs/boxo/releases/tag/v0.24.2) + [v0.24.3](https://github.com/ipfs/boxo/releases/tag/v0.24.3) + - This includes a number of fixes and bitswap improvements, and support for filtering from [IPIP-484](https://specs.ipfs.tech/ipips/ipip-0484/) in delegated HTTP routing and IPNI queries. +- update `go-libp2p` to [v0.37.0](https://github.com/libp2p/go-libp2p/releases/tag/v0.37.0) + - This update required removal of `Swarm.RelayService.MaxReservationsPerPeer` configuration option from Kubo. If you had it set, remove it from your configuration file. +- update `go-libp2p-kad-dht` to [v0.27.0](https://github.com/libp2p/go-libp2p-kad-dht/releases/tag/v0.27.0) +- update `go-libp2p-pubsub` to [v0.12.0](https://github.com/libp2p/go-libp2p-pubsub/releases/tag/v0.12.0) +- update `p2p-forge/client` to [v0.0.2](https://github.com/ipshipyard/p2p-forge/releases/tag/v0.0.2) +- removed `go-homedir` + - The `github.com/mitchellh/go-homedir` repo is archived, no longer needed, and no longer maintained. + - `homedir.Dir` is replaced by the stdlib `os.UserHomeDir` + - `homedir.Expand` is replaced by `fsutil.ExpandHome` in the `github.com/ipfs/kubo/misc/fsutil` package. + - The new `github.com/ipfs/kubo/misc/fsutil` package contains file utility code previously located elsewhere in kubo. + +### 📝 Changelog + +### 👨‍👩‍👧‍👦 Contributors diff --git a/docs/changelogs/v0.4.md b/docs/changelogs/v0.4.md index 7096d2327..bdc0f004b 100644 --- a/docs/changelogs/v0.4.md +++ b/docs/changelogs/v0.4.md @@ -306,7 +306,7 @@ handshake). ### Commands -This release brings no new commands but does introduce a few changes, bugfixes, +This release brings no new commands but does introduce a few changes, bug fixes, and enhancements. This section is hardly complete but it lists the most noticeable changes. @@ -335,7 +335,7 @@ browsers (see [#4143](https://github.com/ipfs/go-ipfs/issues/4143). #### Human Readable Numbers -The `ipfs bitswap stat` and and `ipfs object stat` commands now support a +The `ipfs bitswap stat` and `ipfs object stat` commands now support a `--humanize` flag that formats numbers with human-readable units (GiB, MiB, etc.). @@ -1122,7 +1122,7 @@ earlier on start. - add function to marshal raw nodes to json ([ipfs/go-merkledag#36](https://github.com/ipfs/go-merkledag/pull/36)) - fix some performance regressions when reading protobuf nodes ([ipfs/go-merkledag#34](https://github.com/ipfs/go-merkledag/pull/34)) - github.com/ipfs/go-metrics-interface: - - update the counter interface to match prometheus ([ipfs/go-metrics-interface#2](https://github.com/ipfs/go-metrics-interface/pull/2)) + - update the counter interface to match Prometheus ([ipfs/go-metrics-interface#2](https://github.com/ipfs/go-metrics-interface/pull/2)) - github.com/ipfs/go-mfs: - Return node from FlushPath ([ipfs/go-mfs#72](https://github.com/ipfs/go-mfs/pull/72)) - Wire up context to FlushPath ([ipfs/go-mfs#70](https://github.com/ipfs/go-mfs/pull/70)) @@ -1349,7 +1349,7 @@ Second up, `ipfs ls` now has a new `--stream` flag. In IPFS, very large directories (e.g., Wikipedia) are split up into multiple chunks (shards) as there are too many entries to fit in a single block. Unfortunately, `ipfs ls` buffers the _entire_ file list in memory and then sorts it. This means that -`ipfs ls /ipfs/QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco/wiki` (wikipedia) +`ipfs ls /ipfs/QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco/wiki` (Wikipedia) will take a _very_ long time to return anything (it'll also use quite a bit of memory). @@ -1541,7 +1541,7 @@ The next steps are: - [CORS] Bubble go-ipfs-cmds 2.0.10 - Updates CORS library ([ipfs/go-ipfs#5919](https://github.com/ipfs/go-ipfs/pull/5919)) - reduce verbosity of daemon start ([ipfs/go-ipfs#5904](https://github.com/ipfs/go-ipfs/pull/5904)) - feat: update to Web UI v2.3.2 ([ipfs/go-ipfs#5899](https://github.com/ipfs/go-ipfs/pull/5899)) - - CoreAPI: Don't panic when testing incomplete implementions ([ipfs/go-ipfs#5900](https://github.com/ipfs/go-ipfs/pull/5900)) + - CoreAPI: Don't panic when testing incomplete implementations ([ipfs/go-ipfs#5900](https://github.com/ipfs/go-ipfs/pull/5900)) - gateway: fix CORs headers ([ipfs/go-ipfs#5893](https://github.com/ipfs/go-ipfs/pull/5893)) - Local Gateway option ([ipfs/go-ipfs#5649](https://github.com/ipfs/go-ipfs/pull/5649)) - Show hash on gateway ([ipfs/go-ipfs#5830](https://github.com/ipfs/go-ipfs/pull/5830)) @@ -1590,7 +1590,7 @@ The next steps are: - cmds/bitswap: sort wantlist ([ipfs/go-ipfs#5759](https://github.com/ipfs/go-ipfs/pull/5759)) - cmds/update: use new cmds lib ([ipfs/go-ipfs#5730](https://github.com/ipfs/go-ipfs/pull/5730)) - cmds/file: use new cmds lib ([ipfs/go-ipfs#5756](https://github.com/ipfs/go-ipfs/pull/5756)) - - cmds: remove reduntant func ([ipfs/go-ipfs#5750](https://github.com/ipfs/go-ipfs/pull/5750)) + - cmds: remove redundant func ([ipfs/go-ipfs#5750](https://github.com/ipfs/go-ipfs/pull/5750)) - commands/refs: use new cmds ([ipfs/go-ipfs#5679](https://github.com/ipfs/go-ipfs/pull/5679)) - commands/pin: use new cmds lib ([ipfs/go-ipfs#5674](https://github.com/ipfs/go-ipfs/pull/5674)) - commands/boostrap: use new cmds ([ipfs/go-ipfs#5678](https://github.com/ipfs/go-ipfs/pull/5678)) @@ -1656,7 +1656,7 @@ The next steps are: - add codecs for Dash blocks, tx ([ipfs/go-cid#78](https://github.com/ipfs/go-cid/pull/78)) - github.com/ipfs/go-cidutil: - Fix Travis CI to run all tests. ([ipfs/go-cidutil#11](https://github.com/ipfs/go-cidutil/pull/11)) - - Changes needed for `--cid-base` option in go-ipfs (simplified vesion) ([ipfs/go-cidutil#10](https://github.com/ipfs/go-cidutil/pull/10)) + - Changes needed for `--cid-base` option in go-ipfs (simplified version) ([ipfs/go-cidutil#10](https://github.com/ipfs/go-cidutil/pull/10)) - add a utility method for sorting CID slices ([ipfs/go-cidutil#5](https://github.com/ipfs/go-cidutil/pull/5)) - github.com/libp2p/go-conn-security: - fix link to usage example in README ([libp2p/go-conn-security#4](https://github.com/libp2p/go-conn-security/pull/4)) @@ -1844,7 +1844,7 @@ The next steps are: - fix a fetch deadlock on error ([ipfs/go-merkledag#21](https://github.com/ipfs/go-merkledag/pull/21)) - Wait for all go routines to finish before function returns ([ipfs/go-merkledag#19](https://github.com/ipfs/go-merkledag/pull/19)) - github.com/ipfs/go-metrics-prometheus: - - use prometheus instead of gxed ([ipfs/go-metrics-prometheus#3](https://github.com/ipfs/go-metrics-prometheus/pull/3)) + - use Prometheus instead of gxed ([ipfs/go-metrics-prometheus#3](https://github.com/ipfs/go-metrics-prometheus/pull/3)) - github.com/ipfs/go-mfs: - fix(mv): dst filename error ([ipfs/go-mfs#62](https://github.com/ipfs/go-mfs/pull/62)) - fix over-wait in WaitPub ([ipfs/go-mfs#53](https://github.com/ipfs/go-mfs/pull/53)) @@ -2096,7 +2096,7 @@ You can track progress in https://github.com/ipfs/go-ipfs/issues/4498 We introduced new path type which introduces distinction between IPLD and IPFS (unixfs) paths. From now on paths prefixed with `/ipld/` will always use IPLD link traversal and `/ipfs/` will use unixfs path resolver, which -takes things like shardnig into account. +takes things like sharding into account. Note that this is only initial support and there likely are some bugs in how the paths are handled internally, so consider this feature @@ -2114,7 +2114,7 @@ approach: a local IPFS node). To fix the security issue, we intend to switch IPFS gateway links -`https://ipfs.io/ipfs/CID` to to `https://CID.ipfs.dweb.link`. This way, the CID +`https://ipfs.io/ipfs/CID` to `https://CID.ipfs.dweb.link`. This way, the CID will be a part of the ["origin"](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin) so each IPFS website will get a separate security origin. @@ -2203,7 +2203,7 @@ Fixes (i.e., users take note): - refactor(command): modify int to int64 ([ipfs/go-ipfs#5612](https://github.com/ipfs/go-ipfs/pull/5612)) - fix(core): ipns config RecordLifetime panic ([ipfs/go-ipfs#5648](https://github.com/ipfs/go-ipfs/pull/5648)) - simplify dag put and correctly take pin lock ([ipfs/go-ipfs#5667](https://github.com/ipfs/go-ipfs/pull/5667)) - - fix prometheus concurrent map write bug ([ipfs/go-ipfs#5706](https://github.com/ipfs/go-ipfs/pull/5706)) + - fix Prometheus concurrent map write bug ([ipfs/go-ipfs#5706](https://github.com/ipfs/go-ipfs/pull/5706)) Regressions Fixes (fixes for bugs introduced since the last release): - namesys: properly attach path in name.Resolve ([ipfs/go-ipfs#5660](https://github.com/ipfs/go-ipfs/pull/5660)) @@ -2291,7 +2291,7 @@ Internal: - fix: maketarball.sh for OSX ([ipfs/go-ipfs#5575](https://github.com/ipfs/go-ipfs/pull/5575)) - test the correct return value when checking directory size ([ipfs/go-ipfs#5580](https://github.com/ipfs/go-ipfs/pull/5580)) - coreapi unixfs: remove Cat ([ipfs/go-ipfs#5574](https://github.com/ipfs/go-ipfs/pull/5574)) - - Explicitally use BufferedDAG after removing Batch from importers ([ipfs/go-ipfs#5626](https://github.com/ipfs/go-ipfs/pull/5626)) + - Explicitly use BufferedDAG after removing Batch from importers ([ipfs/go-ipfs#5626](https://github.com/ipfs/go-ipfs/pull/5626)) Cleanup: - Fix some weird code in core/coreunix/add.go ([ipfs/go-ipfs#5354](https://github.com/ipfs/go-ipfs/pull/5354)) @@ -2337,7 +2337,7 @@ github.com/ipfs/go-datastore github.com/ipfs/go-cid - Add tests for Set type ([ipfs/go-cid#63](https://github.com/ipfs/go-cid/pull/63)) - Create new Builder interface for creating CIDs. ([ipfs/go-cid#53](https://github.com/ipfs/go-cid/pull/53)) - - cid-fmt Enhancments ([ipfs/go-cid#61](https://github.com/ipfs/go-cid/pull/61)) + - cid-fmt enhancements ([ipfs/go-cid#61](https://github.com/ipfs/go-cid/pull/61)) - add String benchmark ([ipfs/go-cid#44](https://github.com/ipfs/go-cid/pull/44)) - add a streaming CID set ([ipfs/go-cid#67](https://github.com/ipfs/go-cid/pull/67)) - Extract non-core functionality from go-cid into go-cidutil ([ipfs/go-cid#69](https://github.com/ipfs/go-cid/pull/69)) @@ -2362,7 +2362,7 @@ github.com/ipfs/go-metrics-prometheus - use an existing metric when it has already been registered ([ipfs/go-metrics-prometheus#1](https://github.com/ipfs/go-metrics-prometheus/pull/1)) github.com/ipfs/go-metrics-interface - - update the counter interface to match prometheus ([ipfs/go-metrics-interface#2](https://github.com/ipfs/go-metrics-interface/pull/2)) + - update the counter interface to match Prometheus ([ipfs/go-metrics-interface#2](https://github.com/ipfs/go-metrics-interface/pull/2)) github.com/ipfs/go-ipld-format - add copy dagservice function ([ipfs/go-ipld-format#41](https://github.com/ipfs/go-ipld-format/pull/41)) @@ -2624,18 +2624,18 @@ add it yourself, but doing so was not easy). With this, ipfs can ingest and operate over Git repositories and commit graphs directly. For more information on this, see [the go-ipld-git repo](https://github.com/ipfs/go-ipld-git). -Finally, we've included many smaller bugfixes, refactorings, improved +Finally, we've included many smaller bug fixes, refactorings, improved documentation, and a good bit more. For the full details, see the changelog below. ## v0.4.16-rc3 2018-07-09 -- Bugfixes +- Bug fixes - Fix dht commands when ipns over pubsub is enabled ([ipfs/go-ipfs#5200](https://github.com/ipfs/go-ipfs/pull/5200)) - Fix content routing when ipns over pubsub is enabled ([ipfs/go-ipfs#5200](https://github.com/ipfs/go-ipfs/pull/5200)) - Correctly handle multi-hop dnslink resolution ([ipfs/go-ipfs#5202](https://github.com/ipfs/go-ipfs/pull/5202)) ## v0.4.16-rc2 2018-07-05 -- Bugfixes +- Bug fixes - Fix usage of file name vs path name in adder ([ipfs/go-ipfs#5167](https://github.com/ipfs/go-ipfs/pull/5167)) - Fix `ipfs update` working with migrations ([ipfs/go-ipfs#5194](https://github.com/ipfs/go-ipfs/pull/5194)) - Documentation @@ -2656,7 +2656,7 @@ below. - Add package overview comments to coreapi ([ipfs/go-ipfs#5108](https://github.com/ipfs/go-ipfs/pull/5108)) - Add README to docs folder ([ipfs/go-ipfs#5095](https://github.com/ipfs/go-ipfs/pull/5095)) - Add system requirements to README ([ipfs/go-ipfs#5137](https://github.com/ipfs/go-ipfs/pull/5137)) -- Bugfixes +- Bug fixes - Fix goroutine leak in pin verify ([ipfs/go-ipfs#5011](https://github.com/ipfs/go-ipfs/pull/5011)) - Fix commit string in version ([ipfs/go-ipfs#4982](https://github.com/ipfs/go-ipfs/pull/4982)) - Fix `key rename` command output error ([ipfs/go-ipfs#4962](https://github.com/ipfs/go-ipfs/pull/4962)) @@ -2688,7 +2688,7 @@ below. - Disable the MacOS tests in jenkins ([ipfs/go-ipfs#5119](https://github.com/ipfs/go-ipfs/pull/5119)) - Make republisher test robust against timing issues ([ipfs/go-ipfs#5125](https://github.com/ipfs/go-ipfs/pull/5125)) - Archive sharness trash dirs in jenkins ([ipfs/go-ipfs#5071](https://github.com/ipfs/go-ipfs/pull/5071)) - - Fixup DHT sharness tests ([ipfs/go-ipfs#5114](https://github.com/ipfs/go-ipfs/pull/5114)) + - Fix up DHT sharness tests ([ipfs/go-ipfs#5114](https://github.com/ipfs/go-ipfs/pull/5114)) - Dependencies - Update go-ipld-git to fix mergetag resolving ([ipfs/go-ipfs#4988](https://github.com/ipfs/go-ipfs/pull/4988)) - Fix duplicate /x/sys imports ([ipfs/go-ipfs#5068](https://github.com/ipfs/go-ipfs/pull/5068)) @@ -2757,7 +2757,7 @@ access. - Add docs for --profile=lowpower ([ipfs/go-ipfs#4970](https://github.com/ipfs/go-ipfs/pull/4970)) - Improve Windows build documentation ([ipfs/go-ipfs#4691](https://github.com/ipfs/go-ipfs/pull/4691)) -- Bugfixes +- Bug fixes - Check CIDs in base case when diffing nodes ([ipfs/go-ipfs#4767](https://github.com/ipfs/go-ipfs/pull/4767)) - Support for CIDv1 with custom mhtype in `ipfs block put` ([ipfs/go-ipfs#4563](https://github.com/ipfs/go-ipfs/pull/4563)) - Clean path in DagArchive ([ipfs/go-ipfs#4743](https://github.com/ipfs/go-ipfs/pull/4743)) @@ -2806,7 +2806,7 @@ access. ## v0.4.14 2018-03-22 -Ipfs 0.4.14 is a big release with a large number of improvements and bugfixes. +Ipfs 0.4.14 is a big release with a large number of improvements and bug fixes. It is also the first release of 2018, and our first release in over three months. The release took longer than expected due to our refactoring and extracting of our commands library. This refactor had two stages. The first @@ -2920,7 +2920,7 @@ remove them before updating. - Add godocs for path module ([ipfs/go-ipfs#4689](https://github.com/ipfs/go-ipfs/pull/4689)) - Add godocs for pin module ([ipfs/go-ipfs#4696](https://github.com/ipfs/go-ipfs/pull/4696)) - Update link to filestore experimental status ([ipfs/go-ipfs#4557](https://github.com/ipfs/go-ipfs/pull/4557)) -- Bugfixes +- Bug fixes - Remove trailing slash in ipfs get paths, fixes #3729 ([ipfs/go-ipfs#4365](https://github.com/ipfs/go-ipfs/pull/4365)) - fix deadlock in bitswap sessions ([ipfs/go-ipfs#4407](https://github.com/ipfs/go-ipfs/pull/4407)) - Fix two race conditions (and possibly go routine leaks) in commands ([ipfs/go-ipfs#4406](https://github.com/ipfs/go-ipfs/pull/4406)) @@ -2973,7 +2973,7 @@ remove them before updating. Ipfs 0.4.13 is a patch release that fixes two high priority issues that were discovered in the 0.4.12 release. -Bugfixes: +Bug fixes: - Fix periodic bitswap deadlock ([ipfs/go-ipfs#4386](https://github.com/ipfs/go-ipfs/pull/4386)) - Fix badgerds crash on startup ([ipfs/go-ipfs#4384](https://github.com/ipfs/go-ipfs/pull/4384)) @@ -3037,7 +3037,7 @@ filed pull requests, and contributed in any other way to this release! - Add more info about `ipfs add` chunker option ([ipfs/go-ipfs#4306](https://github.com/ipfs/go-ipfs/pull/4306)) - Remove cruft in readme and mention discourse forum ([ipfs/go-ipfs#4345](https://github.com/ipfs/go-ipfs/pull/4345)) - Add note about updating before reporting issues ([ipfs/go-ipfs#4361](https://github.com/ipfs/go-ipfs/pull/4361)) -- Bugfixes +- Bug fixes - Fix FreeBSD build issues ([ipfs/go-ipfs#4275](https://github.com/ipfs/go-ipfs/pull/4275)) - Don't crash when Datastore.StorageMax is not defined ([ipfs/go-ipfs#4246](https://github.com/ipfs/go-ipfs/pull/4246)) - Do not call 'Connect' on NewStream in bitswap ([ipfs/go-ipfs#4317](https://github.com/ipfs/go-ipfs/pull/4317)) @@ -3176,7 +3176,7 @@ you. - Update release docs ([ipfs/go-ipfs#4165](https://github.com/ipfs/go-ipfs/pull/4165)) - Add documentation for datastore configs ([ipfs/go-ipfs#4223](https://github.com/ipfs/go-ipfs/pull/4223)) - General update and clean-up of docs ([ipfs/go-ipfs#4222](https://github.com/ipfs/go-ipfs/pull/4222)) -- Bugfixes +- Bug fixes - Fix shutdown check in t0023 ([ipfs/go-ipfs#3969](https://github.com/ipfs/go-ipfs/pull/3969)) - Fix pinning of unixfs sharded directories ([ipfs/go-ipfs#3975](https://github.com/ipfs/go-ipfs/pull/3975)) - Show escaped url in gateway 404 message ([ipfs/go-ipfs#4005](https://github.com/ipfs/go-ipfs/pull/4005)) @@ -3197,7 +3197,7 @@ you. ### v0.4.10 - 2017-06-27 Ipfs 0.4.10 is a patch release that contains several exciting new features, -bugfixes and general improvements. Including new commands, easier corruption +bug fixes and general improvements. Including new commands, easier corruption recovery, and a generally cleaner codebase. The `ipfs pin` command has two new subcommands, `verify` and `update`. `ipfs @@ -3258,7 +3258,7 @@ other requested improvements. See below for the full list of changes. - Change 'neccessary' to 'necessary' ([ipfs/go-ipfs#3941](https://github.com/ipfs/go-ipfs/pull/3941)) - README.md: add Nix to the linux package managers ([ipfs/go-ipfs#3939](https://github.com/ipfs/go-ipfs/pull/3939)) - More verbose errors in filestore ([ipfs/go-ipfs#3964](https://github.com/ipfs/go-ipfs/pull/3964)) -- Bugfixes +- Bug fixes - Fix typo in message when file size check fails ([ipfs/go-ipfs#3895](https://github.com/ipfs/go-ipfs/pull/3895)) - Clean up bitswap ledgers when disconnecting ([ipfs/go-ipfs#3437](https://github.com/ipfs/go-ipfs/pull/3437)) - Make odds of 'process added after close' panic less likely ([ipfs/go-ipfs#3940](https://github.com/ipfs/go-ipfs/pull/3940)) @@ -3270,7 +3270,7 @@ other requested improvements. See below for the full list of changes. ### v0.4.9 - 2017-04-30 -Ipfs 0.4.9 is a maintenance release that contains several useful bugfixes and +Ipfs 0.4.9 is a maintenance release that contains several useful bug fixes and improvements. Notably, `ipfs add` has gained the ability to select which CID version will be output. The common ipfs hash that looks like this: `QmRjNgF2mRLDT8AzCPsQbw1EYF2hDTFgfUmJokJPhCApYP` is a multihash. Multihashes @@ -3307,8 +3307,8 @@ Aside from the CID flag, there were many other changes as noted below: - Change issue template to use Severity instead of Priority ([ipfs/go-ipfs#3834](https://github.com/ipfs/go-ipfs/pull/3834)) - Fix link to commit hook script in contribute.md ([ipfs/go-ipfs#3863](https://github.com/ipfs/go-ipfs/pull/3863)) - Fix install_unsupported for openbsd, add docs ([ipfs/go-ipfs#3880](https://github.com/ipfs/go-ipfs/pull/3880)) -- Bugfixes - - Fix wanlist typo in prometheus metric name ([ipfs/go-ipfs#3841](https://github.com/ipfs/go-ipfs/pull/3841)) +- Bug fixes + - Fix wantlist typo in Prometheus metric name ([ipfs/go-ipfs#3841](https://github.com/ipfs/go-ipfs/pull/3841)) - Fix `make install` not using ldflags for git hash ([ipfs/go-ipfs#3838](https://github.com/ipfs/go-ipfs/pull/3838)) - Fix `make install` not installing dependencies ([ipfs/go-ipfs#3848](https://github.com/ipfs/go-ipfs/pull/3848)) - Fix erroneous Cache-Control: immutable on dir listings ([ipfs/go-ipfs#3870](https://github.com/ipfs/go-ipfs/pull/3870)) @@ -3323,11 +3323,11 @@ Aside from the CID flag, there were many other changes as noted below: ### v0.4.8 - 2017-03-29 -Ipfs 0.4.8 brings with it several improvements, bugfixes, documentation +Ipfs 0.4.8 brings with it several improvements, bug fixes, documentation improvements, and the long awaited directory sharding code. Currently, when too many items are added into a unixfs directory, the object -gets too large and you may experience issues. To pervent this problem, and +gets too large and you may experience issues. To prevent this problem, and generally make working really large directories more efficient, we have implemented a HAMT structure for unixfs. To enable this feature, run: ``` @@ -3357,7 +3357,7 @@ look at all the other cool things added in 0.4.8 below. - Improve 'name' and 'key' helptexts ([ipfs/go-ipfs#3806](https://github.com/ipfs/go-ipfs/pull/3806)) - Update link to paper in dev.md ([ipfs/go-ipfs#3812](https://github.com/ipfs/go-ipfs/pull/3812)) - Add test to enforce helptext on commands ([ipfs/go-ipfs#2648](https://github.com/ipfs/go-ipfs/pull/2648)) -- Bugfixes +- Bug fixes - Remove bloom filter check on Put call in blockstore ([ipfs/go-ipfs#3782](https://github.com/ipfs/go-ipfs/pull/3782)) - Re-add the GOPATH checking functionality ([ipfs/go-ipfs#3787](https://github.com/ipfs/go-ipfs/pull/3787)) - Use fsrepo.IsInitialized to test for initialization ([ipfs/go-ipfs#3805](https://github.com/ipfs/go-ipfs/pull/3805)) @@ -3397,7 +3397,7 @@ stream multiplexing protocol was available previously via the `--enable-mplex-experiment` daemon flag, but has now graduated to being 'less experimental' and no longer requires the flag to use it. -Aside from those, we have a good number of bugfixes, perf improvements and new +Aside from those, we have a good number of bug fixes, perf improvements and new tests. Heres a list of highlights: - Features @@ -3416,7 +3416,7 @@ tests. Heres a list of highlights: - Documentation - Add Arch Linux install instructions to readme ([ipfs/go-ipfs#3742](https://github.com/ipfs/go-ipfs/pull/3742)) - Improve release checklist document ([ipfs/go-ipfs#3717](https://github.com/ipfs/go-ipfs/pull/3717)) -- Bugfixes +- Bug fixes - Fix drive root parsing on windows ([ipfs/go-ipfs#3328](https://github.com/ipfs/go-ipfs/pull/3328)) - Fix panic in ipfs get when passing no parameters to API ([ipfs/go-ipfs#3768](https://github.com/ipfs/go-ipfs/pull/3768)) - Fix breakage of `ipfs pin add` api output ([ipfs/go-ipfs#3760](https://github.com/ipfs/go-ipfs/pull/3760)) @@ -3437,7 +3437,7 @@ tests. Heres a list of highlights: ### v0.4.6 - 2017-02-21 -Ipfs 0.4.6 contains several bugfixes related to migrations and also contains a +Ipfs 0.4.6 contains several bug fixes related to migrations and also contains a few other improvements to other parts of the codebase. Notably: - The default config will now contain some ipv6 addresses for bootstrap nodes. @@ -3454,7 +3454,7 @@ few other improvements to other parts of the codebase. Notably: - Documentation - Add the snap installation instructions ([ipfs/go-ipfs#3663](https://github.com/ipfs/go-ipfs/pull/3663)) - Add closed PRs, Issues throughput ([ipfs/go-ipfs#3602](https://github.com/ipfs/go-ipfs/pull/3602)) -- Bugfixes +- Bug fixes - Fix auto-migration on docker nodes ([ipfs/go-ipfs#3698](https://github.com/ipfs/go-ipfs/pull/3698)) - Update flatfs to v1.1.2, fixing directory fd issue ([ipfs/go-ipfs#3711](https://github.com/ipfs/go-ipfs/pull/3711)) - General Changes and Refactorings @@ -3529,7 +3529,7 @@ few other improvements to other parts of the codebase. Notably: - Fix issue where wantlist fullness wasn't included in messages ([ipfs/go-ipfs#3461](https://github.com/ipfs/go-ipfs/pull/3461)) - Only pass keys down newBlocks chan in bitswap ([ipfs/go-ipfs#3271](https://github.com/ipfs/go-ipfs/pull/3271)) -- Bugfixes +- Bug fixes - gateway: fix --writable flag ([ipfs/go-ipfs#3206](https://github.com/ipfs/go-ipfs/pull/3206)) - Fix relative seek in unixfs not expanding file properly ([ipfs/go-ipfs#3095](https://github.com/ipfs/go-ipfs/pull/3095)) - Update multicodec service names for ipfs services ([ipfs/go-ipfs#3132](https://github.com/ipfs/go-ipfs/pull/3132)) @@ -3676,7 +3676,7 @@ expected, but none was given: [ipfs/go-ipfs#3050](https://github.com/ipfs/go-ipf ## v0.4.3-rc2 - 2016-08-04 -This release includes bugfixes and fixes for regressions that were introduced +This release includes bug fixes and fixes for regressions that were introduced between 0.4.2 and 0.4.3-rc1. - Regressions @@ -3716,7 +3716,7 @@ This is the first Release Candidate. Unless there are vulnerabilities or regress - Content Providers are now stored on disk to gain savings on process memory. (@whyrusleeping, [ipfs/go-ipfs#2804](https://github.com/ipfs/go-ipfs/pull/2804), [ipfs/go-ipfs#2860](https://github.com/ipfs/go-ipfs/pull/2860)) - Migrations of the fs-repo (usually stored at `~/.ipfs`) now run automatically. If there's a TTY available, you'll get prompted when running `ipfs daemon`, and in addition you can use the `--migrate=true` or `--migrate=false` options to avoid the prompt. (@whyrusleeping, @lgierth, [ipfs/go-ipfs#2939](https://github.com/ipfs/go-ipfs/pull/2939)) - The internal naming of blocks in the blockstore has changed, which requires a migration of the fs-repo, from version 3 to 4. (@whyrusleeping, [ipfs/go-ipfs#2903](https://github.com/ipfs/go-ipfs/pull/2903)) - - We now automatically raise the file descriptor limit to 1024 if neccessary. (@whyrusleeping, [ipfs/go-ipfs#2884](https://github.com/ipfs/go-ipfs/pull/2884), [ipfs/go-ipfs#2891](https://github.com/ipfs/go-ipfs/pull/2891)) + - We now automatically raise the file descriptor limit to 1024 if necessary. (@whyrusleeping, [ipfs/go-ipfs#2884](https://github.com/ipfs/go-ipfs/pull/2884), [ipfs/go-ipfs#2891](https://github.com/ipfs/go-ipfs/pull/2891)) - After a long struggle with deadlocks and hanging connections, we've decided to disable the uTP transport by default for now. (@whyrusleeping, [ipfs/go-ipfs#2840](https://github.com/ipfs/go-ipfs/pull/2840), [ipfs/go-libp2p-transport@88244000](https://github.com/ipfs/go-libp2p-transport/commit/88244000f0ce8851ffcfbac746ebc0794b71d2a4)) - There is now documentation for the configuration options in `docs/config.md`. (@whyrusleeping, [ipfs/go-ipfs#2974](https://github.com/ipfs/go-ipfs/pull/2974)) - All commands now sanely handle the combination of stdin and optional flags in certain edge cases. (@lgierth, [ipfs/go-ipfs#2952](https://github.com/ipfs/go-ipfs/pull/2952)) @@ -3727,7 +3727,7 @@ This is the first Release Candidate. Unless there are vulnerabilities or regress - Add `Datastore.HashOnRead` option for verifying block hashes on read access. (@Kubuxu, [ipfs/go-ipfs#2904](https://github.com/ipfs/go-ipfs/pull/2904)) - Add `Datastore.BloomFilterSize` option for tuning the blockstore's new lookup bloom filter. (@Kubuxu, [ipfs/go-ipfs#2973](https://github.com/ipfs/go-ipfs/pull/2973)) -- Bugfixes +- Bug fixes - Fix publishing of local IPNS entries, and more. (@whyrusleeping, [ipfs/go-ipfs#2943](https://github.com/ipfs/go-ipfs/pull/2943)) - Fix progress bars in `ipfs add` and `ipfs get`. (@whyrusleeping, [ipfs/go-ipfs#2893](https://github.com/ipfs/go-ipfs/pull/2893), [ipfs/go-ipfs#2948](https://github.com/ipfs/go-ipfs/pull/2948)) @@ -3781,7 +3781,7 @@ This is the first Release Candidate. Unless there are vulnerabilities or regress - Consolidate `--verbose` description across commands. (@Kubuxu, [ipfs/go-ipfs#2746](https://github.com/ipfs/go-ipfs/pull/2746)) - Allow setting position of default values in command option descriptions. (@Kubuxu, [ipfs/go-ipfs#2744](https://github.com/ipfs/go-ipfs/pull/2744)) - Set explicit default values for boolean command options. (@RichardLitt, [ipfs/go-ipfs#2657](https://github.com/ipfs/go-ipfs/pull/2657)) - - Autogenerate command synopsises. (@Kubuxu, [ipfs/go-ipfs#2785](https://github.com/ipfs/go-ipfs/pull/2785)) + - Autogenerate command synopses. (@Kubuxu, [ipfs/go-ipfs#2785](https://github.com/ipfs/go-ipfs/pull/2785)) - Fix and improve lots of documentation. (@RichardLitt, [ipfs/go-ipfs#2741](https://github.com/ipfs/go-ipfs/pull/2741), [ipfs/go-ipfs#2781](https://github.com/ipfs/go-ipfs/pull/2781)) - Improve command descriptions to fit a width of 78 characters. (@RichardLitt, [ipfs/go-ipfs#2779](https://github.com/ipfs/go-ipfs/pull/2779), [ipfs/go-ipfs#2780](https://github.com/ipfs/go-ipfs/pull/2780), [ipfs/go-ipfs#2782](https://github.com/ipfs/go-ipfs/pull/2782)) - Fix filename conflict in the debugging guide. (@Kubuxu, [ipfs/go-ipfs#2752](https://github.com/ipfs/go-ipfs/pull/2752)) @@ -3843,7 +3843,7 @@ There are also a few other nice improvements. * Removals * Remove -f option from `ipfs init` command. (@whyrusleeping) -* Bugfixes +* Bug fixes * Fix `ipfs object patch` argument handling and validation. (@jbenet) * Fix `ipfs config edit` command by running it client-side. (@Kubuxu) * Set default value for `ipfs refs` arguments. (@richardlitt) @@ -3859,7 +3859,7 @@ There are also a few other nice improvements. * Use gx for iptb. (@chriscool) * Update gx and gx-go. (@chriscool) * Make blocks.Block an interface. (@kevina) - * Silence check for Docker existance. (@chriscool) + * Silence check for Docker existence. (@chriscool) * Add dist_get script for fetching tools from dist.ipfs.tech. (@whyrusleeping) * Add proper defaults to all `ipfs` commands. (@richardlitt) * Remove dead `count` option from `ipfs pin ls`. (@richardlitt) @@ -3892,12 +3892,12 @@ hang bugfix that was shipped in the 0.4.0 release. * Fix package manifest fields (@lgierth) * remove incfusever dead-code (@whyrusleeping) * remove a ton of unused godeps (@whyrusleeping) - * metrics: add prometheus back (@lgierth) + * metrics: add Prometheus back (@lgierth) * clean up dead code and config fields (@whyrusleeping) * Add log events when blocks are added/removed from the blockstore (@michealmure) * repo: don't create logs directory, not used any longer (@lgierth) -* Bugfixes +* Bug fixes * fixed ipfs name resolve --local multihash error (@pfista) * ipfs patch commands won't return null links field anymore (@whyrusleeping) * Make non recursive resolve print the result (@Kubuxu) @@ -3927,7 +3927,7 @@ hang bugfix that was shipped in the 0.4.0 release. ## v0.4.0 - 2016-04-05 -This is a major release with plenty of new features and bugfixes. +This is a major release with plenty of new features and bug fixes. It also includes breaking changes which make it incompatible with v0.3.x on the networking layer. diff --git a/docs/changelogs/v0.5.md b/docs/changelogs/v0.5.md index 0fa488845..aa5f9c957 100644 --- a/docs/changelogs/v0.5.md +++ b/docs/changelogs/v0.5.md @@ -564,7 +564,7 @@ As usual, this release contains several Windows specific fixes and improvements: - fix: update the dht to fix yggdrasil ([ipfs/go-ipfs#7186](https://github.com/ipfs/go-ipfs/pull/7186)) - Choose architecture when download tini into docker container ([ipfs/go-ipfs#7187](https://github.com/ipfs/go-ipfs/pull/7187)) - Fix typos and cleanup ([ipfs/go-ipfs#7181](https://github.com/ipfs/go-ipfs/pull/7181)) - - Fixtypos ([ipfs/go-ipfs#7180](https://github.com/ipfs/go-ipfs/pull/7180)) + - Fix typos ([ipfs/go-ipfs#7180](https://github.com/ipfs/go-ipfs/pull/7180)) - feat: webui 2.7.5 ([ipfs/go-ipfs#7176](https://github.com/ipfs/go-ipfs/pull/7176)) - integration test for the dual dht ([ipfs/go-ipfs#7151](https://github.com/ipfs/go-ipfs/pull/7151)) - fix: subdomain redirect for dir CIDs ([ipfs/go-ipfs#7165](https://github.com/ipfs/go-ipfs/pull/7165)) @@ -640,7 +640,7 @@ As usual, this release contains several Windows specific fixes and improvements: - test(sharness): fix typo ([ipfs/go-ipfs#6835](https://github.com/ipfs/go-ipfs/pull/6835)) - test: E2E tests against ipfs-webui HEAD ([ipfs/go-ipfs#6825](https://github.com/ipfs/go-ipfs/pull/6825)) - mkreleaslog: improve edge-cases ([ipfs/go-ipfs#6833](https://github.com/ipfs/go-ipfs/pull/6833)) - - fix: dont fail to collect profiles if no ipfs bin ([ipfs/go-ipfs#6829](https://github.com/ipfs/go-ipfs/pull/6829)) + - fix: don't fail to collect profiles if no ipfs bin ([ipfs/go-ipfs#6829](https://github.com/ipfs/go-ipfs/pull/6829)) - update dockerfile and use openssl ([ipfs/go-ipfs#6828](https://github.com/ipfs/go-ipfs/pull/6828)) - docs: define Gateway.PathPrefixes ([ipfs/go-ipfs#6826](https://github.com/ipfs/go-ipfs/pull/6826)) - fix(badgerds): turn off sync writes by default ([ipfs/go-ipfs#6819](https://github.com/ipfs/go-ipfs/pull/6819)) @@ -661,7 +661,7 @@ As usual, this release contains several Windows specific fixes and improvements: - extract the pinner to go-ipfs-pinner and dagutils into go-merkledag ([ipfs/go-ipfs#6771](https://github.com/ipfs/go-ipfs/pull/6771)) - fix #2203: omit the charset attribute when Content-Type is text/html ([ipfs/go-ipfs#6743](https://github.com/ipfs/go-ipfs/pull/6743)) - Pin ls traverses all indirect pins ([ipfs/go-ipfs#6705](https://github.com/ipfs/go-ipfs/pull/6705)) - - fix: ignore nonexistant when force rm ([ipfs/go-ipfs#6773](https://github.com/ipfs/go-ipfs/pull/6773)) + - fix: ignore nonexistent when force rm ([ipfs/go-ipfs#6773](https://github.com/ipfs/go-ipfs/pull/6773)) - introduce IpfsNode Plugin ([ipfs/go-ipfs#6719](https://github.com/ipfs/go-ipfs/pull/6719)) - improve documentation and fix dht put bug ([ipfs/go-ipfs#6750](https://github.com/ipfs/go-ipfs/pull/6750)) - Adding alias for `ipfs repo stat`. ([ipfs/go-ipfs#6769](https://github.com/ipfs/go-ipfs/pull/6769)) @@ -673,7 +673,7 @@ As usual, this release contains several Windows specific fixes and improvements: - Add high-level go-ipfs architecture diagram ([ipfs/go-ipfs#6727](https://github.com/ipfs/go-ipfs/pull/6727)) - docs: remove extra ) on the example README ([ipfs/go-ipfs#6733](https://github.com/ipfs/go-ipfs/pull/6733)) - update maintainer label ([ipfs/go-ipfs#6735](https://github.com/ipfs/go-ipfs/pull/6735)) - - ipfs namespace is now being provided to prometheus ([ipfs/go-ipfs#6643](https://github.com/ipfs/go-ipfs/pull/6643)) + - ipfs namespace is now being provided to Prometheus ([ipfs/go-ipfs#6643](https://github.com/ipfs/go-ipfs/pull/6643)) - feat: web ui 2.5.8 ([ipfs/go-ipfs#6718](https://github.com/ipfs/go-ipfs/pull/6718)) - docs: add connmgr to config.md toc ([ipfs/go-ipfs#6712](https://github.com/ipfs/go-ipfs/pull/6712)) - feat: web ui 2.5.7 ([ipfs/go-ipfs#6707](https://github.com/ipfs/go-ipfs/pull/6707)) @@ -815,7 +815,7 @@ As usual, this release contains several Windows specific fixes and improvements: - Feat: Track Session Peer Latency More Accurately ([ipfs/go-bitswap#149](https://github.com/ipfs/go-bitswap/pull/149)) - ci(circleci): add benchmark comparisons ([ipfs/go-bitswap#147](https://github.com/ipfs/go-bitswap/pull/147)) - aggressively free memory ([ipfs/go-bitswap#143](https://github.com/ipfs/go-bitswap/pull/143)) - - Enchanced logging for bitswap ([ipfs/go-bitswap#137](https://github.com/ipfs/go-bitswap/pull/137)) + - Enhanced logging for bitswap ([ipfs/go-bitswap#137](https://github.com/ipfs/go-bitswap/pull/137)) - fix: rand.Intn(0) panics ([ipfs/go-bitswap#144](https://github.com/ipfs/go-bitswap/pull/144)) - fix some naming nits and broadcast on search ([ipfs/go-bitswap#139](https://github.com/ipfs/go-bitswap/pull/139)) - feat(sessions): add rebroadcasting, search backoff ([ipfs/go-bitswap#133](https://github.com/ipfs/go-bitswap/pull/133)) @@ -993,7 +993,7 @@ As usual, this release contains several Windows specific fixes and improvements: - fix(dagreader): remove a buggy workaround for a gateway issue ([ipfs/go-unixfs#80](https://github.com/ipfs/go-unixfs/pull/80)) - fix: correctly handle symlink file sizes ([ipfs/go-unixfs#78](https://github.com/ipfs/go-unixfs/pull/78)) - fix: return the correct error from RemoveChild ([ipfs/go-unixfs#76](https://github.com/ipfs/go-unixfs/pull/76)) - - update the the last go-merkledag ([ipfs/go-unixfs#75](https://github.com/ipfs/go-unixfs/pull/75)) + - update the last go-merkledag ([ipfs/go-unixfs#75](https://github.com/ipfs/go-unixfs/pull/75)) - fix: enumerate children ([ipfs/go-unixfs#74](https://github.com/ipfs/go-unixfs/pull/74)) - github.com/ipfs/interface-go-ipfs-core (v0.0.8 -> v0.2.7): - Add pin ls tests for indirect pin traversal and pin type precedence ([ipfs/interface-go-ipfs-core#47](https://github.com/ipfs/interface-go-ipfs-core/pull/47)) @@ -1075,7 +1075,7 @@ As usual, this release contains several Windows specific fixes and improvements: - fix: optimize isRelay ([libp2p/go-libp2p-kad-dht#585](https://github.com/libp2p/go-libp2p-kad-dht/pull/585)) - feat: expose WANActive ([libp2p/go-libp2p-kad-dht#580](https://github.com/libp2p/go-libp2p-kad-dht/pull/580)) - fix: improve error handling in dual dht ([libp2p/go-libp2p-kad-dht#582](https://github.com/libp2p/go-libp2p-kad-dht/pull/582)) - - fix: dedup addresses ([libp2p/go-libp2p-kad-dht#581](https://github.com/libp2p/go-libp2p-kad-dht/pull/581)) + - fix: deduplicate addresses ([libp2p/go-libp2p-kad-dht#581](https://github.com/libp2p/go-libp2p-kad-dht/pull/581)) - Fix bug in periodic peer pinging ([libp2p/go-libp2p-kad-dht#579](https://github.com/libp2p/go-libp2p-kad-dht/pull/579)) - Dual DHT scaffold ([libp2p/go-libp2p-kad-dht#570](https://github.com/libp2p/go-libp2p-kad-dht/pull/570)) - fix: linting fixes ([libp2p/go-libp2p-kad-dht#578](https://github.com/libp2p/go-libp2p-kad-dht/pull/578)) diff --git a/docs/changelogs/v0.7.md b/docs/changelogs/v0.7.md index f962a6800..0160916ba 100644 --- a/docs/changelogs/v0.7.md +++ b/docs/changelogs/v0.7.md @@ -76,7 +76,7 @@ The scripts in https://github.com/ipfs/go-ipfs-example-plugin have been updated - fix: ipfs pin ls - ignore pins that have errors ([ipfs/go-ipfs#7612](https://github.com/ipfs/go-ipfs/pull/7612)) - docs(config): fix Peering header ([ipfs/go-ipfs#7623](https://github.com/ipfs/go-ipfs/pull/7623)) - sharness: use dnsaddr example in ipfs p2p command tests ([ipfs/go-ipfs#7620](https://github.com/ipfs/go-ipfs/pull/7620)) - - fix(key): dont allow backup key to be named 'self' ([ipfs/go-ipfs#7615](https://github.com/ipfs/go-ipfs/pull/7615)) + - fix(key): don't allow backup key to be named 'self' ([ipfs/go-ipfs#7615](https://github.com/ipfs/go-ipfs/pull/7615)) - [BOUNTY] Directory page UI improvements ([ipfs/go-ipfs#7536](https://github.com/ipfs/go-ipfs/pull/7536)) - fix: make assets deterministic ([ipfs/go-ipfs#7609](https://github.com/ipfs/go-ipfs/pull/7609)) - use ed25519 keys by default ([ipfs/go-ipfs#7579](https://github.com/ipfs/go-ipfs/pull/7579)) @@ -157,7 +157,7 @@ The scripts in https://github.com/ipfs/go-ipfs-example-plugin have been updated - github.com/ipfs/go-graphsync (v0.0.5 -> v0.1.1): - docs(CHANGELOG): update for v0.1.1 - docs(CHANGELOG): update for v0.1.0 release ([ipfs/go-graphsync#84](https://github.com/ipfs/go-graphsync/pull/84)) - - Dedup by key extension (#83) ([ipfs/go-graphsync#83](https://github.com/ipfs/go-graphsync/pull/83)) + - Deduplicate by key extension (#83) ([ipfs/go-graphsync#83](https://github.com/ipfs/go-graphsync/pull/83)) - Release infrastructure (#81) ([ipfs/go-graphsync#81](https://github.com/ipfs/go-graphsync/pull/81)) - feat(persistenceoptions): add unregister ability (#80) ([ipfs/go-graphsync#80](https://github.com/ipfs/go-graphsync/pull/80)) - fix(message): regen protobuf code (#79) ([ipfs/go-graphsync#79](https://github.com/ipfs/go-graphsync/pull/79)) @@ -212,7 +212,7 @@ The scripts in https://github.com/ipfs/go-ipfs-example-plugin have been updated - Merge branch 'assembler-upgrade-to-codecs' - Path clarifications ([ipld/go-ipld-prime#47](https://github.com/ipld/go-ipld-prime/pull/47)) - Merge branch 'research-admissions' - - Add a typed link node to allow traversal with code gen'd builders across links ([ipld/go-ipld-prime#41](https://github.com/ipld/go-ipld-prime/pull/41)) + - Add a typed link node to allow traversal with code generated builders across links ([ipld/go-ipld-prime#41](https://github.com/ipld/go-ipld-prime/pull/41)) - Merge branch 'research-admissions' - Library updates. - Feat/add code gen disclaimer ([ipld/go-ipld-prime#39](https://github.com/ipld/go-ipld-prime/pull/39)) @@ -276,7 +276,7 @@ The scripts in https://github.com/ipfs/go-ipfs-example-plugin have been updated - github.com/libp2p/go-libp2p-pubsub (v0.3.1 -> v0.3.5): - regenerate protobufs (#381) ([libp2p/go-libp2p-pubsub#381](https://github.com/libp2p/go-libp2p-pubsub/pull/381)) - track validation time - - fullfill promise as soon as a message begins validation + - fulfill promise as soon as a message begins validation - don't apply penalty in self origin rejections - add behaviour penalty threshold - Add String() method to Topic. diff --git a/docs/changelogs/v0.8.md b/docs/changelogs/v0.8.md index fc5e61f51..7f4e1d759 100644 --- a/docs/changelogs/v0.8.md +++ b/docs/changelogs/v0.8.md @@ -128,7 +128,7 @@ Go 1.15 (the latest version of Go) [no longer supports](https://github.com/golan - Response Assembler Refactor (#138) ([ipfs/go-graphsync#138](https://github.com/ipfs/go-graphsync/pull/138)) - Add error listener on receiver (#136) ([ipfs/go-graphsync#136](https://github.com/ipfs/go-graphsync/pull/136)) - Run testplan on in CI (#137) ([ipfs/go-graphsync#137](https://github.com/ipfs/go-graphsync/pull/137)) - - fix(responsemanager): fix network error propogation (#133) ([ipfs/go-graphsync#133](https://github.com/ipfs/go-graphsync/pull/133)) + - fix(responsemanager): fix network error propagation (#133) ([ipfs/go-graphsync#133](https://github.com/ipfs/go-graphsync/pull/133)) - testground test for graphsync (#132) ([ipfs/go-graphsync#132](https://github.com/ipfs/go-graphsync/pull/132)) - docs(CHANGELOG): update for v0.5.2 ([ipfs/go-graphsync#130](https://github.com/ipfs/go-graphsync/pull/130)) - RegisterNetworkErrorListener should fire when there's an error connecting to the peer (#127) ([ipfs/go-graphsync#127](https://github.com/ipfs/go-graphsync/pull/127)) @@ -285,7 +285,7 @@ Go 1.15 (the latest version of Go) [no longer supports](https://github.com/golan - add support for priority topic delivery weights - tweak duplicate/reject weights - decay global counters after 2 min - - decouple global coutner decay from source counter decay + - decouple global counter decay from source counter decay - add warning for failure to parse IP out of remote multiaddr - more docs - configure the peer gater using a parameter object, docs and stuff diff --git a/docs/changelogs/v0.9.md b/docs/changelogs/v0.9.md index e210c7c24..7289adde7 100644 --- a/docs/changelogs/v0.9.md +++ b/docs/changelogs/v0.9.md @@ -209,7 +209,7 @@ SECIO was deprecated and turned off by default given the prevalence of TLS and N - fix(gw): remove hardcoded hostnames ([ipfs/go-ipfs#8069](https://github.com/ipfs/go-ipfs/pull/8069)) - Fix transposed words in docs/config.md ([ipfs/go-ipfs#8051](https://github.com/ipfs/go-ipfs/pull/8051)) - fix: update root help ([ipfs/go-ipfs#8052](https://github.com/ipfs/go-ipfs/pull/8052)) - - chore: dont docker tag rc as latest ([ipfs/go-ipfs#8055](https://github.com/ipfs/go-ipfs/pull/8055)) + - chore: don't docker tag rc as latest ([ipfs/go-ipfs#8055](https://github.com/ipfs/go-ipfs/pull/8055)) - Add info to "pin rm" help about how to tell if pin is indirect ([ipfs/go-ipfs#8044](https://github.com/ipfs/go-ipfs/pull/8044)) - build(deps): bump contrib.go.opencensus.io/exporter/prometheus from 0.2.0 to 0.3.0 ([ipfs/go-ipfs#8020](https://github.com/ipfs/go-ipfs/pull/8020)) - fix(gw): remove use of Clear-Site-Data in subdomain router ([ipfs/go-ipfs#7890](https://github.com/ipfs/go-ipfs/pull/7890)) @@ -223,7 +223,7 @@ SECIO was deprecated and turned off by default given the prevalence of TLS and N - fix: return an error if repo verify is canceled ([ipfs/go-ipfs#7973](https://github.com/ipfs/go-ipfs/pull/7973)) - doc: document security fix policy ([ipfs/go-ipfs#7991](https://github.com/ipfs/go-ipfs/pull/7991)) - feat(gw): /ipfs/ipfs/{cid} → /ipfs/{cid} ([ipfs/go-ipfs#7930](https://github.com/ipfs/go-ipfs/pull/7930)) - - Fix: innacuracies in MFS command documentation. ([ipfs/go-ipfs#8001](https://github.com/ipfs/go-ipfs/pull/8001)) + - Fix: inaccuracies in MFS command documentation. ([ipfs/go-ipfs#8001](https://github.com/ipfs/go-ipfs/pull/8001)) - Feat: Re-import InitializeKeyspace code from go-namesys ([ipfs/go-ipfs#7984](https://github.com/ipfs/go-ipfs/pull/7984)) - revert registration of metrics against unexposed prom registry ([ipfs/go-ipfs#7986](https://github.com/ipfs/go-ipfs/pull/7986)) - Extract the namesys and the keystore submodules ([ipfs/go-ipfs#7925](https://github.com/ipfs/go-ipfs/pull/7925)) @@ -266,7 +266,7 @@ SECIO was deprecated and turned off by default given the prevalence of TLS and N - docs(architecture): update architecture docs (#154) ([ipfs/go-graphsync#154](https://github.com/ipfs/go-graphsync/pull/154)) - release v0.7.0 ([ipfs/go-graphsync#152](https://github.com/ipfs/go-graphsync/pull/152)) - chore: update deps (#151) ([ipfs/go-graphsync#151](https://github.com/ipfs/go-graphsync/pull/151)) - - Automatically record heap profiles in testplans (#147) ([ipfs/go-graphsync#147](https://github.com/ipfs/go-graphsync/pull/147)) + - Automatically record heap profiles in test plans (#147) ([ipfs/go-graphsync#147](https://github.com/ipfs/go-graphsync/pull/147)) - feat(deps): update go-ipld-prime v0.7.0 (#145) ([ipfs/go-graphsync#145](https://github.com/ipfs/go-graphsync/pull/145)) - Release/v0.6.0 ([ipfs/go-graphsync#144](https://github.com/ipfs/go-graphsync/pull/144)) - github.com/ipfs/go-ipfs-blockstore (v0.1.4 -> v0.1.6): @@ -360,7 +360,7 @@ SECIO was deprecated and turned off by default given the prevalence of TLS and N - node/gendemo: use the new code generator - Merge pull request #96 , originally known as ipld/cidlink-only-usable-as-ptr - Codec revamp ([ipld/go-ipld-prime#112](https://github.com/ipld/go-ipld-prime/pull/112)) - - Allow overriden types (#116) ([ipld/go-ipld-prime#116](https://github.com/ipld/go-ipld-prime/pull/116)) + - Allow overridden types (#116) ([ipld/go-ipld-prime#116](https://github.com/ipld/go-ipld-prime/pull/116)) - add import to ipld in ipldsch_types.go ([ipld/go-ipld-prime#115](https://github.com/ipld/go-ipld-prime/pull/115)) - Codegen output rearrange ([ipld/go-ipld-prime#105](https://github.com/ipld/go-ipld-prime/pull/105)) - Validate struct builder sufficiency ([ipld/go-ipld-prime#111](https://github.com/ipld/go-ipld-prime/pull/111)) @@ -540,7 +540,7 @@ SECIO was deprecated and turned off by default given the prevalence of TLS and N - improve string representation of timeout errors ([lucas-clemente/quic-go#3118](https://github.com/lucas-clemente/quic-go/pull/3118)) - fix flaky timeout test ([lucas-clemente/quic-go#3105](https://github.com/lucas-clemente/quic-go/pull/3105)) - fix calculation of the time for the next keep alive - - add a 0-RTT test with different connecton ID lengths ([lucas-clemente/quic-go#3098](https://github.com/lucas-clemente/quic-go/pull/3098)) + - add a 0-RTT test with different connection ID lengths ([lucas-clemente/quic-go#3098](https://github.com/lucas-clemente/quic-go/pull/3098)) - only run Ginkgo focus detection in staged files in pre-commit hook ([lucas-clemente/quic-go#3099](https://github.com/lucas-clemente/quic-go/pull/3099)) - allow 0-RTT when flow control windows are increased ([lucas-clemente/quic-go#3096](https://github.com/lucas-clemente/quic-go/pull/3096)) - improve the 0-RTT rejection integration test ([lucas-clemente/quic-go#3097](https://github.com/lucas-clemente/quic-go/pull/3097)) @@ -705,7 +705,7 @@ SECIO was deprecated and turned off by default given the prevalence of TLS and N - Implement simultaneous open extension ([multiformats/go-multistream#42](https://github.com/multiformats/go-multistream/pull/42)) - reduce the number of streams in the stress tests, fix error handling ([multiformats/go-multistream#54](https://github.com/multiformats/go-multistream/pull/54)) - github.com/whyrusleeping/cbor-gen (v0.0.0-20200710004633-5379fc63235d -> v0.0.0-20210219115102-f37d292932f2): - - feat: allow unmarshaling of struct with more fields than marshaled struct ([whyrusleeping/cbor-gen#50](https://github.com/whyrusleeping/cbor-gen/pull/50)) + - feat: allow unmarshalling of struct with more fields than marshaled struct ([whyrusleeping/cbor-gen#50](https://github.com/whyrusleeping/cbor-gen/pull/50)) - chore: add a license file ([whyrusleeping/cbor-gen#49](https://github.com/whyrusleeping/cbor-gen/pull/49)) - fix: enforce maxlen in ReadByteArray() ([whyrusleeping/cbor-gen#43](https://github.com/whyrusleeping/cbor-gen/pull/43)) - use unix nanoseconds for encoding Cbortime ([whyrusleeping/cbor-gen#41](https://github.com/whyrusleeping/cbor-gen/pull/41)) diff --git a/docs/command-completion.md b/docs/command-completion.md index b84483e61..207f611bf 100644 --- a/docs/command-completion.md +++ b/docs/command-completion.md @@ -24,3 +24,16 @@ The simplest way to use the completions logic: To install the completions permanently, they can be moved to `/etc/fish/completions` or `~/.config/fish/completions` or sourced from your `~/.config/fish/config.fish` file. + +## ZSH + +The zsh shell is also supported: + +The simplest way to "eval" the completions logic: + +```bash +> eval "$(ipfs commands completion zsh)" +``` + +To install the completions permanently, they can be moved to +`/etc/bash_completion.d` or sourced from your `~/.zshrc` file. diff --git a/docs/config.md b/docs/config.md index 87f2cf1dc..e2b169452 100644 --- a/docs/config.md +++ b/docs/config.md @@ -9,16 +9,6 @@ config file at runtime. - [The Kubo config file](#the-kubo-config-file) - [Table of Contents](#table-of-contents) - - [Profiles](#profiles) - - [Types](#types) - - [`flag`](#flag) - - [`priority`](#priority) - - [`strings`](#strings) - - [`duration`](#duration) - - [`optionalInteger`](#optionalinteger) - - [`optionalBytes`](#optionalbytes) - - [`optionalString`](#optionalstring) - - [`optionalDuration`](#optionalduration) - [`Addresses`](#addresses) - [`Addresses.API`](#addressesapi) - [`Addresses.Gateway`](#addressesgateway) @@ -28,12 +18,21 @@ config file at runtime. - [`Addresses.NoAnnounce`](#addressesnoannounce) - [`API`](#api) - [`API.HTTPHeaders`](#apihttpheaders) + - [`API.Authorizations`](#apiauthorizations) + - [`API.Authorizations: AuthSecret`](#apiauthorizations-authsecret) + - [`API.Authorizations: AllowedPaths`](#apiauthorizations-allowedpaths) - [`AutoNAT`](#autonat) - [`AutoNAT.ServiceMode`](#autonatservicemode) - [`AutoNAT.Throttle`](#autonatthrottle) - [`AutoNAT.Throttle.GlobalLimit`](#autonatthrottlegloballimit) - [`AutoNAT.Throttle.PeerLimit`](#autonatthrottlepeerlimit) - [`AutoNAT.Throttle.Interval`](#autonatthrottleinterval) + - [`AutoTLS`](#autotls) + - [`AutoTLS.Enabled`](#autotlsenabled) + - [`AutoTLS.DomainSuffix`](#autotlsdomainsuffix) + - [`AutoTLS.RegistrationEndpoint`](#autotlsregistrationendpoint) + - [`AutoTLS.RegistrationToken`](#autotlsregistrationtoken) + - [`AutoTLS.CAEndpoint`](#autotlscaendpoint) - [`Bootstrap`](#bootstrap) - [`Datastore`](#datastore) - [`Datastore.StorageMax`](#datastorestoragemax) @@ -50,6 +49,9 @@ config file at runtime. - [`Gateway`](#gateway) - [`Gateway.NoFetch`](#gatewaynofetch) - [`Gateway.NoDNSLink`](#gatewaynodnslink) + - [`Gateway.DeserializedResponses`](#gatewaydeserializedresponses) + - [`Gateway.DisableHTMLErrors`](#gatewaydisablehtmlerrors) + - [`Gateway.ExposeRoutingAPI`](#gatewayexposeroutingapi) - [`Gateway.HTTPHeaders`](#gatewayhttpheaders) - [`Gateway.RootRedirect`](#gatewayrootredirect) - [`Gateway.FastDirIndexThreshold`](#gatewayfastdirindexthreshold) @@ -60,6 +62,7 @@ config file at runtime. - [`Gateway.PublicGateways: UseSubdomains`](#gatewaypublicgateways-usesubdomains) - [`Gateway.PublicGateways: NoDNSLink`](#gatewaypublicgateways-nodnslink) - [`Gateway.PublicGateways: InlineDNSLink`](#gatewaypublicgateways-inlinednslink) + - [`Gateway.PublicGateways: DeserializedResponses`](#gatewaypublicgateways-deserializedresponses) - [Implicit defaults of `Gateway.PublicGateways`](#implicit-defaults-of-gatewaypublicgateways) - [`Gateway` recipes](#gateway-recipes) - [`Identity`](#identity) @@ -71,11 +74,13 @@ config file at runtime. - [`Internal.Bitswap.EngineBlockstoreWorkerCount`](#internalbitswapengineblockstoreworkercount) - [`Internal.Bitswap.EngineTaskWorkerCount`](#internalbitswapenginetaskworkercount) - [`Internal.Bitswap.MaxOutstandingBytesPerPeer`](#internalbitswapmaxoutstandingbytesperpeer) + - [`Internal.Bitswap.ProviderSearchDelay`](#internalbitswapprovidersearchdelay) - [`Internal.UnixFSShardingSizeThreshold`](#internalunixfsshardingsizethreshold) - [`Ipns`](#ipns) - [`Ipns.RepublishPeriod`](#ipnsrepublishperiod) - [`Ipns.RecordLifetime`](#ipnsrecordlifetime) - [`Ipns.ResolveCacheSize`](#ipnsresolvecachesize) + - [`Ipns.MaxCacheTTL`](#ipnsmaxcachettl) - [`Ipns.UsePubsub`](#ipnsusepubsub) - [`Migration`](#migration) - [`Migration.DownloadSources`](#migrationdownloadsources) @@ -99,6 +104,7 @@ config file at runtime. - [`Pubsub.Router`](#pubsubrouter) - [`Pubsub.DisableSigning`](#pubsubdisablesigning) - [`Pubsub.SeenMessagesTTL`](#pubsubseenmessagesttl) + - [`Pubsub.SeenMessagesStrategy`](#pubsubseenmessagesstrategy) - [`Peering`](#peering) - [`Peering.Peers`](#peeringpeers) - [`Reprovider`](#reprovider) @@ -106,6 +112,8 @@ config file at runtime. - [`Reprovider.Strategy`](#reproviderstrategy) - [`Routing`](#routing) - [`Routing.Type`](#routingtype) + - [`Routing.AcceleratedDHTClient`](#routingaccelerateddhtclient) + - [`Routing.LoopbackAddressesOnLanDHT`](#routingloopbackaddressesonlandht) - [`Routing.Routers`](#routingrouters) - [`Routing.Routers: Type`](#routingrouters-type) - [`Routing.Routers: Parameters`](#routingrouters-parameters) @@ -144,7 +152,6 @@ config file at runtime. - [`Swarm.ResourceMgr.Enabled`](#swarmresourcemgrenabled) - [`Swarm.ResourceMgr.MaxMemory`](#swarmresourcemgrmaxmemory) - [`Swarm.ResourceMgr.MaxFileDescriptors`](#swarmresourcemgrmaxfiledescriptors) - - [`Swarm.ResourceMgr.Limits`](#swarmresourcemgrlimits) - [`Swarm.ResourceMgr.Allowlist`](#swarmresourcemgrallowlist) - [`Swarm.Transports`](#swarmtransports) - [`Swarm.Transports.Network`](#swarmtransportsnetwork) @@ -153,7 +160,7 @@ config file at runtime. - [`Swarm.Transports.Network.QUIC`](#swarmtransportsnetworkquic) - [`Swarm.Transports.Network.Relay`](#swarmtransportsnetworkrelay) - [`Swarm.Transports.Network.WebTransport`](#swarmtransportsnetworkwebtransport) - - [How to enable WebTransport](#how-to-enable-webtransport) + - [`Swarm.Transports.Network.WebRTCDirect`](#swarmtransportsnetworkwebrtcdirect) - [`Swarm.Transports.Security`](#swarmtransportssecurity) - [`Swarm.Transports.Security.TLS`](#swarmtransportssecuritytls) - [`Swarm.Transports.Security.SECIO`](#swarmtransportssecuritysecio) @@ -164,169 +171,38 @@ config file at runtime. - [`DNS`](#dns) - [`DNS.Resolvers`](#dnsresolvers) - [`DNS.MaxCacheTTL`](#dnsmaxcachettl) - -## Profiles - -Configuration profiles allow to tweak configuration quickly. Profiles can be -applied with the `--profile` flag to `ipfs init` or with the `ipfs config profile -apply` command. When a profile is applied a backup of the configuration file -will be created in `$IPFS_PATH`. - -The available configuration profiles are listed below. You can also find them -documented in `ipfs config profile --help`. - -- `server` - - Disables local host discovery, recommended when - running IPFS on machines with public IPv4 addresses. - -- `randomports` - - Use a random port number for the incoming swarm connections. - -- `default-datastore` - - Configures the node to use the default datastore (flatfs). - - Read the "flatfs" profile description for more information on this datastore. - - This profile may only be applied when first initializing the node. - -- `local-discovery` - - Enables local discovery (enabled by default). Useful to re-enable local discovery after it's - disabled by another profile (e.g., the server profile). - -- `test` - - Reduces external interference of IPFS daemon, this - is useful when using the daemon in test environments. - -- `default-networking` - - Restores default network settings. - Inverse profile of the test profile. - -- `flatfs` - - Configures the node to use the flatfs datastore. Flatfs is the default datastore. - - This is the most battle-tested and reliable datastore. - You should use this datastore if: - - - You need a very simple and very reliable datastore, and you trust your - filesystem. This datastore stores each block as a separate file in the - underlying filesystem so it's unlikely to lose data unless there's an issue - with the underlying file system. - - You need to run garbage collection in a way that reclaims free space as soon as possible. - - You want to minimize memory usage. - - You are ok with the default speed of data import, or prefer to use `--nocopy`. - - This profile may only be applied when first initializing the node. - - -- `badgerds` - - Configures the node to use the experimental badger datastore. Keep in mind that this **uses an outdated badger 1.x**. - - Use this datastore if some aspects of performance, - especially the speed of adding many gigabytes of files, are critical. However, be aware that: - - - This datastore will not properly reclaim space when your datastore is - smaller than several gigabytes. If you run IPFS with `--enable-gc`, you plan on storing very little data in - your IPFS node, and disk usage is more critical than performance, consider using - `flatfs`. - - This datastore uses up to several gigabytes of memory. - - Good for medium-size datastores, but may run into performance issues if your dataset is bigger than a terabyte. - - The current implementation is based on old badger 1.x which is no longer supported by the upstream team. - - This profile may only be applied when first initializing the node. - -- `lowpower` - - Reduces daemon overhead on the system. Affects node - functionality - performance of content discovery and data - fetching may be degraded. Local data won't be announced on routing systems like DHT. - - - `Swarm.ConnMgr` set to maintain minimum number of p2p connections at a time. - - Disables [`Reprovider`](#reprovider) service → no CID will be announced on DHT and other routing systems(!) - - Disables AutoNAT. - - Use this profile with caution. - -## Types - -This document refers to the standard JSON types (e.g., `null`, `string`, -`number`, etc.), as well as a few custom types, described below. - -### `flag` - -Flags allow enabling and disabling features. However, unlike simple booleans, -they can also be `null` (or omitted) to indicate that the default value should -be chosen. This makes it easier for Kubo to change the defaults in the -future unless the user _explicitly_ sets the flag to either `true` (enabled) or -`false` (disabled). Flags have three possible states: - -- `null` or missing (apply the default value). -- `true` (enabled) -- `false` (disabled) - -### `priority` - -Priorities allow specifying the priority of a feature/protocol and disabling the -feature/protocol. Priorities can take one of the following values: - -- `null`/missing (apply the default priority, same as with flags) -- `false` (disabled) -- `1 - 2^63` (priority, lower is preferred) - -### `strings` - -Strings is a special type for conveniently specifying a single string, an array -of strings, or null: - -- `null` -- `"a single string"` -- `["an", "array", "of", "strings"]` - -### `duration` - -Duration is a type for describing lengths of time, using the same format go -does (e.g, `"1d2h4m40.01s"`). - -### `optionalInteger` - -Optional integers allow specifying some numerical value which has -an implicit default when missing from the config file: - -- `null`/missing will apply the default value defined in Kubo sources (`.WithDefault(value)`) -- an integer between `-2^63` and `2^63-1` (i.e. `-9223372036854775808` to `9223372036854775807`) - -### `optionalBytes` - -Optional Bytes allow specifying some number of bytes which has -an implicit default when missing from the config file: - -- `null`/missing (apply the default value defined in Kubo sources) -- a string value indicating the number of bytes, including human readable representations: - - [SI sizes](https://en.wikipedia.org/wiki/Metric_prefix#List_of_SI_prefixes) (metric units, powers of 1000), e.g. `1B`, `2kB`, `3MB`, `4GB`, `5TB`, …) - - [IEC sizes](https://en.wikipedia.org/wiki/Binary_prefix#IEC_prefixes) (binary units, powers of 1024), e.g. `1B`, `2KiB`, `3MiB`, `4GiB`, `5TiB`, …) - -### `optionalString` - -Optional strings allow specifying some string value which has -an implicit default when missing from the config file: - -- `null`/missing will apply the default value defined in Kubo sources (`.WithDefault("value")`) -- a string - -### `optionalDuration` - -Optional durations allow specifying some duration value which has -an implicit default when missing from the config file: - -- `null`/missing will apply the default value defined in Kubo sources (`.WithDefault("1h2m3s")`) -- a string with a valid [go duration](#duration) (e.g, `"1d2h4m40.01s"`). + - [`Import`](#import) + - [`Import.CidVersion`](#importcidversion) + - [`Import.UnixFSRawLeaves`](#importunixfsrawleaves) + - [`Import.UnixFSChunker`](#importunixfschunker) + - [`Import.HashFunction`](#importhashfunction) + - [`Version`](#version) + - [`Version.AgentSuffix`](#versionagentsuffix) + - [`Version.SwarmCheckEnabled`](#versionswarmcheckenabled) + - [`Version.SwarmCheckPercentThreshold`](#versionswarmcheckpercentthreshold) + - [Profiles](#profiles) + - [`server` profile](#server-profile) + - [`randomports` profile](#randomports-profile) + - [`default-datastore` profile](#default-datastore-profile) + - [`local-discovery` profile](#local-discovery-profile) + - [`default-networking` profile](#default-networking-profile) + - [`flatfs` profile](#flatfs-profile) + - [`pebbleds` profile](#pebbleds-profile) + - [`badgerds` profile](#badgerds-profile) + - [`lowpower` profile](#lowpower-profile) + - [`announce-off` profile](#announce-off-profile) + - [`announce-on` profile](#announce-on-profile) + - [`legacy-cid-v0` profile](#legacy-cid-v0-profile) + - [`test-cid-v1` profile](#test-cid-v1-profile) + - [Types](#types) + - [`flag`](#flag) + - [`priority`](#priority) + - [`strings`](#strings) + - [`duration`](#duration) + - [`optionalInteger`](#optionalinteger) + - [`optionalBytes`](#optionalbytes) + - [`optionalString`](#optionalstring) + - [`optionalDuration`](#optionalduration) ## `Addresses` @@ -334,8 +210,8 @@ Contains information about various listener addresses to be used by this node. ### `Addresses.API` -Multiaddr or array of multiaddrs describing the address to serve the local HTTP -API on. +[Multiaddr][multiaddr] or array of multiaddrs describing the address to serve +the local [Kubo RPC API](https://docs.ipfs.tech/reference/kubo/rpc/) (`/api/v0`). Supported Transports: @@ -344,12 +220,12 @@ Supported Transports: Default: `/ip4/127.0.0.1/tcp/5001` -Type: `strings` (multiaddrs) +Type: `strings` ([multiaddrs][multiaddr]) ### `Addresses.Gateway` -Multiaddr or array of multiaddrs describing the address to serve the local -gateway on. +[Multiaddr][multiaddr] or array of multiaddrs describing the address to serve +the local [HTTP gateway](https://specs.ipfs.tech/http-gateways/) (`/ipfs`, `/ipns`) on. Supported Transports: @@ -358,36 +234,35 @@ Supported Transports: Default: `/ip4/127.0.0.1/tcp/8080` -Type: `strings` (multiaddrs) +Type: `strings` ([multiaddrs][multiaddr]) ### `Addresses.Swarm` -An array of multiaddrs describing which addresses to listen on for p2p swarm +An array of [multiaddrs][multiaddr] describing which addresses to listen on for p2p swarm connections. Supported Transports: * tcp/ip{4,6} - `/ipN/.../tcp/...` * websocket - `/ipN/.../tcp/.../ws` -* quic (Draft-29) - `/ipN/.../udp/.../quic` - can share the same two tuple with `/quic-v1` and `/quic-v1/webtransport` -* quicv1 (RFC9000) - `/ipN/.../udp/.../quic-v1` - can share the same two tuple with `/quic` and `/quic-v1/webtransport` -* webtransport `/ipN/.../udp/.../quic-v1/webtransport` - can share the same two tuple with `/quic` and `/quic-v1` +* quicv1 (RFC9000) - `/ipN/.../udp/.../quic-v1` - can share the same two tuple with `/quic-v1/webtransport` +* webtransport `/ipN/.../udp/.../quic-v1/webtransport` - can share the same two tuple with `/quic-v1` + +Note that quic (Draft-29) used to be supported with the format `/ipN/.../udp/.../quic`, but has since been [removed](https://github.com/libp2p/go-libp2p/releases/tag/v0.30.0). Default: ```json [ "/ip4/0.0.0.0/tcp/4001", "/ip6/::/tcp/4001", - "/ip4/0.0.0.0/udp/4001/quic", "/ip4/0.0.0.0/udp/4001/quic-v1", "/ip4/0.0.0.0/udp/4001/quic-v1/webtransport", - "/ip6/::/udp/4001/quic", "/ip6/::/udp/4001/quic-v1", "/ip6/::/udp/4001/quic-v1/webtransport" ] ``` -Type: `array[string]` (multiaddrs) +Type: `array[string]` ([multiaddrs][multiaddr]) ### `Addresses.Announce` @@ -396,7 +271,7 @@ network. If empty, the daemon will announce inferred swarm addresses. Default: `[]` -Type: `array[string]` (multiaddrs) +Type: `array[string]` ([multiaddrs][multiaddr]) ### `Addresses.AppendAnnounce` @@ -405,27 +280,35 @@ override inferred swarm addresses if non-empty. Default: `[]` -Type: `array[string]` (multiaddrs) +Type: `array[string]` ([multiaddrs][multiaddr]) ### `Addresses.NoAnnounce` An array of swarm addresses not to announce to the network. Takes precedence over `Addresses.Announce` and `Addresses.AppendAnnounce`. +> [!TIP] +> The [`server` configuration profile](#server-profile) fills up this list with sensible defaults, +> preventing announcement of non-routable IP addresses (e.g., `/ip4/192.168.0.0/ipcidr/16`, +> which is the [multiaddress][multiaddr] representation of `192.168.0.0/16`) but you should always +> check settings against your own network and/or hosting provider. + Default: `[]` -Type: `array[string]` (multiaddrs) +Type: `array[string]` ([multiaddrs][multiaddr]) ## `API` -Contains information used by the API gateway. + +Contains information used by the [Kubo RPC API](https://docs.ipfs.tech/reference/kubo/rpc/). ### `API.HTTPHeaders` -Map of HTTP headers to set on responses from the API HTTP server. + +Map of HTTP headers to set on responses from the RPC (`/api/v0`) HTTP server. Example: ```json { - "Foo": ["bar"] + "Foo": ["bar"] } ``` @@ -433,9 +316,90 @@ Default: `null` Type: `object[string -> array[string]]` (header names -> array of header values) +### `API.Authorizations` + +The `API.Authorizations` field defines user-based access restrictions for the +[Kubo RPC API](https://docs.ipfs.tech/reference/kubo/rpc/), which is located at +`Addresses.API` under `/api/v0` paths. + +By default, the RPC API is accessible without restrictions as it is only +exposed on `127.0.0.1` and safeguarded with Origin check and implicit +[CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) headers that +block random websites from accessing the RPC. + +When entries are defined in `API.Authorizations`, RPC requests will be declined +unless a corresponding secret is present in the HTTP [`Authorization` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization), +and the requested path is included in the `AllowedPaths` list for that specific +secret. + +Default: `null` + +Type: `object[string -> object]` (user name -> authorization object, see bellow) + +For example, to limit RPC access to Alice (access `id` and MFS `files` commands with HTTP Basic Auth) +and Bob (full access with Bearer token): + +```json +{ + "API": { + "Authorizations": { + "Alice": { + "AuthSecret": "basic:alice:password123", + "AllowedPaths": ["/api/v0/id", "/api/v0/files"] + }, + "Bob": { + "AuthSecret": "bearer:secret-token123", + "AllowedPaths": ["/api/v0"] + } + } + } +} + +``` + +#### `API.Authorizations: AuthSecret` + +The `AuthSecret` field denotes the secret used by a user to authenticate, +usually via HTTP [`Authorization` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization). + +Field format is `type:value`, and the following types are supported: + +- `bearer:` For secret Bearer tokens, set as `bearer:token`. + - If no known `type:` prefix is present, `bearer:` is assumed. +- `basic`: For HTTP Basic Auth introduced in [RFC7617](https://datatracker.ietf.org/doc/html/rfc7617). Value can be: + - `basic:user:pass` + - `basic:base64EncodedBasicAuth` + +One can use the config value for authentication via the command line: + +``` +ipfs id --api-auth basic:user:pass +``` + +Type: `string` + +#### `API.Authorizations: AllowedPaths` + +The `AllowedPaths` field is an array of strings containing allowed RPC path +prefixes. Users authorized with the related `AuthSecret` will only be able to +access paths prefixed by the specified prefixes. + +For instance: + +- If set to `["/api/v0"]`, the user will have access to the complete RPC API. +- If set to `["/api/v0/id", "/api/v0/files"]`, the user will only have access + to the `id` command and all MFS commands under `files`. + +Note that `/api/v0/version` is always permitted access to allow version check +to ensure compatibility. + +Default: `[]` + +Type: `array[string]` + ## `AutoNAT` -Contains the configuration options for the AutoNAT service. The AutoNAT service +Contains the configuration options for the libp2p's [AutoNAT](https://github.com/libp2p/specs/tree/master/autonat) service. The AutoNAT service helps other nodes on the network determine if they're publicly reachable from the rest of the internet. @@ -444,17 +408,26 @@ the rest of the internet. When unset (default), the AutoNAT service defaults to _enabled_. Otherwise, this field can take one of two values: -* "enabled" - Enable the service (unless the node determines that it, itself, - isn't reachable by the public internet). -* "disabled" - Disable the service. +* `enabled` - Enable the V1+V2 service (unless the node determines that it, + itself, isn't reachable by the public internet). +* `legacy-v1` - Same as `enabled` but only V1 service is enabled. Used for testing + during as few releases as we [transition to V2](https://github.com/ipfs/kubo/issues/10091), will be removed in the future. +* `disabled` - Disable the service. Additional modes may be added in the future. -Type: `string` (one of `"enabled"` or `"disabled"`) +> [!IMPORTANT] +> We are in the progress of [rolling out AutoNAT V2](https://github.com/ipfs/kubo/issues/10091). +> Right now, by default, a publicly dialable Kubo provides both V1 and V2 service to other peers, +> but only V1 is used by Kubo as a client. In a future release we will remove V1 and switch client to use V2. + +Default: `enabled` + +Type: `optionalString` ### `AutoNAT.Throttle` -When set, this option configure's the AutoNAT services throttling behavior. By +When set, this option configures the AutoNAT services throttling behavior. By default, Kubo will rate-limit the number of NAT checks performed for other nodes to 30 per minute, and 3 per peer. @@ -482,13 +455,121 @@ Default: 1 Minute Type: `duration` (when `0`/unset, the default value is used) +## `AutoTLS` + +> [!CAUTION] +> This is an **EXPERIMENTAL** opt-in feature and should not be used in production yet. +> Feel free to enable it and [report issues](https://github.com/ipfs/kubo/issues/new/choose) if you want to help with testing. +> Track progress in [kubo#10560](https://github.com/ipfs/kubo/issues/10560). + +AutoTLS feature enables publicly reachable Kubo nodes (those dialable from the public +internet) to automatically obtain a wildcard TLS certificate for a DNS name +unique to their PeerID at `*.[PeerID].libp2p.direct`. This enables direct +libp2p connections and retrieval of IPFS content from browsers [Secure Context](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts) +using transports such as [Secure WebSockets](https://github.com/libp2p/specs/blob/master/websockets/README.md), +without requiring user to do any manual domain registration and ceritficate configuration. + +Under the hood, [p2p-forge] client uses public utility service at `libp2p.direct` as an [ACME DNS-01 Challenge](https://letsencrypt.org/docs/challenge-types/#dns-01-challenge) +broker enabling peer to obtain a wildcard TLS certificate tied to public key of their [PeerID](https://docs.libp2p.io/concepts/fundamentals/peers/#peer-id). + +By default, the certificates are requested from Let's Encrypt. Origin and rationale for this project can be found in [community.letsencrypt.org discussion](https://community.letsencrypt.org/t/feedback-on-raising-certificates-per-registered-domain-to-enable-peer-to-peer-networking/223003). + +> [!NOTE] +> Public good DNS and [p2p-forge] infrastructure at `libp2p.direct` is run by the team at [Interplanetary Shipyard](https://ipshipyard.com). +> +> + +[p2p-forge]: https://github.com/ipshipyard/p2p-forge + +Default: `{}` + +Type: `object` + +### `AutoTLS.Enabled` + +> [!CAUTION] +> This is an **EXPERIMENTAL** opt-in feature and should not be used in production yet. +> Feel free to enable it and [report issues](https://github.com/ipfs/kubo/issues/new/choose) if you want to help with testing. +> Track progress in [kubo#10560](https://github.com/ipfs/kubo/issues/10560). + +Enables AutoTLS feature to get DNS+TLS for [libp2p Secure WebSocket](https://github.com/libp2p/specs/blob/master/websockets/README.md) listeners defined in [`Addresses.Swarm`](#addressesswarm), such as `/ip4/0.0.0.0/tcp/4002/tls/sni/*.libp2p.direct/ws` and `/ip6/::/tcp/4002/tls/sni/*.libp2p.direct/ws`. + +If `.../tls/sni/*.libp2p.direct/ws` [multiaddr] is present in [`Addresses.Swarm`](#addressesswarm) +with SNI segment ending with [`AutoTLS.DomainSuffix`](#autotlsdomainsuffix), +Kubo will obtain and set up a trusted PKI TLS certificate for it, making it diallable from web browser's [Secure Contexts](https://w3c.github.io/webappsec-secure-contexts/). + +> [!IMPORTANT] +> Caveats: +> - Requires your Kubo node to be publicly diallable. +> - If you want to test this with a node that is behind a NAT and uses manual port forwarding or UPnP (`Swarm.DisableNatPortMap=false`), +> add catch-all `/ip4/0.0.0.0/tcp/4002/tls/sni/*.libp2p.direct/ws` and `/ip6/::/tcp/4002/tls/sni/*.libp2p.direct/ws` to [`Addresses.Swarm`](#addressesswarm) +> and **wait 5-15 minutes** for libp2p node to set up and learn about own public addresses via [AutoNAT](#autonat). +> - If your node is fresh and just started, the [p2p-forge] client may produce and log ERRORs during this time, but once a publicly diallable addresses are set up, a subsequent retry should be successful. +> - Listeners defined in [`Addresses.Swarm`](#addressesswarm) with `/tls/sni` must use a separate port from other TCP listeners, e.g. `4002` instead of the default `4001`. +> - A separate port (`/tcp/4002`) has to be used instead of `/tcp/4001` because we wait for TCP port sharing ([go-libp2p#2984](https://github.com/libp2p/go-libp2p/issues/2684)) to be implemented. +> - If you use manual port forwarding, make sure incoming connections to this additional port are allowed the same way `4001` ones already are. +> - The TLS certificate is used only for [libp2p WebSocket](https://github.com/libp2p/specs/blob/master/websockets/README.md) connections. +> - Right now, this is NOT used for hosting a [Gateway](#gateway) over HTTPS (that use case still requires manual TLS setup on reverse proxy, and your own domain). + +> [!TIP] +> - Debugging can be enabled by setting environment variable `GOLOG_LOG_LEVEL="error,autotls=debug,p2p-forge/client=debug"` +> - Certificates are stored in `$IPFS_PATH/p2p-forge-certs`. Removing directory and restarting daemon will trigger certificate rotation. + +Default: `false` + +Type: `flag` + +### `AutoTLS.DomainSuffix` + +Optional override of the parent domain suffix that will be used in DNS+TLS+WebSockets multiaddrs generated by [p2p-forge] client. +Do not change this unless you self-host [p2p-forge]. + +Default: `libp2p.direct` (public good run by [Interplanetary Shipyard](https://ipshipyard.com)) + +Type: `optionalString` + +### `AutoTLS.RegistrationEndpoint` + +Optional override of [p2p-forge] HTTP registration API. +Do not change this unless you self-host [p2p-forge] under own domain. + +> [!IMPORTANT] +> The default endpoint performs [libp2p Peer ID Authentication over HTTP](https://github.com/libp2p/specs/blob/master/http/peer-id-auth.md) +> (prooving ownership of PeerID), probes if your Kubo node can correctly answer to a [libp2p Identify](https://github.com/libp2p/specs/tree/master/identify) query. +> This ensures only a correctly configured, publicly diallable Kubo can initiate [ACME DNS-01 challenge](https://letsencrypt.org/docs/challenge-types/#dns-01-challenge) for `peerid.libp2p.direct`. + +Default: `https://registration.libp2p.direct` (public good run by [Interplanetary Shipyard](https://ipshipyard.com)) + +Type: `optionalString` + +### `AutoTLS.RegistrationToken` + +Optional value for `Forge-Authorization` token sent with request to `RegistrationEndpoint` +(useful for private/self-hosted/test instances of [p2p-forge], unset by default). + +Default: `""` + +Type: `optionalString` + +### `AutoTLS.CAEndpoint` + +Optional override of CA ACME API used by [p2p-forge] system. +Do not change this unless you self-host [p2p-forge] under own domain. + +> [!IMPORTANT] +> CAA DNS record at `libp2p.direct` limits CA choice to Let's Encrypt. If you want to use a different CA, use your own domain. + +Default: [certmagic.LetsEncryptProductionCA](https://pkg.go.dev/github.com/caddyserver/certmagic#pkg-constants) (see [community.letsencrypt.org discussion](https://community.letsencrypt.org/t/feedback-on-raising-certificates-per-registered-domain-to-enable-peer-to-peer-networking/223003)) + +Type: `optionalString` + ## `Bootstrap` -Bootstrap is an array of multiaddrs of trusted nodes that your node connects to, to fetch other nodes of the network on startup. +Bootstrap is an array of [multiaddrs][multiaddr] of trusted nodes that your node connects to, to fetch other nodes of the network on startup. Default: The ipfs.io bootstrap nodes -Type: `array[string]` (multiaddrs) +Type: `array[string]` ([multiaddrs][multiaddr]) ## `Datastore` @@ -558,39 +639,34 @@ Spec defines the structure of the ipfs datastore. It is a composable structure, where each datastore is represented by a json object. Datastores can wrap other datastores to provide extra functionality (eg metrics, logging, or caching). -This can be changed manually, however, if you make any changes that require a -different on-disk structure, you will need to run the [ipfs-ds-convert -tool](https://github.com/ipfs/ipfs-ds-convert) to migrate data into the new -structures. - -For more information on possible values for this configuration option, see -[docs/datastores.md](datastores.md) +> [!NOTE] +> For more information on possible values for this configuration option, see [`kubo/docs/datastores.md`](datastores.md) Default: ``` { "mounts": [ - { - "child": { - "path": "blocks", - "shardFunc": "/repo/flatfs/shard/v1/next-to-last/2", - "sync": true, - "type": "flatfs" - }, - "mountpoint": "/blocks", - "prefix": "flatfs.datastore", - "type": "measure" - }, - { - "child": { - "compression": "none", - "path": "datastore", - "type": "levelds" - }, - "mountpoint": "/", - "prefix": "leveldb.datastore", - "type": "measure" - } + { + "child": { + "path": "blocks", + "shardFunc": "/repo/flatfs/shard/v1/next-to-last/2", + "sync": true, + "type": "flatfs" + }, + "mountpoint": "/blocks", + "prefix": "flatfs.datastore", + "type": "measure" + }, + { + "child": { + "compression": "none", + "path": "datastore", + "type": "levelds" + }, + "mountpoint": "/", + "prefix": "leveldb.datastore", + "type": "measure" + } ], "type": "mount" } @@ -616,7 +692,7 @@ Type: `bool` #### `Discovery.MDNS.Interval` -**REMOVED:** this is not configurable any more +**REMOVED:** this is not configurable anymore in the [new mDNS implementation](https://github.com/libp2p/zeroconf#readme). ## `Experimental` @@ -627,6 +703,8 @@ Toggle and configure experimental features of Kubo. Experimental features are li Options for the HTTP gateway. +**NOTE:** support for `/api/v0` under the gateway path is now deprecated. It will be removed in future versions: https://github.com/ipfs/kubo/issues/10312. + ### `Gateway.NoFetch` When set to true, the gateway will only serve content already in the local repo @@ -646,24 +724,49 @@ Default: `false` Type: `bool` +### `Gateway.DeserializedResponses` + +An optional flag to explicitly configure whether this gateway responds to deserialized +requests, or not. By default, it is enabled. When disabling this option, the gateway +operates as a Trustless Gateway only: https://specs.ipfs.tech/http-gateways/trustless-gateway/. + +Default: `true` + +Type: `flag` + +### `Gateway.DisableHTMLErrors` + +An optional flag to disable the pretty HTML error pages of the gateway. Instead, +a `text/plain` page will be returned with the raw error message from Kubo. + +It is useful for whitelabel or middleware deployments that wish to avoid +`text/html` responses with IPFS branding and links on error pages in browsers. + +Default: `false` + +Type: `flag` + +### `Gateway.ExposeRoutingAPI` + +An optional flag to expose Kubo `Routing` system on the gateway port +as an [HTTP `/routing/v1`](https://specs.ipfs.tech/routing/http-routing-v1/) endpoint on `127.0.0.1`. +Use reverse proxy to expose it on a different hostname. + +This endpoint can be used by other Kubo instances, as illustrated in +[`delegated_routing_v1_http_proxy_test.go`](https://github.com/ipfs/kubo/blob/master/test/cli/delegated_routing_v1_http_proxy_test.go). +Kubo will filter out routing results which are not actionable, for example, all +graphsync providers will be skipped. If you need a generic pass-through, see +standalone router implementation named [someguy](https://github.com/ipfs/someguy). + +Default: `false` + +Type: `flag` + ### `Gateway.HTTPHeaders` Headers to set on gateway responses. -Default: -```json -{ - "Access-Control-Allow-Headers": [ - "X-Requested-With" - ], - "Access-Control-Allow-Methods": [ - "GET" - ], - "Access-Control-Allow-Origin": [ - "*" - ] -} -``` +Default: `{}` + implicit CORS headers from `boxo/gateway#AddAccessControlHeaders` and [ipfs/specs#423](https://github.com/ipfs/specs/issues/423) Type: `object[string -> array[string]]` @@ -681,11 +784,9 @@ Type: `string` (url) ### `Gateway.Writable` -A boolean to configure whether the gateway is writeable or not. +**REMOVED**: this option no longer available as of [Kubo 0.20](https://github.com/ipfs/kubo/blob/master/docs/changelogs/v0.20.md). -Default: `false` - -Type: `bool` +We are working on developing a modern replacement. To support our efforts, please leave a comment describing your use case in [ipfs/specs#375](https://github.com/ipfs/specs/issues/375). ### `Gateway.PathPrefixes` @@ -693,7 +794,18 @@ Type: `bool` ### `Gateway.PublicGateways` -`PublicGateways` is a dictionary for defining gateway behavior on specified hostnames. +> [!IMPORTANT] +> This configuration is **NOT** for HTTP Client, it is for HTTP Server – use this ONLY if you want to run your own IPFS gateway. + +`PublicGateways` is a configuration map used for dictionary for customizing gateway behavior +on specified hostnames that point at your Kubo instance. + +It is useful when you want to run [Path gateway](https://specs.ipfs.tech/http-gateways/path-gateway/) on `example.com/ipfs/cid`, +and [Subdomain gateway](https://specs.ipfs.tech/http-gateways/subdomain-gateway/) on `cid.ipfs.example.org`, +or limit `verifiable.example.net` to response types defined in [Trustless Gateway](https://specs.ipfs.tech/http-gateways/trustless-gateway/) specification. + +> [!CAUTION] +> Keys (Hostnames) MUST be unique. Do not use the same parent domain for multiple gateway types, it will break origin isolation. Hostnames can optionally be defined with one or more wildcards. @@ -711,14 +823,14 @@ Example: "Gateway": { "PublicGateways": { "example.com": { - "Paths": ["/ipfs", "/ipns"], + "Paths": ["/ipfs"], } } } } ``` -Above enables `http://example.com/ipfs/*` and `http://example.com/ipns/*` but not `http://example.com/api/*` +Above enables `http://example.com/ipfs/*` but not `http://example.com/ipns/*` Default: `[]` @@ -726,7 +838,9 @@ Type: `array[string]` #### `Gateway.PublicGateways: UseSubdomains` -A boolean to configure whether the gateway at the hostname provides [Origin isolation](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy) +A boolean to configure whether the gateway at the hostname should be +a [Subdomain Gateway](https://specs.ipfs.tech/http-gateways/subdomain-gateway/) +and provide [Origin isolation](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy) between content roots. - `true` - enables [subdomain gateway](https://docs.ipfs.tech/how-to/address-ipfs-on-web/#subdomain-gateway) at `http://*.{hostname}/` @@ -737,13 +851,12 @@ between content roots. "PublicGateways": { "dweb.link": { "UseSubdomains": true, - "Paths": ["/ipfs", "/ipns"], + "Paths": ["/ipfs", "/ipns"] } } } ``` - **Backward-compatible:** requests for content paths such as `http://{hostname}/ipfs/{cid}` produce redirect to `http://{cid}.ipfs.{hostname}` - - **API:** if `/api` is on the `Paths` whitelist, `http://{hostname}/api/{cmd}` produces redirect to `http://api.{hostname}/api/{cmd}` - `false` - enables [path gateway](https://docs.ipfs.tech/how-to/address-ipfs-on-web/#path-gateway) at `http://{hostname}/*` - Example: @@ -752,7 +865,7 @@ between content roots. "PublicGateways": { "ipfs.io": { "UseSubdomains": false, - "Paths": ["/ipfs", "/ipns", "/api"], + "Paths": ["/ipfs", "/ipns"] } } } @@ -776,7 +889,7 @@ Type: `bool` An optional flag to explicitly configure whether subdomain gateway's redirects (enabled by `UseSubdomains: true`) should always inline a DNSLink name (FQDN) -into a single DNS label: +into a single DNS label ([specification](https://specs.ipfs.tech/http-gateways/subdomain-gateway/#host-request-header)): ``` //example.com/ipns/example.net → HTTP 301 → //example-net.ipns.example.com @@ -792,6 +905,22 @@ Default: `false` Type: `flag` +#### `Gateway.PublicGateways: DeserializedResponses` + +An optional flag to explicitly configure whether this gateway responds to deserialized +requests, or not. By default, it is enabled. + +When disabled, the gateway operates strictly as a [Trustless Gateway](https://specs.ipfs.tech/http-gateways/trustless-gateway/). + +> [!TIP] +> Disabling deserialized responses will protect you from acting as a free web hosting, +> while still allowing trustless clients like [@helia/verified-fetch](https://www.npmjs.com/package/@helia/verified-fetch) +> to utilize it for [trustless, verifiable data retrieval](https://docs.ipfs.tech/reference/http/gateway/#trustless-verifiable-retrieval). + +Default: same as global `Gateway.DeserializedResponses` + +Type: `flag` + #### Implicit defaults of `Gateway.PublicGateways` Default entries for `localhost` hostname and loopback IPs are always present. @@ -851,7 +980,7 @@ Below is a list of the most common public gateway setups. $ ipfs config --json Gateway.PublicGateways '{ "ipfs.io": { "UseSubdomains": false, - "Paths": ["/ipfs", "/ipns", "/api"] + "Paths": ["/ipfs", "/ipns"] } }' ``` @@ -897,7 +1026,7 @@ Type: `string` (base64 encoded) ## `Internal` -This section includes internal knobs for various subsystems to allow advanced users with big or private infrastructures to fine-tune some behaviors without the need to recompile Kubo. +This section includes internal knobs for various subsystems to allow advanced users with big or private infrastructures to fine-tune some behaviors without the need to recompile Kubo. **Be aware that making informed change here requires in-depth knowledge and most users should leave these untouched. All knobs listed here are subject to breaking changes between versions.** @@ -910,7 +1039,7 @@ based on the metrics `ipfs_bitswap_active_tasks`, `ipfs_bitswap_pending_tasks`, `ipfs_bitswap_pending_block_tasks` and `ipfs_bitswap_active_block_tasks` reported by bitswap. -These metrics can be accessed as the prometheus endpoint at `{Addresses.API}/debug/metrics/prometheus` (default: `http://127.0.0.1:5001/debug/metrics/prometheus`) +These metrics can be accessed as the Prometheus endpoint at `{Addresses.API}/debug/metrics/prometheus` (default: `http://127.0.0.1:5001/debug/metrics/prometheus`) The value of `ipfs_bitswap_active_tasks` is capped by `EngineTaskWorkerCount`. @@ -934,7 +1063,7 @@ while `ipfs_bitswap_active_block_tasks` is at its maximum, there is indication t available block tasks is creating a bottleneck (either due to high-latency block operations, or due to high number of block operations per bitswap peer task). In such cases, try increasing the `EngineBlockstoreWorkerCount`. -If this adjustment still does not increase the throuput of the node, there might +If this adjustment still does not increase the throughput of the node, there might be hardware limitations like I/O or CPU. #### `Internal.Bitswap.TaskWorkerCount` @@ -963,13 +1092,21 @@ Type: `optionalInteger` (thread count, `null` means default which is 8) #### `Internal.Bitswap.MaxOutstandingBytesPerPeer` Maximum number of bytes (across all tasks) pending to be processed and sent to any individual peer. -This number controls fairness and can very from 250Kb (very fair) to 10Mb (less fair, with more work +This number controls fairness and can vary from 250Kb (very fair) to 10Mb (less fair, with more work dedicated to peers who ask for more). Values below 250Kb could cause thrashing. Values above 10Mb open the potential for aggressively-wanting peers to consume all resources and deteriorate the quality provided to less aggressively-wanting peers. Type: `optionalInteger` (byte count, `null` means default which is 1MB) +### `Internal.Bitswap.ProviderSearchDelay` + +This parameter determines how long to wait before looking for providers outside of bitswap. +Other routing systems like the Amino DHT are able to provide results in less than a second, so lowering +this number will allow faster peers lookups in some cases. + +Type: `optionalDuration` (`null` means default which is 1s) + ### `Internal.UnixFSShardingSizeThreshold` The sharding threshold used internally to decide whether a UnixFS directory should be sharded or not. @@ -998,7 +1135,7 @@ Type: `interval` or an empty string for the default. A time duration specifying the value to set on ipns records for their validity lifetime. -Default: 24 hours. +Default: 48 hours. Type: `interval` or an empty string for the default. @@ -1011,6 +1148,35 @@ Default: `128` Type: `integer` (non-negative, 0 means the default) +### `Ipns.MaxCacheTTL` + +Maximum duration for which entries are valid in the name system cache. Applied +to everything under `/ipns/` namespace, allows you to cap +the [Time-To-Live (TTL)](https://specs.ipfs.tech/ipns/ipns-record/#ttl-uint64) of +[IPNS Records](https://specs.ipfs.tech/ipns/ipns-record/) +AND also DNSLink TXT records (when DoH-specific [`DNS.MaxCacheTTL`](https://github.com/ipfs/kubo/blob/master/docs/config.md#dnsmaxcachettl) +is not set to a lower value). + +When `Ipns.MaxCacheTTL` is set, it defines the upper bound limit of how long a +[IPNS Name](https://specs.ipfs.tech/ipns/ipns-record/#ipns-name) lookup result +will be cached and read from cache before checking for updates. + +**Examples:** +* `"1m"` IPNS results are cached 1m or less (good compromise for system where + faster updates are desired). +* `"0s"` IPNS caching is effectively turned off (useful for testing, bad for production use) + - **Note:** setting this to `0` will turn off TTL-based caching entirely. + This is discouraged in production environments. It will make IPNS websites + artificially slow because IPNS resolution results will expire as soon as + they are retrieved, forcing expensive IPNS lookup to happen on every + request. If you want near-real-time IPNS, set it to a low, but still + sensible value, such as `1m`. + +Default: No upper bound, [TTL from IPNS Record](https://specs.ipfs.tech/ipns/ipns-record/#ttl-uint64) (see `ipns name publish --help`) is always respected. + + +Type: `optionalDuration` + ### `Ipns.UsePubsub` Enables IPFS over pubsub experiment for publishing IPNS records in real time. @@ -1091,7 +1257,7 @@ Example: "API" : { "Endpoint" : "https://pinningservice.tld:1234/my/api/path", "Key" : "someOpaqueKey" - } + } } } } @@ -1154,13 +1320,15 @@ Type: `duration` ## `Pubsub` +**DEPRECATED**: See [#9717](https://github.com/ipfs/kubo/issues/9717) + Pubsub configures the `ipfs pubsub` subsystem. To use, it must be enabled by passing the `--enable-pubsub-experiment` flag to the daemon or via the `Pubsub.Enabled` flag below. ### `Pubsub.Enabled` -**EXPERIMENTAL:** read about current limitations at [experimental-features.md#ipfs-pubsub](./experimental-features.md#ipfs-pubsub). +**DEPRECATED**: See [#9717](https://github.com/ipfs/kubo/issues/9717) Enables the pubsub system. @@ -1170,6 +1338,8 @@ Type: `flag` ### `Pubsub.Router` +**DEPRECATED**: See [#9717](https://github.com/ipfs/kubo/issues/9717) + Sets the default router used by pubsub to route messages to peers. This can be one of: * `"floodsub"` - floodsub is a basic router that simply _floods_ messages to all @@ -1185,6 +1355,8 @@ Type: `string` (one of `"floodsub"`, `"gossipsub"`, or `""` (apply default)) ### `Pubsub.DisableSigning` +**DEPRECATED**: See [#9717](https://github.com/ipfs/kubo/issues/9717) + Disables message signing and signature verification. Enable this option if you're operating in a completely trusted network. @@ -1198,8 +1370,10 @@ Type: `bool` ### `Pubsub.SeenMessagesTTL` -Configures the duration after which a previously seen Pubsub Message ID can be -forgotten about. +**DEPRECATED**: See [#9717](https://github.com/ipfs/kubo/issues/9717) + +Controls the time window within which duplicate messages, identified by Message +ID, will be identified and won't be emitted again. A smaller value for this parameter means that Pubsub messages in the cache will be garbage collected sooner, which can result in a smaller cache. At the same @@ -1215,6 +1389,31 @@ Default: see `TimeCacheDuration` from [go-libp2p-pubsub](https://github.com/libp Type: `optionalDuration` +### `Pubsub.SeenMessagesStrategy` + +**DEPRECATED**: See [#9717](https://github.com/ipfs/kubo/issues/9717) + +Determines how the time-to-live (TTL) countdown for deduplicating Pubsub +messages is calculated. + +The Pubsub seen messages cache is a LRU cache that keeps messages for up to a +specified time duration. After this duration has elapsed, expired messages will +be purged from the cache. + +The `last-seen` cache is a sliding-window cache. Every time a message is seen +again with the SeenMessagesTTL duration, its timestamp slides forward. This +keeps frequently occurring messages cached and prevents them from being +continually propagated, especially because of issues that might increase the +number of duplicate messages in the network. + +The `first-seen` cache will store new messages and purge them after the +SeenMessagesTTL duration, even if they are seen multiple times within this +duration. + +Default: `last-seen` (see [go-libp2p-pubsub](https://github.com/libp2p/go-libp2p-pubsub)) + +Type: `optionalString` + ## `Peering` Configures the peering subsystem. The peering subsystem configures Kubo to @@ -1266,7 +1465,7 @@ The set of peers with which to peer. }, { "ID": "QmPeerID2", - "Addrs": ["/ip4/18.1.1.2/tcp/4001", "/ip4/18.1.1.2/udp/4001/quic"] + "Addrs": ["/ip4/18.1.1.2/tcp/4001", "/ip4/18.1.1.2/udp/4001/quic-v1"] } ] } @@ -1274,7 +1473,7 @@ The set of peers with which to peer. } ``` -Where `ID` is the peer ID and `Addrs` is a set of known addresses for the peer. If no addresses are specified, the DHT will be queried. +Where `ID` is the peer ID and `Addrs` is a set of known addresses for the peer. If no addresses are specified, the Amino DHT will be queried. Additional fields may be added in the future. @@ -1287,27 +1486,41 @@ Type: `array[peering]` ### `Reprovider.Interval` Sets the time between rounds of reproviding local content to the routing -system. If unset, it defaults to 12 hours. If set to the value `"0"` it will -disable content reproviding. +system. + +- If unset, it uses the implicit safe default. +- If set to the value `"0"` it will disable content reproviding. Note: disabling content reproviding will result in other nodes on the network not being able to discover that you have the objects that you have. If you want to have this disabled and keep the network aware of what you have, you must manually announce your content periodically. -Type: `duration` +Default: `22h` (`DefaultReproviderInterval`) + +Type: `optionalDuration` (unset for the default) ### `Reprovider.Strategy` Tells reprovider what should be announced. Valid strategies are: - `"all"` - announce all CIDs of stored blocks + - Order: root blocks of direct and recursive pins are announced first, then the rest of blockstore - `"pinned"` - only announce pinned CIDs recursively (both roots and child blocks) + - Order: root blocks of direct and recursive pins are announced first, then the child blocks of recursive pins - `"roots"` - only announce the root block of explicitly pinned CIDs + - **⚠️ BE CAREFUL:** node with `roots` strategy will not announce child blocks. + It makes sense only for use cases where the entire DAG is fetched in full, + and a graceful resume does not have to be guaranteed: the lack of child + announcements means an interrupted retrieval won't be able to find + providers for the missing block in the middle of a file, unless the peer + happens to already be connected to a provider and ask for child CID over + bitswap. +- `"flat"` - same as `all`, announce all CIDs of stored blocks, but without prioritizing anything Default: `"all"` -Type: `string` (or unset for the default, which is "all") +Type: `optionalString` (unset for the default) ## `Routing` @@ -1315,15 +1528,17 @@ Contains options for content, peer, and IPNS routing mechanisms. ### `Routing.Type` -There are multiple routing options: "auto", "none", "dht" and "custom". +There are multiple routing options: "auto", "autoclient", "none", "dht", "dhtclient", and "custom". -* **DEFAULT:** If unset, or set to "auto", your node will use the IPFS DHT +* **DEFAULT:** If unset, or set to "auto", your node will use the public IPFS DHT (aka "Amino") and parallel HTTP routers listed below for additional speed. +* If set to "autoclient", your node will behave as in "auto" but without running a DHT server. + * If set to "none", your node will use _no_ routing system. You'll have to explicitly connect to peers that have the content you're looking for. -* If set to "dht" (or "dhtclient"/"dhtserver"), your node will ONLY use the IPFS DHT (no HTTP routers). +* If set to "dht" (or "dhtclient"/"dhtserver"), your node will ONLY use the Amino DHT (no HTTP routers). * If set to "custom", all default routers are disabled, and only ones defined in `Routing.Routers` will be used. @@ -1341,28 +1556,89 @@ When `Routing.Type` is set to `auto` or `dht`, your node will start as a DHT cli switch to a DHT server when and if it determines that it's reachable from the public internet (e.g., it's not behind a firewall). -To force a specific DHT-only mode, client or server, set `Routing.Type` to +To force a specific Amino DHT-only mode, client or server, set `Routing.Type` to `dhtclient` or `dhtserver` respectively. Please do not set this to `dhtserver` unless you're sure your node is reachable from the public network. -When `Routing.Type` is set to `auto` your node will accelerate some types of routing -by leveraging HTTP endpoints compatible with [IPIP-337](https://github.com/ipfs/specs/pull/337) -in addition to the IPFS DHT. +When `Routing.Type` is set to `auto` or `autoclient` your node will accelerate some types of routing +by leveraging HTTP endpoints compatible with [Delegated Routing V1 HTTP API](https://specs.ipfs.tech/routing/http-routing-v1/) +introduced in [IPIP-337](https://github.com/ipfs/specs/pull/337) +in addition to the Amino DHT. By default, an instance of [IPNI](https://github.com/ipni/specs/blob/main/IPNI.md#readme) at https://cid.contact is used. + Alternative routing rules can be configured in `Routing.Routers` after setting `Routing.Type` to `custom`. Default: `auto` (DHT + IPNI) Type: `optionalString` (`null`/missing means the default) + +### `Routing.AcceleratedDHTClient` + +This alternative Amino DHT client with a Full-Routing-Table strategy will +do a complete scan of the DHT every hour and record all nodes found. +Then when a lookup is tried instead of having to go through multiple Kad hops it +is able to find the 20 final nodes by looking up the in-memory recorded network table. + +This means sustained higher memory to store the routing table +and extra CPU and network bandwidth for each network scan. +However the latency of individual read/write operations should be ~10x faster +and provide throughput up to 6 million times faster on larger datasets! + +This is not compatible with `Routing.Type` `custom`. If you are using composable routers +you can configure this individually on each router. + +When it is enabled: +- Client DHT operations (reads and writes) should complete much faster +- The provider will now use a keyspace sweeping mode allowing to keep alive + CID sets that are multiple orders of magnitude larger. + - The standard Bucket-Routing-Table DHT will still run for the DHT server (if + the DHT server is enabled). This means the classical routing table will + still be used to answer other nodes. + This is critical to maintain to not harm the network. +- The operations `ipfs stats dht` will default to showing information about the accelerated DHT client + +**Caveats:** +1. Running the accelerated client likely will result in more resource consumption (connections, RAM, CPU, bandwidth) + - Users that are limited in the number of parallel connections their machines/networks can perform will likely suffer + - The resource usage is not smooth as the client crawls the network in rounds and reproviding is similarly done in rounds + - Users who previously had a lot of content but were unable to advertise it on the network will see an increase in + egress bandwidth as their nodes start to advertise all of their CIDs into the network. If you have lots of data + entering your node that you don't want to advertise, then consider using [Reprovider Strategies](#reproviderstrategy) + to reduce the number of CIDs that you are reproviding. Similarly, if you are running a node that deals mostly with + short-lived temporary data (e.g. you use a separate node for ingesting data then for storing and serving it) then + you may benefit from using [Strategic Providing](experimental-features.md#strategic-providing) to prevent advertising + of data that you ultimately will not have. +2. Currently, the DHT is not usable for queries for the first 5-10 minutes of operation as the routing table is being +prepared. This means operations like searching the DHT for particular peers or content will not work initially. + - You can see if the DHT has been initially populated by running `ipfs stats dht` +3. Currently, the accelerated DHT client is not compatible with LAN-based DHTs and will not perform operations against +them + +Default: `false` + +Type: `flag` + +### `Routing.LoopbackAddressesOnLanDHT` + +**EXPERIMENTAL: `Routing.LoopbackAddressesOnLanDHT` configuration may change in future release** + +Whether loopback addresses (e.g. 127.0.0.1) should not be ignored on the local LAN DHT. + +Most users do not need this setting. It can be useful during testing, when multiple Kubo nodes run on the same machine but some of them do not have `Discovery.MDNS.Enabled`. + +Default: `false` + +Type: `bool` (missing means `false`) + ### `Routing.Routers` **EXPERIMENTAL: `Routing.Routers` configuration may change in future release** Map of additional Routers. -Allows for extending the default routing (DHT) with alternative Router +Allows for extending the default routing (Amino DHT) with alternative Router implementations. The map key is a name of a Router, and the value is its configuration. @@ -1397,8 +1673,8 @@ HTTP: - `MaxProvideConcurrency`: It determines the number of threads used when providing content. GOMAXPROCS by default. DHT: - - `"Mode"`: Mode used by the DHT. Possible values: "server", "client", "auto" - - `"AcceleratedDHTClient"`: Set to `true` if you want to use the experimentalDHT. + - `"Mode"`: Mode used by the Amino DHT. Possible values: "server", "client", "auto" + - `"AcceleratedDHTClient"`: Set to `true` if you want to use the acceleratedDHT. - `"PublicIPNetwork"`: Set to `true` to create a `WAN` DHT. Set to `false` to create a `LAN` DHT. Parallel: @@ -1431,7 +1707,7 @@ Type: `object[string->object]` **Examples:** -Complete example using 2 Routers, DHT (LAN/WAN) and parallel. +Complete example using 2 Routers, Amino DHT (LAN/WAN) and parallel. ``` $ ipfs config Routing.Type --json '"custom"' @@ -1506,10 +1782,11 @@ node will try to connect to one or more private IP addresses whenever dialing another node, even if this other node is on a different network. This may trigger netscan alerts on some hosting providers or cause strain in some setups. -The `server` configuration profile fills up this list with sensible defaults, -preventing dials to all non-routable IP addresses (e.g., `192.168.0.0/16`) but -you should always check settings against your own network and/or hosting -provider. +> [!TIP] +> The [`server` configuration profile](#server-profile) fills up this list with sensible defaults, +> preventing dials to all non-routable IP addresses (e.g., `/ip4/192.168.0.0/ipcidr/16`, +> which is the [multiaddress][multiaddr] representation of `192.168.0.0/16`) but you should always +> check settings against your own network and/or hosting provider. Default: `[]` @@ -1527,7 +1804,7 @@ Type: `bool` ### `Swarm.DisableNatPortMap` -Disable automatic NAT port forwarding. +Disable automatic NAT port forwarding (turn off [UPnP](https://en.wikipedia.org/wiki/Universal_Plug_and_Play)). When not disabled (default), Kubo asks NAT devices (e.g., routers), to open up an external port and forward it to the port Kubo is running on. When this @@ -1581,8 +1858,8 @@ Type: `flag` #### `Swarm.RelayClient.StaticRelays` -Your node will use these statically configured relay servers (V1 or V2) -instead of discovering public relays V2 from the network. +Your node will use these statically configured relay servers +instead of discovering public relays ([Circuit Relay v2](https://github.com/libp2p/specs/blob/master/relay/circuit-v2.md)) from the network. Default: `[]` @@ -1611,7 +1888,7 @@ Type: `flag` #### `Swarm.RelayService.Limit` -Limits applied to every relayed connection. +Limits are applied to every relayed connection. Default: `{}` @@ -1672,12 +1949,7 @@ Type: `optionalInteger` #### `Swarm.RelayService.MaxReservationsPerPeer` -Maximum number of reservations originating from the same peer. - -Default: `4` - -Type: `optionalInteger` - +**REMOVED in kubo 0.32 due to [go-libp2p#2974](https://github.com/libp2p/go-libp2p/pull/2974)** #### `Swarm.RelayService.MaxReservationsPerIP` @@ -1811,7 +2083,9 @@ This value is also used to scale the limit on various resources at various scope when the default limits (discussed in [libp2p resource management](./libp2p-resource-management.md)) are used. For example, increasing this value will increase the default limit for incoming connections. -Default: `[TOTAL_SYSTEM_MEMORY]/4` +It is possible to inspect the runtime limits via `ipfs swarm resources --help`. + +Default: `[TOTAL_SYSTEM_MEMORY]/2` Type: `optionalBytes` #### `Swarm.ResourceMgr.MaxFileDescriptors` @@ -1824,70 +2098,14 @@ This param is ignored on Windows. Default `[TOTAL_SYSTEM_FILE_DESCRIPTORS]/2` Type: `optionalInteger` -#### `Swarm.ResourceMgr.Limits` - -Map of resource limits [per scope](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager#resource-scopes). - -The map supports fields from the [`LimitConfig` struct](https://github.com/libp2p/go-libp2p/blob/master/p2p/host/resource-manager/limit_defaults.go#L111). - -[`BaseLimit`s](https://github.com/libp2p/go-libp2p/blob/master/p2p/host/resource-manager/limit.go#L89) can be set for any scope, and within the `BaseLimit`, all limit s are optional. - -The `Swarm.ResourceMgr.Limits` override the default limits described above. -Any override `BaseLimits` or limit s from `Swarm.ResourceMgr.Limits` -that aren't specified will use the [computed default limits](./libp2p-resource-management.md#computed-default-limits). - -Example #1: setting limits for a specific scope -```json -{ - "Swarm": { - "ResourceMgr": { - "Limits": { - "System": { - "Memory": 1073741824, - "FD": 512, - "Conns": 1024, - "ConnsInbound": 256, - "ConnsOutbound": 1024, - "Streams": 16384, - "StreamsInbound": 4096, - "StreamsOutbound": 16384 - } - } - } - } -} -``` - -Example #2: setting a specific limit -```json -{ - "Swarm": { - "ResourceMgr": { - "Limits": { - "Transient": { - "ConnsOutbound": 256, - } - } - } - } -} -``` - -It is also possible to inspect and adjust some runtime limits via `ipfs swarm stats --help` and `ipfs swarm limit --help`. -Changes made via `ipfs swarm limit` are persisted in `Swarm.ResourceMgr.Limits`. - -Default: `{}` (use the [computed defaults](./libp2p-resource-management.md#computed-default-limits)) - -Type: `object[string->object]` - #### `Swarm.ResourceMgr.Allowlist` -A list of multiaddrs that can bypass normal system limits (but are still limited by the allowlist scope). +A list of [multiaddrs][libp2p-multiaddrs] that can bypass normal system limits (but are still limited by the allowlist scope). Convenience config around [go-libp2p-resource-manager#Allowlist.Add](https://pkg.go.dev/github.com/libp2p/go-libp2p/p2p/host/resource-manager#Allowlist.Add). Default: `[]` -Type: `array[string]` (multiaddrs) +Type: `array[string]` ([multiaddrs][multiaddr]) ### `Swarm.Transports` @@ -1900,16 +2118,16 @@ Configuration section for libp2p _network_ transports. Transports enabled in this section will be used for dialing. However, to receive connections on these transports, multiaddrs for these transports must be added to `Addresses.Swarm`. -Supported transports are: QUIC, TCP, WS, Relay and WebTransport. +Supported transports are: QUIC, TCP, WS, Relay, WebTransport and WebRTCDirect. Each field in this section is a `flag`. #### `Swarm.Transports.Network.TCP` -[TCP](https://en.wikipedia.org/wiki/Transmission_Control_Protocol) is the most -widely used transport by Kubo nodes. It doesn't directly support encryption -and/or multiplexing, so libp2p will layer a security & multiplexing transport -over it. +[TCP](https://en.wikipedia.org/wiki/Transmission_Control_Protocol) is a simple +and widely deployed transport, it should be compatible with most implementations +and network configurations. TCP doesn't directly support encryption and/or +multiplexing, so libp2p will layer a security & multiplexing transport over it. Default: Enabled @@ -1937,27 +2155,29 @@ Listen Addresses: #### `Swarm.Transports.Network.QUIC` -[QUIC](https://en.wikipedia.org/wiki/QUIC) is a UDP-based transport with -built-in encryption and multiplexing. The primary benefits over TCP are: +[QUIC](https://en.wikipedia.org/wiki/QUIC) is the most widely used transport by +Kubo nodes. It is a UDP-based transport with built-in encryption and +multiplexing. The primary benefits over TCP are: -1. It doesn't require a file descriptor per connection, easing the load on the OS. -2. It currently takes 2 round trips to establish a connection (our TCP transport - currently takes 6). +1. It takes 1 round trip to establish a connection (our TCP transport + currently takes 4). +2. No [Head-of-Line blocking](https://en.wikipedia.org/wiki/Head-of-line_blocking). +3. It doesn't require a file descriptor per connection, easing the load on the OS. Default: Enabled Type: `flag` Listen Addresses: -* /ip4/0.0.0.0/udp/4001/quic (default) -* /ip6/::/udp/4001/quic (default) +- `/ip4/0.0.0.0/udp/4001/quic-v1` (default) +- `/ip6/::/udp/4001/quic-v1` (default) #### `Swarm.Transports.Network.Relay` [Libp2p Relay](https://github.com/libp2p/specs/tree/master/relay) proxy transport that forms connections by hopping between multiple libp2p nodes. Allows IPFS node to connect to other peers using their `/p2p-circuit` -multiaddrs. This transport is primarily useful for bypassing firewalls and +[multiaddrs][libp2p-multiaddrs]. This transport is primarily useful for bypassing firewalls and NATs. See also: @@ -1997,11 +2217,47 @@ Default: Enabled Type: `flag` +Listen Addresses: +- `/ip4/0.0.0.0/udp/4001/quic-v1/webtransport` (default) +- `/ip6/::/udp/4001/quic-v1/webtransport` (default) + +#### `Swarm.Transports.Network.WebRTCDirect` + +[WebRTC Direct](https://github.com/libp2p/specs/blob/master/webrtc/webrtc-direct.md) +is a transport protocol that provides another way for browsers to +connect to the rest of the libp2p network. WebRTC Direct allows for browser +nodes to connect to other nodes without special configuration, such as TLS +certificates. This can be useful for browser nodes that do not yet support +[WebTransport](https://blog.libp2p.io/2022-12-19-libp2p-webtransport/), +which is still relatively new and has [known issues](https://github.com/libp2p/js-libp2p/issues/2572). + +Enabling this transport allows Kubo node to act on `/udp/4001/webrtc-direct` +listeners defined in `Addresses.Swarm`, `Addresses.Announce` or +`Addresses.AppendAnnounce`. + +> [!NOTE] +> WebRTC Direct is browser-to-node. It cannot be used to connect a browser +> node to a node that is behind a NAT or firewall (without UPnP port mapping). +> The browser-to-private requires using normal +> [WebRTC](https://github.com/libp2p/specs/blob/master/webrtc/webrtc.md), +> which is currently being worked on in +> [go-libp2p#2009](https://github.com/libp2p/go-libp2p/issues/2009). + +Default: Enabled + +Type: `flag` + +Listen Addresses: +- `/ip4/0.0.0.0/udp/4001/webrtc-direct` (default) +- `/ip6/::/udp/4001/webrtc-direct` (default) + ### `Swarm.Transports.Security` Configuration section for libp2p _security_ transports. Transports enabled in this section will be used to secure unencrypted connections. +This does not concern all the QUIC transports which use QUIC's builtin encryption. + Security transports are configured with the `priority` type. When establishing an _outbound_ connection, Kubo will try each security @@ -2010,9 +2266,9 @@ receiver supports. When establishing an _inbound_ connection, Kubo will let the initiator choose the protocol, but will refuse to use any of the disabled transports. -Supported transports are: TLS (priority 100) and Noise (priority 300). +Supported transports are: TLS (priority 100) and Noise (priority 200). -No default priority will ever be less than 100. +No default priority will ever be less than 100. Lower values have precedence. #### `Swarm.Transports.Security.TLS` @@ -2026,7 +2282,7 @@ Type: `priority` #### `Swarm.Transports.Security.SECIO` -Support for SECIO has been removed. Please remove this option from your config. +**REMOVED**: support for SECIO has been removed. Please remove this option from your config. #### `Swarm.Transports.Security.Noise` @@ -2035,7 +2291,7 @@ TLS as the cross-platform, default libp2p protocol due to ease of implementation. It is currently enabled by default but with low priority as it's not yet widely supported. -Default: `300` +Default: `200` Type: `priority` @@ -2044,11 +2300,13 @@ Type: `priority` Configuration section for libp2p _multiplexer_ transports. Transports enabled in this section will be used to multiplex duplex connections. -Multiplexer transports are secured the same way security transports are, with +This does not concern all the QUIC transports which use QUIC's builtin muxing. + +Multiplexer transports are configured the same way security transports are, with the `priority` type. Like with security transports, the initiator gets their first choice. -Supported transports are: Yamux (priority 100) and Mplex (priority 200) +Supported transport is only: Yamux (priority 100) No default priority will ever be less than 100. @@ -2062,25 +2320,14 @@ Type: `priority` ### `Swarm.Transports.Multiplexers.Mplex` -Mplex is the default multiplexer used when communicating between Kubo and all -other IPFS and libp2p implementations. Unlike Yamux: +**REMOVED**: See https://github.com/ipfs/kubo/issues/9958 -* Mplex is a simpler protocol. -* Mplex is more efficient. -* Mplex does not have built-in keepalives. -* Mplex does not support backpressure. Unfortunately, this means that, if a - single stream to a peer gets backed up for a period of time, the mplex - transport will kill the stream to allow the others to proceed. On the other - hand, the lack of backpressure means mplex can be significantly faster on some - high-latency connections. - -Default: `200` - -Type: `priority` +Support for Mplex has been [removed from Kubo and go-libp2p](https://github.com/libp2p/specs/issues/553). +Please remove this option from your config. ## `DNS` -Options for configuring DNS resolution for [DNSLink](https://docs.ipfs.tech/concepts/dnslink/) and `/dns*` [Multiaddrs](https://github.com/multiformats/multiaddr/). +Options for configuring DNS resolution for [DNSLink](https://docs.ipfs.tech/concepts/dnslink/) and `/dns*` [Multiaddrs][libp2p-multiaddrs]. ### `DNS.Resolvers` @@ -2129,9 +2376,318 @@ If present, the upper bound is applied to DoH resolvers in [`DNS.Resolvers`](#dn Note: this does NOT work with Go's default DNS resolver. To make this a global setting, add a `.` entry to `DNS.Resolvers` first. **Examples:** -* `"5m"` DNS entries are kept for 5 minutes or less. +* `"1m"` DNS entries are kept for 1 minute or less. * `"0s"` DNS entries expire as soon as they are retrieved. Default: Respect DNS Response TTL Type: `optionalDuration` + +## `Import` + +Options to configure the default options used for ingesting data, in commands such as `ipfs add` or `ipfs block put`. All affected commands are detailed per option. + +Note that using flags will override the options defined here. + +### `Import.CidVersion` + +The default CID version. Commands affected: `ipfs add`. + +Default: `0` + +Type: `optionalInteger` + +### `Import.UnixFSRawLeaves` + +The default UnixFS raw leaves option. Commands affected: `ipfs add`, `ipfs files write`. + +Default: `false` if `CidVersion=0`; `true` if `CidVersion=1` + +Type: `flag` + +### `Import.UnixFSChunker` + +The default UnixFS chunker. Commands affected: `ipfs add`. + +Default: `size-262144` + +Type: `optionalString` + +### `Import.HashFunction` + +The default hash function. Commands affected: `ipfs add`, `ipfs block put`, `ipfs dag put`. + +Default: `sha2-256` + +Type: `optionalString` + +## `Version` + +Options to configure agent version announced to the swarm, and leveraging +other peers version for detecting when there is time to update. + +### `Version.AgentSuffix` + +Optional suffix to the AgentVersion presented by `ipfs id` and exposed via [libp2p identify protocol](https://github.com/libp2p/specs/blob/master/identify/README.md#agentversion). + +The value from config takes precedence over value passed via `ipfs daemon --agent-version-suffix`. + +> [!NOTE] +> Setting a custom version suffix helps with ecosystem analysis, such as Amino DHT reports published at https://stats.ipfs.network + +Default: `""` (no suffix, or value from `ipfs daemon --agent-version-suffix=`) + +Type: `optionalString` + +### `Version.SwarmCheckEnabled` + +Observe the AgentVersion of swarm peers and log warning when +`SwarmCheckPercentThreshold` of peers runs version higher than this node. + +Default: `true` + +Type: `flag` + +### `Version.SwarmCheckPercentThreshold` + +Control the percentage of `kubo/` peers running new version required to +trigger update warning. + +Default: `5` + +Type: `optionalInteger` (1-100) + +## Profiles + +Configuration profiles allow to tweak configuration quickly. Profiles can be +applied with the `--profile` flag to `ipfs init` or with the `ipfs config profile +apply` command. When a profile is applied a backup of the configuration file +will be created in `$IPFS_PATH`. + +Configuration profiles can be applied additively. For example, both the `test-cid-v1` and `lowpower` profiles can be applied one after the other. +The available configuration profiles are listed below. You can also find them +documented in `ipfs config profile --help`. + +### `server` profile + +Disables local [`Discovery.MDNS`](#discoverymdns), [turns off uPnP NAT port mapping](#swarmdisablenatportmap), and blocks connections to +IPv4 and IPv6 prefixes that are [private, local only, or unrouteable](https://github.com/ipfs/kubo/blob/b71cf0d15904bdef21fe2eee5f1118a274309a4d/config/profile.go#L24-L43). + +Recommended when running IPFS on machines with public IPv4 addresses (no NAT, no uPnP) +at providers that interpret local IPFS discovery and traffic as netscan abuse ([example](https://github.com/ipfs/kubo/issues/10327)). + +### `randomports` profile + +Use a random port number for the incoming swarm connections. +Used for testing. + +### `default-datastore` profile + +Configures the node to use the default datastore (flatfs). + +Read the "flatfs" profile description for more information on this datastore. + +This profile may only be applied when first initializing the node. + +### `local-discovery` profile + +Enables local [`Discovery.MDNS`](#discoverymdns) (enabled by default). + +Useful to re-enable local discovery after it's disabled by another profile +(e.g., the server profile). + +`test` profile + +Reduces external interference of IPFS daemon, this +is useful when using the daemon in test environments. + +### `default-networking` profile + +Restores default network settings. +Inverse profile of the test profile. + +### `flatfs` profile + +Configures the node to use the flatfs datastore. +Flatfs is the default, most battle-tested and reliable datastore. + +You should use this datastore if: + +- You need a very simple and very reliable datastore, and you trust your + filesystem. This datastore stores each block as a separate file in the + underlying filesystem so it's unlikely to lose data unless there's an issue + with the underlying file system. +- You need to run garbage collection in a way that reclaims free space as soon as possible. +- You want to minimize memory usage. +- You are ok with the default speed of data import, or prefer to use `--nocopy`. + +> [!WARNING] +> This profile may only be applied when first initializing the node via `ipfs init --profile flatfs` + +> [!NOTE] +> See caveats and configuration options at [`datastores.md#flatfs`](datastores.md#flatfs) + +### `pebbleds` profile + +Configures the node to use the pebble high-performance datastore. + +Pebble is a LevelDB/RocksDB inspired key-value store focused on performance and internal usage by CockroachDB. +You should use this datastore if: + +- You need a datastore that is focused on performance. +- You need a datastore that is good for multi-terrabyte data sets. +- You need reliability by default, but may choose to disable WAL for maximum performance when reliability is not critical. +- You want a datastore that does not need GC cycles and does not use more space than necessary +- You want a datastore that does not take several minutes to start with large repositories +- You want a datastore that performs well even with default settings, but can optimized by setting configuration to tune it for your specific needs. + +> [!WARNING] +> This profile may only be applied when first initializing the node via `ipfs init --profile pebbleds` + +> [!NOTE] +> See other caveats and configuration options at [`datastores.md#pebbleds`](datastores.md#pebbleds) + +### `badgerds` profile + +Configures the node to use the legacy badgerv1 datastore. + +> [!CAUTION] +> This is based on very old badger 1.x, which has known bugs and is no longer supported by the upstream team. +> It is provided here only for pre-existing users, allowing them to migrate away to more modern datastore. +> Do not use it for new deployments, unless you really, really know what you are doing. + +Also, be aware that: + +- This datastore will not properly reclaim space when your datastore is + smaller than several gigabytes. If you run IPFS with `--enable-gc`, you plan on storing very little data in + your IPFS node, and disk usage is more critical than performance, consider using + `flatfs`. +- This datastore uses up to several gigabytes of memory. +- Good for medium-size datastores, but may run into performance issues if your dataset is bigger than a terabyte. +- The current implementation is based on old badger 1.x which is no longer supported by the upstream team. + +> [!WARNING] +> This profile may only be applied when first initializing the node via `ipfs init --profile badgerds` + +> [!NOTE] +> See other caveats and configuration options at [`datastores.md#pebbleds`](datastores.md#pebbleds) + +### `lowpower` profile + +Reduces daemon overhead on the system by disabling optional swarm services. + +- [`Routing.Type`](#routingtype) set to `autoclient` (no DHT server, only client). +- `Swarm.ConnMgr` set to maintain minimum number of p2p connections at a time. +- Disables [`AutoNAT`](#autonat). +- Disables [`Swam.RelayService`](#swarmrelayservice). + +> [!NOTE] +> This profile is provided for legacy reasons. +> With modern Kubo setting the above should not be necessary. + +### `announce-off` profile + +Disables [Reprovider](#reprovider) system (and announcing to Amino DHT). + +> [!CAUTION] +> The main use case for this is setups with manual Peering.Peers config. +> Data from this node will not be announced on the DHT. This will make +> DHT-based routing an data retrieval impossible if this node is the only +> one hosting it, and other peers are not already connected to it. + +### `announce-on` profile + +(Re-)enables [Reprovider](#reprovider) system (reverts [`announce-off` profile](#annouce-off-profile). + +### `legacy-cid-v0` profile + +Makes UnixFS import (`ipfs add`) produce legacy CIDv0 with no raw leaves, sha2-256 and 256 KiB chunks. + +> [!NOTE] +> This profile is provided for legacy users and should not be used for new projects. + +### `test-cid-v1` profile + +Makes UnixFS import (`ipfs add`) produce modern CIDv1 with raw leaves, sha2-256 and 1 MiB chunks. + +> [!NOTE] +> This profile will become the new implicit default, provided for testing purposes. +> Follow [kubo#4143](https://github.com/ipfs/kubo/issues/4143) for more details. + +## Types + +This document refers to the standard JSON types (e.g., `null`, `string`, +`number`, etc.), as well as a few custom types, described below. + +### `flag` + +Flags allow enabling and disabling features. However, unlike simple booleans, +they can also be `null` (or omitted) to indicate that the default value should +be chosen. This makes it easier for Kubo to change the defaults in the +future unless the user _explicitly_ sets the flag to either `true` (enabled) or +`false` (disabled). Flags have three possible states: + +- `null` or missing (apply the default value). +- `true` (enabled) +- `false` (disabled) + +### `priority` + +Priorities allow specifying the priority of a feature/protocol and disabling the +feature/protocol. Priorities can take one of the following values: + +- `null`/missing (apply the default priority, same as with flags) +- `false` (disabled) +- `1 - 2^63` (priority, lower is preferred) + +### `strings` + +Strings is a special type for conveniently specifying a single string, an array +of strings, or null: + +- `null` +- `"a single string"` +- `["an", "array", "of", "strings"]` + +### `duration` + +Duration is a type for describing lengths of time, using the same format go +does (e.g, `"1d2h4m40.01s"`). + +### `optionalInteger` + +Optional integers allow specifying some numerical value which has +an implicit default when missing from the config file: + +- `null`/missing will apply the default value defined in Kubo sources (`.WithDefault(value)`) +- an integer between `-2^63` and `2^63-1` (i.e. `-9223372036854775808` to `9223372036854775807`) + +### `optionalBytes` + +Optional Bytes allow specifying some number of bytes which has +an implicit default when missing from the config file: + +- `null`/missing (apply the default value defined in Kubo sources) +- a string value indicating the number of bytes, including human readable representations: + - [SI sizes](https://en.wikipedia.org/wiki/Metric_prefix#List_of_SI_prefixes) (metric units, powers of 1000), e.g. `1B`, `2kB`, `3MB`, `4GB`, `5TB`, …) + - [IEC sizes](https://en.wikipedia.org/wiki/Binary_prefix#IEC_prefixes) (binary units, powers of 1024), e.g. `1B`, `2KiB`, `3MiB`, `4GiB`, `5TiB`, …) + +### `optionalString` + +Optional strings allow specifying some string value which has +an implicit default when missing from the config file: + +- `null`/missing will apply the default value defined in Kubo sources (`.WithDefault("value")`) +- a string + +### `optionalDuration` + +Optional durations allow specifying some duration value which has +an implicit default when missing from the config file: + +- `null`/missing will apply the default value defined in Kubo sources (`.WithDefault("1h2m3s")`) +- a string with a valid [go duration](#duration) (e.g, `"1d2h4m40.01s"`). + +---- + +[multiaddr]: https://docs.ipfs.tech/concepts/glossary/#multiaddr diff --git a/docs/content-blocking.md b/docs/content-blocking.md new file mode 100644 index 000000000..fad63ad9e --- /dev/null +++ b/docs/content-blocking.md @@ -0,0 +1,76 @@ +

+
+ content blocking logo +
+ Content Blocking in Kubo +
+

+ +Kubo ships with built-in support for denylist format from [IPIP-383](https://github.com/ipfs/specs/pull/383). + +## Default behavior + +Official Kubo build does not ship with any denylists enabled by default. + +Content blocking is an opt-in decision made by the operator of `ipfs daemon`. + +## How to enable blocking + +Place a `*.deny` file in one of directories: + +- `$IPFS_PATH/denylists/` (`$HOME/.ipfs/denylists/` if `IPFS_PATH` is not set) +- `$XDG_CONFIG_HOME/ipfs/denylists/` (`$HOME/.config/ipfs/denylists/` if `XDG_CONFIG_HOME` is not set) +- `/etc/ipfs/denylists/` (global) + +Files need to be present before starting the `ipfs daemon` in order to be watched for any new updates +appended once started. Any other changes (such as removal of entries, prepending of entries, or +insertion of new entries before the EOF at time of daemon starting) will not be detected or processed +after boot; a restart of the daemon will be required for them to be factored in. + +If an entire new denylist file is added, `ipfs daemon` also needs to be restarted to track it. + +CLI and Gateway users will receive errors in response to request impacted by a blocklist: + +``` +Error: /ipfs/QmQvjk82hPkSaZsyJ8vNER5cmzKW7HyGX5XVusK7EAenCN is blocked and cannot be provided +``` + +End user is not informed about the exact reason, see [How to +debug](#how-to-debug) if you need to find out which line of which denylist +caused the request to be blocked. + +## Denylist file format + +[NOpfs](https://github.com/ipfs-shipyard/nopfs) supports the format from [IPIP-383](https://github.com/ipfs/specs/pull/383). + +Clear-text rules are simple: just put content paths to block, one per line. +Paths with unicode and whitespace need to be percend-encoded: + +``` +/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR +/ipfs/bafybeihfg3d7rdltd43u3tfvncx7n5loqofbsobojcadtmokrljfthuc7y/927%20-%20Standards/927%20-%20Standards.png +``` + +Sensitive content paths can be double-hashed to block without revealing them. +Double-hashed list example: https://badbits.dwebops.pub/badbits.deny + +See [IPIP-383](https://github.com/ipfs/specs/pull/383) for detailed format specification and more examples. + +## How to suspend blocking without removing denylists + +Set `IPFS_CONTENT_BLOCKING_DISABLE` environment variable to `true` and restart the daemon. + + +## How to debug + +Debug logging of `nopfs` subsystem can be enabled with `GOLOG_LOG_LEVEL="nopfs=debug"` + +All block events are logged as warnings on a separate level named `nopfs-blocks`. + +To only log requests for blocked content set `GOLOG_LOG_LEVEL="nopfs-blocks=warn"`: + +``` +WARN (...) QmRFniDxwxoG2n4AcnGhRdjqDjCM5YeUcBE75K8WXmioH3: blocked (test.deny:9) +``` + + diff --git a/docs/customizing.md b/docs/customizing.md new file mode 100644 index 000000000..0f078999f --- /dev/null +++ b/docs/customizing.md @@ -0,0 +1,66 @@ +# Customizing Kubo + +You may want to customize Kubo if you want to reuse most of Kubo's machinery. This document discusses some approaches you may consider for customizing Kubo, and their tradeoffs. + +Some common use cases for customizing Kubo include: + +- Using a custom datastore for storing blocks, pins, or other Kubo metadata +- Adding a custom data transfer protocol into Kubo +- Customizing Kubo internals, such as adding allowlist/blocklist functionality to Bitswap +- Adding new commands, interfaces, functionality, etc. to Kubo while reusing the libp2p swarm +- Building on top of Kubo's configuration and config migration functionality + +## Summary +This table summarizes the tradeoffs between the approaches below: + +| | [Boxo](#boxo-build-your-own-binary) | [Kubo Plugin](#kubo-plugins) | [Bespoke Extension Point](#bespoke-extension-points) | [Go Plugin](#go-plugins) | [Fork](#fork-kubo) | +|:-------------------:|:-----------------------------------:|:----------------------------:|:----------------------------------------------------:|:------------------------:|:------------------:| +| Supported? | ✅ | ✅ | ✅ | ❌ | ❌ | +| Future-proof? | ✅ | ❌ | ✅ | ❌ | ❌ | +| Fully customizable? | ✅ | ✅ | ❌ | ✅ | ✅ | +| Fast to implement? | ❌ | ✅ | ✅ | ✅ | ✅ | +| Dynamic at runtime? | ❌ | ❌ | ✅ | ✅ | ❌ | +| Add new commands? | ❌ | ✅ | ❌ | ✅ | ✅ | + +## Boxo: build your own binary +The best way to reuse Kubo functionality is to pick the functionality you need directly from [Boxo](https://github.com/ipfs/boxo) and compile your own binary. + +Boxo's raison d'etre is to be an IPFS component toolbox to support building custom-made implementations and applications. If your use case is not easy to implement with Boxo, you may want to consider adding whatever functionality is needed to Boxo instead of customizing Kubo, so that the community can benefit. If you are interested in this option, please reach out to Boxo maintainers, who will be happy to help you scope & plan the work. See [Boxo's FAQ](https://github.com/ipfs/boxo#help) for more info. + +## Kubo Plugins +Kubo plugins are a set of interfaces that may be implemented and injected into Kubo. Generally you should recompile the Kubo binary with your plugins added. A popular example of a Kubo plugin is [go-ds-s3](https://github.com/ipfs/go-ds-s3), which can be used to store blocks in Amazon S3. + +Some plugins, such as the `fx` plugin, allow deep customization of Kubo internals. As a result, Kubo maintainers can't guarantee backwards compatibility with these, so you may need to adapt to breaking changes when upgrading to new Kubo versions. + +For more information about the different types of Kubo plugins, see [plugins.md](./plugins.md). + +Kubo plugins can also be injected at runtime using Go plugins (see below), but these are hard to use and not well supported by Go, so we don't recommend them. + +### Kubo binary imports + +It is possible to depend on the package `cmd/ipfs/kubo` as a way of using Kubo plugins that is an alternative to recompiling Kubo with additional preloaded plugins. + +This gives a more Go-centric dependency updating flow to building a new binary with preloaded plugins by simply requiring updating a Kubo dependency rather than needing to update Kubo source code and recompile. + +## Bespoke Extension Points +Certain Kubo functionality may have their own extension points. For example: + +* Kubo supports the [Routing v1](https://github.com/ipfs/specs/blob/main/routing/ROUTING_V1_HTTP.md) API for delegating content routing to external processes +* Kubo supports the [Pinning Service API](https://github.com/ipfs/pinning-services-api-spec) for delegating pinning to external processes +* Kubo supports [DNSLink](https://dnslink.dev/) for delegating name->CID mappings to DNS + +(This list is not exhaustive.) + +These can generally be developed and deployed as sidecars (or full external services) without modifying the Kubo binary. + +## Go Plugins +Go provides [dynamic plugins](https://pkg.go.dev/plugin) which can be loaded at runtime into a Go binary. + +Kubo currently works with Go plugins. But using Go plugins requires that you compile the plugin using the exact same version of the Go toolchain with the same configuration (build flags, environment variables, etc.). As a result, you likely need to build Kubo and the plugins together at the same time, and at that point you may as well just compile the functionality directly into Kubo and avoid Go plugins. + +As a result, we don't recommend using Go plugins, and are likely to remove them in a future release of Kubo. + +## Fork Kubo +The "nuclear option" is to fork Kubo into your own repo, make your changes, and periodically sync your repo with the Kubo repo. This can be a good option if your changes are significant and you can commit to keeping your repo in sync with Kubo. + +Kubo maintainers can't make any backwards compatibility guarantees about Kubo internals, so by choosing this option you're accepting the risk that you may need to spend more time adapting to breaking changes. diff --git a/docs/datastores.md b/docs/datastores.md index db729bf97..f2051b601 100644 --- a/docs/datastores.md +++ b/docs/datastores.md @@ -3,6 +3,13 @@ This document describes the different possible values for the `Datastore.Spec` field in the ipfs configuration file. +- [flatfs](#flatfs) +- [levelds](#levelds) +- [pebbleds](#pebbleds) +- [badgerds](#badgerds) +- [mount](#mount) +- [measure](#measure) + ## flatfs Stores each key value pair as a file on the filesystem. @@ -35,12 +42,47 @@ Uses a leveldb database to store key value pairs. } ``` +## pebbleds + +Uses [pebble](https://github.com/cockroachdb/pebble) as a key value store. + +```json +{ + "type": "pebbleds", + "path": "", +} +``` + +The following options are availble for tuning pebble. +If they are not configured (or assigned their zero-valued), then default values are used. + +* `bytesPerSync`: int, Sync sstables periodically in order to smooth out writes to disk. (default: 512KB) +* `bisableWAL`: true|false, Disable the write-ahead log (WAL) at expense of prohibiting crash recovery. (default: false) +* `cacheSize`: Size of pebble's shared block cache. (default: 8MB) +* `l0CompactionThreshold`: int, Count of L0 files necessary to trigger an L0 compaction. +* `l0StopWritesThreshold`: int, Limit on L0 read-amplification, computed as the number of L0 sublevels. +* `lBaseMaxBytes`: int, Maximum number of bytes for LBase. The base level is the level which L0 is compacted into. +* `maxConcurrentCompactions`: int, Maximum number of concurrent compactions. (default: 1) +* `memTableSize`: int, Size of a MemTable in steady state. The actual MemTable size starts at min(256KB, MemTableSize) and doubles for each subsequent MemTable up to MemTableSize (default: 4MB) +* `memTableStopWritesThreshold`: int, Limit on the number of queued of MemTables. (default: 2) +* `walBytesPerSync`: int: Sets the number of bytes to write to a WAL before calling Sync on it in the background. (default: 0, no background syncing) +* `walMinSyncSeconds`: int: Sets the minimum duration between syncs of the WAL. (default: 0) + +> [!TIP] +> Start using pebble with only default values and configure tuning items are needed for your needs. For a more complete description of these values, see: `https://pkg.go.dev/github.com/cockroachdb/pebble@vA.B.C#Options` (where `A.B.C` is pebble version from Kubo's `go.mod`). + ## badgerds Uses [badger](https://github.com/dgraph-io/badger) as a key value store. +> [!CAUTION] +> This is based on very old badger 1.x, which has known bugs and is no longer supported by the upstream team. +> It is provided here only for pre-existing users, allowing them to migrate away to more modern datastore. +> Do not use it for new deployments, unless you really, really know what you are doing. + + * `syncWrites`: Flush every write to disk before continuing. Setting this to false is safe as kubo will automatically flush writes to disk before and after performing critical operations like pinning. However, you can set this to true to be extra-safe (at the cost of a 2-3x slowdown when adding files). -* `truncate`: Truncate the DB if a partially written sector is found (defaults to true). There is no good reason to set this to false unless you want to manually recover partially written (and unpinned) blocks if kubo crashes half-way through a adding a file. +* `truncate`: Truncate the DB if a partially written sector is found (defaults to true). There is no good reason to set this to false unless you want to manually recover partially written (and unpinned) blocks if kubo crashes half-way through adding a file. ```json { diff --git a/docs/delegated-routing.md b/docs/delegated-routing.md index 9e2b9c48b..6f15972ed 100644 --- a/docs/delegated-routing.md +++ b/docs/delegated-routing.md @@ -4,16 +4,25 @@ - Related Issues: - https://github.com/ipfs/kubo/issues/9188 - https://github.com/ipfs/kubo/issues/9079 + - https://github.com/ipfs/kubo/pull/9877 ## Summary -Previously we only used DHT for content routing and content providing. After kubo-0.14.0 release we added support for [delegated routing using Reframe protocol](https://github.com/ipfs/kubo/pull/8997). +Previously we only used the Amino DHT for content routing and content +providing. -Now we need a better way to add different routers using different protocols like Reframe or DHT, and be able to configure them to cover different use cases. +Kubo 0.14 introduced experimental support for [delegated routing](https://github.com/ipfs/kubo/pull/8997), +which then got changed and standardized as [Routing V1 HTTP API](https://specs.ipfs.tech/routing/http-routing-v1/). + +Kubo 0.23.0 release added support for [self-hosting Routing V1 HTTP API server](https://github.com/ipfs/kubo/blob/master/docs/changelogs/v0.23.md#self-hosting-routingv1-endpoint-for-delegated-routing-needs). + +Now we need a better way to add different routers using different protocols +like [Routing V1](https://specs.ipfs.tech/routing/http-routing-v1/) or Amino +DHT, and be able to configure them (future routing systems to come) to cover different use cases. ## Motivation -The actual routing implementation is not enough. Some users needs to have more options when configuring the routing system. The new implementations should be able to: +The actual routing implementation is not enough. Some users need to have more options when configuring the routing system. The new implementations should be able to: - [x] Be user-friendly and easy enough to configure, but also versatile - [x] Configurable Router execution order @@ -33,22 +42,22 @@ The `Routing` configuration section will contain the following keys: #### Routers -`Routers` will be a key-value list of routers that will be available to use. The key is the router name and the value is all the needed configurations for that router. the `Type` will define the routing kind. The main router types will be `reframe` and `dht`, but we will implement two special routers used to execute a set of routers in parallel or sequentially: `parallel` router and `sequential` router. +`Routers` will be a key-value list of routers that will be available to use. The key is the router name and the value is all the needed configurations for that router. the `Type` will define the routing kind. The main router types will be `http` and `dht`, but we will implement two special routers used to execute a set of routers in parallel or sequentially: `parallel` router and `sequential` router. Depending on the routing type, it will use different parameters: -##### Reframe +##### HTTP Params: -- `"Endpoint"`: URL endpoint implementing Reframe protocol. +- `"Endpoint"`: URL of HTTP server with endpoints that implement [Delegated Routing V1 HTTP API](https://specs.ipfs.tech/routing/http-routing-v1/) protocol. -##### DHT +##### Amino DHT Params: -- `"Mode"`: Mode used by the DHT. Possible values: "server", "client", "auto" +- `"Mode"`: Mode used by the Amino DHT. Possible values: "server", "client", "auto" - `"AcceleratedDHTClient"`: Set to `true` if you want to use the experimentalDHT. -- `"PublicIPNetwork"`: Set to `true` to create a `WAN` DHT. Set to `false` to create a `LAN` DHT. +- `"PublicIPNetwork"`: Set to `true` to create a `WAN` Amino DHT. Set to `false` to create a `LAN` DHT. ##### Parallel @@ -80,10 +89,10 @@ The value will contain: "Routing": { "Type": "custom", "Routers": { - "storetheindex": { - "Type": "reframe", + "http-delegated": { + "Type": "http", "Parameters": { - "Endpoint": "https://cid.contact/reframe" + "Endpoint": "https://delegated-ipfs.dev" // /routing/v1 (https://specs.ipfs.tech/routing/http-routing-v1/) } }, "dht-lan": { @@ -114,7 +123,7 @@ The value will contain: "RouterName": "dht-wan" }, { - "RouterName": "storetheindex" + "RouterName": "http-delegated" } ] } @@ -133,7 +142,7 @@ The value will contain: "Timeout": "100ms" }, { - "RouterName": "storetheindex", + "RouterName": "http-delegated", "ExecuteAfter": "100ms" } ] @@ -152,7 +161,7 @@ The value will contain: "Timeout": "300ms" }, { - "RouterName": "storetheindex", + "RouterName": "http-delegated", "Timeout": "300ms" } ] @@ -169,7 +178,7 @@ The value will contain: "RouterName": "dht-wan" }, { - "RouterName": "storetheindex" + "RouterName": "http-delegated" } ] } @@ -192,75 +201,6 @@ The value will contain: } ``` -Added YAML for clarity: - -```yaml ---- -Type: custom -Routers: - storetheindex: - Type: reframe - Parameters: - Endpoint: https://cid.contact/reframe - dht-lan: - Type: dht - Parameters: - Mode: server - PublicIPNetwork: false - AcceleratedDHTClient: false - dht-wan: - Type: dht - Parameters: - Mode: auto - PublicIPNetwork: true - AcceleratedDHTClient: false - find-providers-router: - Type: parallel - Parameters: - Routers: - - RouterName: dht-lan - IgnoreErrors: true - - RouterName: dht-wan - - RouterName: storetheindex - provide-router: - Type: parallel - Parameters: - Routers: - - RouterName: dht-lan - IgnoreErrors: true - - RouterName: dht-wan - ExecuteAfter: 100ms - Timeout: 100ms - - RouterName: storetheindex - ExecuteAfter: 100ms - get-ipns-router: - Type: sequential - Parameters: - Routers: - - RouterName: dht-lan - IgnoreErrors: true - - RouterName: dht-wan - Timeout: 300ms - - RouterName: storetheindex - Timeout: 300ms - put-ipns-router: - Type: parallel - Parameters: - Routers: - - RouterName: dht-lan - - RouterName: dht-wan - - RouterName: storetheindex -Methods: - find-providers: - RouterName: find-providers-router - provide: - RouterName: provide-router - get-ipns: - RouterName: get-ipns-router - put-ipns: - RouterName: put-ipns-router -``` - ### Error cases - If any of the routers fails, the output will be an error by default. - You can use `IgnoreErrors:true` to ignore errors for a specific router output @@ -394,49 +334,6 @@ As test fixtures we can add different use cases here and see how the configurati } ``` -Yaml representation for clarity: - -```yaml ---- -Type: custom -Routers: - dht-lan: - Type: dht - Parameters: - Mode: server - PublicIPNetwork: false - dht-wan: - Type: dht - Parameters: - Mode: auto - PublicIPNetwork: true - parallel-dht-strict: - Type: parallel - Parameters: - Routers: - - RouterName: dht-lan - - RouterName: dht-wan - parallel-dht: - Type: parallel - Parameters: - Routers: - - RouterName: dht-lan - IgnoreError: true - - RouterName: dht-wan -Methods: - provide: - RouterName: dht-wan - find-providers: - RouterName: parallel-dht-strict - find-peers: - RouterName: parallel-dht-strict - get-ipns: - RouterName: parallel-dht - put-ipns: - RouterName: parallel-dht - -``` - ### Compatibility ~~We need to create a config migration using [fs-repo-migrations](https://github.com/ipfs/fs-repo-migrations). We should remove the `Routing.Type` param and add the configuration specified [previously](#Mimic-previous-dual-DHT-config).~~ diff --git a/docs/environment-variables.md b/docs/environment-variables.md index 98b449054..b384e51ad 100644 --- a/docs/environment-variables.md +++ b/docs/environment-variables.md @@ -75,13 +75,13 @@ Warning: Enabling tracing will likely affect performance. ## `IPFS_FUSE_DEBUG` -Enables fuse debug logging. +If SET, enables fuse debug logging. Default: false ## `YAMUX_DEBUG` -Enables debug logging for the yamux stream muxer. +If SET, enables debug logging for the yamux stream muxer. Default: false @@ -114,6 +114,41 @@ $ ipfs resolve -r /ipns/dnslink-test2.example.com /ipfs/bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am ``` +## `IPFS_HTTP_ROUTERS` + +Overrides all implicit HTTP routers enabled when `Routing.Type=auto` with +the space-separated list of URLs provided in this variable. +Useful for testing and debugging in offline contexts. + +Example: + +```console +$ ipfs config Routing.Type auto +$ IPFS_HTTP_ROUTERS="http://127.0.0.1:7423" ipfs daemon +``` + +The above will replace implicit HTTP routers with single one, allowing for +inspection/debug of HTTP requests sent by Kubo via `while true ; do nc -l 7423; done` +or more advanced tools like [mitmproxy](https://docs.mitmproxy.org/stable/#mitmproxy). + +Default: `config.DefaultHTTPRouters` + +## `IPFS_HTTP_ROUTERS_FILTER_PROTOCOLS` + +Overrides values passed with `filter-protocols` parameter defined in IPIP-484. +Value is space-separated. + +```console +$ IPFS_HTTP_ROUTERS_FILTER_PROTOCOLS="unknown transport-bitswap transport-foo" ipfs daemon +``` + +Default: `config.DefaultHTTPRoutersFilterProtocols` + +## `IPFS_CONTENT_BLOCKING_DISABLE` + +Disables the content-blocking subsystem. No denylists will be watched and no +content will be blocked. + ## `LIBP2P_TCP_REUSEPORT` Kubo tries to reuse the same source port for all connections to improve NAT @@ -147,70 +182,5 @@ and outputs it to `rcmgr.json.gz` Default: disabled (not set) # Tracing -For advanced configuration (e.g. ratio-based sampling), see also: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/sdk-environment-variables.md -## `OTEL_TRACES_EXPORTER` -Specifies the exporters to use as a comma-separated string. Each exporter has a set of additional environment variables used to configure it. The following values are supported: - -- `otlp` -- `jaeger` -- `zipkin` -- `file` -- appends traces to a JSON file on the filesystem - -Setting this enables OpenTelemetry tracing. - -**NOTE** Tracing support is experimental: releases may contain tracing-related breaking changes. - -Default: "" (no exporters) - -## `OTLP Exporter` -Unless specified in this section, the OTLP exporter uses the environment variables documented here: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md - -### `OTEL_EXPORTER_OTLP_PROTOCOL` -Specifies the OTLP protocol to use, which is one of: - -- `grpc` -- `http/protobuf` - -Default: "grpc" - -## `Jaeger Exporter` - -See: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/sdk-environment-variables.md#jaeger-exporter - -## `Zipkin Exporter` -See: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/sdk-environment-variables.md#zipkin-exporter - -## `File Exporter` -### `OTEL_EXPORTER_FILE_PATH` -Specifies the filesystem path for the JSON file. - -Default: "$PWD/traces.json" - -### How to use Jaeger UI - -One can use the `jaegertracing/all-in-one` Docker image to run a full Jaeger -stack and configure Kubo to publish traces to it (here, in an ephemeral -container): - -```console -$ docker run --rm -it --name jaeger \ - -e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \ - -p 5775:5775/udp \ - -p 6831:6831/udp \ - -p 6832:6832/udp \ - -p 5778:5778 \ - -p 16686:16686 \ - -p 14268:14268 \ - -p 14268:14269 \ - -p 14250:14250 \ - -p 9411:9411 \ - jaegertracing/all-in-one -``` - -Then, in other terminal, start Kubo with Jaeger tracing enabled: -``` -$ OTEL_TRACES_EXPORTER=jaeger ipfs daemon -``` - -Finally, the [Jaeger UI](https://github.com/jaegertracing/jaeger-ui#readme) is available at http://localhost:16686 +For tracing configuration, please check: https://github.com/ipfs/boxo/blob/main/docs/tracing.md diff --git a/docs/examples/kubo-as-a-library/README.md b/docs/examples/kubo-as-a-library/README.md index c9a320985..bd41b5ee7 100644 --- a/docs/examples/kubo-as-a-library/README.md +++ b/docs/examples/kubo-as-a-library/README.md @@ -1,6 +1,6 @@ # Use Kubo (go-ipfs) as a library to spawn a node and add a file -> This tutorial is the sibling of the [js-ipfs IPFS 101 tutorial](https://github.com/ipfs-examples/js-ipfs-examples/tree/master/examples/ipfs-101#readme). +> Note: if you are trying to customize or extend Kubo, you should read the [Customizing Kubo](../../customizing.md) doc By the end of this tutorial, you will learn how to: diff --git a/docs/examples/kubo-as-a-library/go.mod b/docs/examples/kubo-as-a-library/go.mod index 2f61b8aa5..69cd5cec2 100644 --- a/docs/examples/kubo-as-a-library/go.mod +++ b/docs/examples/kubo-as-a-library/go.mod @@ -1,218 +1,240 @@ module github.com/ipfs/kubo/examples/kubo-as-a-library -go 1.18 +go 1.23 // Used to keep this in sync with the current version of kubo. You should remove // this if you copy this example. replace github.com/ipfs/kubo => ./../../.. require ( - github.com/ipfs/go-ipfs-files v0.2.0 - github.com/ipfs/interface-go-ipfs-core v0.8.1 - github.com/ipfs/kubo v0.14.0-rc1 - github.com/libp2p/go-libp2p v0.24.2 - github.com/multiformats/go-multiaddr v0.8.0 + github.com/ipfs/boxo v0.24.3 + github.com/ipfs/kubo v0.0.0-00010101000000-000000000000 + github.com/libp2p/go-libp2p v0.37.0 + github.com/multiformats/go-multiaddr v0.13.0 ) require ( bazil.org/fuse v0.0.0-20200117225306-7b5117fecadc // indirect github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect - github.com/Stebalien/go-bitfield v0.0.1 // indirect - github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a // indirect + github.com/DataDog/zstd v1.4.5 // indirect + github.com/Jorropo/jsync v1.0.1 // indirect + github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b // indirect github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5 // indirect - github.com/benbjohnson/clock v1.3.0 // 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/cenkalti/backoff v2.2.1+incompatible // indirect - github.com/cenkalti/backoff/v4 v4.1.3 // 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.2.0 // indirect - github.com/containerd/cgroups v1.0.4 // indirect + github.com/caddyserver/certmagic v0.21.4 // indirect + github.com/caddyserver/zerossl v0.1.3 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/ceramicnetwork/go-dag-jose v0.1.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cockroachdb/errors v1.11.3 // indirect + github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect + github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect + github.com/cockroachdb/pebble v1.1.2 // indirect + github.com/cockroachdb/redact v1.1.5 // indirect + github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // 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-20170627025303-887ab5e44cc3 // indirect + github.com/crackcomm/go-gitignore v0.0.0-20241020182519-7843d2ba8fdf // indirect github.com/cskr/pubsub v1.0.2 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // 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.2 // indirect + github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/docker/go-units v0.5.0 // indirect - github.com/dustin/go-humanize v1.0.0 // indirect - github.com/elastic/gosigar v0.14.2 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/elastic/gosigar v0.14.3 // indirect github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 // indirect - github.com/flynn/noise v1.0.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/go-logr/logr v1.2.3 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.6 // indirect + github.com/getsentry/sentry-go v0.27.0 // indirect + github.com/go-jose/go-jose/v4 v4.0.4 // indirect + github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // 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/mock v1.6.0 // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/glog v1.2.2 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/gopacket v1.1.19 // indirect - github.com/google/pprof v0.0.0-20221203041831-ce31453925ec // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/gorilla/mux v1.8.0 // indirect - github.com/gorilla/websocket v1.5.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect - github.com/hannahhoward/go-pubsub v0.0.0-20200423002714-8d62886cc36e // indirect + github.com/google/pprof v0.0.0-20241017200806-017d972448fc // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/websocket v1.5.3 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect - github.com/huin/goupnp v1.0.3 // indirect + github.com/hashicorp/golang-lru v1.0.2 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/huin/goupnp v1.3.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/go-bitfield v1.0.0 // indirect - github.com/ipfs/go-bitswap v0.11.0 // indirect - github.com/ipfs/go-block-format v0.0.3 // indirect - github.com/ipfs/go-blockservice v0.5.0 // indirect - github.com/ipfs/go-cid v0.3.2 // 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-cid v0.4.1 // indirect github.com/ipfs/go-cidutil v0.1.0 // indirect github.com/ipfs/go-datastore v0.6.0 // indirect - github.com/ipfs/go-delegated-routing v0.7.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-fetcher v1.6.1 // indirect - github.com/ipfs/go-filestore v1.2.0 // indirect + github.com/ipfs/go-ds-pebble v0.4.0 // indirect github.com/ipfs/go-fs-lock v0.0.7 // indirect - github.com/ipfs/go-graphsync v0.14.1-0.20221120210616-975d7aaeb15b // indirect - github.com/ipfs/go-ipfs-blockstore v1.2.0 // indirect - github.com/ipfs/go-ipfs-chunker v0.0.5 // indirect + github.com/ipfs/go-ipfs-blockstore v1.3.1 // indirect github.com/ipfs/go-ipfs-delay v0.0.1 // indirect - github.com/ipfs/go-ipfs-ds-help v1.1.0 // indirect - github.com/ipfs/go-ipfs-exchange-interface v0.2.0 // indirect - github.com/ipfs/go-ipfs-exchange-offline v0.3.0 // indirect - github.com/ipfs/go-ipfs-keystore v0.1.0 // indirect - github.com/ipfs/go-ipfs-pinner v0.2.1 // indirect - github.com/ipfs/go-ipfs-posinfo v0.0.1 // indirect - github.com/ipfs/go-ipfs-pq v0.0.2 // indirect - github.com/ipfs/go-ipfs-provider v0.8.1 // indirect - github.com/ipfs/go-ipfs-routing v0.3.0 // indirect - github.com/ipfs/go-ipfs-util v0.0.2 // indirect - github.com/ipfs/go-ipld-cbor v0.0.5 // indirect - github.com/ipfs/go-ipld-format v0.4.0 // 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.2 // indirect + github.com/ipfs/go-ipfs-util v0.0.3 // indirect + github.com/ipfs/go-ipld-cbor v0.2.0 // 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.1.1 // indirect - github.com/ipfs/go-ipns v0.3.0 // indirect - github.com/ipfs/go-libipfs v0.0.0-20221208220359-356ce09dd4a1 // indirect + github.com/ipfs/go-ipld-legacy v0.2.1 // indirect github.com/ipfs/go-log v1.0.5 // indirect github.com/ipfs/go-log/v2 v2.5.1 // indirect - github.com/ipfs/go-merkledag v0.8.1 // indirect + github.com/ipfs/go-merkledag v0.11.0 // indirect github.com/ipfs/go-metrics-interface v0.0.1 // indirect - github.com/ipfs/go-mfs v0.2.1 // indirect - github.com/ipfs/go-namesys v0.6.0 // indirect - github.com/ipfs/go-path v0.3.0 // indirect - github.com/ipfs/go-peertaskqueue v0.8.0 // indirect - github.com/ipfs/go-unixfs v0.4.1 // indirect - github.com/ipfs/go-unixfsnode v1.4.0 // indirect - github.com/ipfs/go-verifcid v0.0.2 // indirect - github.com/ipld/edelweiss v0.2.0 // indirect - github.com/ipld/go-codec-dagpb v1.4.1 // indirect - github.com/ipld/go-ipld-prime v0.19.0 // indirect + github.com/ipfs/go-peertaskqueue v0.8.1 // indirect + github.com/ipfs/go-unixfsnode v1.9.2 // 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.14.2 // indirect + github.com/ipld/go-codec-dagpb v1.6.0 // indirect + github.com/ipld/go-ipld-prime v0.21.0 // indirect + github.com/ipshipyard/p2p-forge v0.0.2 // 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/klauspost/compress v1.15.12 // indirect - github.com/klauspost/cpuid/v2 v2.2.1 // indirect - github.com/koron/go-ssdp v0.0.3 // indirect + github.com/klauspost/compress v1.17.11 // indirect + github.com/klauspost/cpuid/v2 v2.2.8 // indirect + github.com/koron/go-ssdp v0.0.4 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/libdns/libdns v0.2.2 // 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.2.0 // indirect - github.com/libp2p/go-libp2p-kad-dht v0.20.0 // indirect - github.com/libp2p/go-libp2p-kbucket v0.5.0 // indirect - github.com/libp2p/go-libp2p-pubsub v0.8.2 // indirect + github.com/libp2p/go-flow-metrics v0.2.0 // indirect + github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect + github.com/libp2p/go-libp2p-kad-dht v0.27.0 // indirect + github.com/libp2p/go-libp2p-kbucket v0.6.4 // indirect + github.com/libp2p/go-libp2p-pubsub v0.12.0 // 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.4.1 // indirect + github.com/libp2p/go-libp2p-routing-helpers v0.7.4 // indirect github.com/libp2p/go-libp2p-xor v0.1.0 // indirect - github.com/libp2p/go-mplex v0.7.0 // indirect - github.com/libp2p/go-msgio v0.2.0 // indirect - github.com/libp2p/go-nat 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-openssl v0.1.0 // indirect - github.com/libp2p/go-reuseport v0.2.0 // indirect - github.com/libp2p/go-yamux/v4 v4.0.0 // 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/lucas-clemente/quic-go v0.31.1 // indirect - github.com/marten-seemann/qpack v0.3.0 // indirect - github.com/marten-seemann/qtls-go1-18 v0.1.3 // indirect - github.com/marten-seemann/qtls-go1-19 v0.1.1 // indirect github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect - github.com/marten-seemann/webtransport-go v0.4.3 // indirect - github.com/mattn/go-isatty v0.0.16 // indirect - github.com/mattn/go-pointer v0.0.1 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/miekg/dns v1.1.50 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mholt/acmez/v2 v2.0.3 // indirect + github.com/miekg/dns v1.1.62 // 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.0 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/minio/sha256-simd 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-multiaddr-dns v0.3.1 // indirect + github.com/multiformats/go-multiaddr-dns v0.4.0 // indirect github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect - github.com/multiformats/go-multibase v0.1.1 // indirect - github.com/multiformats/go-multicodec v0.7.0 // indirect - github.com/multiformats/go-multihash v0.2.1 // indirect - github.com/multiformats/go-multistream v0.3.3 // indirect + github.com/multiformats/go-multibase v0.2.0 // indirect + github.com/multiformats/go-multicodec v0.9.0 // indirect + github.com/multiformats/go-multihash v0.2.3 // indirect + github.com/multiformats/go-multistream v0.5.0 // indirect github.com/multiformats/go-varint v0.0.7 // indirect - github.com/onsi/ginkgo/v2 v2.5.1 // indirect - github.com/opencontainers/runtime-spec v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/onsi/ginkgo/v2 v2.20.2 // 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.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/pion/datachannel v1.5.9 // indirect + github.com/pion/dtls/v2 v2.2.12 // indirect + github.com/pion/ice/v2 v2.3.36 // indirect + github.com/pion/interceptor v0.1.37 // 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.9 // indirect + github.com/pion/sctp v1.8.33 // indirect + github.com/pion/sdp/v3 v3.0.9 // indirect + github.com/pion/srtp/v2 v2.0.20 // indirect + github.com/pion/stun v0.6.1 // indirect + github.com/pion/transport/v2 v2.2.10 // indirect + github.com/pion/turn/v2 v2.1.6 // indirect + github.com/pion/webrtc/v3 v3.3.4 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e // indirect - github.com/prometheus/client_golang v1.14.0 // indirect - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.37.0 // indirect - github.com/prometheus/procfs v0.8.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/polydawn/refmt v0.89.0 // indirect + github.com/prometheus/client_golang v1.20.5 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.60.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/quic-go/qpack v0.5.1 // indirect + github.com/quic-go/quic-go v0.48.1 // indirect + github.com/quic-go/webtransport-go v0.8.1-0.20241018022711-4ac2c9250e66 // indirect github.com/raulk/go-watchdog v1.3.0 // indirect - github.com/samber/lo v1.36.0 // indirect - github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect + github.com/rogpeppe/go-internal v1.13.1 // indirect + github.com/samber/lo v1.47.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect - github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect + github.com/stretchr/testify v1.9.0 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // 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-gen v0.0.0-20210219115102-f37d292932f2 // indirect + github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11 // indirect + github.com/whyrusleeping/cbor-gen v0.1.2 // 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/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 // indirect - github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee // indirect + github.com/wlynxg/anet v0.0.5 // indirect + github.com/zeebo/blake3 v0.2.4 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/otel v1.7.0 // indirect - go.opentelemetry.io/otel/exporters/jaeger v1.7.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.7.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.7.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.7.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.7.0 // indirect - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.7.0 // indirect - go.opentelemetry.io/otel/exporters/zipkin v1.7.0 // indirect - go.opentelemetry.io/otel/sdk v1.7.0 // indirect - go.opentelemetry.io/otel/trace v1.7.0 // indirect - go.opentelemetry.io/proto/otlp v0.16.0 // indirect - go.uber.org/atomic v1.10.0 // indirect - go.uber.org/dig v1.15.0 // indirect - go.uber.org/fx v1.18.2 // indirect - go.uber.org/multierr v1.8.0 // indirect - go.uber.org/zap v1.24.0 // indirect - go4.org v0.0.0-20200411211856-f5505b9728dd // indirect - golang.org/x/crypto v0.3.0 // indirect - golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect - golang.org/x/mod v0.7.0 // indirect - golang.org/x/net v0.3.0 // indirect - golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.3.0 // indirect - golang.org/x/text v0.5.0 // indirect - golang.org/x/tools v0.3.0 // indirect - golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect - google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1 // indirect - google.golang.org/grpc v1.47.0 // indirect - google.golang.org/protobuf v1.28.1 // indirect - gopkg.in/square/go-jose.v2 v2.5.1 // indirect - lukechampine.com/blake3 v1.1.7 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect + go.opentelemetry.io/otel v1.31.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0 // indirect + go.opentelemetry.io/otel/exporters/zipkin v1.31.0 // indirect + go.opentelemetry.io/otel/metric v1.31.0 // indirect + go.opentelemetry.io/otel/sdk v1.31.0 // indirect + go.opentelemetry.io/otel/trace v1.31.0 // indirect + go.opentelemetry.io/proto/otlp v1.3.1 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/dig v1.18.0 // indirect + go.uber.org/fx v1.23.0 // indirect + go.uber.org/mock v0.5.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + go4.org v0.0.0-20230225012048-214862532bf5 // indirect + golang.org/x/crypto v0.28.0 // indirect + golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect + golang.org/x/mod v0.21.0 // indirect + golang.org/x/net v0.30.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/text v0.19.0 // indirect + golang.org/x/tools v0.26.0 // indirect + golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect + gonum.org/v1/gonum v0.15.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 // indirect + google.golang.org/grpc v1.67.1 // indirect + google.golang.org/protobuf v1.35.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + lukechampine.com/blake3 v1.3.0 // indirect ) diff --git a/docs/examples/kubo-as-a-library/go.sum b/docs/examples/kubo-as-a-library/go.sum index 1addcac12..e12055d92 100644 --- a/docs/examples/kubo-as-a-library/go.sum +++ b/docs/examples/kubo-as-a-library/go.sum @@ -10,288 +10,208 @@ cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxK 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.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= 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/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= 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/gpu/mtl v0.0.0-20201218220906-28db891af037/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= 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/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/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y= -github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= +github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= +github.com/DataDog/zstd v1.4.5/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/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= -github.com/Shopify/sarama v1.30.0/go.mod h1:zujlQQx1kzHsh4jfV1USnptCQrHAEZ2Hk8fTKCulPVs= -github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/Shopify/toxiproxy/v2 v2.1.6-0.20210914104332-15ea381dcdae/go.mod h1:/cvHQkZ1fst0EmZnA5dFtiQdWCNCFYzb+uE2vqVgvx0= -github.com/Stebalien/go-bitfield v0.0.1 h1:X3kbSSPUaJK60wV2hjOPZwmpljr6VGCqdq4cBLhbQBo= -github.com/Stebalien/go-bitfield v0.0.1/go.mod h1:GNjFpasyUVkHMsfEOk8EFLJ9syQ6SI+XWrX9Wf2XH0s= -github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= -github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= -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-20210927113745-59d0afb8317a h1:E/8AP5dFtMhl5KPJz66Kt9G0n+7Sn41Fy1wv9/jHOrc= -github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b h1:mimo19zliBX/vSQ6PWWSL9lK8qwHozUj03+zLoEB8O0= +github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs= 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/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/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= -github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= -github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= 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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 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-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-20190605094302-a0d1e3e36d50/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/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= -github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94= 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/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= 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/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/snappy-go v1.0.0/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/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= -github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= -github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= -github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/caddyserver/certmagic v0.21.4 h1:e7VobB8rffHv8ZZpSiZtEwnLDHUwLVYLWzWSa1FfKI0= +github.com/caddyserver/certmagic v0.21.4/go.mod h1:swUXjQ1T9ZtMv95qj7/InJvWLXURU85r+CfG0T+ZbDE= +github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA= +github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4= +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/ceramicnetwork/go-dag-jose v0.1.1 h1:7pObs22egc14vSS3AfCFfS1VmaL4lQUsAK7OGC3PlKk= +github.com/ceramicnetwork/go-dag-jose v0.1.1/go.mod h1:8ptnYwY2Z2y/s5oJnNBn/UCxLg6CpramNJ2ZXF/5aNY= 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.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= +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/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/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= 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-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= +github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= +github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8= +github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4= +github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/pebble v1.1.2 h1:CUh2IPtR4swHlEj48Rhfzw6l/d0qA31fItcIszQVIsA= +github.com/cockroachdb/pebble v1.1.2/go.mod h1:4exszw1r40423ZsmkG/09AFEG83I0uDgfujJdbL6kYU= +github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= -github.com/containerd/cgroups v1.0.4 h1:jN/mbWBEaz+T1pi5OFtnkQ+8qnmEbAr1Oo1FRm5B0dA= -github.com/containerd/cgroups v1.0.4/go.mod h1:nLNQtsF7Sl2HxNebu77i1R0oDlhiTG+kO4JTrUzo6IA= +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-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/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/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 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/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 h1:HVTnpeuvF6Owjd5mniCL8DEXo7uYXdQEmOP4FJbV5tg= -github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3/go.mod h1:p1d6YEZWvFzEh4KLyvBcVSnrfNDDvK2zfK/4x2v/4pE= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/crackcomm/go-gitignore v0.0.0-20241020182519-7843d2ba8fdf h1:dwGgBWn84wUS1pVikGiruW+x5XM4amhjaZO20vCjay4= +github.com/crackcomm/go-gitignore v0.0.0-20241020182519-7843d2ba8fdf/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/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/go.mod h1:rQYf4tfk5sSwFsnDg3qYaBxSjsD9S8+59vW0dKUgme4= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/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.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= -github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= -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/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.1/go.mod h1:FRmFw3uxvcpa8zG3Rxs0th+hCLIuaQg8HlNV5bjgnuU= 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/ristretto v0.0.2 h1:a5WaUrDa0qm0YrAAS1tUykT5El3kt62KNZZeMxQn3po= github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-farm v0.0.0-20190104051053-3adb47b1fb0f/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= +github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= 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 v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= -github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= -github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +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/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/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +github.com/elastic/gosigar v0.14.3 h1:xwkKwPia+hSfg9GqrCUKYdId102m9qTJIIr7egmK/uo= +github.com/elastic/gosigar v0.14.3/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= 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.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 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.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +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.0.0 h1:DlTHqmzmvcEiKj+4RYo/imoswx/4r6iBlCMfVtrMXpQ= -github.com/flynn/noise v1.0.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= -github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +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/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= -github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= 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.2/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +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.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc= +github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc= +github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= +github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= 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-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= 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/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= -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-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/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E= +github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/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-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +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/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.0/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.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/glog v1.2.2 h1:1+mZ9upx1Dh6FmUTFR1naJ77miKiXgALjWOZ3NVFPmY= +github.com/golang/glog v1.2.2/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-20191027212112-611e8accdfc9/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/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= 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.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= @@ -301,8 +221,9 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD 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 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 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.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -312,40 +233,30 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a 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.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +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.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= 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/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-20221203041831-ce31453925ec h1:fR20TYVVwhK4O7r7y+McjRYyaTH6/vjwJOajE+XhlzM= -github.com/google/pprof v0.0.0-20221203041831-ce31453925ec/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20241017200806-017d972448fc h1:NGyrhhFhwvRAZg02jnYVg3GBQy0qGBKmFQJwaPmpmxs= +github.com/google/pprof v0.0.0-20241017200806-017d972448fc/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/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= @@ -353,602 +264,264 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4= github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= -github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +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.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/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/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= -github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= 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/hannahhoward/go-pubsub v0.0.0-20200423002714-8d62886cc36e h1:3YKHER4nmd7b5qy5t0GWDTwSn4OyRgfAXSmo6VnryBY= -github.com/hannahhoward/go-pubsub v0.0.0-20200423002714-8d62886cc36e/go.mod h1:I8h3MITA53gN9OnWGCgaMa0JWVRdXthWw4M3CPM54OY= -github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= -github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= 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-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 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/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= 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/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/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= -github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= -github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= -github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y= -github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= +github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= +github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= 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/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/ipfs/bbloom v0.0.1/go.mod h1:oqo8CVWsJFMOZqTglBG4wydCE4IQA/G2/SEofB0rjUI= +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/go-bitfield v1.0.0 h1:y/XHm2GEmD9wKngheWNNCNL0pzrWXZwCdQGv1ikXknQ= -github.com/ipfs/go-bitfield v1.0.0/go.mod h1:N/UiujQy+K+ceU1EF5EkVd1TNqevLrCQMIcAEPrdtus= -github.com/ipfs/go-bitswap v0.1.0/go.mod h1:FFJEf18E9izuCqUtHxbWEvq+reg7o4CW5wSAE1wsxj0= -github.com/ipfs/go-bitswap v0.1.2/go.mod h1:qxSWS4NXGs7jQ6zQvoPY3+NmOfHHG47mhkiLzBpJQIs= -github.com/ipfs/go-bitswap v0.5.1/go.mod h1:P+ckC87ri1xFLvk74NlXdP0Kj9RmWAh4+H78sC6Qopo= -github.com/ipfs/go-bitswap v0.6.0/go.mod h1:Hj3ZXdOC5wBJvENtdqsixmzzRukqd8EHLxZLZc3mzRA= +github.com/ipfs/boxo v0.24.3 h1:gldDPOWdM3Rz0v5LkVLtZu7A7gFNvAlWcmxhCqlHR3c= +github.com/ipfs/boxo v0.24.3/go.mod h1:h0DRzOY1IBFDHp6KNvrJLMFdSXTYID0Zf+q7X05JsNg= +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.1/go.mod h1:DK/YYcsSUIVAFNwo/KZCdIIbpN0ROH/baNLgayt4pFc= -github.com/ipfs/go-block-format v0.0.2/go.mod h1:AWR46JfpcObNfg3ok2JHDUfdiHRgWhJgCQF+KIgOPJY= -github.com/ipfs/go-block-format v0.0.3 h1:r8t66QstRp/pd/or4dpnbVfXT5Gt7lOqRvC+/dDTpMc= github.com/ipfs/go-block-format v0.0.3/go.mod h1:4LmD4ZUw0mhO+JSKdpWwrzATiEfM7WWgQ8H5l6P8MVk= -github.com/ipfs/go-blockservice v0.1.0/go.mod h1:hzmMScl1kXHg3M2BjTymbVPjv627N7sYcvYaKbop39M= -github.com/ipfs/go-blockservice v0.2.1/go.mod h1:k6SiwmgyYgs4M/qt+ww6amPeUH9EISLRBnvUurKJhi8= -github.com/ipfs/go-blockservice v0.3.0/go.mod h1:P5ppi8IHDC7O+pA0AlGTF09jruB2h+oP3wVVaZl8sfk= -github.com/ipfs/go-blockservice v0.5.0 h1:B2mwhhhVQl2ntW2EIpaWPwSCxSuqr5fFA93Ms4bYLEY= -github.com/ipfs/go-blockservice v0.5.0/go.mod h1:W6brZ5k20AehbmERplmERn8o2Ni3ZZubvAxaIUeaT6w= -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-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.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/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= -github.com/ipfs/go-cid v0.1.0/go.mod h1:rH5/Xv83Rfy8Rw6xG+id3DYAMUVmem1MowoKwdXmN2o= -github.com/ipfs/go-cid v0.3.2 h1:OGgOd+JCFM+y1DjWPmVH+2/4POtpDzwcr7VgnB7mZXc= -github.com/ipfs/go-cid v0.3.2/go.mod h1:gQ8pKqT/sUxGY+tIwy1RPpAojYu7jAyCp5Tz1svoupw= +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.0.1/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= -github.com/ipfs/go-datastore v0.0.5/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.3.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/go.mod h1:eXTcaaiN6uOlVCLS9GjJUJtlvJfM3xk23w3fyfrmmJs= github.com/ipfs/go-datastore v0.5.0/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-delegated-routing v0.7.0 h1:43FyMnKA+8XnyX68Fwg6aoGkqrf8NS5aG7p644s26PU= -github.com/ipfs/go-delegated-routing v0.7.0/go.mod h1:u4zxjUWIe7APUW5ds9CfD0tJX3vM9JhIeNqA8kE4vHE= 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-badger v0.3.0 h1:xREL3V0EH9S219kFFueOYJJTcjgNSZ2HY1iSvN7U1Ro= github.com/ipfs/go-ds-badger v0.3.0/go.mod h1:1ke6mXNqeV8K3y5Ak2bAA0osoTfmxUdupVCGm4QUIek= 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.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-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-fetcher v1.6.1 h1:UFuRVYX5AIllTiRhi5uK/iZkfhSpBCGX7L70nSZEmK8= -github.com/ipfs/go-fetcher v1.6.1/go.mod h1:27d/xMV8bodjVs9pugh/RCjjK2OZ68UgAMspMdingNo= -github.com/ipfs/go-filestore v1.2.0 h1:O2wg7wdibwxkEDcl7xkuQsPvJFRBVgVSsOJ/GP6z3yU= -github.com/ipfs/go-filestore v1.2.0/go.mod h1:HLJrCxRXquTeEEpde4lTLMaE/MYJZD7WHLkp9z6+FF8= +github.com/ipfs/go-ds-pebble v0.4.0 h1:88lgFAs2ck8jCQ8lMYRBtksEg18r9BlvTxIMnNJkZaQ= +github.com/ipfs/go-ds-pebble v0.4.0/go.mod h1:ZyYU+weIni+4NG/Yjva+cPkU3ghlsU1HA2R/VLHJ9sM= 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-graphsync v0.14.1-0.20221120210616-975d7aaeb15b h1:h+U91xq+a2jQh4oI0ZvxnaJ7s+6VAI8yyQE9jXEiSD0= -github.com/ipfs/go-graphsync v0.14.1-0.20221120210616-975d7aaeb15b/go.mod h1:Esdasda7sNmIlauOhqBG+J4yw60sE3EUftEpuOGX9to= -github.com/ipfs/go-ipfs-blockstore v0.0.1/go.mod h1:d3WClOmRQKFnJ0Jz/jj/zmksX0ma1gROTlovZKBmN08= -github.com/ipfs/go-ipfs-blockstore v0.1.0/go.mod h1:5aD0AvHPi7mZc6Ci1WCAhiBQu2IsfTduLl+422H6Rqw= -github.com/ipfs/go-ipfs-blockstore v0.2.1/go.mod h1:jGesd8EtCM3/zPgx+qr0/feTXGUeRai6adgwC+Q+JvE= -github.com/ipfs/go-ipfs-blockstore v1.1.2/go.mod h1:w51tNR9y5+QXB0wkNcHt4O2aSZjTdqaEWaQdSxEyUOY= -github.com/ipfs/go-ipfs-blockstore v1.2.0 h1:n3WTeJ4LdICWs/0VSfjHrlqpPpl6MZ+ySd3j8qz0ykw= -github.com/ipfs/go-ipfs-blockstore v1.2.0/go.mod h1:eh8eTFLiINYNSNawfZOC7HOxNTxpB1PFuA5E1m/7exE= +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.1/go.mod h1:tWewYK0we3+rMbOh7pPFGDyypCtvGcBFymgY4rSDLAw= -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-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 v0.0.1/go.mod h1:gtP9xRaZXqIQRh1HRpp595KbBEdgqWFxefeVKOV8sxo= -github.com/ipfs/go-ipfs-ds-help v0.1.1/go.mod h1:SbBafGJuGsPI/QL3j9Fc5YPLeAu+SzOkI0gFwAg+mOs= -github.com/ipfs/go-ipfs-ds-help v1.1.0 h1:yLE2w9RAsl31LtfMt91tRZcrx+e61O5mDxFRR994w4Q= -github.com/ipfs/go-ipfs-ds-help v1.1.0/go.mod h1:YR5+6EaebOhfcqVCyqemItCLthrpVNot+rsOU/5IatU= -github.com/ipfs/go-ipfs-exchange-interface v0.0.1/go.mod h1:c8MwfHjtQjPoDyiy9cFquVtVHkO9b9Ob3FG91qJnWCM= -github.com/ipfs/go-ipfs-exchange-interface v0.1.0/go.mod h1:ych7WPlyHqFvCi/uQI48zLZuAWVP5iTQPXEfVaw5WEI= -github.com/ipfs/go-ipfs-exchange-interface v0.2.0 h1:8lMSJmKogZYNo2jjhUs0izT+dck05pqUw4mWNW9Pw6Y= -github.com/ipfs/go-ipfs-exchange-interface v0.2.0/go.mod h1:z6+RhJuDQbqKguVyslSOuVDhqF9JtTrO3eptSAiW2/Y= -github.com/ipfs/go-ipfs-exchange-offline v0.0.1/go.mod h1:WhHSFCVYX36H/anEKQboAzpUws3x7UeEGkzQc3iNkM0= -github.com/ipfs/go-ipfs-exchange-offline v0.1.1/go.mod h1:vTiBRIbzSwDD0OWm+i3xeT0mO7jG2cbJYatp3HPk5XY= -github.com/ipfs/go-ipfs-exchange-offline v0.2.0/go.mod h1:HjwBeW0dvZvfOMwDP0TSKXIHf2s+ksdP4E3MLDRtLKY= +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-files v0.0.3/go.mod h1:INEFm0LL2LWXBhNJ2PMIIb2w45hpXgPjNoE7yA8Y1d4= -github.com/ipfs/go-ipfs-files v0.2.0 h1:z6MCYHQSZpDWpUSK59Kf0ajP1fi4gLCf6fIulVsp8A8= -github.com/ipfs/go-ipfs-files v0.2.0/go.mod h1:vT7uaQfIsprKktzbTPLnIsd+NGw9ZbYwSq0g3N74u0M= -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-pinner v0.2.1 h1:kw9hiqh2p8TatILYZ3WAfQQABby7SQARdrdA+5Z5QfY= -github.com/ipfs/go-ipfs-pinner v0.2.1/go.mod h1:l1AtLL5bovb7opnG77sh4Y10waINz3Y1ni6CvTzx7oo= -github.com/ipfs/go-ipfs-posinfo v0.0.1 h1:Esoxj+1JgSjX0+ylc0hUmJCOv6V2vFoZiETLR6OtpRs= -github.com/ipfs/go-ipfs-posinfo v0.0.1/go.mod h1:SwyeVP+jCwiDu0C313l/8jg6ZxM0qqtlt2a0vILTc1A= -github.com/ipfs/go-ipfs-pq v0.0.1/go.mod h1:LWIqQpqfRG3fNc5XsnIhz/wQ2XXGyugQwls7BgUmUfY= -github.com/ipfs/go-ipfs-pq v0.0.2 h1:e1vOOW6MuOwG2lqxcLA+wEn93i/9laCY8sXAw76jFOY= -github.com/ipfs/go-ipfs-pq v0.0.2/go.mod h1:LWIqQpqfRG3fNc5XsnIhz/wQ2XXGyugQwls7BgUmUfY= -github.com/ipfs/go-ipfs-provider v0.8.1 h1:qt670pYmcNH3BCjyXDgg07o2WsTRsOdMwYc25ukCdjQ= -github.com/ipfs/go-ipfs-provider v0.8.1/go.mod h1:qCpwpoohIRVXvNzkygzsM3qdqP/sXlrogtA5I45tClc= -github.com/ipfs/go-ipfs-routing v0.1.0/go.mod h1:hYoUkJLyAUKhF58tysKpids8RNDPO42BVMgK5dNsoqY= -github.com/ipfs/go-ipfs-routing v0.2.1/go.mod h1:xiNNiwgjmLqPS1cimvAw6EyB9rkVDbiocA4yY+wRNLM= +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.2 h1:QCK7VtL91FH17KROVVy5KrzDx2hu68QvB2FTWk08ZQk= +github.com/ipfs/go-ipfs-redirects-file v0.1.2/go.mod h1:yIiTlLcDEM/8lS6T3FlCEXZktPPqSOyuY6dEzVqw7Fw= 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 h1:59Sswnk1MFaiq+VcaknX7aYEyGyGDAA73ilhEK2POp8= github.com/ipfs/go-ipfs-util v0.0.2/go.mod h1:CbPtkWJzjLdEcezDns2XYaehFVNXG9zrdrtMecczcsQ= -github.com/ipfs/go-ipld-cbor v0.0.2/go.mod h1:wTBtrQZA3SoFKMVkp6cn6HMRteIB1VsmHA0AQFOn7Nc= -github.com/ipfs/go-ipld-cbor v0.0.3/go.mod h1:wTBtrQZA3SoFKMVkp6cn6HMRteIB1VsmHA0AQFOn7Nc= -github.com/ipfs/go-ipld-cbor v0.0.5 h1:ovz4CHKogtG2KB/h1zUp5U0c/IzZrL435rCh5+K/5G8= -github.com/ipfs/go-ipld-cbor v0.0.5/go.mod h1:BkCduEx3XBCO6t2Sfo5BaHzuok7hbhdMm9Oh8B2Ftq4= -github.com/ipfs/go-ipld-format v0.0.1/go.mod h1:kyJtbkDALmFHv3QR6et67i35QzO3S0dCDnkOJhcZkms= -github.com/ipfs/go-ipld-format v0.0.2/go.mod h1:4B6+FM2u9OJ9zCV+kSbgFAZlOrv1Hqbf0INGQgiKf9k= -github.com/ipfs/go-ipld-format v0.2.0/go.mod h1:3l3C1uKoadTPbeNfrDi+xMInYKlx2Cvg1BuydPSdzQs= -github.com/ipfs/go-ipld-format v0.3.0/go.mod h1:co/SdBE8h99968X0hViiw1MNlh6fvxxnHpvVLnH7jSM= -github.com/ipfs/go-ipld-format v0.4.0 h1:yqJSaJftjmjc9jEOFYlpkwOLVKv68OD27jFLlSghBlQ= -github.com/ipfs/go-ipld-format v0.4.0/go.mod h1:co/SdBE8h99968X0hViiw1MNlh6fvxxnHpvVLnH7jSM= +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.2.0 h1:VHIW3HVIjcMd8m4ZLZbrYpwjzqlVUfjLM7oK4T5/YF0= +github.com/ipfs/go-ipld-cbor v0.2.0/go.mod h1:Cp8T7w1NKcu4AQJLqK0tWpd1nkgTxEVB5C6kVpLW6/0= +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.1.0/go.mod h1:86f5P/srAmh9GcIcWQR9lfFLZPrIyyXQeVlOWeeWEuI= -github.com/ipfs/go-ipld-legacy v0.1.1 h1:BvD8PEuqwBHLTKqlGFTHSwrwFOMkVESEvwIYwR2cdcc= -github.com/ipfs/go-ipld-legacy v0.1.1/go.mod h1:8AyKFCjgRPsQFf15ZQgDB8Din4DML/fOmKZkkFkrIEg= -github.com/ipfs/go-ipns v0.3.0 h1:ai791nTgVo+zTuq2bLvEGmWP1M0A6kGTXUsgv/Yq67A= -github.com/ipfs/go-ipns v0.3.0/go.mod h1:3cLT2rbvgPZGkHJoPO1YMJeh6LtkxopCkKFcio/wE24= -github.com/ipfs/go-libipfs v0.0.0-20221208220359-356ce09dd4a1 h1:p/eMmtJfOliZh/SCVv239Wxj2lCo5IN4j5bdNmeGueM= -github.com/ipfs/go-libipfs v0.0.0-20221208220359-356ce09dd4a1/go.mod h1:blLqyfvHD86wgXMJ8GR4QQWYeg1ZvFHOhX3DT340Nj8= +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-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/go.mod h1:oDCg2FkjogeFOhqqb+N39l2RpTNPL6F/StPkB3kPgcs= 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.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/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM= 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.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY= github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI= -github.com/ipfs/go-merkledag v0.2.3/go.mod h1:SQiXrtSts3KGNmgOzMICy5c0POOpUNQLvB3ClKnBAlk= -github.com/ipfs/go-merkledag v0.3.2/go.mod h1:fvkZNNZixVW6cKSZ/JfLlON5OlgTXNdRLz0p6QG/I2M= -github.com/ipfs/go-merkledag v0.5.1/go.mod h1:cLMZXx8J08idkp5+id62iVftUQV+HlYJ3PIhDfZsjA4= -github.com/ipfs/go-merkledag v0.6.0/go.mod h1:9HSEwRd5sV+lbykiYP+2NC/3o6MZbKNaa4hfNcH5iH0= -github.com/ipfs/go-merkledag v0.8.1 h1:N3yrqSre/ffvdwtHL4MXy0n7XH+VzN8DlzDrJySPa94= -github.com/ipfs/go-merkledag v0.8.1/go.mod h1:uYUlWE34GhbcTjGuUDEcdPzsEtOdnOupL64NgSRjmWI= +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-mfs v0.2.1 h1:5jz8+ukAg/z6jTkollzxGzhkl3yxm022Za9f2nL5ab8= -github.com/ipfs/go-mfs v0.2.1/go.mod h1:Woj80iuw4ajDnIP6+seRaoHpPsc9hmL0pk/nDNDWP88= -github.com/ipfs/go-namesys v0.6.0 h1:w4+Wq9bCILnuZRT1RBBdzZQFqtJeDG1duzN8mIDnHZ0= -github.com/ipfs/go-namesys v0.6.0/go.mod h1:0L+3CHBgHxr08Cg+chVo9Ew285PGQfToThjll4g0/d4= -github.com/ipfs/go-path v0.2.1/go.mod h1:NOScsVgxfC/eIw4nz6OiGwK42PjaSJ4Y/ZFPn1Xe07I= -github.com/ipfs/go-path v0.3.0 h1:tkjga3MtpXyM5v+3EbRvOHEoo+frwi4oumw5K+KYWyA= -github.com/ipfs/go-path v0.3.0/go.mod h1:NOScsVgxfC/eIw4nz6OiGwK42PjaSJ4Y/ZFPn1Xe07I= -github.com/ipfs/go-peertaskqueue v0.1.0/go.mod h1:Jmk3IyCcfl1W3jTW3YpghSwSEC6IJ3Vzz/jUmWw8Z0U= -github.com/ipfs/go-peertaskqueue v0.7.0/go.mod h1:M/akTIE/z1jGNXMU7kFB4TeSEFvj68ow0Rrb04donIU= -github.com/ipfs/go-peertaskqueue v0.8.0 h1:JyNO144tfu9bx6Hpo119zvbEL9iQ760FHOiJYsUjqaU= -github.com/ipfs/go-peertaskqueue v0.8.0/go.mod h1:cz8hEnnARq4Du5TGqiWKgMr/BOSQ5XOgMOh1K5YYKKM= -github.com/ipfs/go-unixfs v0.2.4/go.mod h1:SUdisfUjNoSDzzhGVxvCL9QO/nKdwXdr+gbMUdqcbYw= -github.com/ipfs/go-unixfs v0.3.1/go.mod h1:h4qfQYzghiIc8ZNFKiLMFWOTzrWIAtzYQ59W/pCFf1o= -github.com/ipfs/go-unixfs v0.4.1 h1:nmJFKvF+khK03PIWyCxxydD/nkQX315NZDcgvRqMXf0= -github.com/ipfs/go-unixfs v0.4.1/go.mod h1:2SUDFhUSzrcL408B1qpIkJJ5HznnyTzweViPXUAvkNg= -github.com/ipfs/go-unixfsnode v1.1.2/go.mod h1:5dcE2x03pyjHk4JjamXmunTMzz+VUtqvPwZjIEkfV6s= -github.com/ipfs/go-unixfsnode v1.4.0 h1:9BUxHBXrbNi8mWHc6j+5C580WJqtVw9uoeEKn4tMhwA= -github.com/ipfs/go-unixfsnode v1.4.0/go.mod h1:qc7YFFZ8tABc58p62HnIYbUMwj9chhUuFWmxSokfePo= -github.com/ipfs/go-verifcid v0.0.1/go.mod h1:5Hrva5KBeIog4A+UpqlaIU+DEstipcJYQQZc0g37pY0= -github.com/ipfs/go-verifcid v0.0.2 h1:XPnUv0XmdH+ZIhLGKg6U2vaPaRDXb9urMyNVCE7uvTs= -github.com/ipfs/go-verifcid v0.0.2/go.mod h1:40cD9x1y4OWnFXbLNJYRe7MpNvWlMn3LZAG5Wb4xnPU= -github.com/ipfs/interface-go-ipfs-core v0.8.1 h1:nuFG0YJ429Wd5gtRb3ivlblpknZ5VfDVKZkmOG2TnNQ= -github.com/ipfs/interface-go-ipfs-core v0.8.1/go.mod h1:WYC2H6Mu7aGqhlupi/CVawcs0X1Me4uRvV0rcTlo3zM= -github.com/ipld/edelweiss v0.2.0 h1:KfAZBP8eeJtrLxLhi7r3N0cBCo7JmwSRhOJp3WSpNjk= -github.com/ipld/edelweiss v0.2.0/go.mod h1:FJAzJRCep4iI8FOFlRriN9n0b7OuX3T/S9++NpBDmA4= -github.com/ipld/go-car v0.4.0 h1:U6W7F1aKF/OJMHovnOVdst2cpQE5GhmHibQkAixgNcQ= -github.com/ipld/go-car/v2 v2.1.1/go.mod h1:+2Yvf0Z3wzkv7NeI69i8tuZ+ft7jyjPYIWZzeVNeFcI= -github.com/ipld/go-car/v2 v2.4.0 h1:8jI6/iKlyLqRZzLz31jFWTqKvslaVzFsin305sOuqNQ= -github.com/ipld/go-codec-dagpb v1.3.0/go.mod h1:ga4JTU3abYApDC3pZ00BC2RSvC3qfBb9MSJkMLSwnhA= -github.com/ipld/go-codec-dagpb v1.4.1 h1:CUQJaOPRgSZ27OUPgUWtvdvvd2d17/IGGAIMOo4yYp0= -github.com/ipld/go-codec-dagpb v1.4.1/go.mod h1:XdXTO/TUD/ra9RcK/NfmwBfr1JpFxM2uRKaB9oe4LxE= -github.com/ipld/go-ipld-prime v0.9.1-0.20210324083106-dc342a9917db/go.mod h1:KvBLMr4PX1gWptgkzRjVZCrLmSGcZCb/jioOQwCqZN8= +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-test v0.0.4 h1:DKT66T6GBB6PsDFLoO56QZPrOmzJkqU1FZH5C9ySkew= +github.com/ipfs/go-test v0.0.4/go.mod h1:qhIM1EluEfElKKM6fnWxGn822/z9knUGM1+I/OAQNKI= +github.com/ipfs/go-unixfsnode v1.9.2 h1:0A12BYs4XOtDPJTMlwmNPlllDfqcc4yie4e919hcUXk= +github.com/ipfs/go-unixfsnode v1.9.2/go.mod h1:v1nuMFHf4QTIhFUdPMvg1nQu7AqDLvIdwyvJ531Ot1U= +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/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.14.2 h1:9ERr7KXpCC7If0rChZLhYDlyr6Bes6yRKPJnCO3hdHY= +github.com/ipld/go-car/v2 v2.14.2/go.mod h1:0iPB/825lTZLU2zPK5bVTk/R3V2612E1VI279OGSXWA= +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.0/go.mod h1:9ASQLwUFLptCov6lIYc70GRB4V7UTyLD0IJtrDJe6ZM= -github.com/ipld/go-ipld-prime v0.14.1/go.mod h1:QcE4Y9n/ZZr8Ijg5bGPT0GqYWgZ1704nH0RDcQtgTP0= -github.com/ipld/go-ipld-prime v0.16.0/go.mod h1:axSCuOCBPqrH+gvXr2w9uAOulJqBPhHPT2PjoiiU1qA= -github.com/ipld/go-ipld-prime v0.19.0 h1:5axC7rJmPc17Emw6TelxGwnzALk0PdupZ2oj2roDj04= -github.com/ipld/go-ipld-prime v0.19.0/go.mod h1:Q9j3BaVXwaA3o5JUDNvptDDr/x8+F7FG6XJ8WI3ILg4= -github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20211210234204-ce2a1c70cd73/go.mod h1:2PJ0JgxyB08t0b2WKrcuqI3di0V+5n6RS/LTUJhkoxY= -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/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/ipshipyard/p2p-forge v0.0.2 h1:86y9LxGB8sGxYQ/If5sNx+c8C/huSpBUg3UZ1uvtym8= +github.com/ipshipyard/p2p-forge v0.0.2/go.mod h1:taPeh3PDSO8Ual0/N2tIOAUXPV8gZoPF3uPXoUyiq14= 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-random v0.0.0-20190219211222-123a90aedc0c h1:uUx61FiAa1GI6ZmVd2wf2vULeQZIKG66eybjNXKYCz4= -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/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= -github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= -github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= -github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= -github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc= -github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= 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/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -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.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -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.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 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/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/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.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM= -github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.0.6/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.1 h1:U33DW0aiEj633gHYw3LoDNfkDiYnE5Q8M/TKJn2f2jI= -github.com/klauspost/cpuid/v2 v2.2.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= +github.com/klauspost/cpuid/v2 v2.2.8/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.0-20180514024734-4a0ed625a78b/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= -github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= -github.com/koron/go-ssdp v0.0.3 h1:JivLMY45N76b4p/vsWGOKewBQu6uf39y8l+AQ7sDKx8= -github.com/koron/go-ssdp v0.0.3/go.mod h1:b2MxI6yh02pKrsyNoQUsk4+YNikaGhe4894J+Q5lDvA= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +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/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 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -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/libp2p/go-addr-util v0.0.1/go.mod h1:4ac6O7n9rIAKB1dnd+s8IbbMXkt+oBpzX4/+RACcnlQ= -github.com/libp2p/go-addr-util v0.0.2/go.mod h1:Ecd6Fb3yIuLzq4bD7VcywcVSBtefcAwnUISBM3WG15E= +github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s= +github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= 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-conn-security-multistream v0.1.0/go.mod h1:aw6eD7LOsHEX7+2hJkDxw1MteijaVcI+/eP2/x3J1xc= -github.com/libp2p/go-conn-security-multistream v0.2.0/go.mod h1:hZN4MjlNetKD3Rq5Jb/P5ohUnFLNzEAR4DLSzpn2QLU= -github.com/libp2p/go-conn-security-multistream v0.2.1/go.mod h1:cR1d8gA0Hr59Fj6NhaTpFhJZrjSYuNmhpT2r25zYR70= 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-eventbus v0.1.0/go.mod h1:vROgu5cs5T7cv7POWlWxBaVLxfSegC5UGQf8A2eEmx4= -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.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.1.0/go.mod h1:6D/2OBauqLUoqcADOJpn9WbKqvaM07tDw68qHM0BxUM= -github.com/libp2p/go-libp2p v0.1.1/go.mod h1:I00BRo1UuUSdpuc8Q2mN7yDF/oTUTRAX6JWpTiK9Rp8= -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.14.3/go.mod h1:d12V4PdKbpL0T1/gsUNN8DfgMuRPDX8bS2QxCZlwRH0= -github.com/libp2p/go-libp2p v0.24.2 h1:iMViPIcLY0D6zr/f+1Yq9EavCZu2i7eDstsr1nEwSAk= -github.com/libp2p/go-libp2p v0.24.2/go.mod h1:WuxtL2V8yGjam03D93ZBC19tvOUiPpewYv1xdFGWu1k= -github.com/libp2p/go-libp2p-asn-util v0.2.0 h1:rg3+Os8jbnO5DxkC7K/Utdi+DkY3q/d1/1q+8WeNAsw= -github.com/libp2p/go-libp2p-asn-util v0.2.0/go.mod h1:WoaWxbHKBymSN41hWSq/lGKJEca7TNm58+gGJi2WsLI= -github.com/libp2p/go-libp2p-autonat v0.1.0/go.mod h1:1tLf2yXxiE/oKGtDwPYWTSYG3PtvYlJmg7NeVtPRqH8= -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.2/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/go.mod h1:eduNKXGTioTuQAUcZ5epXi9vMl+t4d8ugUBRQ4SqaNQ= -github.com/libp2p/go-libp2p-circuit v0.1.0/go.mod h1:Ahq4cY3V9VJcHcn1SBXjr78AbFkZeIRmfunbA7pmFh8= -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/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.2/go.mod h1:9dAcntw/n46XycV4RnlBq3BpgrmyUi9LuoTNdPrbUco= -github.com/libp2p/go-libp2p-core v0.0.3/go.mod h1:j+YQMNz9WNSkNezXOsahp9kwZBKBvxLpKD316QWSJXE= -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-flow-metrics v0.2.0 h1:EIZzjmeOE6c8Dav0sNv35vhZxATIXWZg6j/C08XmmDw= +github.com/libp2p/go-flow-metrics v0.2.0/go.mod h1:st3qqfu8+pMfh+9Mzqb2GTiwrAGjIPszEjZmtksN8Jc= +github.com/libp2p/go-libp2p v0.37.0 h1:8K3mcZgwTldydMCNOiNi/ZJrOB9BY+GlI3UxYzxBi9A= +github.com/libp2p/go-libp2p v0.37.0/go.mod h1:GOKmSN99scDuYGTwaTbQPR8Nt6dxrK3ue7OjW2NGDg4= +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-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.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.7.0/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-core v0.8.0/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-core v0.8.1/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-core v0.8.2/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-core v0.8.5/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-crypto v0.1.0/go.mod h1:sPUokVISZiy+nNuTTH/TY+leRSxnFj/2GLjtOTW90hI= -github.com/libp2p/go-libp2p-discovery v0.1.0/go.mod h1:4F/x+aldVHjHDHuX85x1zWoFTGElt8HnoDzwkFZm29g= -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/go.mod h1:+srtPIU9gDaBNu//UHvcdliKBIcr4SfDcm0/PfPJLug= -github.com/libp2p/go-libp2p-kad-dht v0.20.0 h1:1bcMa74JFwExCHZMFEmjtHzxX5DovhJ07EtR6UOTEpc= -github.com/libp2p/go-libp2p-kad-dht v0.20.0/go.mod h1:qPIXdiZsLczhV4/+4EO1jE8ae0YCW4ZOogc4WVIyTEU= +github.com/libp2p/go-libp2p-kad-dht v0.27.0 h1:1Ea32tVTPiAfaLpPMbaBWFJgbsi/JpMqC2YBuFdf32o= +github.com/libp2p/go-libp2p-kad-dht v0.27.0/go.mod h1:ixhjLuzaXSGtWsKsXTj7erySNuVC4UP7NO015cRrF14= github.com/libp2p/go-libp2p-kbucket v0.3.1/go.mod h1:oyjT5O7tS9CQurok++ERgc46YLwEpuGoFq9ubvoUOio= -github.com/libp2p/go-libp2p-kbucket v0.5.0 h1:g/7tVm8ACHDxH29BGrpsQlnNeu+6OF1A9bno/4/U1oA= -github.com/libp2p/go-libp2p-kbucket v0.5.0/go.mod h1:zGzGCpQd78b5BNTDGHNDLaTt9aDK/A02xeZp9QeFC4U= -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.4.0/go.mod h1:yCyWJE2sc6TBTnFpjvLuEJgTSw/u+MamvzILKdX7asw= -github.com/libp2p/go-libp2p-mplex v0.4.1/go.mod h1:cmy+3GfqfM1PceHTLL7zQzAAYaryDu6iPSC+CIb094g= -github.com/libp2p/go-libp2p-nat v0.0.4/go.mod h1:N9Js/zVtAXqaeT99cXgTV9e75KpnWCvVOiGzlcHmBbY= -github.com/libp2p/go-libp2p-nat v0.0.5/go.mod h1:1qubaE5bTZMJE+E/uu2URroMbzdubFz1ChgiN79yKPE= -github.com/libp2p/go-libp2p-nat v0.0.6/go.mod h1:iV59LVhB3IkFvS6S6sauVTSOrNEANnINbI/fkaLimiw= -github.com/libp2p/go-libp2p-netutil v0.1.0/go.mod h1:3Qv/aDqtMLTUyQeundkKsA+YCThNdbQD54k3TqjpbFU= -github.com/libp2p/go-libp2p-noise v0.2.0/go.mod h1:IEbYhBBzGyvdLBoxxULL/SGbJARhUeqlO8lVSREYu2Q= -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-kbucket v0.6.4 h1:OjfiYxU42TKQSB8t8WYd8MKhYhMJeO2If+NiuKfb6iQ= +github.com/libp2p/go-libp2p-kbucket v0.6.4/go.mod h1:jp6w82sczYaBsAypt5ayACcRJi0lgsba7o4TzJKEfWA= 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/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= -github.com/libp2p/go-libp2p-peerstore v0.2.7/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= -github.com/libp2p/go-libp2p-pnet v0.2.0/go.mod h1:Qqvq6JH/oMZGwqs3N1Fqhv8NVhrdYcO0BW4wssv21LA= -github.com/libp2p/go-libp2p-pubsub v0.8.2 h1:QLGUmkgKmwEVxVDYGsqc5t9CykOMY2Y21cXQHjR462I= -github.com/libp2p/go-libp2p-pubsub v0.8.2/go.mod h1:e4kT+DYjzPUYGZeWk4I+oxCSYTXizzXii5LDRRhjKSw= +github.com/libp2p/go-libp2p-pubsub v0.12.0 h1:PENNZjSfk8KYxANRlpipdS7+BfLmOl3L2E/6vSNjbdI= +github.com/libp2p/go-libp2p-pubsub v0.12.0/go.mod h1:Oi0zw9aw8/Y5GC99zt+Ef2gYAl+0nZlwdJonDyOz/sE= 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-quic-transport v0.10.0/go.mod h1:RfJbZ8IqXIhxBRm5hqUEJqjiiY8xmEuq3HUDS993MkA= -github.com/libp2p/go-libp2p-record v0.1.0/go.mod h1:ujNc8iuE5dlKWVy6wuL6dd58t0n7xI4hAIl8pE6wu5Q= 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.4.1 h1:rOlZiFpUt7SgHm4w62MBvWaQ4UHh7bVJnSnor6RN7j8= -github.com/libp2p/go-libp2p-routing-helpers v0.4.1/go.mod h1:dYEAgkVhqho3/YKxfOEGdFMIcWfAFNlZX8iAIihYA2E= -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.5.0/go.mod h1:sU9i6BoHE0Ve5SKz3y9WfKrh8dUat6JknzUehFx8xW4= -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/go.mod h1:Q+PFXYoiYFN5CAEG2w3gLPEzotlKsNSbKQ/lImlOWF0= +github.com/libp2p/go-libp2p-routing-helpers v0.7.4 h1:6LqS1Bzn5CfDJ4tzvP9uwh42IB7TJLNFJA6dEeGBv84= +github.com/libp2p/go-libp2p-routing-helpers v0.7.4/go.mod h1:we5WDj9tbolBXOuF1hGOkR+r7Uh1408tQbAKaT5n1LE= github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= -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.2/go.mod h1:NR8ne1VwfreD5VIWIU62Agt/J18ekORFU/j1i2y8zvk= +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-libp2p-yamux v0.2.0/go.mod h1:Db2gU+XfLpm6E4rG5uGCFX6uXA8MEXOxFcRoXUODaK8= -github.com/libp2p/go-libp2p-yamux v0.2.1/go.mod h1:1FBXiHDk1VyRM1C0aez2bCfHQ4vMZKkAQzZbkSQt5fI= -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.4/go.mod h1:tfrXbyaTqqSU654GTvK3ocnSZL3BuHoeTSqhcel1wsE= -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/go.mod h1:0Oy/A9PQlwBytDRp4wSkFnzHYDKcpLot35JQ6msjvYQ= -github.com/libp2p/go-mplex v0.7.0 h1:BDhFZdlk5tbr0oyFq/xv/NPGfjbnrsDam1EvutpBDbY= -github.com/libp2p/go-mplex v0.7.0/go.mod h1:rW8ThnRcYWft/Jb2jeORBmPd6xuG3dGxWN/W168L9EU= -github.com/libp2p/go-msgio v0.0.2/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= -github.com/libp2p/go-msgio v0.0.3/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/go.mod h1:4ecVB6d9f4BDSL5fqvPiC4A3KivjWn+Venn/1ALLMWA= -github.com/libp2p/go-msgio v0.2.0 h1:W6shmB+FeynDrUVl2dgFQvzfBZcXiyqY4VmpQLu9FqU= -github.com/libp2p/go-msgio v0.2.0/go.mod h1:dBVM1gW3Jk9XqHkU4eKdGvVHdLa51hoGfll6jMJMSlY= -github.com/libp2p/go-nat v0.0.3/go.mod h1:88nUEt0k0JD45Bk93NIwDqjlhiOwOoV36GchpcVc1yI= -github.com/libp2p/go-nat v0.0.4/go.mod h1:Nmw50VAvKuk38jUBcmNh6p9lUJLoODbJRvYAa/+KSDo= -github.com/libp2p/go-nat v0.0.5/go.mod h1:B7NxsVNPZmRLvMOwiEO1scOSyjA56zxYAGv1yQgRkEU= -github.com/libp2p/go-nat v0.1.0 h1:MfVsH6DLcpa04Xr+p8hmVRG4juse0s3J8HyNWYHffXg= -github.com/libp2p/go-nat v0.1.0/go.mod h1:X7teVkwRHNInVNWQiO/tAiAVRwSr5zoRz4YSTC3uRBM= -github.com/libp2p/go-netroute v0.1.2/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk= -github.com/libp2p/go-netroute v0.1.3/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk= -github.com/libp2p/go-netroute v0.1.5/go.mod h1:V1SR3AaECRkEQCoFFzYwVYWvYIEtlxx89+O3qcpCl4A= -github.com/libp2p/go-netroute v0.1.6/go.mod h1:AqhkMh0VuWmfgtxKPp3Oc1LdU5QSWS7wl0QLhSZqXxQ= +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.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/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= -github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo= -github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= -github.com/libp2p/go-reuseport v0.0.1/go.mod h1:jn6RmB1ufnQwl0Q1f+YxAj8isJgDCQzaaxIFYDhcYEA= -github.com/libp2p/go-reuseport v0.0.2/go.mod h1:SPD+5RwGC7rcnzngoYC86GjPzjSywuQyMVAheVBD9nQ= -github.com/libp2p/go-reuseport v0.2.0 h1:18PRvIMlpY6ZK85nIAicSBuXXvrYoSw3dsBAR7zc560= -github.com/libp2p/go-reuseport v0.2.0/go.mod h1:bvVho6eLMm6Bz5hmU0LYN3ixd3nPPvtIlaURZZgOY4k= -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/go.mod h1:trPa7r/7TJK/d+0hdBLOCGvpQQVOU74OXbNCIMkufGw= -github.com/libp2p/go-sockaddr v0.0.2/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= -github.com/libp2p/go-sockaddr v0.1.0/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= -github.com/libp2p/go-sockaddr v0.1.1/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/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.3/go.mod h1:9dvr03yqrPyYGIEN6Dy5UvdJZjyPFvl1S/igQ5QD1SU= -github.com/libp2p/go-testutil v0.1.0/go.mod h1:81b2n5HypcVyrCg/MJx4Wgfp/VHojytjVe/gLzZ2Ehc= -github.com/libp2p/go-ws-transport v0.1.0/go.mod h1:rjw1MG1LU9YDC6gzmwObkPd/Sqwhw7yT74kj3raBFuo= -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.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.2.3/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/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE= -github.com/libp2p/go-yamux/v2 v2.2.0/go.mod h1:3So6P6TV6r75R9jiBpiIKgU/66lOarCZjqROGxzPpPQ= -github.com/libp2p/go-yamux/v4 v4.0.0 h1:+Y80dV2Yx/kv7Y7JKu0LECyVdMXm1VUoko+VQ9rBfZQ= -github.com/libp2p/go-yamux/v4 v4.0.0/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4= +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/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= -github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= -github.com/lucas-clemente/quic-go v0.19.3/go.mod h1:ADXpNbTQjq1hIzCpB+y/k5iz4n4z4IwqoLb94Kh5Hu8= -github.com/lucas-clemente/quic-go v0.31.1 h1:O8Od7hfioqq0PMYHDyBkxU2aA7iZ2W9pjbrWuja2YR4= -github.com/lucas-clemente/quic-go v0.31.1/go.mod h1:0wFbizLgYzqHqtlyxyCaJKlE7bYgE6JQ+54TLd/Dq2g= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= -github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= 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/qpack v0.3.0 h1:UiWstOgT8+znlkDPOg2+3rIuYXJ2CnGDkGUXN6ki6hE= -github.com/marten-seemann/qpack v0.3.0/go.mod h1:cGfKPBiP4a9EQdxCwEwI/GEeWAsjSekBvx/X8mh58+g= -github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs= -github.com/marten-seemann/qtls-go1-15 v0.1.1/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I= -github.com/marten-seemann/qtls-go1-18 v0.1.3 h1:R4H2Ks8P6pAtUagjFty2p7BVHn3XiwDAl7TTQf5h7TI= -github.com/marten-seemann/qtls-go1-18 v0.1.3/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4= -github.com/marten-seemann/qtls-go1-19 v0.1.1 h1:mnbxeq3oEyQxQXwI4ReCgW9DPoPR94sNlqWoDZnjRIE= -github.com/marten-seemann/qtls-go1-19 v0.1.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI= 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/marten-seemann/webtransport-go v0.4.3 h1:vkt5o/Ci+luknRteWdYGYH1KcB7ziup+J+1PzZJIvmg= -github.com/marten-seemann/webtransport-go v0.4.3/go.mod h1:4xcfySgZMLP4aG5GBGj1egP7NlpfwgYJ1WJMvPPiVMU= 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.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 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.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.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-pointer v0.0.1 h1:n+XhsuGeVO6MEAp7xyEukFINEa+Quek5psIR/ylA6o0= -github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +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/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/mholt/acmez/v2 v2.0.3 h1:CgDBlEwg3QBp6s45tPQmFIBrkRIkBT4rW4orMM6p4sw= +github.com/mholt/acmez/v2 v2.0.3/go.mod h1:pQ1ysaDeGrIMvJ9dfJMk5kJNkn7L2sb3UhyrX6Q91cw= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -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.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.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= -github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= +github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ= +github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ= 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= @@ -957,28 +530,16 @@ github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc h1:PTfri+PuQmWDqERdn 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.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/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= -github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +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/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -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.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= @@ -989,203 +550,161 @@ github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYg 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.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/go.mod h1:uPbspcUPd5AfaP6ql3ujFY+QWzmBD8uLLL4bXW0XfGc= -github.com/multiformats/go-multiaddr v0.3.3/go.mod h1:lCKNGP1EQ1eZ35Za2wlqnabm9xQkib3fyB+nZXHLag0= -github.com/multiformats/go-multiaddr v0.8.0 h1:aqjksEcqK+iD/Foe1RRFsGZh8+XFiGo7FgUCZlpv3LU= -github.com/multiformats/go-multiaddr v0.8.0/go.mod h1:Fs50eBDWvZu+l3/9S6xAE7ZYj6yhxlvaVZjakWN7xRs= -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/go.mod h1:TJ5pr5bBO7Y1B18djPuRsVkduhQH2YqYSbxWJzYGdK0= +github.com/multiformats/go-multiaddr v0.13.0 h1:BCBzs61E3AGHcYYTv8dqRH43ZfyrqM8RXVPT8t13tLQ= +github.com/multiformats/go-multiaddr v0.13.0/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.0.1/go.mod h1:aBYjqL4T/7j4Qx+R73XSv/8JsgnRFlf0w2KGLCmXl3Q= +github.com/multiformats/go-multiaddr-dns v0.4.0 h1:P76EJ3qzBXpUXZ3twdCDx/kvagMsNo0LMFXpyms/zgU= +github.com/multiformats/go-multiaddr-dns v0.4.0/go.mod h1:7hfthtB4E4pQwirrz+J0CcDUfbWzTqEzVyYKKIKpgkc= 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/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/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc= -github.com/multiformats/go-multibase v0.1.1 h1:3ASCDsuLX8+j4kx58qnJ4YFq/JWTJpCyDW27ztsVTOI= -github.com/multiformats/go-multibase v0.1.1/go.mod h1:ZEjHE+IsUrgp5mhlEAYjMtZwK1k4haNkcaPg9aoe1a8= -github.com/multiformats/go-multicodec v0.3.0/go.mod h1:qGGaQmioCDh+TeFOnxrbU0DaIPw8yFgAZgFG0V7p1qQ= -github.com/multiformats/go-multicodec v0.3.1-0.20210902112759-1539a079fd61/go.mod h1:1Hj/eHRaVWSXiSNNfcEPcwZleTmdNP81xlxDLnWU9GQ= -github.com/multiformats/go-multicodec v0.3.1-0.20211210143421-a526f306ed2c/go.mod h1:1Hj/eHRaVWSXiSNNfcEPcwZleTmdNP81xlxDLnWU9GQ= -github.com/multiformats/go-multicodec v0.7.0 h1:rTUjGOwjlhGHbEMbPoSUJowG1spZTVsITRANCjKTUAQ= -github.com/multiformats/go-multicodec v0.7.0/go.mod h1:GUC8upxSBE4oG+q3kWZRw/+6yC1BqO550bjhWsJbZlw= +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.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.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.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.1 h1:aem8ZT0VA2nCHHk7bPJ1BjUbHNciqZC/d16Vve9l108= -github.com/multiformats/go-multihash v0.2.1/go.mod h1:WxoMcYG85AZVQUyRyo9s4wULvW5qrI9vb2Lt6evduFc= -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.1/go.mod h1:5GZPQZbkWOLOn3J2y4Y99vVW7vOfsAflxARk3x14o6k= -github.com/multiformats/go-multistream v0.2.2/go.mod h1:UIcnm7Zuo8HKG+HkWgfQsGL+/MIEhyTqbODbIUwSXKs= -github.com/multiformats/go-multistream v0.3.3 h1:d5PZpjwRgVlbwfdTDjife7XszfZd8KYWfROYFlGcR8o= -github.com/multiformats/go-multistream v0.3.3/go.mod h1:ODRoqamLUsETKS9BNcII4gcRsJBU5VAwRIv7O39cEXg= +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.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/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/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/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= -github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= -github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= -github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= -github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 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.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= -github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +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.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/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= 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.5.1 h1:auzK7OI497k6x4OvWq+TKAcpcSAlod0doAH72oIN0Jw= -github.com/onsi/ginkgo/v2 v2.5.1/go.mod h1:63DOGlLAH8+REH8jUGdL3YpCpu7JODesutUjdENfUAc= -github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4= +github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= 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/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.24.0 h1:+0glovB9Jd6z3VR+ScSwQqXVTIfJcGA9UBM8yzQxhqg= -github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= -github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= +github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= -github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= +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-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= -github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/openzipkin/zipkin-go v0.4.0 h1:CtfRrOVZtbDj8rt1WXjklw0kqqJQwICrCKmlfUuBUUw= -github.com/openzipkin/zipkin-go v0.4.0/go.mod h1:4c3sLeE8xjNqehmF5RpAFLPLJxXscc0R4l6Zg0P1tTQ= -github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +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/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= 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/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pion/datachannel v1.5.9 h1:LpIWAOYPyDrXtU+BW7X0Yt/vGtYxtXQ8ql7dFfYUVZA= +github.com/pion/datachannel v1.5.9/go.mod h1:kDUuk4CU4Uxp82NH4LQZbISULkX/HtzKa4P7ldf9izE= +github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= +github.com/pion/dtls/v2 v2.2.12 h1:KP7H5/c1EiVAAKUmXyCzPiQe5+bCJrpOeKg/L05dunk= +github.com/pion/dtls/v2 v2.2.12/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= +github.com/pion/ice/v2 v2.3.36 h1:SopeXiVbbcooUg2EIR8sq4b13RQ8gzrkkldOVg+bBsc= +github.com/pion/ice/v2 v2.3.36/go.mod h1:mBF7lnigdqgtB+YHkaY/Y6s6tsyRyo4u4rPGRuOjUBQ= +github.com/pion/interceptor v0.1.37 h1:aRA8Zpab/wE7/c0O3fh1PqY0AJI3fCSEM5lRWJVorwI= +github.com/pion/interceptor v0.1.37/go.mod h1:JzxbJ4umVTlZAf+/utHzNesY8tmRkM2lVmkS82TTj8Y= +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.9 h1:E2HX740TZKaqdcPmf4pw6ZZuG8u5RlMMt+l3dxeu6Wk= +github.com/pion/rtp v1.8.9/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= +github.com/pion/sctp v1.8.33 h1:dSE4wX6uTJBcNm8+YlMg7lw1wqyKHggsP5uKbdj+NZw= +github.com/pion/sctp v1.8.33/go.mod h1:beTnqSzewI53KWoG3nqB282oDMGrhNxBdb+JZnkCwRM= +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.20 h1:HNNny4s+OUmG280ETrCdgFndp4ufx3/uy85EawYEhTk= +github.com/pion/srtp/v2 v2.0.20/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.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.10 h1:ucLBLE8nuxiHfvkFKnkDQRYWYfp8ejf4YBOPfaQpw6Q= +github.com/pion/transport/v2 v2.2.10/go.mod h1:sq1kSLWs+cHW9E+2fJP95QudkzbK7wscs8yYgQToO5E= +github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= +github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0= +github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo= +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.3.4 h1:v2heQVnXTSqNRXcaFQVOhIOYkLMxOu1iJG8uy1djvkk= +github.com/pion/webrtc/v3 v3.3.4/go.mod h1:liNa+E1iwyzyXqNUwvoMRNQ10x8h8FOeJKL8RkIbamE= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= 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/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= -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-20190221155625-df39d6c2d992/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= -github.com/polydawn/refmt v0.0.0-20190408063855-01bf1e26dd14/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= -github.com/polydawn/refmt v0.0.0-20190807091052-3d65705ee9f1/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= -github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e h1:ZOcivgkkFRnjfoTcGsDq3UQYiBmekwLA+qg0OjyB/ls= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/polydawn/refmt v0.89.0 h1:ADJTApkvkeBZsN0tBTx8QjpD9JkmxbKp0cxfr9qszm4= +github.com/polydawn/refmt v0.89.0/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw= 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 v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU= -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.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= -github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/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.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +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.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -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.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= +github.com/prometheus/common v0.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA= +github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= 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.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -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 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/rabbitmq/amqp091-go v1.1.0/go.mod h1:ogQDLSOACsLPsIq0NpbtiifNZi2YOz0VTJ0kHRghqbM= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= +github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= +github.com/quic-go/quic-go v0.48.1 h1:y/8xmfWI9qmGTc+lBr4jKRUWLGSlSigv847ULJ4hYXA= +github.com/quic-go/quic-go v0.48.1/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs= +github.com/quic-go/webtransport-go v0.8.1-0.20241018022711-4ac2c9250e66 h1:4WFk6u3sOT6pLa1kQ50ZVdm8BQFgJNA117cepZxtLIg= +github.com/quic-go/webtransport-go v0.8.1-0.20241018022711-4ac2c9250e66/go.mod h1:Vp72IJajgeOL6ddqrAhmp7IM9zbTcgkQxD/YdxrVwMw= 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/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -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 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/rs/zerolog v1.21.0/go.mod h1:ZPhntP/xmq1nnND05hhpAh2QMhSsA4UN3MGZ6O2J3hM= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= 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/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/samber/lo v1.36.0 h1:4LaOxH1mHnbDGhTVE0i1z8v/lWaQW8AIfOD3HU4mSaw= -github.com/samber/lo v1.36.0/go.mod h1:HLeWcJRRyLKp3+/XBJvOrerCQn9mhdKMHyd7IRlgeQ8= -github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc= +github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU= 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= @@ -1211,234 +730,185 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV 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/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/assertions v1.0.0 h1:UVQPSSmc3qtTi+zPPkCXvZX9VvW/xT/NsRvKfwY81a8= -github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= -github.com/smartystreets/goconvey v0.0.0-20190222223459-a17d461953aa/go.mod h1:2RVY1rIf+2J2o/IM9+vPq9RzmHDSseB7FoXiSNIUsoU= -github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= +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/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= 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.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= 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.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 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/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= 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 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= 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.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 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/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/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= +github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= -github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 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.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= +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.10.0 h1:E86YlUMYfwIacEsQGlnTvjk1IgYkyTGjPhF0RnwTCmw= -github.com/warpfork/go-wish v0.0.0-20180510122957-5ad1f5abf436/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= -github.com/warpfork/go-wish v0.0.0-20190328234359-8b3e70f8e830/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= -github.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a h1:G++j5e0OC488te356JvdhaM8YS6nMsjLAYF7JxCv07w= +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.0.0-20200123233031-1cdf64d27158/go.mod h1:Xj/M2wWU+QdTdRbu/L/1dIZY8/Wb2K9pAhtroQuxJJI= -github.com/whyrusleeping/cbor-gen v0.0.0-20210219115102-f37d292932f2 h1:bsUlNhdmbtlfdLVXAVfuvKQ01RnWAM09TVrJkI7NZs4= -github.com/whyrusleeping/cbor-gen v0.0.0-20210219115102-f37d292932f2/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= +github.com/whyrusleeping/cbor-gen v0.1.2 h1:WQFlrPhpcQl+M2/3dP5cvlTLWPVsL6LGBb9jJt6l/cA= +github.com/whyrusleeping/cbor-gen v0.1.2/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-logging v0.0.1/go.mod h1:lDPYj54zutzG1XYfHAhcc7oNXEburHQBn+Iqd4yS4vE= -github.com/whyrusleeping/go-notifier v0.0.0-20170827234753-097c5d47330f/go.mod h1:cZNvX9cFybI01GriPRMXDtczuvUhgbcYr9iCGaNlRv8= -github.com/whyrusleeping/mafmt v1.2.8/go.mod h1:faQJFPbLSxzD9xpA02ttW/tS9vZykNvXwGvqIpk20FA= -github.com/whyrusleeping/mdns v0.0.0-20180901202407-ef14215e6b30/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4= -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/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee h1:lYbXeSvJi5zk5GLKVuid9TVjS9a0OmLIDKTfoZBL6Ow= -github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee/go.mod h1:m2aV4LZI4Aez7dP5PMyVKEHhUyEJ/RjmPEDOpDvudHg= +github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= +github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU= +github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= -github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= -github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 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= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= +github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI= +github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE= +github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= +github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= -go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= 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.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/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= -go.opentelemetry.io/otel v1.7.0 h1:Z2lA3Tdch0iDcrhJXDIlC94XE+bxok1F9B+4Lz/lGsM= -go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk= -go.opentelemetry.io/otel/exporters/jaeger v1.7.0 h1:wXgjiRldljksZkZrldGVe6XrG9u3kYDyQmkZwmm5dI0= -go.opentelemetry.io/otel/exporters/jaeger v1.7.0/go.mod h1:PwQAOqBgqbLQRKlj466DuD2qyMjbtcPpfPfj+AqbSBs= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.7.0 h1:7Yxsak1q4XrJ5y7XBnNwqWx9amMZvoidCctv62XOQ6Y= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.7.0/go.mod h1:M1hVZHNxcbkAlcvrOMlpQ4YOO3Awf+4N2dxkZL3xm04= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.7.0 h1:cMDtmgJ5FpRvqx9x2Aq+Mm0O6K/zcUkH73SFz20TuBw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.7.0/go.mod h1:ceUgdyfNv4h4gLxHR0WNfDiiVmZFodZhZSbOLhpxqXE= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.7.0 h1:MFAyzUPrTwLOwCi+cltN0ZVyy4phU41lwH+lyMyQTS4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.7.0/go.mod h1:E+/KKhwOSw8yoPxSSuUHG6vKppkvhN+S1Jc7Nib3k3o= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.7.0 h1:pLP0MH4MAqeTEV0g/4flxw9O8Is48uAIauAnjznbW50= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.7.0/go.mod h1:aFXT9Ng2seM9eizF+LfKiyPBGy8xIZKwhusC1gIu3hA= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.7.0 h1:8hPcgCg0rUJiKE6VWahRvjgLUrNl7rW2hffUEPKXVEM= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.7.0/go.mod h1:K4GDXPY6TjUiwbOh+DkKaEdCF8y+lvMoM6SeAPyfCCM= -go.opentelemetry.io/otel/exporters/zipkin v1.7.0 h1:X0FZj+kaIdLi29UiyrEGDhRTYsEXj9GdEW5Y39UQFEE= -go.opentelemetry.io/otel/exporters/zipkin v1.7.0/go.mod h1:9YBXeOMFLQGwNEjsxMRiWPGoJX83usGMhbCmxUbNe5I= -go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= -go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= -go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= -go.opentelemetry.io/otel/sdk v1.7.0 h1:4OmStpcKVOfvDOgCt7UriAPtKolwIhxpnSNI/yK+1B0= -go.opentelemetry.io/otel/sdk v1.7.0/go.mod h1:uTEOTwaqIVuTGiJN7ii13Ibp75wJmYUDe374q6cZwUU= -go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= -go.opentelemetry.io/otel/trace v1.7.0 h1:O37Iogk1lEkMRXewVtZ1BBTVn5JEp8GrJvP92bJqC6o= -go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.16.0 h1:WHzDWdXUvbc5bG2ObdrGfaNpQz7ft7QN9HHmJlbiB1E= -go.opentelemetry.io/proto/otlp v0.16.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM= +go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= +go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 h1:K0XaT3DwHAcV4nKLzcQvwAgSyisUghWoY20I7huthMk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0/go.mod h1:B5Ki776z/MBnVha1Nzwp5arlzBbE3+1jk+pGmaP5HME= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0 h1:FFeLy03iVTXP6ffeN2iXrxfGsZGCjVx0/4KlizjyBwU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0/go.mod h1:TMu73/k1CP8nBUpDLc71Wj/Kf7ZS9FK5b53VapRsP9o= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 h1:lUsI2TYsQw2r1IASwoROaCnjdj2cvC2+Jbxvk6nHnWU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0/go.mod h1:2HpZxxQurfGxJlJDblybejHB6RX6pmExPNe517hREw4= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0 h1:UGZ1QwZWY67Z6BmckTU+9Rxn04m2bD3gD6Mk0OIOCPk= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0/go.mod h1:fcwWuDuaObkkChiDlhEpSq9+X1C0omv+s5mBtToAQ64= +go.opentelemetry.io/otel/exporters/zipkin v1.31.0 h1:CgucL0tj3717DJnni7HVVB2wExzi8c2zJNEA2BhLMvI= +go.opentelemetry.io/otel/exporters/zipkin v1.31.0/go.mod h1:rfzOVNiSwIcWtEC2J8epwG26fiaXlYvLySJ7bwsrtAE= +go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE= +go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY= +go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk= +go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0= +go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys= +go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 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.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= -go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/dig v1.15.0 h1:vq3YWr8zRj1eFGC7Gvf907hE0eRjPTZ1d3xHadD6liE= -go.uber.org/dig v1.15.0/go.mod h1:pKHs0wMynzL6brANhB2hLMro+zalv1osARTviTcqHLM= -go.uber.org/fx v1.18.2 h1:bUNI6oShr+OVFQeU8cDNbnN7VFsu+SsjHzUF51V/GAU= -go.uber.org/fx v1.18.2/go.mod h1:g0V1KMQ66zIRk8bLu3Ea5Jt2w/cHlOIp4wdRsgh0JaY= -go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +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.18.0 h1:imUL1UiY0Mg4bqbFfsRQO5G4CGRBec/ZujWTvSVp3pw= +go.uber.org/dig v1.18.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= +go.uber.org/fx v1.23.0 h1:lIr/gYWQGfTwGcSXWXu4vP5Ws6iqnNEIY+F/aFzCKTg= +go.uber.org/fx v1.23.0/go.mod h1:o/D9n+2mLP6v1EG+qsdT1O8wKopYAsqZasju97SDFCU= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= -go.uber.org/goleak v1.1.12/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.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= +go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= 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.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= -go.uber.org/multierr v1.8.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.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= -go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= -go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= -go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +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 h1:BNJlw5kRTzdmyfh5U8F93HA2OwkP7ZGwA51eJ/0wKOU= 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-20181029021203-45a5f77698d3/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-20190530122614-20be4c3c3ed5/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-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/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-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/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-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/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-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210920023735-84f357641f63/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A= -golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +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.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= 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-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= 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-20210615023648-acb5c1269671/go.mod h1:DVyR6MI7P4kEQgvZJSj1fQGrWIi2RzIrfYWycwheUAc= -golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= -golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY= +golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= 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= @@ -1454,30 +924,22 @@ golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPI golang.org/x/lint v0.0.0-20200302205851-738671d3881b/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/mobile v0.0.0-20201217150744-e6ae53a27f4f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= 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.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +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.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= 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-20181023162649-9b4f9f5ad519/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-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/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-20190125091013-d26f9f9a57f3/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= @@ -1485,46 +947,32 @@ golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn 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-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -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-20190813141303-74dc4d7220e7/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-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-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-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 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-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= 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-20210428140749-89ef3d95e781/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-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/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.3.0 h1:VWL6FNY2bEEmsGVKabSlHu5Irp34xmMRoqb/9lF9lxk= -golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +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.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= 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= @@ -1532,9 +980,6 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr 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-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= 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= @@ -1542,116 +987,94 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ 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.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +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.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.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-20180823144017-11551d06cbcc/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-20181026203630-95b1ffbd15a5/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-20181107165924-66b7b1311ac8/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-20181122145206-62eef0e2fa9b/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-20190302025703-b6889370fb10/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-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-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-20190524122548-abf6ff778158/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190526052359-791d8a0f4d09/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/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-20190826190057-c7b8b68b1456/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-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-20191220142924-d4481acd189f/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-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-20210112080510-489259a85091/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-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-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210317225723-c4fcb01b228e/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-20210514084401-e8d321eab015/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-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-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20221010170243-090e33056c14/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.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.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.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.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/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.5/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.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +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.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.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= 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= @@ -1675,54 +1098,35 @@ golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtn 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-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 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-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/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-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.3.0 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM= -golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= +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.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= 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-20220609144429-65e65417b02f h1:uF6paiQQebLeSXkrTqHqz0MXhXXS1KgF41eUdBNvxK0= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= +golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/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.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= 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= @@ -1731,14 +1135,6 @@ google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsb 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/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= @@ -1746,7 +1142,6 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 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/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= @@ -1756,7 +1151,6 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn 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-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= 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= @@ -1764,56 +1158,26 @@ google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvx 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-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-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1 h1:b9mVrqYfq3P4bCdaLg1qtBnPzUYgglsIdjZkL/fQVOE= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 h1:T6rh4haD3GVYsgEfWExoCZA2o2FmbNyKpTuAxbEFPTg= +google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:wp2WsuBYj6j8wUdo3ToZsdxxixbvQNAHqVJrTgi5E5M= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 h1:QCqS/PdaHTSWGvupk2F/ehwHtGc0/GYkT+3GAcR1CCc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= 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.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/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.28.1/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.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8= -google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= +google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= 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= @@ -1822,39 +1186,26 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi 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.27.1/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 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= 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/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= 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/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= -gopkg.in/square/go-jose.v2 v2.5.1/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/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 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= @@ -1870,18 +1221,12 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh 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= -honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= -lukechampine.com/blake3 v1.1.6/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= -lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0= -lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= -pgregory.net/rapid v0.4.7 h1:MTNRktPuv5FNqOO151TM9mDTa+XHcX6ypYeISDVD14g= -pgregory.net/rapid v0.4.7/go.mod h1:UYpPVyjFHzYBGHIxLFoupi8vwk6rXNzRY9OMvVxFIOU= +lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= +lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= +pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= +pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= 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= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= 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= diff --git a/docs/examples/kubo-as-a-library/main.go b/docs/examples/kubo-as-a-library/main.go index 08a80bdef..765e83c6d 100644 --- a/docs/examples/kubo-as-a-library/main.go +++ b/docs/examples/kubo-as-a-library/main.go @@ -11,9 +11,9 @@ import ( "strings" "sync" - files "github.com/ipfs/go-ipfs-files" - icore "github.com/ipfs/interface-go-ipfs-core" - icorepath "github.com/ipfs/interface-go-ipfs-core/path" + "github.com/ipfs/boxo/files" + "github.com/ipfs/boxo/path" + icore "github.com/ipfs/kubo/core/coreiface" ma "github.com/multiformats/go-multiaddr" "github.com/ipfs/kubo/config" @@ -85,7 +85,7 @@ func createTempRepo() (string, error) { /// ------ Spawning the node -// Creates an IPFS node and returns its coreAPI +// Creates an IPFS node and returns its coreAPI. func createNode(ctx context.Context, repoPath string) (*core.IpfsNode, error) { // Open the repo repo, err := fsrepo.Open(repoPath) @@ -107,7 +107,7 @@ func createNode(ctx context.Context, repoPath string) (*core.IpfsNode, error) { var loadPluginsOnce sync.Once -// Spawns a node to be used just for this run (i.e. creates a tmp repo) +// Spawns a node to be used just for this run (i.e. creates a tmp repo). func spawnEphemeral(ctx context.Context) (icore.CoreAPI, *core.IpfsNode, error) { var onceErr error loadPluginsOnce.Do(func() { @@ -320,11 +320,11 @@ func main() { } }() - exampleCIDStr := peerCidFile.Cid().String() + exampleCIDStr := peerCidFile.RootCid().String() fmt.Printf("Fetching a file from the network with CID %s\n", exampleCIDStr) outputPath := outputBasePath + exampleCIDStr - testCID := icorepath.New(exampleCIDStr) + testCID := path.FromCid(peerCidFile.RootCid()) rootNode, err := ipfsB.Unixfs().Get(ctx, testCID) if err != nil { diff --git a/docs/experimental-features.md b/docs/experimental-features.md index cd85cb200..eb4f2ff14 100644 --- a/docs/experimental-features.md +++ b/docs/experimental-features.md @@ -12,7 +12,6 @@ When you add a new experimental feature to kubo or change an experimental feature, you MUST please make a PR updating this document, and link the PR in the above issue. -- [ipfs pubsub](#ipfs-pubsub) - [Raw leaves for unixfs files](#raw-leaves-for-unixfs-files) - [ipfs filestore](#ipfs-filestore) - [ipfs urlstore](#ipfs-urlstore) @@ -27,40 +26,11 @@ the above issue. - [Strategic Providing](#strategic-providing) - [Graphsync](#graphsync) - [Noise](#noise) -- [Accelerated DHT Client](#accelerated-dht-client) +- [Optimistic Provide](#optimistic-provide) +- [HTTP Gateway over Libp2p](#http-gateway-over-libp2p) --- -## ipfs pubsub - -### State - -Candidate, disabled by default but will be enabled by default in 0.6.0. - -### In Version - -0.4.5 (`--enable-pubsub-experiment`) -0.11.0 (`Pubsub.Enabled` flag in config) - -### How to enable - -Run your daemon with the `--enable-pubsub-experiment` flag -or modify your ipfs config and restart the daemon: -``` -ipfs config --json Pubsub.Enabled true -``` - -Then use the `ipfs pubsub` commands. - -NOTE: `--enable-pubsub-experiment` CLI flag overrides `Pubsub.Enabled` config. - -Configuration documentation can be found in [kubo/docs/config.md](./config.md#pubsub) - -### Road to being a real feature - -- [ ] Needs to not impact peers who don't use pubsub: - https://github.com/libp2p/go-libp2p-pubsub/issues/332 - ## Raw Leaves for unixfs files Allows files to be added with no formatting in the leaf nodes of the graph. @@ -148,6 +118,9 @@ It allows ipfs to only connect to other peers who have a shared secret key. Stable but not quite ready for prime-time. +> [!WARNING] +> Limited to TCP transport, comes with overhead of double-encryption. See details below. + ### In Version 0.4.7 @@ -156,7 +129,7 @@ Stable but not quite ready for prime-time. Generate a pre-shared-key using [ipfs-swarm-key-gen](https://github.com/Kubuxu/go-ipfs-swarm-key-gen)): ``` -go get github.com/Kubuxu/go-ipfs-swarm-key-gen/ipfs-swarm-key-gen +go install github.com/Kubuxu/go-ipfs-swarm-key-gen/ipfs-swarm-key-gen@latest ipfs-swarm-key-gen > ~/.ipfs/swarm.key ``` @@ -194,7 +167,12 @@ configured, the daemon will fail to start. - [x] Needs more people to use and report on how well it works - [ ] More documentation -- [ ] Needs better tooling/UX. +- [ ] Improve / future proof libp2p support (see [libp2p/specs#489](https://github.com/libp2p/specs/issues/489)) + - [ ] Currently limited to TCP-only, and double-encrypts all data sent on TCP. This is slow. + - [ ] Does not work with QUIC: [go-libp2p#1432](https://github.com/libp2p/go-libp2p/issues/1432) +- [ ] Needs better tooling/UX + - [ ] Detect lack of peers when swarm key is present and prompt user to set up bootstrappers/peering + - [ ] ipfs-webui will not load unless blocks are present in private swarm. Detect it and prompt user to import CAR with webui. ## ipfs p2p @@ -390,7 +368,7 @@ We also support the use of protocol names of the form /x/$NAME/http where $NAME ## FUSE FUSE makes it possible to mount `/ipfs` and `/ipns` namespaces in your OS, -allowing argitrary apps access to IPFS using a subset of filesystem abstractions. +allowing arbitrary apps access to IPFS using a subset of filesystem abstractions. It is considered EXPERIMENTAL due to limited (and buggy) support on some platforms. @@ -543,30 +521,16 @@ ipfs config --json Experimental.StrategicProviding true - [ ] provide roots - [ ] provide all - [ ] provide strategic - + ## GraphSync ### State -Experimental, disabled by default. +Removed, no plans to reintegrate either as experimental or stable feature. -[GraphSync](https://github.com/ipfs/go-graphsync) is the next-gen graph exchange -protocol for IPFS. +[Trustless Gateway over Libp2p](#http-gateway-over-libp2p) should be easier to use for unixfs usecases and support basic wildcard car streams for non unixfs. -When this feature is enabled, IPFS will make files available over the graphsync -protocol. However, IPFS will not currently use this protocol to _fetch_ files. - -### How to enable - -Modify your ipfs config: - -``` -ipfs config --json Experimental.GraphsyncEnabled true -``` - -### Road to being a real feature - -- [ ] We need to confirm that it can't be used to DoS a node. The server-side logic for GraphSync is quite complex and, if we're not careful, the server might end up performing unbounded work when responding to a malicious request. +See https://github.com/ipfs/kubo/pull/9747 for more information. ## Noise @@ -576,55 +540,124 @@ Stable, enabled by default [Noise](https://github.com/libp2p/specs/tree/master/noise) libp2p transport based on the [Noise Protocol Framework](https://noiseprotocol.org/noise.html). While TLS remains the default transport in Kubo, Noise is easier to implement and is thus the "interop" transport between IPFS and libp2p implementations. -## Accelerated DHT Client +## Optimistic Provide ### In Version -0.9.0 +0.20.0 ### State -Experimental, default-disabled. +Experimental, disabled by default. -Utilizes an alternative DHT client that searches for and maintains more information about the network -in exchange for being more performant. +When the Amino DHT client tries to store a provider in the DHT, it typically searches for the 20 peers that are closest to the +target key. However, this process can be time-consuming, as the search terminates only after no closer peers are found +among the three currently (during the query) known closest ones. In cases where these closest peers are slow to respond +(which often happens if they are located at the edge of the DHT network), the query gets blocked by the slowest peer. + +To address this issue, the `OptimisticProvide` feature can be enabled. This feature allows the client to estimate the +network size and determine how close a peer _likely_ needs to be to the target key to be within the 20 closest peers. +While searching for the closest peers in the DHT, the client will _optimistically_ store the provider record with peers +and abort the query completely when the set of currently known 20 closest peers are also _likely_ the actual 20 closest +ones. This heuristic approach can significantly speed up the process, resulting in a speed improvement of 2x to >10x. When it is enabled: -- DHT operations should complete much faster than with it disabled -- A batching reprovider system will be enabled which takes advantage of some properties of the experimental client to - very efficiently put provider records into the network -- The standard DHT client (and server if enabled) are run alongside the alternative client -- The operations `ipfs stats dht` and `ipfs stats provide` will have different outputs - - `ipfs stats provide` only works when the accelerated DHT client is enabled and shows various statistics regarding - the provider/reprovider system - - `ipfs stats dht` will default to showing information about the new client + +- Amino DHT provide operations should complete much faster than with it disabled +- This can be tested with commands such as `ipfs routing provide` + +**Tradeoffs** + +There are now the classic client, the accelerated DHT client, and optimistic provide that improve the provider process. +There are different trade-offs with all of them. The accelerated DHT client is still faster to provide large amounts +of provider records at the cost of high resource requirements. Optimistic provide doesn't have the high resource +requirements but might not choose optimal peers and is not as fast as the accelerated client, but still much faster +than the classic client. **Caveats:** -1. Running the experimental client likely will result in more resource consumption (connections, RAM, CPU, bandwidth) - - Users that are limited in the number of parallel connections their machines/networks can perform will likely suffer - - Currently, the resource usage is not smooth as the client crawls the network in rounds and reproviding is similarly - done in rounds - - Users who previously had a lot of content but were unable to advertise it on the network will see an increase in - egress bandwidth as their nodes start to advertise all of their CIDs into the network. If you have lots of data - entering your node that you don't want to advertise consider using [Reprovider Strategies](config.md#reproviderstrategy) - to reduce the number of CIDs that you are reproviding. Similarly, if you are running a node that deals mostly with - short-lived temporary data (e.g. you use a separate node for ingesting data then for storing and serving it) then - you may benefit from using [Strategic Providing](#strategic-providing) to prevent advertising of data that you - ultimately will not have. -2. Currently, the DHT is not usable for queries for the first 5-10 minutes of operation as the routing table is being -prepared. This means operations like searching the DHT for particular peers or content will not work - - You can see if the DHT has been initially populated by running `ipfs stats dht` -3. Currently, the accelerated DHT client is not compatible with LAN-based DHTs and will not perform operations against -them -### How to enable +1. Providing optimistically requires a current network size estimation. This estimation is calculated through routing + table refresh queries and is only available after the daemon has been running for some time. If there is no network + size estimation available the client will transparently fall back to the classic approach. +2. The chosen peers to store the provider records might not be the actual closest ones. Measurements showed that this + is not a problem. +3. The optimistic provide process returns already after 15 out of the 20 provider records were stored with peers. The + reasoning here is that one out of the remaining 5 peers are very likely to time out and delay the whole process. To + limit the number of in-flight async requests there is the second `OptimisticProvideJobsPoolSize` setting. Currently, + this is set to 60. This means that at most 60 parallel background requests are allowed to be in-flight. If this + limit is exceeded optimistic provide will block until all 20 provider records are written. This is still 2x faster + than the classic approach but not as fast as returning early which yields >10x speed-ups. +4. Since the in-flight background requests are likely to time out, they are not consuming many resources and the job + pool size could probably be much higher. + +For more information, see: + +- Project doc: https://protocollabs.notion.site/Optimistic-Provide-2c79745820fa45649d48de038516b814 +- go-libp2p-kad-dht: https://github.com/libp2p/go-libp2p-kad-dht/pull/783 + +### Configuring +To enable: ``` -ipfs config --json Experimental.AcceleratedDHTClient true +ipfs config --json Experimental.OptimisticProvide true +``` + +If you want to change the `OptimisticProvideJobsPoolSize` setting from its default of 60: + +``` +ipfs config --json Experimental.OptimisticProvideJobsPoolSize 120 ``` ### Road to being a real feature - [ ] Needs more people to use and report on how well it works -- [ ] Should be usable for queries (even if slower/less efficient) shortly after startup -- [ ] Should be usable with non-WAN DHTs +- [ ] Should prove at least equivalent availability of provider records as the classic approach + +## HTTP Gateway over Libp2p + +### In Version + +0.23.0 + +### State + +Experimental, disabled by default. + +Enables serving a subset of the [IPFS HTTP Gateway](https://specs.ipfs.tech/http-gateways/) semantics over libp2p `/http/1.1` protocol. + +Notes: +- This feature only about serving verifiable gateway requests over libp2p: + - Deserialized responses are not supported. + - Only operate on `/ipfs` resources (no `/ipns` atm) + - Only support requests for `application/vnd.ipld.raw` and + `application/vnd.ipld.car` (from [Trustless Gateway Specification](https://specs.ipfs.tech/http-gateways/trustless-gateway/), + where data integrity can be verified). + - Only serve data that is already local to the node (i.e. similar to a + [`Gateway.NoFetch`](https://github.com/ipfs/kubo/blob/master/docs/config.md#gatewaynofetch)) +- While Kubo currently mounts the gateway API at the root (i.e. `/`) of the + libp2p `/http/1.1` protocol, that is subject to change. + - The way to reliably discover where a given HTTP protocol is mounted on a + libp2p endpoint is via the `.well-known/libp2p` resource specified in the + [http+libp2p specification](https://github.com/libp2p/specs/pull/508) + - The identifier of the protocol mount point under `/http/1.1` listener is + `/ipfs/gateway`, as noted in + [ipfs/specs#434](https://github.com/ipfs/specs/pull/434). + +### How to enable + +Modify your ipfs config: + +``` +ipfs config --json Experimental.GatewayOverLibp2p true +``` + +### Road to being a real feature + +- [ ] Needs more people to use and report on how well it works +- [ ] Needs UX work for exposing non-recursive "HTTP transport" (NoFetch) over both libp2p and plain TCP (and sharing the configuration) +- [ ] Needs a mechanism for HTTP handler to signal supported features ([IPIP-425](https://github.com/ipfs/specs/pull/425)) +- [ ] Needs an option for Kubo to detect peers that have it enabled and prefer HTTP transport before falling back to bitswap (and use CAR if peer supports dag-scope=entity from [IPIP-402](https://github.com/ipfs/specs/pull/402)) + +## Accelerated DHT Client + +This feature now lives at [`Routing.AcceleratedDHTClient`](https://github.com/ipfs/kubo/blob/master/docs/config.md#routingaccelerateddhtclient). diff --git a/docs/file-transfer.md b/docs/file-transfer.md index 8f38ca765..e61ddc1b3 100644 --- a/docs/file-transfer.md +++ b/docs/file-transfer.md @@ -46,9 +46,9 @@ addresses (like the example below), then your nodes are online. "PublicKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZb6znj3LQZKP1+X81exf+vbnqNCMtHjZ5RKTCm7Fytnfe+AI1fhs9YbZdkgFkM1HLxmIOLQj2bMXPIGxUM+EnewN8tWurx4B3+lR/LWNwNYcCFL+jF2ltc6SE6BC8kMLEZd4zidOLPZ8lIRpd0x3qmsjhGefuRwrKeKlR4tQ3C76ziOms47uLdiVVkl5LyJ5+mn4rXOjNKt/oy2O4m1St7X7/yNt8qQgYsPfe/hCOywxCEIHEkqmil+vn7bu4RpAtsUzCcBDoLUIWuU3i6qfytD05hP8Clo+at+l//ctjMxylf3IQ5qyP+yfvazk+WHcsB0tWueEmiU5P2nfUUIR3AgMBAAE=", "Addresses": [ "/ip4/127.0.0.1/tcp/4001/p2p/QmTNwsFkLAed15kQEC1ZJWPfoNbBQnMFojfJKQ9sZj1dk8", - "/ip4/127.0.0.1/udp/4001/quic/p2p/QmTNwsFkLAed15kQEC1ZJWPfoNbBQnMFojfJKQ9sZj1dk8", + "/ip4/127.0.0.1/udp/4001/quic-v1/p2p/QmTNwsFkLAed15kQEC1ZJWPfoNbBQnMFojfJKQ9sZj1dk8", "/ip4/192.168.2.131/tcp/4001/p2p/QmTNwsFkLAed15kQEC1ZJWPfoNbBQnMFojfJKQ9sZj1dk8", - "/ip4/192.168.2.131/udp/4001/quic/p2p/QmTNwsFkLAed15kQEC1ZJWPfoNbBQnMFojfJKQ9sZj1dk8", + "/ip4/192.168.2.131/udp/4001/quic-v1/p2p/QmTNwsFkLAed15kQEC1ZJWPfoNbBQnMFojfJKQ9sZj1dk8", ], "AgentVersion": "go-ipfs/0.4.11-dev/", "ProtocolVersion": "ipfs/0.1.0" @@ -68,12 +68,12 @@ pitfalls that people run into) ### Checking providers When requesting content on ipfs, nodes search the DHT for 'provider records' to see who has what content. Let's manually do that on node B to make sure that -node B is able to determine that node A has the data. Run `ipfs dht findprovs +node B is able to determine that node A has the data. Run `ipfs routing findprovs `. We expect to see the peer ID of node A printed out. If this command returns nothing (or returns IDs that are not node A), then no record of A having the data exists on the network. This can happen if the data is added while node A does not have a daemon running. If this happens, you can run `ipfs -dht provide ` on node A to announce to the network that you have that +routing provide ` on node A to announce to the network that you have that hash. Then if you restart the `ipfs get` command, node B should now be able to tell that node A has the content it wants. If node A's peer ID showed up in the initial `findprovs` call, or manually providing the hash didn't resolve the @@ -85,18 +85,18 @@ In the case where node B simply cannot form a connection to node A, despite knowing that it needs to, the likely culprit is a bad NAT. When node B learns that it needs to connect to node A, it checks the DHT for addresses for node A, and then starts trying to connect to them. We can check those addresses by -running `ipfs dht findpeer ` on node B. This command should +running `ipfs routing findpeer ` on node B. This command should return a list of addresses for node A. If it doesn't return any addresses, then you should try running the manual providing command from the previous steps. Example output of addresses might look something like this: ``` /ip4/127.0.0.1/tcp/4001 -/ip4/127.0.0.1/udp/4001/quic +/ip4/127.0.0.1/udp/4001/quic-v1 /ip4/192.168.2.133/tcp/4001 -/ip4/192.168.2.133/udp/4001/quic +/ip4/192.168.2.133/udp/4001/quic-v1 /ip4/88.157.217.196/tcp/63674 -/ip4/88.157.217.196/udp/63674/quic +/ip4/88.157.217.196/udp/63674/quic-v1 ``` In this case, we can see a localhost (127.0.0.1) address, a LAN address (the diff --git a/docs/fuse.md b/docs/fuse.md index 2b64a7856..7744a0d45 100644 --- a/docs/fuse.md +++ b/docs/fuse.md @@ -147,5 +147,15 @@ sudo umount /ipfs sudo umount /ipns ``` +#### Mounting fails with "error mounting: could not resolve name" + +Make sure your node's IPNS address has a directory published: +``` +$ mkdir hello/; echo 'hello' > hello/hello.txt; ipfs add -rQ ./hello/ +QmU5PLEGqjetW4RAmXgHpEFL7nVCL3vFnEyrCKUfRk4MSq + +$ ipfs name publish QmU5PLEGqjetW4RAmXgHpEFL7nVCL3vFnEyrCKUfRk4MSq +``` + If you manage to mount on other systems (or followed an alternative path to one above), please contribute to these docs :D diff --git a/docs/gateway.md b/docs/gateway.md index b24d10f0c..3a616a158 100644 --- a/docs/gateway.md +++ b/docs/gateway.md @@ -12,14 +12,20 @@ Kubo's Gateway implementation follows [ipfs/specs: Specification for HTTP Gatewa By default, Kubo nodes run a [path gateway](https://docs.ipfs.tech/how-to/address-ipfs-on-web/#path-gateway) at `http://127.0.0.1:8080/` -and a [subdomain gateway](https://docs.ipfs.tech/how-to/address-ipfs-on-web/#subdomain-gateway) at `http://localhost:8080/` +and a [subdomain gateway](https://docs.ipfs.tech/how-to/address-ipfs-on-web/#subdomain-gateway) at `http://localhost:8080/`. + +The path one also implements [trustless gateway spec](https://specs.ipfs.tech/http-gateways/trustless-gateway/) +and supports [trustless responses](https://docs.ipfs.tech/reference/http/gateway/#trustless-verifiable-retrieval) as opt-in via `Accept` header. Additional listening addresses and gateway behaviors can be set in the [config](#configuration) file. ### Public gateways -Protocol Labs provides a public gateway at `https://ipfs.io` (path) and `https://dweb.link` (subdomain). -If you've ever seen a link in the form `https://ipfs.io/ipfs/Qm...`, that's being served from *our* gateway. +Protocol Labs provides a public gateway at +`https://ipfs.io` ([path](https://specs.ipfs.tech/http-gateways/path-gateway/)), +`https://dweb.link` ([subdomain](https://docs.ipfs.tech/how-to/address-ipfs-on-web/#subdomain-gateway)), +and `https://trustless-gateway.link` ([trustless](https://specs.ipfs.tech/http-gateways/trustless-gateway/) only). +If you've ever seen a link in the form `https://ipfs.io/ipfs/Qm...`, that's being served from a *public goods* gateway. There is a list of third-party public gateways provided by the IPFS community at https://ipfs.github.io/public-gateway-checker/ @@ -61,7 +67,7 @@ Gateway](https://dnslink.dev/#example-ipfs-gateway) for instructions. When downloading files, browsers will usually guess a file's filename by looking at the last component of the path. Unfortunately, when linking *directly* to a file (with no containing directory), the final component is just a CID -(`Qm...`). This isn't exactly user-friendly. +(`bafy..` or `Qm...`). This isn't exactly user-friendly. To work around this issue, you can add a `filename=some_filename` parameter to your query string to explicitly specify the filename. For example: @@ -85,6 +91,11 @@ or by sending `Accept: application/vnd.ipld.{format}` HTTP header with one of su ## Content-Types +Majority of resources can be retrieved trustlessly by requesting specific content type via `Accept` header or `?format=raw|car|ipns-record` URL query parameter. + +See [trustless gateway specification](https://specs.ipfs.tech/http-gateways/trustless-gateway/) +and [verifiable retrieval documentation](https://docs.ipfs.tech/reference/http/gateway/#trustless-verifiable-retrieval) for more details. + ### `application/vnd.ipld.raw` Returns a byte array for a single `raw` block. @@ -103,11 +114,9 @@ Support for user-provided IPLD selectors is tracked in https://github.com/ipfs/k This is a rough equivalent of `ipfs dag export`. -## Deprecated Subset of RPC API +### `application/vnd.ipfs.ipns-record` -For legacy reasons, the gateway port exposes a small subset of RPC API under `/api/v0/`. -While this read-only API exposes a read-only, "safe" subset of the normal API, -it is deprecated and should not be used for greenfield projects. +Only works on `/ipns/{ipns-name}` content paths that use cryptographically signed [IPNS Records](https://specs.ipfs.tech/ipns/ipns-record/). -Where possible, leverage `/ipfs/` and `/ipns/` endpoints. -along with `application/vnd.ipld.*` Content-Types instead. +Returns [IPNS Record in Protobuf Serialization Format](https://specs.ipfs.tech/ipns/ipns-record/#record-serialization-format) +which can be verified on end client, without trusting gateway. diff --git a/docs/http-rpc-clients.md b/docs/http-rpc-clients.md index c011b98f7..c53c0b5d0 100644 --- a/docs/http-rpc-clients.md +++ b/docs/http-rpc-clients.md @@ -1,14 +1,13 @@ # HTTP/RPC Clients -Kubo provides official HTTP RPC (`/api/v0`) clients for selected lanaguages: +Kubo provides official HTTP RPC (`/api/v0`) clients for selected languages: -- [js-kubo-rpc-client](https://github.com/ipfs/js-kubo-rpc-client) - Official JS client for talking to Kubo RPC over HTTP -- [go-ipfs-api](https://github.com/ipfs/go-ipfs-api) - The go interface to ipfs's HTTP RPC - Follow https://github.com/ipfs/kubo/issues/9124 for coming changes. -- [go-ipfs-http-client](https://github.com/ipfs/go-ipfs-http-client) - IPFS CoreAPI implementation using HTTP RPC - Follow https://github.com/ipfs/kubo/issues/9124 for coming changes. +| Language | Package Name | Github Repository | +|:--------:|:-------------------:|--------------------------------------------| +| JS | kubo-rpc-client | https://github.com/ipfs/js-kubo-rpc-client | +| Go | `rpc` | [`../client/rpc`](../client/rpc) | -## Recommended clients +There are community-maintained libraries for other languages, +but the Kubo team does provide support for them, YMMV: -| Language | Package Name | Github Repository | -|:--------:|:-------------------:|---------------------------------------------| -| JS | kubo-rpc-client | https://github.com/ipfs/js-kubo-rpc-client | -| Go | go-ipfs-http-client | https://github.com/ipfs/go-ipfs-http-client | +- https://docs.ipfs.tech/reference/kubo-rpc-cli/ diff --git a/docs/implement-api-bindings.md b/docs/implement-api-bindings.md index 996a6b8ac..3587ac21f 100644 --- a/docs/implement-api-bindings.md +++ b/docs/implement-api-bindings.md @@ -39,10 +39,9 @@ function calls. For example: #### CLI API Transport In the commandline, IPFS uses a traditional flag and arg-based mapping, where: -- the first arguments selects the command, as in git - e.g. `ipfs object get` +- the first arguments selects the command, as in git - e.g. `ipfs dag get` - the flags specify options - e.g. `--enc=protobuf -q` -- the rest are positional arguments - e.g. - `ipfs object patch add-linkfoo ` +- the rest are positional arguments - e.g. `ipfs key rename ` - files are specified by filename, or through stdin (NOTE: When kubo runs the daemon, the CLI API is actually converted to HTTP diff --git a/docs/libp2p-resource-management.md b/docs/libp2p-resource-management.md index 83c44251d..a778f680d 100644 --- a/docs/libp2p-resource-management.md +++ b/docs/libp2p-resource-management.md @@ -1,3 +1,4 @@ + # libp2p Network Resource Manager (`Swarm.ResourceMgr`) ## Purpose @@ -13,48 +14,46 @@ Good places to start are: ## Table of Contents -- [libp2p Network Resource Manager (`Swarm.ResourceMgr`)](#libp2p-network-resource-manager-smallswarmresourcemgrsmall) - - [Purpose](#purpose) - - [Levels of Configuration](#levels-of-configuration) - - [Approach](#approach) - - [Computed Default Limits](#computed-default-limits) - - [User Supplied Override Limits](#user-supplied-override-limits) - - [Infinite limits](#infinite-limits) - - [FAQ](#faq) - - [What do these "Protected from exceeding resource limits" log messages mean?](#what-do-these-protected-from-exceeding-resource-limits-log-messages-mean) - - [What are the "Application error ... cannot reserve ..." messages?](#what-are-the-application-error--cannot-reserve--messages) - - [How does the resource manager (ResourceMgr) relate to the connection manager (ConnMgr)?](#how-does-the-resource-manager-resourcemgr-relate-to-the-connection-manager-connmgr) - - [How does one see the Active Limits?](#how-does-one-see-the-active-limits) - - [How does one see the Computed Default Limits?](#how-does-one-see-the-computed-default-limits) - - [How does one monitor libp2p resource usage?](#how-does-one-monitor-libp2p-resource-usage) - - [History](#history) +- [Purpose](#purpose) +- [🙋 Help! The resource manager is protecting my node but I want to understand more](#-help--the-resource-manager-is-protecting-my-node-but-i-want-to-understand-more) +- [Table of Contents](#table-of-contents) +- [Levels of Configuration](#levels-of-configuration) + - [Approach](#approach) + - [Computed Default Limits](#computed-default-limits) + - [User Supplied Override Limits](#user-supplied-override-limits) +- [FAQ](#faq) + - [What do these "Protected from exceeding resource limits" log messages mean?](#what-do-these-protected-from-exceeding-resource-limits-log-messages-mean) + - [How does one see the Active Limits?](#how-does-one-see-the-active-limits) + - [How does one see the Computed Default Limits?](#how-does-one-see-the-computed-default-limits) + - [How does one monitor libp2p resource usage?](#how-does-one-monitor-libp2p-resource-usage) + - [How does the resource manager (ResourceMgr) relate to the connection manager (ConnMgr)?](#how-does-the-resource-manager-resourcemgr-relate-to-the-connection-manager-connmgr) + - [What are the "Application error 0x0 (remote) ... cannot reserve ..." messages?](#what-are-the-application-error-0x0-remote--cannot-reserve--messages) +- [History](#history) ## Levels of Configuration See also the [`Swarm.ResourceMgr` config docs](./config.md#swarmresourcemgr). - ### Approach libp2p's resource manager provides tremendous flexibility but also adds complexity. There are these levels of limit configuration for resource management protection: 1. "The user who does nothing" - In this case Kubo attempts to give some sane defaults discussed below based on the amount of memory and file descriptors their system has. This should protect the node from many attacks. - -1. "Slightly more advanced user" - They can tweak the default limits discussed below. - Where the defaults aren't good enough, a good set of higher-level "knobs" are exposed to satisfy most use cases - without requiring users to wade into all the intricacies of libp2p's resource manager. - The "knobs"/inputs are `Swarm.ResourceMgr.MaxMemory` and `Swarm.ResourceMgr.MaxFileDescriptors` as described below. -1. "Power user" - They specify overrides to computed default limits via `ipfs swarm limit` and `Swarm.ResourceMgr.Limits`; +2. "Slightly more advanced user" - Where the defaults aren't good enough, a good set of higher-level "knobs" are exposed to satisfy most use cases + without requiring users to wade into all the intricacies of libp2p's resource manager. + The "knobs"/inputs are `Swarm.ResourceMgr.MaxMemory` and `Swarm.ResourceMgr.MaxFileDescriptors` as described below. + +3. "Power user" - They [specify override limits](#user-supplied-override-limits) and own their own destiny without Kubo getting in the way. ### Computed Default Limits With the `Swarm.ResourceMgr.MaxMemory` and `Swarm.ResourceMgr.MaxFileDescriptors` inputs defined, -[resource manager limits](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager#limits) are created at the -[system](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager#the-system-scope), -[transient](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager#the-transient-scope), +[resource manager limits](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager#limits) are created at the +[system](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager#the-system-scope), +[transient](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager#the-transient-scope), and [peer](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager#peer-scopes) scopes. -Other scopes are ignored (by being set to "[~infinity](#infinite-limits])". +Other scopes are ignored (by being set to "unlimited"). The reason these scopes are chosen is because: - `system` - This gives us the coarse-grained control we want so we can reason about the system as a whole. @@ -66,13 +65,17 @@ The reason these scopes are chosen is because: We are reliant on the system scope for protection here in the malicious case. The reason for having a peer scope is to protect against unintentional DoS attacks (e.g., bug in a peer which is causing it to "misbehave"). - In the unintional case, we want to make sure a "misbehaving" node doesn't consume more resources than necessary. + In the unintentional case, we want to make sure a "misbehaving" node doesn't consume more resources than necessary. -Within these scopes, limits are just set on -[memory](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager#memory), -[file descriptors (FD)](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager#file-descriptors), [*inbound* connections](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager#connections), -and [*inbound* streams](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager#streams). +Within these scopes, limits are set on: +1. [memory](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager#memory) +2. [file descriptors (FD)](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager#file-descriptors) +3. [*inbound* connections](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager#connections). Limits are set based on the `Swarm.ResourceMgr.MaxMemory` and `Swarm.ResourceMgr.MaxFileDescriptors` inputs above. + +There are also some special cases where minimum values are enforced. +For example, Kubo maintainers have found in practice that it's a footgun to have too low of a value for `System.ConnsInbound` and a default minimum is used. (See [core/node/libp2p/rcmgr_defaults.go](https://github.com/ipfs/kubo/blob/master/core/node/libp2p/rcmgr_defaults.go) for specifics.) + We trust this node to behave properly and thus don't limit *outbound* connection/stream limits. We apply any limits that libp2p has for its protocols/services since we assume libp2p knows best here. @@ -80,88 +83,75 @@ since we assume libp2p knows best here. Source: [core/node/libp2p/rcmgr_defaults.go](https://github.com/ipfs/kubo/blob/master/core/node/libp2p/rcmgr_defaults.go) ### User Supplied Override Limits -Once Kubo has the [Computed Default Limits](#computed-default-limits), it then applies any user-supplied [`Swarm.ResourceMgr.Limits`](https://github.com/ipfs/kubo/blob/master/docs/config.md#swarmresourcemgrlimits) on top. -These become the [active limits](#how-does-one-see-the-active-limits). - -While `Swarm.ResourceMgr.Limits` can be edited directly, it is also possible to use `ipfs swarm limit` command to inspect and tweak specific limits at runtime. - - -To see all resources that are close to hitting their respective limit: - -```console -$ ipfs swarm stats --min-used-limit-perc=90 all -``` - -To modify limits for specific scope (e.g. `system`): - -```console -$ ipfs swarm limit system > change.json -$ vi change.json -$ ipfs swarm limit system change.json -``` - -Learn more: `ipfs swarm limit --help` - -### Infinite limits -There isn't a way via config to specify infinite limits (see [go-libp2p#1935](https://github.com/libp2p/go-libp2p/issues/1935)). For example, "-1" is not infinity. To work around this, Kubo uses a magic number of "1000000000" to denote infinity since it's effectively infinite. +A user who wants fine control over the limits used by the go-libp2p resource manager can specify overrides to the [computed default limits](#computed-default-limits). +This is done by defining limits in ``$IPFS_PATH/libp2p-resource-limit-overrides.json``. +These values trump anything else and are parsed directly by go-libp2p. +(See the [go-libp2p Resource Manager README](https://github.com/libp2p/go-libp2p/blob/master/p2p/host/resource-manager/README.md) for formatting.) ## FAQ ### What do these "Protected from exceeding resource limits" log messages mean? -"Protected from exceeding resource limits" log messages denote that the resource manager is working and that it prevented additional resources being used beyond the set limits. Per [libp2p code](https://github.com/libp2p/go-libp2p/blob/master/p2p/host/resource-manager/scope.go), these messages take the form of "$scope: cannot reserve $limitKey". +"Protected from exceeding resource limits" log messages denote that the resource manager is working and that it prevented additional resources from being used beyond the set limits. Per [libp2p code](https://github.com/libp2p/go-libp2p/blob/master/p2p/host/resource-manager/scope.go), these messages take the form of "$scope: cannot reserve $limitKey". As an example: > Protected from exceeding resource limits 2 times: "system: cannot reserve inbound connection: resource limit exceeded" -This means that there were 2 recent occurences where the libp2p resource manager prevented an inbound connection at the "system" [scope](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager#resource-scopes). -Specificaly the ``Swarm.ResourceMgr.Limits.System.ConnsInbound`` [active limit](#how-does-one-see-the-active-limits) was hit. +This means that there were 2 recent occurrences where the libp2p resource manager prevented an inbound connection at the "system" [scope](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager#resource-scopes). +Specifically the ``System.ConnsInbound`` limit was hit. -This can be analyzed by viewing the limit with `ipfs swarm limit system` and comparing the usage with `ipfs swarm stats system`. -`ConnsInbound` is likely close or at the limit value. +This can be analyzed by viewing the limit and current usage with `ipfs swarm resources`. +`System.ConnsInbound` is likely close or at the limit value. -The simiplest way to identify all resources across all scopes that are close to exceeding their limit is with a command like `ipfs swarm stats --min-used-limit-perc=90 all`. +The simplest way to identify all resources across all scopes that are close to exceeding their limit (>90% usage) is with a command like `ipfs swarm resources | egrep "9.\..%"` Sources: * [kubo resource manager logging](https://github.com/ipfs/kubo/blob/master/core/node/libp2p/rcmgr_logging.go) * [libp2p resource manager messages](https://github.com/libp2p/go-libp2p/blob/master/p2p/host/resource-manager/scope.go) -### What are the "Application error ... cannot reserve ..." messages? -These are messages from a *remote* go-libp2p peer (likely another Kubo node) with the resource manager enabled on why it failed to establish a connection. - -This can be confusing, but these `Application error ... cannot reserve ...` messages can occur even if your local node has the resoure manager disabled. - -You can distinguish resource manager messages originating from your local node if they're from the `resourcemanager` / `libp2p/rcmgr_logging.go` logger -or you see the string that is unique to Kubo (and not in go-libp2p): "Protected from exceeding resource limits". - -There is a go-libp2p issue ([#1928](https://github.com/libp2p/go-libp2p/issues/1928)) to make it clearer that this is an error message originating from a remote peer. - -### How does the resource manager (ResourceMgr) relate to the connection manager (ConnMgr)? -As discussed [here](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager#connmanager-vs-resource-manager) -these are separate systems in go-libp2p. -Kubo also configures the ConnMgr separately from ResourceMgr. There is no checking to make sure the limits between the systems are congruent. - -Ideally `Swarm.ConnMgr.HighWater` is less than `Swarm.ResourceMgr.Limits.System.ConnsInbound`. -This is so the ConnMgr can kick in and cleanup connections based on connection priorities before the hard limits of the ResourceMgr are applied. -If `Swarm.ConnMgr.HighWater` is greater than `Swarm.ResourceMgr.Limits.System.ConnsInbound`, -existing low priority idle connections can prevent new high priority connections from being established. -The ResourceMgr doesn't know that the new connection is high priority and simply blocks it because of the limit its enforcing. - ### How does one see the Active Limits? A dump of what limits are actually being used by the resource manager ([Computed Default Limits](#computed-default-limits) + [User Supplied Override Limits](#user-supplied-override-limits)) -can be obtained by `ipfs swarm limit all`. +can be obtained by `ipfs swarm resources`. ### How does one see the Computed Default Limits? -This can be observed with an empty [`Swarm.ResourceMgr.Limits`](https://github.com/ipfs/kubo/blob/master/docs/config.md#swarmresourcemgrlimits) -and then [seeing the active limits](#how-does-one-see-the-active-limits). +This can be observed [seeing the active limits](#how-does-one-see-the-active-limits) assuming one hasn't detoured into "power user" mode with [User Supplied Override Limits](#user-supplied-override-limits). ### How does one monitor libp2p resource usage? + For [monitoring libp2p resource usage](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager#monitoring), -various `*rcmgr_*` metrics can be accessed as the prometheus endpoint at `{Addresses.API}/debug/metrics/prometheus` (default: `http://127.0.0.1:5001/debug/metrics/prometheus`). +various `*rcmgr_*` metrics can be accessed as the Prometheus endpoint at `{Addresses.API}/debug/metrics/prometheus` (default: `http://127.0.0.1:5001/debug/metrics/prometheus`). There are also [pre-built Grafana dashboards](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager/obs/grafana-dashboards) that can be added to a Grafana instance. A textual view of current resource usage and a list of services, protocols, and peers can be obtained via `ipfs swarm stats --help` +### How does the resource manager (ResourceMgr) relate to the connection manager (ConnMgr)? +As discussed [here](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager#connmanager-vs-resource-manager) +these are separate systems in go-libp2p. +Kubo performs sanity checks to ensure that some of the hard limits of the ResourceMgr are sufficiently greater than the soft limits of the ConnMgr. + +The soft limit of `Swarm.ConnMgr.HighWater` needs to be less than the resource manager hard limit `System.ConnsInbound` for the configuration to make sense. +This ensures the ConnMgr cleans up connections based on connection priorities before the hard limits of the ResourceMgr are applied. +If `Swarm.ConnMgr.HighWater` is greater than resource manager's `System.ConnsInbound`, +existing low-priority idle connections can prevent new high-priority connections from being established. +The ResourceMgr doesn't know that the new connection is high priority and simply blocks it because of the limit its enforcing. + +To ensure the ConnMgr and ResourceMgr are congruent, the ResourceMgr [computed default limits](#computed-default-limits) are adjusted such that: +1. `System.ConnsInbound` >= `max(Swarm.ConnMgr.HighWater * 2, DefaultResourceMgrMinInboundConns)` AND +2. `System.StreamsInbound` is greater than any new/adjusted `Swarm.ResourceMgr.Limits.System.ConnsInbound` value so that there's enough streams per connection. + +Source: [core/node/libp2p/rcmgr_defaults.go](https://github.com/ipfs/kubo/blob/master/core/node/libp2p/rcmgr_defaults.go) + +### What are the "Application error 0x0 (remote) ... cannot reserve ..." messages? +These are messages coming from old (pre go-libp2p 0.26) *remote* go-libp2p peers (likely another older Kubo node) with the resource manager enabled on why it failed to establish a connection. + +This can be confusing, but these `Application error 0x0 (remote) ... cannot reserve ...` messages can occur even if your local node has the resource manager disabled. + +You can distinguish resource manager messages originating from your local node if they're from the `resourcemanager` / `libp2p/rcmgr_logging.go` logger +or you see the string that is unique to Kubo (and not in go-libp2p): "Protected from exceeding resource limits". + +See more info in this go-libp2p issue ([#1928](https://github.com/libp2p/go-libp2p/issues/1928)). go-libp2p 0.26 / Kubo 0.19 onwards this confusing error message was removed. + + ## History -Kubo first [exposed this functionality in Kubo 0.13](./changelogs/v0.13.md#-libp2p-network-resource-manager-swarmresourcemgr), but it was disabled by default. It was then enabled by default in [Kubo 0.17](./changelogs/v0.17.md#libp2p-resource-management-enabled-by-default). Until that point, Kubo was vulnerable to unbound resource usage which could bring down nodes. Introducing limits like this by default after the fact is tricky, which is why there have been changes and improvements afterwards. +Kubo first [exposed this functionality in Kubo 0.13](./changelogs/v0.13.md#-libp2p-network-resource-manager-swarmresourcemgr), but it was disabled by default. It was then enabled by default in [Kubo 0.17](./changelogs/v0.17.md#libp2p-resource-management-enabled-by-default). Until that point, Kubo was vulnerable to unbound resource usage which could bring down nodes. Introducing limits like this by default after the fact is tricky, which is why there have been changes and improvements afterwards. The general trend since 0.17 with (0.18)[./changeloges/v0.18.md#improving-libp2p-resource-management-integration] and 0.19 has been to simplify and provide less options (and footguns!) for users and better documentation. diff --git a/docs/plugins.md b/docs/plugins.md index 1feca834c..86cfe1c51 100644 --- a/docs/plugins.md +++ b/docs/plugins.md @@ -51,14 +51,15 @@ plugin type is likely the best interim solution. ### fx (experimental) Fx plugins let you customize the [fx](https://pkg.go.dev/go.uber.org/fx) dependency graph and configuration, -by customizing the`fx.Option`s that are passed to `fx` when the IPFS node is initialized. +by customizing the`fx.Option`s that are passed to `fx` when the Kubo node is initialized. -For example, you can inject custom implementations of interfaces such as [exchange.Interface](https://github.com/ipfs/go-ipfs-exchange-interface) -or [pin.Pinner](https://github.com/ipfs/go-ipfs-pinner) by adding an option like `fx.Replace(fx.Annotate(customExchange, fx.As(new(exchange.Interface))))`. +For example, you can override an interface such as [exchange.Interface](https://github.com/ipfs/go-ipfs-exchange-interface) +or [pin.Pinner](https://github.com/ipfs/go-ipfs-pinner) with a custom implementation by appending an option like +`fx.Decorate(func() exchange.Interface { return customExchange })`. Fx supports some advanced customization. Simple interface replacements like above are unlikely to break in the future, but the more invasive your changes, the more likely they are to break between releases. Kubo cannot guarantee backwards -compatibility for invasive `fx` customizations. +compatibility for `fx` customizations. Fx options are applied across every execution of the `ipfs` binary, including: @@ -68,7 +69,7 @@ Fx options are applied across every execution of the `ipfs` binary, including: - etc. So if you plug in a blockservice that disallows non-allowlisted CIDs, then this may break migrations -that fetch migration code over IPFS. +that fetch migration code over the IPFS network. ### Internal diff --git a/docs/releases_thunderdome.md b/docs/releases_thunderdome.md new file mode 100644 index 000000000..53034b3bb --- /dev/null +++ b/docs/releases_thunderdome.md @@ -0,0 +1,60 @@ +# Testing Kubo releases with Thunderdome +This document is for running Thunderdome tests by release engineers as part of releasing Kubo. + +We use Thunderdome to replay ipfs.io gateway traffic in a controlled environment against two different versions of Kubo, and we record metrics and compare them to look for logic or performance regressions before releasing a new Kubo version. + +For background information about how Thunderdome works, see: https://github.com/ipfs-shipyard/thunderdome + +## Prerequisites + +* Ensure you have access to the "IPFS Stewards" vault in 1Password, which contains the requisite AWS Console and API credentials +* Ensure you have Docker and the Docker CLI installed +* Checkout the Thunderdome repo locally (or `git pull` to ensure it's up-to-date) +* Install AWS CLI v2: https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html +* Configure the AWS CLI + * Configure the credentials as described in the [Thunderdome documentation](https://github.com/ipfs-shipyard/thunderdome/blob/main/cmd/thunderdome/README.md#credentials), using the credentials from 1Password +* Make sure the `thunderdome` binary is up-to-date: `go build ./cmd/thunderdome` + +## Add & run an experiment + +Create a new release configuration JSON in the `experiments/` directory, based on the most recent `kubo-release` configuration, and tweak as necessary. Generally we setup the targets to run a commit against the tag of the last release, such as: + +```json + "targets": [ + { + "name": "kubo190-4283b9", + "description": "kubo 0.19.0-rc1", + "build_from_git": { + "repo": "https://github.com/ipfs/kubo.git", + "commit":"4283b9d98f8438fc8751ccc840d8fc24eeae6f13" + } + }, + { + "name": "kubo181", + "description": "kubo 0.18.", + "build_from_git": { + "repo": "https://github.com/ipfs/kubo.git", + "tag":"v0.18.1" + } + } + ] +``` + +Run the experiment (where `$EXPERIMENT_CONFIG_JSON` is a path to the config JSON created above): + +```shell +AWS_PROFILE=thunderdome ./thunderdome deploy --verbose --duration 120 $EXPERIMENT_CONFIG_JSON +``` + +This will build the Docker images, upload them to ECR, and then launch the experiment in Thunderdome. Once the experiment starts, the CLI will exit and the experiment will continue to run for the duration. + +## Analyze Results + +Add a log entry in https://www.notion.so/ceb2047e79f2498494077a2739a6c493 and link to it from the release issue, so that experiment results are publicly visible. + +The `deploy` command will output a link to the Grafana dashboard for the experiment. We don't currently have rigorous acceptance criteria, so you should look for anomalies or changes in the metrics and make sure they are tolerable and explainable. Unexplainable anomalies should be noted in the log with a screenshot, and then root caused. + + +## Open a PR to merge the experiment config into Thunderdome + +This is important for both posterity, and so that someone else can sanity-check the test parameters. diff --git a/docs/specifications/fs-datastore.png b/docs/specifications/fs-datastore.png new file mode 100644 index 000000000..0a5eaaa87 Binary files /dev/null and b/docs/specifications/fs-datastore.png differ diff --git a/docs/specifications/ipfs-repo-contents.png b/docs/specifications/ipfs-repo-contents.png new file mode 100644 index 000000000..cfda9e5d9 Binary files /dev/null and b/docs/specifications/ipfs-repo-contents.png differ diff --git a/docs/specifications/keystore.md b/docs/specifications/keystore.md new file mode 100644 index 000000000..9609b83c2 --- /dev/null +++ b/docs/specifications/keystore.md @@ -0,0 +1,295 @@ +# ![](https://img.shields.io/badge/status-wip-orange.svg?style=flat-square) Keystore + +**Authors(s):** +- [whyrusleeping](github.com/whyrusleeping) +- [Hector Sanjuan](github.com/hsanjuan) + +**Abstract** + +This spec provides definitions and operations for the keystore feature in IPFS. + +# Table of Contents + +- [Goals](#goals) +- [Planned Implementation](#planned-implementation) + - [Key storage](#key-storage) + - [Interface](#interface) + - [Code changes and additions](#code-changes-and-additions) + - [Structures](#structures) + +## Goals + +To have a secure, simple and user-friendly way of storing and managing keys +for use by ipfs. As well as the ability to share these keys, encrypt, decrypt, +sign and verify data. + +## Planned Implementation + +### Key storage + +Storage layout and format is defined in the [`repository_fs`](repository_fs.md) part of the spec. + +### Interface + +#### ipfs key + +``` +USAGE + ipfs key - Create and list IPNS name keypairs + + ipfs key + + 'ipfs key gen' generates a new keypair for usage with IPNS and 'ipfs name + publish'. + + > ipfs key gen --type=rsa --size=2048 mykey + > ipfs name publish --key=mykey QmSomeHash + + 'ipfs key list' lists the available keys. + + > ipfs key list + self + mykey + + +SUBCOMMANDS + ipfs key export - Export a keypair + ipfs key gen - Create a new keypair + ipfs key import - Import a key and prints imported key id + ipfs key list - List all local keypairs. + ipfs key rename - Rename a keypair. + ipfs key rm ... - Remove a keypair. + ipfs key rotate - Rotates the IPFS identity. + + For more information about each command, use: + 'ipfs key --help' +``` + +#### ipfs crypt + +**NOTE:** as of 2023 Q4, `ipfs crypt` commands are not implemented yet. + +``` + ipfs crypt - Perform cryptographic operations using ipfs keypairs + +SUBCOMMANDS: + + ipfs crypt sign - Generates a signature for the given data with a specified key + ipfs crypt verify - Verify that the given data and signature match + ipfs crypt encrypt - Encrypt the given data + ipfs crypt decrypt - Decrypt the given data + +DESCRIPTION: + + `ipfs crypt` is a command used to perform various cryptographic operations + using ipfs keypairs, including: signing, verifying, encrypting and decrypting. +``` + +#### Some subcommands: + +##### ipfs key Gen + + +``` +USAGE + ipfs key gen - Create a new keypair + +SYNOPSIS + ipfs key gen [--type= | -t] [--size= | -s] + [--ipns-base=] [--] + +ARGUMENTS + + - name of key to create + +OPTIONS + + -t, --type string - type of the key to create: rsa, ed25519. Default: + ed25519. + -s, --size int - size of the key to generate. + --ipns-base string - Encoding used for keys: Can either be a multibase + encoded CID or a base58btc encoded multihash. Takes + {b58mh|base36|k|base32|b...}. Default: base36. +``` + +* * * + +##### Key Send + +``` +USAGE + ipfs key - Create and list IPNS name keypairs + +SYNOPSIS + ipfs key + +DESCRIPTION + + 'ipfs key gen' generates a new keypair for usage with IPNS and 'ipfs name + publish'. + + > ipfs key gen --type=rsa --size=2048 mykey + > ipfs name publish --key=mykey QmSomeHash + + 'ipfs key list' lists the available keys. + + > ipfs key list + self + mykey + + +SUBCOMMANDS + ipfs key export - Export a keypair + ipfs key gen - Create a new keypair + ipfs key import - Import a key and prints imported key id + ipfs key list - List all local keypairs. + ipfs key rename - Rename a keypair. + ipfs key rm ... - Remove a keypair. + ipfs key rotate - Rotates the IPFS identity. + + For more information about each command, use: + 'ipfs key --help' +``` + +##### Comments: + +Ensure that the user knows the implications of sending a key. + +* * * + +##### Crypt Encrypt + +``` + ipfs crypt encrypt - Encrypt the given data with a specified key + +ARGUMENTS: + + data - The filename of the data to be encrypted ("-" for stdin) + +OPTIONS: + + -k, -key string - The name of the key to use for encryption (default: localkey) + -o, -output string - The name of the output file (default: stdout) + -c, -cipher string - The cipher to use for the operation + -m, -mode string - The block cipher mode to use for the operation + +DESCRIPTION: + + 'ipfs crypt encrypt' is a command used to encrypt data so that only holders of a certain + key can read it. +``` + +##### Comments: + +This should probably just operate on raw data and not on DAGs. + +* * * + +##### Other Interface Changes + +We will also need to make additions to support keys in other commands, these changes are as follows: + +- `ipfs add` + - Support for a `-encrypt-key` option, for block encrypting the file being added with the key + - also adds an 'encrypted' node above the root unixfs node + - Support for a `-sign-key` option to attach a signature node above the root unixfs node + +- `ipfs block put` + - Support for a `-encrypt-key` option, for encrypting the block before hashing and storing + +- `ipfs object put` + - Support for a `-encrypt-key` option, for encrypting the object before hashing and storing + +- `ipfs name publish` + - Support for a `-key` option to select which keyspace to publish to + +### Code changes and additions + +This sections outlines code organization around this feature. + +#### Keystore package + +The fsrepo carries a `keystore` that can be used to load/store keys. The keystore is implemented following this interface: + +```go +// Keystore provides a key management interface +type Keystore interface { + // Has returns whether or not a key exist in the Keystore + Has(string) (bool, error) + // Put stores a key in the Keystore, if a key with the same name already exists, returns ErrKeyExists + Put(string, ci.PrivKey) error + // Get retrieves a key from the Keystore if it exists, and returns ErrNoSuchKey + // otherwise. + Get(string) (ci.PrivKey, error) + // Delete removes a key from the Keystore + Delete(string) error + // List returns a list of key identifier + List() ([]string, error) +} +``` + +Note: Never store passwords as strings, strings cannot be zeroed out after they are used. +using a byte array allows you to write zeroes over the memory so that the users password +does not linger in memory. + +#### Unixfs + +- new node types, 'encrypted' and 'signed', probably shouldn't be in unixfs, just understood by it +- if new node types are not unixfs nodes, special consideration must be given to the interop + +- DagReader needs to be able to access keystore to seamlessly stream encrypted data we have keys for + - also needs to be able to verify signatures + +#### Importer + +- DagBuilderHelper needs to be able to encrypt blocks + - Dag Nodes should be generated like normal, then encrypted, and their parents should + link to the hash of the encrypted node +- DagBuilderParams should have extra parameters to accommodate creating a DBH that encrypts the blocks + +#### New 'Encrypt' package + +Should contain code for crypto operations on dags. + +Encryption of dags should work by first generating a symmetric key, and using +that key to encrypt all the data. That key should then be encrypted with the +public key chosen and stored in the Encrypted DAG structure. + +Note: One option is to simply add it to the key interface. + +### Structures +Some tentative mockups (in json) of the new DAG structures for signing and encrypting + +Signed DAG: +``` +{ + "Links" : [ + { + "Name":"@content", + "Hash":"QmTheContent", + } + ], + "Data": protobuf{ + "Type":"Signed DAG", + "Signature": "thesignature", + "PubKeyID": "QmPubKeyHash", + } +} +``` + +Encrypted DAG: +``` +{ + "Links" : [ + { + "Name":"@content", + "Hash":"QmRawEncryptedDag", + } + ], + "Data": protobuf{ + "Type":"Encrypted DAG", + "PubKeyID": "QmPubKeyHash", + "Key": "ephemeral symmetric key, encrypted with public key", + } +} +``` diff --git a/docs/specifications/repository.md b/docs/specifications/repository.md new file mode 100644 index 000000000..0e7d663d5 --- /dev/null +++ b/docs/specifications/repository.md @@ -0,0 +1,131 @@ +# ![](https://img.shields.io/badge/status-wip-orange.svg?style=flat-square) IPFS Repo Spec + +**Author(s)**: +- [Juan Benet](github.com/jbenet) + +**Abstract** + +This spec defines an IPFS Repo, its contents, and its interface. It does not specify how the repo data is actually stored, as that is done via swappable implementations. + +# Table of Contents + +- [Definition](#definition) +- [Repo Contents](#repo-contents) + - [version](#version) + - [datastore](#datastore) + - [keystore](#keystore) + - [config (state)](#config-state) + - [locks](#locks) + - [datastore\_spec](#datastore_spec) + - [hooks (TODO)](#hooks-todo) +- [Notes](#notes) + +## Definition + +A `repo` is the storage repository of an IPFS node. It is the subsystem that +actually stores the data IPFS nodes use. All IPFS objects are stored +in a repo (similar to git). + +There are many possible repo implementations, depending on the storage media +used. Most commonly, IPFS nodes use an [fs-repo](repository_fs.md). + +Repo Implementations: +- [fs-repo](repository_fs.md) - stored in the os filesystem +- mem-repo - stored in process memory +- s3-repo - stored in amazon s3 + +## Repo Contents + +The Repo stores a collection of [IPLD](https://github.com/ipld/specs#readme) objects that represent: + +- **config** - node configuration and settings +- **datastore** - content stored locally, and indexing data +- **keystore** - cryptographic keys, including node's identity +- **hooks** - scripts to run at predefined times (not yet implemented) + +Note that the IPLD objects a repo stores are divided into: +- **state** (system, control plane) used for the node's internal state +- **content** (userland, data plane) which represent the user's cached and pinned data. + +Additionally, the repo state must determine the following. These need not be IPLD objects, though it is of course encouraged: + +- **version** - the repo version, required for safe migrations +- **locks** - process semaphores for correct concurrent access +- **datastore_spec** - array of mounting points and their properties + +Finally, the repo also stores the blocks with blobs containing binary data. + +![](./ipfs-repo-contents.png) + +### version + +Repo implementations may change over time, thus they MUST include a `version` recognizable across versions. Meaning that a tool MUST be able to read the `version` of a given repo type. + +For example, the `fs-repo` simply includes a `version` file with the version number. This way, the repo contents can evolve over time but the version remains readable the same way across versions. + +### datastore + +IPFS nodes store some IPLD objects locally. These are either (a) **state objects** required for local operation -- such as the `config` and `keys` -- or (b) **content objects** used to represent data locally available. **Content objects** are either _pinned_ (stored until they are unpinned) or _cached_ (stored until the next repo garbage collection). + +The name "datastore" comes from [go-datastore](https://github.com/jbenet/go-datastore), a library for swappable key-value stores. Like its name-sake, some repo implementations feature swappable datastores, for example: +- an fs-repo with a leveldb datastore +- an fs-repo with a boltdb datastore +- an fs-repo with a union fs and leveldb datastore +- an fs-repo with an s3 datastore +- an s3-repo with a cached fs and s3 datastore + +This makes it easy to change properties or performance characteristics of a repo without an entirely new implementation. + +### keystore + +A Repo typically holds the keys a node has access to, for signing and for encryption. + +Details on operation and storage of the keystore can be found in [`repository_fs.md`](repository_fs.md) and [`keystore.md`](keystore.md). + +### config (state) + +The node's `config` (configuration) is a tree of variables, used to configure various aspects of operation. For example: +- the set of bootstrap peers IPFS uses to connect to the network +- the Swarm, API, and Gateway network listen addresses +- the Datastore configuration regarding the construction and operation of the on-disk storage system. + +There is a set of properties, which are mandatory for the repo usage. Those are `Addresses`, `Discovery`, `Bootstrap`, `Identity`, `Datastore` and `Keychain`. + +It is recommended that `config` files avoid identifying information, so that they may be re-shared across multiple nodes. + +**CHANGES**: today, implementations like js-ipfs and go-ipfs store the peer-id and private key directly in the config. These will be removed and moved out. + +### locks + +IPFS implementations may use multiple processes, or may disallow multiple processes from using the same repo simultaneously. Others may disallow using the same repo but may allow sharing _datastores_ simultaneously. This synchronization is accomplished via _locks_. + +All repos contain the following standard locks: +- `repo.lock` - prevents concurrent access to the repo. Must be held to _read_ or _write_. + +### datastore_spec + +This file is created according to the Datastore configuration specified in the `config` file. It contains an array with all the mounting points that the repo is using, as well as its properties. This way, the `datastore_spec` file must have the same mounting points as defined in the Datastore configuration. + +It is important pointing out that the `Datastore` in config must have a `Spec` property, which defines the structure of the ipfs datastore. It is a composable structure, where each datastore is represented by a json object. + +### hooks (TODO) + +Like git, IPFS nodes will allow `hooks`, a set of user configurable scripts to run at predefined moments in IPFS operations. This makes it easy to customize the behavior of IPFS nodes without changing the implementations themselves. + +## Notes + +#### A Repo uniquely identifies an IPFS Node + +A repository uniquely identifies a node. Running two different IPFS programs with identical repositories -- and thus identical identities -- WILL cause problems. + +Datastores MAY be shared -- with proper synchronization -- though note that sharing datastore access MAY erode privacy. + +#### Repo implementation changes MUST include migrations + +**DO NOT BREAK USERS' DATA.** This is critical. Thus, any changes to a repo's implementation **MUST** be accompanied by a **SAFE** migration tool. + +See https://github.com/jbenet/go-ipfs/issues/537 and https://github.com/jbenet/random-ideas/issues/33 + +#### Repo Versioning + +A repo version is a single incrementing integer. All versions are considered non-compatible. Repos of different versions MUST be run through the appropriate migration tools before use. diff --git a/docs/specifications/repository_fs.md b/docs/specifications/repository_fs.md new file mode 100644 index 000000000..01e30d1c3 --- /dev/null +++ b/docs/specifications/repository_fs.md @@ -0,0 +1,279 @@ +# ![](https://img.shields.io/badge/status-wip-orange.svg?style=flat-square) fs-repo + +**Author(s)**: +- [Juan Benet](github.com/jbenet) +- [David Dias](github.com/daviddias) +- [Hector Sanjuan](github.com/hsanjuan) + +**Abstract** + +This spec defines `fs-repo` version `1`, its formats, and semantics. + +# Table of Contents + +- [Definition](#definition) +- [Contents](#contents) + - [api](#api) + - [blocks/](#blocks) + - [config](#config) + - [hooks/](#hooks) + - [keystore/](#keystore) + - [datastore/](#datastore) + - [logs/](#logs) + - [repo.lock](#repolock) + - [version](#version) +- [Datastore](#datastore-1) +- [Notes](#notes) + - [Location](#location) + - [blocks/ with an fs-datastore](#blocks-with-an-fs-datastore) + - [Reading without the `repo.lock`](#reading-without-the-repolock) + +## Definition + +`fs-repo` is a filesystem implementation of the IPFS [repo](repository.md). + + +## Contents + +![](img/ipfs-repo-contents.png?) + +``` +.ipfs/ +├── api <--- running daemon api addr +├── blocks/ <--- objects stored directly on disk +│ └── aa <--- prefix namespacing like git +│ └── aa <--- N tiers +├── config <--- config file (json or toml) +├── hooks/ <--- hook scripts +├── keystore/ <--- cryptographic keys +│ ├── key_b32name <--- private key with base32-encoded name +├── datastore/ <--- datastore +├── logs/ <--- 1 or more files (log rotate) +│ └── events.log <--- can be tailed +├── repo.lock <--- mutex for repo +└── version <--- version file +``` + +### api + +`./api` is a file that exists to denote an API endpoint to listen to. +- It MAY exist even if the endpoint is no longer live (i.e. it is a _stale_ or left-over `./api` file). + +In the presence of an `./api` file, ipfs tools (e.g. go-ipfs `ipfs daemon`) MUST attempt to delegate to the endpoint, and MAY remove the file if reasonably certain the file is stale. (e.g. endpoint is local, but no process is live) + +The `./api` file is used in conjunction with the `repo.lock`. Clients may opt to use the api service, or wait until the process holding `repo.lock` exits. The file's content is the api endpoint as a [multiaddr](https://github.com/jbenet/multiaddr) + +``` +> cat .ipfs/api +/ip4/127.0.0.1/tcp/5001 +``` + +Notes: +- The API server must remove the api file before releasing the `repo.lock`. +- It is not enough to use the `config` file, as the API addr of a daemon may + have been overridden via ENV or flag. + +#### api file for remote control + +One use case of the `api` file is to have a repo directory like: + +``` +> tree $IPFS_PATH +/Users/jbenet/.ipfs +└── api + +0 directories, 1 files + +> cat $IPFS_PATH/api +/ip4/1.2.3.4/tcp/5001 +``` + +In go-ipfs, this has the same effect as: + +``` +ipfs --api /ip4/1.2.3.4/tcp/5001 +``` + +Meaning that it makes ipfs tools use an ipfs node at the given endpoint, instead of the local directory as a repo. + +In this use case, the rest of the `$IPFS_PATH` may be completely empty, and no other information is necessary. It cannot be said it is a _repo_ per-se. (TODO: come up with a good name for this). + +### blocks/ + +The `block/` component contains the raw data representing all IPFS objects +stored locally, whether pinned or cached. This component is controlled by the ` +datastore`. For example, it may be stored within a leveldb instance in ` +datastore/`, or it may be stored entirely with independent files, like git. + +In the default case, the user uses fs-datastore for all `/blocks` so the +objects are stored in individual files. In other cases, `/blocks` may even be +stored remotely + +- [blocks/ with an fs-datastore](#blocks-with-an-fs-datastore) + +### config + +The `config` file is a JSON or TOML file that contains the tree of +configuration variables. It MUST only be changed while holding the +`repo.lock`, or potentially lose edits. + +### hooks/ + +The `hooks` directory contains executable scripts to be called on specific +events to alter ipfs node behavior. + +Currently available hooks: + +``` +none +``` + +### keystore/ + + +The `keystore` directory holds additional private keys that the node has +access to (the public keys can be derived from them). + +The keystore repository should have `0700` permissions (readable, writable by +the owner only). + +The key files are named as `key_base32encodedNameNoPadding` where `key_` is a +fixed prefix followed by a base32 encoded identifier, **without padding and +downcased**. The identifier usually corresponds to a human-friendly name given +by the user. + +The key files should have '0400' permissions (read-only, by the owner only). + +The `self` key identifier is reserved for the peer's main key, and therefore key named +`key_onswyzq` is allowed in this folder. + +The key files themselves contain a serialized representation of the keys as +defined in the +[libp2p specification](https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md#keys). + +### datastore/ + +The `datastore` directory contains the data for a leveldb instance used to +store operation data for the IPFS node. If the user uses a `boltdb` datastore +instead, the directory will be named `boltdb`. Thus the data files of each +database will not clash. + +TODO: consider whether all should just be named `leveldb/` + +### logs/ + +IPFS implementations put event log files inside the `logs/` directory. The +latest log file is `logs/events`. Others, rotated out may exist, with a +timestamp of their creation. For example: + + + +### repo.lock + +`repo.lock` prevents concurrent access to the repo. Its content SHOULD BE the +PID of the process currently holding the lock. This allows clients to detect +a failed lock and cleanup. + +``` +> cat .ipfs/repo.lock +42 +> ps | grep "ipfs daemon" +42 ttys000 79:05.83 ipfs daemon +``` + +**TODO, ADDRESS DISCREPANCY:** the go-ipfs implementation does not currently store the PID in the file, which in some systems causes failures after a failure or a teardown. This SHOULD NOT require any manual intervention-- a present lock should give new processes enough information to recover. Doing this correctly in a portable, safe way, with good UX is very tricky. We must be careful with TOCTTOU bugs, and multiple concurrent processes capable of running at any moment. The goal is for all processes to operate safely, to avoid bothering the user, and for the repo to always remain in a correct, consistent state. + +### version + +The `version` file contains the repo implementation name and version. This format has changed over time: + +``` +# in version 0 +> cat $repo-at-version-0/version +cat: /Users/jbenet/.ipfs/version: No such file or directory + +# in versions 1 and 2 +> cat $repo-at-version-1/version +1 +> cat $repo-at-version-2/version +2 + +# in versions >3 +> cat $repo-at-version-3/version +fs-repo/3 +``` + +_Any_ fs-repo implementation of _any_ versions `>0` MUST be able to read the +`version` file. It MUST NOT change format between versions. The sole exception is version 0, which had no file. + +**TODO: ADDRESS DISCREPANCY:** versions 1 and 2 of the go-ipfs implementation use just the integer number. It SHOULD have used `fs-repo/`. We could either change the spec and always just use the int, or change go-ipfs in version `>3`. we will have to be backwards compatible. + +## Datastore + +Both the `/blocks` and `/datastore` directories are controlled by the +`datastore` component of the repo. + +## Notes + +### Location + +The `fs-repo` can be located anywhere on the filesystem. By default +clients should search for a repo in: + +``` +~/.ipfs +``` + +Users can tell IPFS programs to look elsewhere with the env var: + +``` +IPFS_PATH=/path/to/repo +``` + +### blocks/ with an fs-datastore + +![](fs-datastore.png) + +Each object is stored in its own file. The filename is the hash of the object. +The files are nested in directories whose names are prefixes of the hash, as +in `.git/objects`. + +For example: +```sh +# multihashes +1220fe389b55ea958590769f9046b0f7268bca90a92e4a9f45cbb30930f4bf89269d # sha2 +1114f623e0ec7f8719fb14a18838d2a3ef4e550b5e53 # sha1 + +# locations of the blocks +.ipfs/blocks/1114/f6/23/e0ec7f8719fb14a18838d2a3ef4e550b5e53 +.ipfs/blocks/1220/fe/38/9b55ea958590769f9046b0f7268bca90a92e4a9f45cbb30930f4bf89269d +``` + +**Important Notes:** +- the hashes are encoded in hex, not the usual base58, because some + filesystems are case insensitive. +- the multihash prefix is two bytes, which would waste two directory levels, + thus these are combined into one. +- the git `idx` and `pack` file formats could be used to coalesce objects + +**TODO: ADDRESS DISCREPANCY:** + +the go-ipfs fs-repo in version 2 uses a different `blocks/` dir layout: + +``` +/Users/jbenet/.ipfs/blocks +├── 12200007 +│ └── 12200007d4e3a319cd8c7c9979280e150fc5dbaae1ce54e790f84ae5fd3c3c1a0475.data +├── 1220000f +│ └── 1220000fadd95a98f3a47c1ba54a26c77e15c1a175a975d88cf198cc505a06295b12.data +``` + +We MUST address whether we should change the fs-repo spec to match go-ipfs in version 2, or we should change go-ipfs to match the fs-repo spec (more tiers). We MUST also address whether the levels are a repo version parameter or a config parameter. There are filesystems in which a different fanout will have wildly different performance. These are mostly networked and legacy filesystems. + +### Reading without the `repo.lock` + +Programs MUST hold the `repo.lock` while reading and writing most files in the +repo. The only two exceptions are: + +- `repo.lock` - so clients may check for it +- `api` - so clients may use the API diff --git a/docs/windows.md b/docs/windows.md index 590f270af..3ed0e4ab2 100644 --- a/docs/windows.md +++ b/docs/windows.md @@ -153,6 +153,6 @@ If you get authentication problems with Git, you might want to take a look at ht `git config --global credential.helper wincred` - **Anything else** -Please search [https://discuss.ipfs.io](https://discuss.ipfs.io/search?q=windows%20category%3A13) for any additional issues you may encounter. If you can't find any existing resolution, feel free to post a question asking for help. +Please search [https://discuss.ipfs.tech](https://discuss.ipfs.tech/search?q=windows%20category%3A13) for any additional issues you may encounter. If you can't find any existing resolution, feel free to post a question asking for help. If you encounter a bug with `kubo` itself (not related to building) please use the [issue tracker](https://github.com/ipfs/kubo/issues) to report it. diff --git a/fuse/ipns/common.go b/fuse/ipns/common.go index 45b46dc20..d22086776 100644 --- a/fuse/ipns/common.go +++ b/fuse/ipns/common.go @@ -3,9 +3,9 @@ package ipns import ( "context" - nsys "github.com/ipfs/go-namesys" - path "github.com/ipfs/go-path" - ft "github.com/ipfs/go-unixfs" + ft "github.com/ipfs/boxo/ipld/unixfs" + "github.com/ipfs/boxo/namesys" + "github.com/ipfs/boxo/path" "github.com/ipfs/kubo/core" ci "github.com/libp2p/go-libp2p/core/crypto" ) @@ -18,7 +18,7 @@ func InitializeKeyspace(n *core.IpfsNode, key ci.PrivKey) error { emptyDir := ft.EmptyDirNode() - err := n.Pinning.Pin(ctx, emptyDir, false) + err := n.Pinning.Pin(ctx, emptyDir, false, "") if err != nil { return err } @@ -28,7 +28,7 @@ func InitializeKeyspace(n *core.IpfsNode, key ci.PrivKey) error { return err } - pub := nsys.NewIpnsPublisher(n.Routing, n.Repo.Datastore()) + pub := namesys.NewIPNSPublisher(n.Routing, n.Repo.Datastore()) return pub.Publish(ctx, key, path.FromCid(emptyDir.Cid())) } diff --git a/fuse/ipns/ipns_test.go b/fuse/ipns/ipns_test.go index 51ac9518c..ece386bf7 100644 --- a/fuse/ipns/ipns_test.go +++ b/fuse/ipns/ipns_test.go @@ -20,7 +20,7 @@ import ( fstest "bazil.org/fuse/fs/fstestutil" racedet "github.com/ipfs/go-detect-race" - u "github.com/ipfs/go-ipfs-util" + "github.com/ipfs/go-test/random" ci "github.com/libp2p/go-libp2p-testing/ci" ) @@ -32,7 +32,7 @@ func maybeSkipFuseTests(t *testing.T) { func randBytes(size int) []byte { b := make([]byte, size) - _, err := io.ReadFull(u.NewTimeSeededRand(), b) + _, err := io.ReadFull(random.NewRand(), b) if err != nil { panic(err) } @@ -56,7 +56,7 @@ func writeFileOrFail(t *testing.T, size int, path string) []byte { func writeFile(size int, path string) ([]byte, error) { data := randBytes(size) - err := os.WriteFile(path, data, 0666) + err := os.WriteFile(path, data, 0o666) return data, err } @@ -151,12 +151,12 @@ func TestIpnsLocalLink(t *testing.T) { t.Fatal(err) } - if linksto != nd.Identity.Pretty() { + if linksto != nd.Identity.String() { t.Fatal("Link invalid") } } -// Test writing a file and reading it back +// Test writing a file and reading it back. func TestIpnsBasicIO(t *testing.T) { if testing.Short() { t.SkipNow() @@ -176,7 +176,7 @@ func TestIpnsBasicIO(t *testing.T) { t.Fatal("Incorrect Read!") } - fname2 := mnt.Dir + "/" + nd.Identity.Pretty() + "/testfile" + fname2 := mnt.Dir + "/" + nd.Identity.String() + "/testfile" rbuf, err = os.ReadFile(fname2) if err != nil { t.Fatal(err) @@ -187,7 +187,7 @@ func TestIpnsBasicIO(t *testing.T) { } } -// Test to make sure file changes persist over mounts of ipns +// Test to make sure file changes persist over mounts of ipns. func TestFilePersistence(t *testing.T) { if testing.Short() { t.SkipNow() @@ -250,7 +250,7 @@ func TestMultipleDirs(t *testing.T) { mnt.Close() } -// Test to make sure the filesystem reports file sizes correctly +// Test to make sure the filesystem reports file sizes correctly. func TestFileSizeReporting(t *testing.T) { if testing.Short() { t.SkipNow() @@ -271,7 +271,7 @@ func TestFileSizeReporting(t *testing.T) { } } -// Test to make sure you can't create multiple entries with the same name +// Test to make sure you can't create multiple entries with the same name. func TestDoubleEntryFailure(t *testing.T) { if testing.Short() { t.SkipNow() @@ -280,12 +280,12 @@ func TestDoubleEntryFailure(t *testing.T) { defer mnt.Close() dname := mnt.Dir + "/local/thisisadir" - err := os.Mkdir(dname, 0777) + err := os.Mkdir(dname, 0o777) if err != nil { t.Fatal(err) } - err = os.Mkdir(dname, 0777) + err = os.Mkdir(dname, 0o777) if err == nil { t.Fatal("Should have gotten error one creating new directory.") } @@ -301,7 +301,7 @@ func TestAppendFile(t *testing.T) { fname := mnt.Dir + "/local/file" data := writeFileOrFail(t, 1300, fname) - fi, err := os.OpenFile(fname, os.O_RDWR|os.O_APPEND, 0666) + fi, err := os.OpenFile(fname, os.O_RDWR|os.O_APPEND, 0o666) if err != nil { t.Fatal(err) } @@ -463,9 +463,8 @@ func TestFSThrash(t *testing.T) { } } -// Test writing a medium sized file one byte at a time +// Test writing a medium sized file one byte at a time. func TestMultiWrite(t *testing.T) { - if testing.Short() { t.SkipNow() } diff --git a/fuse/ipns/ipns_unix.go b/fuse/ipns/ipns_unix.go index c19898919..23704cabd 100644 --- a/fuse/ipns/ipns_unix.go +++ b/fuse/ipns/ipns_unix.go @@ -12,18 +12,19 @@ import ( "io" "os" "strings" + "syscall" - dag "github.com/ipfs/go-merkledag" - ft "github.com/ipfs/go-unixfs" - path "github.com/ipfs/interface-go-ipfs-core/path" + dag "github.com/ipfs/boxo/ipld/merkledag" + ft "github.com/ipfs/boxo/ipld/unixfs" + "github.com/ipfs/boxo/path" fuse "bazil.org/fuse" fs "bazil.org/fuse/fs" + mfs "github.com/ipfs/boxo/mfs" cid "github.com/ipfs/go-cid" logging "github.com/ipfs/go-log" - mfs "github.com/ipfs/go-mfs" - iface "github.com/ipfs/interface-go-ipfs-core" - options "github.com/ipfs/interface-go-ipfs-core/options" + iface "github.com/ipfs/kubo/core/coreiface" + options "github.com/ipfs/kubo/core/coreiface/options" ) func init() { @@ -85,7 +86,7 @@ type Root struct { func ipnsPubFunc(ipfs iface.CoreAPI, key iface.Key) mfs.PubFunc { return func(ctx context.Context, c cid.Cid) error { - _, err := ipfs.Name().Publish(ctx, path.IpfsPath(c), options.Name.Key(key.Name())) + _, err := ipfs.Name().Publish(ctx, path.FromCid(c), options.Name.Key(key.Name())) return err } } @@ -149,7 +150,7 @@ func CreateRoot(ctx context.Context, ipfs iface.CoreAPI, keys map[string]iface.K // Attr returns file attributes. func (r *Root) Attr(ctx context.Context, a *fuse.Attr) error { log.Debug("Root Attr") - a.Mode = os.ModeDir | 0111 // -rw+x + a.Mode = os.ModeDir | 0o111 // -rw+x return nil } @@ -158,7 +159,7 @@ func (r *Root) Lookup(ctx context.Context, name string) (fs.Node, error) { switch name { case "mach_kernel", ".hidden", "._.": // Just quiet some log noise on OS X. - return nil, fuse.ENOENT + return nil, syscall.Errno(syscall.ENOENT) } if lnk, ok := r.LocalLinks[name]; ok { @@ -173,7 +174,7 @@ func (r *Root) Lookup(ctx context.Context, name string) (fs.Node, error) { case *FileNode: return nd, nil default: - return nil, fuse.EIO + return nil, syscall.Errno(syscall.EIO) } } @@ -182,10 +183,10 @@ func (r *Root) Lookup(ctx context.Context, name string) (fs.Node, error) { resolved, err := r.Ipfs.Name().Resolve(ctx, ipnsName) if err != nil { log.Warnf("ipns: namesys resolve error: %s", err) - return nil, fuse.ENOENT + return nil, syscall.Errno(syscall.ENOENT) } - if resolved.Namespace() != "ipfs" { + if resolved.Namespace() != path.IPFSNamespace { return nil, errors.New("invalid path from ipns record") } @@ -212,14 +213,14 @@ func (r *Root) Forget() { } // ReadDirAll reads a particular directory. Will show locally available keys -// as well as a symlink to the peerID key +// as well as a symlink to the peerID key. func (r *Root) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) { log.Debug("Root ReadDirAll") listing := make([]fuse.Dirent, 0, len(r.Keys)*2) for alias, k := range r.Keys { ent := fuse.Dirent{ - Name: k.ID().Pretty(), + Name: k.ID().String(), Type: fuse.DT_Dir, } link := fuse.Dirent{ @@ -231,7 +232,7 @@ func (r *Root) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) { return listing, nil } -// Directory is wrapper over an mfs directory to satisfy the fuse fs interface +// Directory is wrapper over an mfs directory to satisfy the fuse fs interface. type Directory struct { dir *mfs.Directory } @@ -240,7 +241,7 @@ type FileNode struct { fi *mfs.File } -// File is wrapper over an mfs file to satisfy the fuse fs interface +// File is wrapper over an mfs file to satisfy the fuse fs interface. type File struct { fi mfs.FileDescriptor } @@ -248,7 +249,7 @@ type File struct { // Attr returns the attributes of a given node. func (d *Directory) Attr(ctx context.Context, a *fuse.Attr) error { log.Debug("Directory Attr") - a.Mode = os.ModeDir | 0555 + a.Mode = os.ModeDir | 0o555 a.Uid = uint32(os.Getuid()) a.Gid = uint32(os.Getgid()) return nil @@ -262,7 +263,7 @@ func (fi *FileNode) Attr(ctx context.Context, a *fuse.Attr) error { // In this case, the dag node in question may not be unixfs return fmt.Errorf("fuse/ipns: failed to get file.Size(): %s", err) } - a.Mode = os.FileMode(0666) + a.Mode = os.FileMode(0o666) a.Size = uint64(size) a.Uid = uint32(os.Getuid()) a.Gid = uint32(os.Getgid()) @@ -274,7 +275,7 @@ func (d *Directory) Lookup(ctx context.Context, name string) (fs.Node, error) { child, err := d.dir.Child(name) if err != nil { // todo: make this error more versatile. - return nil, fuse.ENOENT + return nil, syscall.Errno(syscall.ENOENT) } switch child := child.(type) { @@ -289,7 +290,7 @@ func (d *Directory) Lookup(ctx context.Context, name string) (fs.Node, error) { } } -// ReadDirAll reads the link structure as directory entries +// ReadDirAll reads the link structure as directory entries. func (d *Directory) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) { listing, err := d.dir.List(ctx) if err != nil { @@ -312,7 +313,7 @@ func (d *Directory) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) { if len(entries) > 0 { return entries, nil } - return nil, fuse.ENOENT + return nil, syscall.Errno(syscall.ENOENT) } func (fi *File) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error { @@ -423,7 +424,7 @@ func (fi *FileNode) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse. if req.Flags&fuse.OpenTruncate != 0 { if req.Flags.IsReadOnly() { log.Error("tried to open a readonly file with truncate") - return nil, fuse.ENOTSUP + return nil, syscall.Errno(syscall.ENOTSUP) } log.Info("Need to truncate file!") err := fd.Truncate(0) @@ -434,7 +435,7 @@ func (fi *FileNode) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse. log.Info("Need to append to file!") if req.Flags.IsReadOnly() { log.Error("tried to open a readonly file with append") - return nil, fuse.ENOTSUP + return nil, syscall.Errno(syscall.ENOTSUP) } _, err := fd.Seek(0, io.SeekEnd) @@ -486,12 +487,12 @@ func (d *Directory) Create(ctx context.Context, req *fuse.CreateRequest, resp *f func (d *Directory) Remove(ctx context.Context, req *fuse.RemoveRequest) error { err := d.dir.Unlink(req.Name) if err != nil { - return fuse.ENOENT + return syscall.Errno(syscall.ENOENT) } return nil } -// Rename implements NodeRenamer +// Rename implements NodeRenamer. func (d *Directory) Rename(ctx context.Context, req *fuse.RenameRequest, newDir fs.Node) error { cur, err := d.dir.Child(req.OldName) if err != nil { @@ -516,7 +517,7 @@ func (d *Directory) Rename(ctx context.Context, req *fuse.RenameRequest, newDir } case *FileNode: log.Error("Cannot move node into a file!") - return fuse.EPERM + return syscall.Errno(syscall.EPERM) default: log.Error("Unknown node type for rename target dir!") return errors.New("unknown fs node type") @@ -531,7 +532,7 @@ func min(a, b int) int { return b } -// to check that out Node implements all the interfaces we want +// to check that out Node implements all the interfaces we want. type ipnsRoot interface { fs.Node fs.HandleReadDirAller @@ -565,5 +566,7 @@ type ipnsFileNode interface { fs.NodeOpener } -var _ ipnsFileNode = (*FileNode)(nil) -var _ ipnsFile = (*File)(nil) +var ( + _ ipnsFileNode = (*FileNode)(nil) + _ ipnsFile = (*File)(nil) +) diff --git a/fuse/ipns/link_unix.go b/fuse/ipns/link_unix.go index 355787b3c..da810c8f9 100644 --- a/fuse/ipns/link_unix.go +++ b/fuse/ipns/link_unix.go @@ -17,7 +17,7 @@ type Link struct { func (l *Link) Attr(ctx context.Context, a *fuse.Attr) error { log.Debug("Link attr.") - a.Mode = os.ModeSymlink | 0555 + a.Mode = os.ModeSymlink | 0o555 return nil } diff --git a/fuse/mount/fuse.go b/fuse/mount/fuse.go index b1060e6fd..02d733b89 100644 --- a/fuse/mount/fuse.go +++ b/fuse/mount/fuse.go @@ -16,7 +16,7 @@ import ( var ErrNotMounted = errors.New("not mounted") -// mount implements go-ipfs/fuse/mount +// mount implements go-ipfs/fuse/mount. type mount struct { mpoint string filesys fs.FS @@ -34,7 +34,7 @@ func NewMount(p goprocess.Process, fsys fs.FS, mountpoint string, allowOther boo var conn *fuse.Conn var err error - var mountOpts = []fuse.MountOption{ + mountOpts := []fuse.MountOption{ fuse.MaxReadahead(64 * 1024 * 1024), fuse.AsyncRead(), } @@ -102,7 +102,7 @@ func (m *mount) mount() error { return nil } -// umount is called exactly once to unmount this service. +// unmount is called exactly once to unmount this service. // note that closing the connection will not always unmount // properly. If that happens, we bring out the big guns // (mount.ForceUnmountManyTimes, exec unmount). diff --git a/fuse/mount/mount.go b/fuse/mount/mount.go index 52784a16d..a52374dd8 100644 --- a/fuse/mount/mount.go +++ b/fuse/mount/mount.go @@ -16,7 +16,7 @@ var log = logging.Logger("mount") var MountTimeout = time.Second * 5 -// Mount represents a filesystem mount +// Mount represents a filesystem mount. type Mount interface { // MountPoint is the path at which this mount is mounted MountPoint() string @@ -65,7 +65,7 @@ func ForceUnmount(m Mount) error { } // UnmountCmd creates an exec.Cmd that is GOOS-specific -// for unmount a FUSE mount +// for unmount a FUSE mount. func UnmountCmd(point string) (*exec.Cmd, error) { switch runtime.GOOS { case "darwin": diff --git a/fuse/node/mount_darwin.go b/fuse/node/mount_darwin.go index 73fa86772..4d2446ecd 100644 --- a/fuse/node/mount_darwin.go +++ b/fuse/node/mount_darwin.go @@ -25,7 +25,7 @@ func init() { // skip fuse checks. const dontCheckOSXFUSEConfigKey = "DontCheckOSXFUSE" -// fuseVersionPkg is the go pkg url for fuse-version +// fuseVersionPkg is the go pkg url for fuse-version. const fuseVersionPkg = "github.com/jbenet/go-fuse-version/fuse-version" // errStrFuseRequired is returned when we're sure the user does not have fuse. @@ -141,9 +141,8 @@ func darwinFuseCheckVersion(node *core.IpfsNode) error { return err } else if skip { return nil // user told us not to check version... ok.... - } else { - return errGFV } + return errGFV } log.Debug("mount: osxfuse version:", ov) diff --git a/fuse/node/mount_test.go b/fuse/node/mount_test.go index 12313ae3e..178fddcf6 100644 --- a/fuse/node/mount_test.go +++ b/fuse/node/mount_test.go @@ -4,6 +4,7 @@ package node import ( + "context" "os" "strings" "testing" @@ -11,8 +12,6 @@ import ( "bazil.org/fuse" - "context" - core "github.com/ipfs/kubo/core" ipns "github.com/ipfs/kubo/fuse/ipns" mount "github.com/ipfs/kubo/fuse/mount" @@ -33,7 +32,7 @@ func mkdir(t *testing.T, path string) { } } -// Test externally unmounting, then trying to unmount in code +// Test externally unmounting, then trying to unmount in code. func TestExternalUnmount(t *testing.T) { if testing.Short() { t.SkipNow() @@ -52,11 +51,8 @@ func TestExternalUnmount(t *testing.T) { t.Fatal(err) } - // get the test dir paths (/tmp/fusetestXXXX) - dir, err := os.MkdirTemp("", "fusetest") - if err != nil { - t.Fatal(err) - } + // get the test dir paths (/tmp/TestExternalUnmount) + dir := t.TempDir() ipfsDir := dir + "/ipfs" ipnsDir := dir + "/ipns" diff --git a/fuse/node/mount_unix.go b/fuse/node/mount_unix.go index 84795b13d..1e509a243 100644 --- a/fuse/node/mount_unix.go +++ b/fuse/node/mount_unix.go @@ -19,14 +19,14 @@ import ( var log = logging.Logger("node") -// fuseNoDirectory used to check the returning fuse error +// fuseNoDirectory used to check the returning fuse error. const fuseNoDirectory = "fusermount: failed to access mountpoint" -// fuseExitStatus1 used to check the returning fuse error +// fuseExitStatus1 used to check the returning fuse error. const fuseExitStatus1 = "fusermount: exit status 1" // platformFuseChecks can get overridden by arch-specific files -// to run fuse checks (like checking the OSXFUSE version) +// to run fuse checks (like checking the OSXFUSE version). var platformFuseChecks = func(*core.IpfsNode) error { return nil } diff --git a/fuse/readonly/ipfs_test.go b/fuse/readonly/ipfs_test.go index 7e79b9cf6..6d667843c 100644 --- a/fuse/readonly/ipfs_test.go +++ b/fuse/readonly/ipfs_test.go @@ -11,7 +11,7 @@ import ( "io" "math/rand" "os" - "path" + gopath "path" "strings" "sync" "testing" @@ -23,14 +23,14 @@ import ( coremock "github.com/ipfs/kubo/core/mock" fstest "bazil.org/fuse/fs/fstestutil" - chunker "github.com/ipfs/go-ipfs-chunker" - files "github.com/ipfs/go-ipfs-files" - u "github.com/ipfs/go-ipfs-util" + chunker "github.com/ipfs/boxo/chunker" + "github.com/ipfs/boxo/files" + dag "github.com/ipfs/boxo/ipld/merkledag" + importer "github.com/ipfs/boxo/ipld/unixfs/importer" + uio "github.com/ipfs/boxo/ipld/unixfs/io" + "github.com/ipfs/boxo/path" ipld "github.com/ipfs/go-ipld-format" - dag "github.com/ipfs/go-merkledag" - importer "github.com/ipfs/go-unixfs/importer" - uio "github.com/ipfs/go-unixfs/io" - ipath "github.com/ipfs/interface-go-ipfs-core/path" + "github.com/ipfs/go-test/random" ci "github.com/libp2p/go-libp2p-testing/ci" ) @@ -42,7 +42,7 @@ func maybeSkipFuseTests(t *testing.T) { func randObj(t *testing.T, nd *core.IpfsNode, size int64) (ipld.Node, []byte) { buf := make([]byte, size) - _, err := io.ReadFull(u.NewTimeSeededRand(), buf) + _, err := io.ReadFull(random.NewRand(), buf) if err != nil { t.Fatal(err) } @@ -79,7 +79,7 @@ func setupIpfsTest(t *testing.T, node *core.IpfsNode) (*core.IpfsNode, *fstest.M return node, mnt } -// Test writing an object and reading it back through fuse +// Test writing an object and reading it back through fuse. func TestIpfsBasicRead(t *testing.T) { if testing.Short() { t.SkipNow() @@ -89,7 +89,7 @@ func TestIpfsBasicRead(t *testing.T) { fi, data := randObj(t, nd, 10000) k := fi.Cid() - fname := path.Join(mnt.Dir, k.String()) + fname := gopath.Join(mnt.Dir, k.String()) rbuf, err := os.ReadFile(fname) if err != nil { t.Fatal(err) @@ -116,13 +116,13 @@ func getPaths(t *testing.T, ipfs *core.IpfsNode, name string, n *dag.ProtoNode) t.Fatal(dag.ErrNotProtobuf) } - sub := getPaths(t, ipfs, path.Join(name, lnk.Name), childpb) + sub := getPaths(t, ipfs, gopath.Join(name, lnk.Name), childpb) out = append(out, sub...) } return out } -// Perform a large number of concurrent reads to stress the system +// Perform a large number of concurrent reads to stress the system. func TestIpfsStressRead(t *testing.T) { if testing.Short() { t.SkipNow() @@ -184,18 +184,22 @@ func TestIpfsStressRead(t *testing.T) { defer wg.Done() for i := 0; i < 2000; i++ { - item := ipath.New(paths[rand.Intn(len(paths))]) + item, err := path.NewPath(paths[rand.Intn(len(paths))]) + if err != nil { + errs <- err + continue + } relpath := strings.Replace(item.String(), item.Namespace(), "", 1) - fname := path.Join(mnt.Dir, relpath) + fname := gopath.Join(mnt.Dir, relpath) rbuf, err := os.ReadFile(fname) if err != nil { errs <- err } - //nd.Context() is never closed which leads to - //hitting 8128 goroutine limit in go test -race mode + // nd.Context() is never closed which leads to + // hitting 8128 goroutine limit in go test -race mode ctx, cancelFunc := context.WithCancel(context.Background()) read, err := api.Unixfs().Get(ctx, item) @@ -229,7 +233,7 @@ func TestIpfsStressRead(t *testing.T) { } } -// Test writing a file and reading it back +// Test writing a file and reading it back. func TestIpfsBasicDirRead(t *testing.T) { if testing.Short() { t.SkipNow() @@ -257,8 +261,8 @@ func TestIpfsBasicDirRead(t *testing.T) { t.Fatal(err) } - dirname := path.Join(mnt.Dir, d1nd.Cid().String()) - fname := path.Join(dirname, "actual") + dirname := gopath.Join(mnt.Dir, d1nd.Cid().String()) + fname := gopath.Join(dirname, "actual") rbuf, err := os.ReadFile(fname) if err != nil { t.Fatal(err) @@ -280,7 +284,7 @@ func TestIpfsBasicDirRead(t *testing.T) { } } -// Test to make sure the filesystem reports file sizes correctly +// Test to make sure the filesystem reports file sizes correctly. func TestFileSizeReporting(t *testing.T) { if testing.Short() { t.SkipNow() @@ -291,7 +295,7 @@ func TestFileSizeReporting(t *testing.T) { fi, data := randObj(t, nd, 10000) k := fi.Cid() - fname := path.Join(mnt.Dir, k.String()) + fname := gopath.Join(mnt.Dir, k.String()) finfo, err := os.Stat(fname) if err != nil { diff --git a/fuse/readonly/readonly_unix.go b/fuse/readonly/readonly_unix.go index 39ca972e5..32be8b123 100644 --- a/fuse/readonly/readonly_unix.go +++ b/fuse/readonly/readonly_unix.go @@ -13,14 +13,13 @@ import ( fuse "bazil.org/fuse" fs "bazil.org/fuse/fs" + mdag "github.com/ipfs/boxo/ipld/merkledag" + ft "github.com/ipfs/boxo/ipld/unixfs" + uio "github.com/ipfs/boxo/ipld/unixfs/io" + "github.com/ipfs/boxo/path" "github.com/ipfs/go-cid" ipld "github.com/ipfs/go-ipld-format" logging "github.com/ipfs/go-log" - mdag "github.com/ipfs/go-merkledag" - path "github.com/ipfs/go-path" - "github.com/ipfs/go-path/resolver" - ft "github.com/ipfs/go-unixfs" - uio "github.com/ipfs/go-unixfs/io" core "github.com/ipfs/kubo/core" ipldprime "github.com/ipld/go-ipld-prime" cidlink "github.com/ipld/go-ipld-prime/linking/cid" @@ -50,7 +49,7 @@ type Root struct { // Attr returns file attributes. func (*Root) Attr(ctx context.Context, a *fuse.Attr) error { - a.Mode = os.ModeDir | 0111 // -rw+x + a.Mode = os.ModeDir | 0o111 // -rw+x return nil } @@ -60,32 +59,38 @@ func (s *Root) Lookup(ctx context.Context, name string) (fs.Node, error) { switch name { case "mach_kernel", ".hidden", "._.": // Just quiet some log noise on OS X. - return nil, fuse.ENOENT + return nil, syscall.Errno(syscall.ENOENT) } - p, err := path.ParsePath(name) + p, err := path.NewPath("/ipfs/" + name) if err != nil { log.Debugf("fuse failed to parse path: %q: %s", name, err) - return nil, fuse.ENOENT + return nil, syscall.Errno(syscall.ENOENT) } - nd, ndLnk, err := resolver.NewBasicResolver(s.Ipfs.UnixFSFetcherFactory).ResolvePath(ctx, p) + imPath, err := path.NewImmutablePath(p) + if err != nil { + log.Debugf("fuse failed to convert path: %q: %s", name, err) + return nil, syscall.Errno(syscall.ENOENT) + } + + nd, ndLnk, err := s.Ipfs.UnixFSPathResolver.ResolvePath(ctx, imPath) if err != nil { // todo: make this error more versatile. - return nil, fuse.ENOENT + return nil, syscall.Errno(syscall.ENOENT) } cidLnk, ok := ndLnk.(cidlink.Link) if !ok { log.Debugf("non-cidlink returned from ResolvePath: %v", ndLnk) - return nil, fuse.ENOENT + return nil, syscall.Errno(syscall.ENOENT) } // convert ipld-prime node to universal node blk, err := s.Ipfs.Blockstore.Get(ctx, cidLnk.Cid) if err != nil { log.Debugf("fuse failed to retrieve block: %v: %s", cidLnk, err) - return nil, fuse.ENOENT + return nil, syscall.Errno(syscall.ENOENT) } var fnd ipld.Node @@ -102,11 +107,11 @@ func (s *Root) Lookup(ctx context.Context, name string) (fs.Node, error) { fnd, err = mdag.RawNodeConverter(blk, nd) default: log.Error("fuse node was not a supported type") - return nil, fuse.ENOTSUP + return nil, syscall.Errno(syscall.ENOTSUP) } if err != nil { - log.Error("could not convert protobuf or raw node") - return nil, fuse.ENOENT + log.Errorf("could not convert protobuf or raw node: %s", err) + return nil, syscall.Errno(syscall.ENOENT) } return &Node{Ipfs: s.Ipfs, Nd: fnd}, nil @@ -115,7 +120,7 @@ func (s *Root) Lookup(ctx context.Context, name string) (fs.Node, error) { // ReadDirAll reads a particular directory. Disallowed for root. func (*Root) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) { log.Debug("read Root") - return nil, fuse.EPERM + return nil, syscall.Errno(syscall.EPERM) } // Node is the core object representing a filesystem tree node. @@ -140,7 +145,7 @@ func (s *Node) loadData() error { func (s *Node) Attr(ctx context.Context, a *fuse.Attr) error { log.Debug("Node attr") if rawnd, ok := s.Nd.(*mdag.RawNode); ok { - a.Mode = 0444 + a.Mode = 0o444 a.Size = uint64(len(rawnd.RawData())) a.Blocks = 1 return nil @@ -153,18 +158,18 @@ func (s *Node) Attr(ctx context.Context, a *fuse.Attr) error { } switch s.cached.Type() { case ft.TDirectory, ft.THAMTShard: - a.Mode = os.ModeDir | 0555 + a.Mode = os.ModeDir | 0o555 case ft.TFile: size := s.cached.FileSize() - a.Mode = 0444 + a.Mode = 0o444 a.Size = uint64(size) a.Blocks = uint64(len(s.Nd.Links())) case ft.TRaw: - a.Mode = 0444 + a.Mode = 0o444 a.Size = uint64(len(s.cached.Data())) a.Blocks = uint64(len(s.Nd.Links())) case ft.TSymlink: - a.Mode = 0777 | os.ModeSymlink + a.Mode = 0o777 | os.ModeSymlink a.Size = uint64(len(s.cached.Data())) default: return fmt.Errorf("invalid data type - %s", s.cached.Type()) @@ -179,12 +184,12 @@ func (s *Node) Lookup(ctx context.Context, name string) (fs.Node, error) { switch err { case os.ErrNotExist, mdag.ErrLinkNotFound: // todo: make this error more versatile. - return nil, fuse.ENOENT + return nil, syscall.Errno(syscall.ENOENT) case nil: // noop default: log.Errorf("fuse lookup %q: %s", name, err) - return nil, fuse.EIO + return nil, syscall.Errno(syscall.EIO) } nd, err := s.Ipfs.DAG.Get(ctx, link.Cid) @@ -196,7 +201,7 @@ func (s *Node) Lookup(ctx context.Context, name string) (fs.Node, error) { return &Node{Ipfs: s.Ipfs, Nd: nd}, nil } -// ReadDirAll reads the link structure as directory entries +// ReadDirAll reads the link structure as directory entries. func (s *Node) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) { log.Debug("Node ReadDir") dir, err := uio.NewDirectoryFromNode(s.Ipfs.DAG, s.Nd) @@ -247,7 +252,7 @@ func (s *Node) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) { if len(entries) > 0 { return entries, nil } - return nil, fuse.ENOENT + return nil, syscall.Errno(syscall.ENOENT) } func (s *Node) Getxattr(ctx context.Context, req *fuse.GetxattrRequest, resp *fuse.GetxattrResponse) error { @@ -285,7 +290,7 @@ func (s *Node) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadR return nil // may be non-nil / not succeeded } -// to check that out Node implements all the interfaces we want +// to check that our Node implements all the interfaces we want. type roRoot interface { fs.Node fs.HandleReadDirAller diff --git a/gc/gc.go b/gc/gc.go index 851b42d3b..51df59e54 100644 --- a/gc/gc.go +++ b/gc/gc.go @@ -7,16 +7,16 @@ import ( "fmt" "strings" - bserv "github.com/ipfs/go-blockservice" + bserv "github.com/ipfs/boxo/blockservice" + bstore "github.com/ipfs/boxo/blockstore" + offline "github.com/ipfs/boxo/exchange/offline" + dag "github.com/ipfs/boxo/ipld/merkledag" + pin "github.com/ipfs/boxo/pinning/pinner" + "github.com/ipfs/boxo/verifcid" cid "github.com/ipfs/go-cid" dstore "github.com/ipfs/go-datastore" - bstore "github.com/ipfs/go-ipfs-blockstore" - offline "github.com/ipfs/go-ipfs-exchange-offline" - pin "github.com/ipfs/go-ipfs-pinner" ipld "github.com/ipfs/go-ipld-format" logging "github.com/ipfs/go-log" - dag "github.com/ipfs/go-merkledag" - "github.com/ipfs/go-verifcid" ) var log = logging.Logger("gc") @@ -154,9 +154,9 @@ func GC(ctx context.Context, bs bstore.GCBlockstore, dstor dstore.Datastore, pn // Descendants recursively finds all the descendants of the given roots and // adds them to the given cid.Set, using the provided dag.GetLinks function // to walk the tree. -func Descendants(ctx context.Context, getLinks dag.GetLinks, set *cid.Set, roots []cid.Cid) error { +func Descendants(ctx context.Context, getLinks dag.GetLinks, set *cid.Set, roots <-chan pin.StreamedPin) error { verifyGetLinks := func(ctx context.Context, c cid.Cid) ([]*ipld.Link, error) { - err := verifcid.ValidateCid(c) + err := verifcid.ValidateCid(verifcid.DefaultAllowlist, c) if err != nil { return nil, err } @@ -167,7 +167,7 @@ func Descendants(ctx context.Context, getLinks dag.GetLinks, set *cid.Set, roots verboseCidError := func(err error) error { if strings.Contains(err.Error(), verifcid.ErrBelowMinimumHashLength.Error()) || strings.Contains(err.Error(), verifcid.ErrPossiblyInsecureHashFunction.Error()) { - err = fmt.Errorf("\"%s\"\nPlease run 'ipfs pin verify'"+ //nolint + err = fmt.Errorf("\"%s\"\nPlease run 'ipfs pin verify'"+ // nolint " to list insecure hashes. If you want to read them,"+ " please downgrade your go-ipfs to 0.4.13\n", err) log.Error(err) @@ -175,19 +175,28 @@ func Descendants(ctx context.Context, getLinks dag.GetLinks, set *cid.Set, roots return err } - for _, c := range roots { - // Walk recursively walks the dag and adds the keys to the given set - err := dag.Walk(ctx, verifyGetLinks, c, func(k cid.Cid) bool { - return set.Visit(toCidV1(k)) - }, dag.Concurrent()) + for { + select { + case <-ctx.Done(): + return ctx.Err() + case wrapper, ok := <-roots: + if !ok { + return nil + } + if wrapper.Err != nil { + return wrapper.Err + } - if err != nil { - err = verboseCidError(err) - return err + // Walk recursively walks the dag and adds the keys to the given set + err := dag.Walk(ctx, verifyGetLinks, wrapper.Pin.Key, func(k cid.Cid) bool { + return set.Visit(toCidV1(k)) + }, dag.Concurrent()) + if err != nil { + err = verboseCidError(err) + return err + } } } - - return nil } // toCidV1 converts any CIDv0s to CIDv1s. @@ -217,11 +226,8 @@ func ColoredSet(ctx context.Context, pn pin.Pinner, ng ipld.NodeGetter, bestEffo } return links, nil } - rkeys, err := pn.RecursiveKeys(ctx) - if err != nil { - return nil, err - } - err = Descendants(ctx, getLinks, gcs, rkeys) + rkeys := pn.RecursiveKeys(ctx, false) + err := Descendants(ctx, getLinks, gcs, rkeys) if err != nil { errors = true select { @@ -243,7 +249,18 @@ func ColoredSet(ctx context.Context, pn pin.Pinner, ng ipld.NodeGetter, bestEffo } return links, nil } - err = Descendants(ctx, bestEffortGetLinks, gcs, bestEffortRoots) + bestEffortRootsChan := make(chan pin.StreamedPin) + go func() { + defer close(bestEffortRootsChan) + for _, root := range bestEffortRoots { + select { + case <-ctx.Done(): + return + case bestEffortRootsChan <- pin.StreamedPin{Pin: pin.Pinned{Key: root}}: + } + } + }() + err = Descendants(ctx, bestEffortGetLinks, gcs, bestEffortRootsChan) if err != nil { errors = true select { @@ -253,18 +270,15 @@ func ColoredSet(ctx context.Context, pn pin.Pinner, ng ipld.NodeGetter, bestEffo } } - dkeys, err := pn.DirectKeys(ctx) - if err != nil { - return nil, err - } - for _, k := range dkeys { - gcs.Add(toCidV1(k)) + dkeys := pn.DirectKeys(ctx, false) + for k := range dkeys { + if k.Err != nil { + return nil, k.Err + } + gcs.Add(toCidV1(k.Pin.Key)) } - ikeys, err := pn.InternalPins(ctx) - if err != nil { - return nil, err - } + ikeys := pn.InternalPins(ctx, false) err = Descendants(ctx, getLinks, gcs, ikeys) if err != nil { errors = true diff --git a/gc/gc_test.go b/gc/gc_test.go new file mode 100644 index 000000000..c5d00714d --- /dev/null +++ b/gc/gc_test.go @@ -0,0 +1,96 @@ +package gc + +import ( + "context" + "testing" + + "github.com/ipfs/boxo/blockservice" + "github.com/ipfs/boxo/blockstore" + "github.com/ipfs/boxo/exchange/offline" + "github.com/ipfs/boxo/ipld/merkledag" + mdutils "github.com/ipfs/boxo/ipld/merkledag/test" + pin "github.com/ipfs/boxo/pinning/pinner" + "github.com/ipfs/boxo/pinning/pinner/dspinner" + "github.com/ipfs/go-cid" + "github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-datastore/sync" + "github.com/multiformats/go-multihash" + "github.com/stretchr/testify/require" +) + +func TestGC(t *testing.T) { + ctx := context.Background() + + ds := dssync.MutexWrap(datastore.NewMapDatastore()) + bs := blockstore.NewGCBlockstore(blockstore.NewBlockstore(ds), blockstore.NewGCLocker()) + bserv := blockservice.New(bs, offline.Exchange(bs)) + dserv := merkledag.NewDAGService(bserv) + pinner, err := dspinner.New(ctx, ds, dserv) + require.NoError(t, err) + + daggen := mdutils.NewDAGGenerator() + + var expectedKept []multihash.Multihash + var expectedDiscarded []multihash.Multihash + + // add some pins + for i := 0; i < 5; i++ { + // direct + root, _, err := daggen.MakeDagNode(dserv.Add, 0, 1) + require.NoError(t, err) + err = pinner.PinWithMode(ctx, root, pin.Direct, "") + require.NoError(t, err) + expectedKept = append(expectedKept, root.Hash()) + + // recursive + root, allCids, err := daggen.MakeDagNode(dserv.Add, 5, 2) + require.NoError(t, err) + err = pinner.PinWithMode(ctx, root, pin.Recursive, "") + require.NoError(t, err) + expectedKept = append(expectedKept, toMHs(allCids)...) + } + + err = pinner.Flush(ctx) + require.NoError(t, err) + + // add more dags to be GCed + for i := 0; i < 5; i++ { + _, allCids, err := daggen.MakeDagNode(dserv.Add, 5, 2) + require.NoError(t, err) + expectedDiscarded = append(expectedDiscarded, toMHs(allCids)...) + } + + // and some other as "best effort roots" + var bestEffortRoots []cid.Cid + for i := 0; i < 5; i++ { + root, allCids, err := daggen.MakeDagNode(dserv.Add, 5, 2) + require.NoError(t, err) + bestEffortRoots = append(bestEffortRoots, root) + expectedKept = append(expectedKept, toMHs(allCids)...) + } + + ch := GC(ctx, bs, ds, pinner, bestEffortRoots) + var discarded []multihash.Multihash + for res := range ch { + require.NoError(t, res.Error) + discarded = append(discarded, res.KeyRemoved.Hash()) + } + + allKeys, err := bs.AllKeysChan(ctx) + require.NoError(t, err) + var kept []multihash.Multihash + for key := range allKeys { + kept = append(kept, key.Hash()) + } + + require.ElementsMatch(t, expectedDiscarded, discarded) + require.ElementsMatch(t, expectedKept, kept) +} + +func toMHs(cids []cid.Cid) []multihash.Multihash { + res := make([]multihash.Multihash, len(cids)) + for i, c := range cids { + res[i] = c.Hash() + } + return res +} diff --git a/go.mod b/go.mod index 39087e0fe..46e0fa9a5 100644 --- a/go.mod +++ b/go.mod @@ -1,253 +1,273 @@ module github.com/ipfs/kubo +go 1.23 + require ( bazil.org/fuse v0.0.0-20200117225306-7b5117fecadc - contrib.go.opencensus.io/exporter/prometheus v0.4.0 - github.com/benbjohnson/clock v1.3.0 + contrib.go.opencensus.io/exporter/prometheus v0.4.2 + github.com/benbjohnson/clock v1.3.5 github.com/blang/semver/v4 v4.0.0 - github.com/cenkalti/backoff/v4 v4.1.3 - github.com/ceramicnetwork/go-dag-jose v0.1.0 - github.com/cespare/xxhash v1.1.0 + github.com/caddyserver/certmagic v0.21.4 + github.com/cenkalti/backoff/v4 v4.3.0 + github.com/ceramicnetwork/go-dag-jose v0.1.1 github.com/cheggaaa/pb v1.0.29 + github.com/cockroachdb/pebble v1.1.2 github.com/coreos/go-systemd/v22 v22.5.0 - github.com/dustin/go-humanize v1.0.0 + github.com/dustin/go-humanize v1.0.1 github.com/elgris/jsondiff v0.0.0-20160530203242-765b5c24c302 github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 - github.com/fsnotify/fsnotify v1.6.0 - github.com/gabriel-vasile/mimetype v1.4.1 - github.com/google/uuid v1.3.0 + github.com/fsnotify/fsnotify v1.7.0 + github.com/google/uuid v1.6.0 github.com/hashicorp/go-multierror v1.1.1 - github.com/ipfs/go-bitswap v0.11.0 - github.com/ipfs/go-block-format v0.0.3 - github.com/ipfs/go-blockservice v0.5.0 - github.com/ipfs/go-cid v0.3.2 + github.com/hashicorp/go-version v1.7.0 + github.com/ipfs-shipyard/nopfs v0.0.12 + github.com/ipfs-shipyard/nopfs/ipfs v0.13.2-0.20231027223058-cde3b5ba964c + github.com/ipfs/boxo v0.24.3 + github.com/ipfs/go-block-format v0.2.0 + github.com/ipfs/go-cid v0.4.1 github.com/ipfs/go-cidutil v0.1.0 github.com/ipfs/go-datastore v0.6.0 - github.com/ipfs/go-delegated-routing v0.7.0 github.com/ipfs/go-detect-race v0.0.1 github.com/ipfs/go-ds-badger v0.3.0 github.com/ipfs/go-ds-flatfs v0.5.1 github.com/ipfs/go-ds-leveldb v0.5.0 github.com/ipfs/go-ds-measure v0.2.0 - github.com/ipfs/go-fetcher v1.6.1 - github.com/ipfs/go-filestore v1.2.0 + github.com/ipfs/go-ds-pebble v0.4.0 github.com/ipfs/go-fs-lock v0.0.7 - github.com/ipfs/go-graphsync v0.14.0 - github.com/ipfs/go-ipfs-blockstore v1.2.0 - github.com/ipfs/go-ipfs-chunker v0.0.5 - github.com/ipfs/go-ipfs-cmds v0.8.1 - github.com/ipfs/go-ipfs-exchange-interface v0.2.0 - github.com/ipfs/go-ipfs-exchange-offline v0.3.0 - github.com/ipfs/go-ipfs-files v0.2.0 - github.com/ipfs/go-ipfs-keystore v0.1.0 - github.com/ipfs/go-ipfs-pinner v0.2.1 - github.com/ipfs/go-ipfs-posinfo v0.0.1 - github.com/ipfs/go-ipfs-provider v0.8.1 - github.com/ipfs/go-ipfs-redirects-file v0.1.1 - github.com/ipfs/go-ipfs-routing v0.3.0 - github.com/ipfs/go-ipfs-util v0.0.2 - github.com/ipfs/go-ipld-format v0.4.0 + github.com/ipfs/go-ipfs-cmds v0.14.0 + github.com/ipfs/go-ipld-cbor v0.2.0 + github.com/ipfs/go-ipld-format v0.6.0 github.com/ipfs/go-ipld-git v0.1.1 - github.com/ipfs/go-ipld-legacy v0.1.1 - github.com/ipfs/go-ipns v0.3.0 - github.com/ipfs/go-libipfs v0.0.0-20221208220359-356ce09dd4a1 + github.com/ipfs/go-ipld-legacy v0.2.1 github.com/ipfs/go-log v1.0.5 github.com/ipfs/go-log/v2 v2.5.1 - github.com/ipfs/go-merkledag v0.8.1 github.com/ipfs/go-metrics-interface v0.0.1 - github.com/ipfs/go-metrics-prometheus v0.0.2 - github.com/ipfs/go-mfs v0.2.1 - github.com/ipfs/go-namesys v0.6.0 - github.com/ipfs/go-path v0.3.0 - github.com/ipfs/go-pinning-service-http-client v0.1.2 - github.com/ipfs/go-unixfs v0.4.1 - github.com/ipfs/go-unixfsnode v1.4.0 - github.com/ipfs/go-verifcid v0.0.2 - github.com/ipfs/interface-go-ipfs-core v0.8.1 - github.com/ipld/go-car v0.4.0 - github.com/ipld/go-car/v2 v2.4.0 - github.com/ipld/go-codec-dagpb v1.4.1 - github.com/ipld/go-ipld-prime v0.19.0 - github.com/jbenet/go-random v0.0.0-20190219211222-123a90aedc0c + github.com/ipfs/go-metrics-prometheus v0.0.3 + github.com/ipfs/go-test v0.0.4 + github.com/ipfs/go-unixfsnode v1.9.2 + github.com/ipld/go-car v0.6.2 + github.com/ipld/go-car/v2 v2.14.2 + github.com/ipld/go-codec-dagpb v1.6.0 + github.com/ipld/go-ipld-prime v0.21.0 + github.com/ipshipyard/p2p-forge v0.0.2 github.com/jbenet/go-temp-err-catcher v0.1.0 github.com/jbenet/goprocess v0.1.4 + github.com/julienschmidt/httprouter v1.3.0 github.com/libp2p/go-doh-resolver v0.4.0 - github.com/libp2p/go-libp2p v0.24.2 - github.com/libp2p/go-libp2p-http v0.4.0 - github.com/libp2p/go-libp2p-kad-dht v0.20.0 - github.com/libp2p/go-libp2p-kbucket v0.5.0 - github.com/libp2p/go-libp2p-pubsub v0.8.2 + github.com/libp2p/go-libp2p v0.37.0 + github.com/libp2p/go-libp2p-http v0.5.0 + github.com/libp2p/go-libp2p-kad-dht v0.27.0 + github.com/libp2p/go-libp2p-kbucket v0.6.4 + github.com/libp2p/go-libp2p-pubsub v0.12.0 github.com/libp2p/go-libp2p-pubsub-router v0.6.0 github.com/libp2p/go-libp2p-record v0.2.0 - github.com/libp2p/go-libp2p-routing-helpers v0.4.0 + github.com/libp2p/go-libp2p-routing-helpers v0.7.4 github.com/libp2p/go-libp2p-testing v0.12.0 github.com/libp2p/go-socket-activation v0.1.0 - github.com/miekg/dns v1.1.50 - github.com/mitchellh/go-homedir v1.1.0 - github.com/multiformats/go-multiaddr v0.8.0 - github.com/multiformats/go-multiaddr-dns v0.3.1 - github.com/multiformats/go-multibase v0.1.1 - github.com/multiformats/go-multicodec v0.7.0 - github.com/multiformats/go-multihash v0.2.1 + github.com/multiformats/go-multiaddr v0.13.0 + github.com/multiformats/go-multiaddr-dns v0.4.0 + github.com/multiformats/go-multibase v0.2.0 + github.com/multiformats/go-multicodec v0.9.0 + github.com/multiformats/go-multihash v0.2.3 github.com/opentracing/opentracing-go v1.2.0 github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 - github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.14.0 - github.com/stretchr/testify v1.8.1 - github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 + github.com/prometheus/client_golang v1.20.5 + github.com/stretchr/testify v1.9.0 + github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d + github.com/tidwall/gjson v1.16.0 + github.com/tidwall/sjson v1.2.5 github.com/whyrusleeping/go-sysinfo v0.0.0-20190219211824-4a357d4b90b1 github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 go.opencensus.io v0.24.0 - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.32.0 - go.opentelemetry.io/otel v1.7.0 - go.opentelemetry.io/otel/exporters/jaeger v1.7.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.7.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.7.0 - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.7.0 - go.opentelemetry.io/otel/exporters/zipkin v1.7.0 - go.opentelemetry.io/otel/sdk v1.7.0 - go.opentelemetry.io/otel/trace v1.7.0 - go.uber.org/dig v1.15.0 - go.uber.org/fx v1.18.2 - go.uber.org/zap v1.24.0 - golang.org/x/crypto v0.3.0 - golang.org/x/mod v0.7.0 - golang.org/x/sync v0.1.0 - golang.org/x/sys v0.3.0 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 + go.opentelemetry.io/contrib/propagators/autoprop v0.46.1 + go.opentelemetry.io/otel v1.31.0 + go.opentelemetry.io/otel/sdk v1.31.0 + go.opentelemetry.io/otel/trace v1.31.0 + go.uber.org/dig v1.18.0 + go.uber.org/fx v1.23.0 + go.uber.org/multierr v1.11.0 + go.uber.org/zap v1.27.0 + golang.org/x/crypto v0.28.0 + golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c + golang.org/x/mod v0.21.0 + golang.org/x/sync v0.8.0 + golang.org/x/sys v0.26.0 + google.golang.org/protobuf v1.35.1 ) require ( github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect - github.com/Kubuxu/go-os-helper v0.0.1 // indirect - github.com/Stebalien/go-bitfield v0.0.1 // indirect - github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a // indirect + github.com/DataDog/zstd v1.4.5 // indirect + github.com/Jorropo/jsync v1.0.1 // indirect + github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b // indirect github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cenkalti/backoff v2.2.1+incompatible // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/containerd/cgroups v1.0.4 // indirect - github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 // indirect + github.com/caddyserver/zerossl v0.1.3 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cockroachdb/errors v1.11.3 // indirect + github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect + github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect + github.com/cockroachdb/redact v1.1.5 // indirect + github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect + github.com/containerd/cgroups v1.1.0 // indirect + github.com/crackcomm/go-gitignore v0.0.0-20241020182519-7843d2ba8fdf // indirect github.com/cskr/pubsub v1.0.2 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // 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.2 // indirect + github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/docker/go-units v0.5.0 // indirect - github.com/elastic/gosigar v0.14.2 // indirect - github.com/felixge/httpsnoop v1.0.2 // indirect - github.com/flynn/noise v1.0.0 // indirect + github.com/elastic/gosigar v0.14.3 // indirect + github.com/fatih/color v1.15.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/go-kit/log v0.2.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.6 // indirect + github.com/getsentry/sentry-go v0.27.0 // indirect + github.com/go-jose/go-jose/v4 v4.0.4 // 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.2.3 // indirect + github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // 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.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/mock v1.6.0 // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/gopacket v1.1.19 // indirect - github.com/google/pprof v0.0.0-20221203041831-ce31453925ec // indirect - github.com/gorilla/mux v1.8.0 // indirect - github.com/gorilla/websocket v1.5.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect - github.com/hannahhoward/go-pubsub v0.0.0-20200423002714-8d62886cc36e // indirect + github.com/google/pprof v0.0.0-20241017200806-017d972448fc // indirect + github.com/gorilla/mux v1.8.1 // indirect + github.com/gorilla/websocket v1.5.3 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect - github.com/huin/goupnp v1.0.3 // indirect + github.com/hashicorp/golang-lru v1.0.2 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/huin/goupnp v1.3.0 // indirect github.com/ipfs/bbloom v0.0.4 // indirect - github.com/ipfs/go-bitfield v1.0.0 // indirect + github.com/ipfs/go-bitfield v1.1.0 // indirect + github.com/ipfs/go-blockservice v0.5.2 // indirect + github.com/ipfs/go-ipfs-blockstore v1.3.1 // indirect github.com/ipfs/go-ipfs-delay v0.0.1 // indirect - github.com/ipfs/go-ipfs-ds-help v1.1.0 // indirect - github.com/ipfs/go-ipfs-pq v0.0.2 // indirect - github.com/ipfs/go-ipld-cbor v0.0.5 // indirect - github.com/ipfs/go-peertaskqueue v0.8.0 // indirect - github.com/ipld/edelweiss v0.2.0 // 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.2 // indirect + github.com/ipfs/go-ipfs-util v0.0.3 // indirect + github.com/ipfs/go-merkledag v0.11.0 // indirect + github.com/ipfs/go-peertaskqueue v0.8.1 // indirect + github.com/ipfs/go-verifcid v0.0.3 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect - github.com/klauspost/compress v1.15.12 // indirect - github.com/klauspost/cpuid/v2 v2.2.1 // indirect - github.com/koron/go-ssdp v0.0.3 // indirect + github.com/klauspost/compress v1.17.11 // indirect + github.com/klauspost/cpuid/v2 v2.2.8 // indirect + github.com/koron/go-ssdp v0.0.4 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/libdns/libdns v0.2.2 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/libp2p/go-cidranger v1.1.0 // indirect - github.com/libp2p/go-flow-metrics v0.1.0 // indirect - github.com/libp2p/go-libp2p-asn-util v0.2.0 // indirect - github.com/libp2p/go-libp2p-gostream v0.5.0 // indirect + github.com/libp2p/go-flow-metrics v0.2.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-xor v0.1.0 // indirect - github.com/libp2p/go-mplex v0.7.0 // indirect - github.com/libp2p/go-msgio v0.2.0 // indirect - github.com/libp2p/go-nat 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-openssl v0.1.0 // indirect - github.com/libp2p/go-reuseport v0.2.0 // indirect - github.com/libp2p/go-yamux/v4 v4.0.0 // 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/lucas-clemente/quic-go v0.31.1 // indirect - github.com/marten-seemann/qpack v0.3.0 // indirect - github.com/marten-seemann/qtls-go1-18 v0.1.3 // indirect - github.com/marten-seemann/qtls-go1-19 v0.1.1 // indirect github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect - github.com/marten-seemann/webtransport-go v0.4.3 // indirect - github.com/mattn/go-colorable v0.1.4 // indirect - github.com/mattn/go-isatty v0.0.16 // indirect - github.com/mattn/go-pointer v0.0.1 // indirect - github.com/mattn/go-runewidth v0.0.4 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect + github.com/mholt/acmez/v2 v2.0.3 // indirect + github.com/miekg/dns v1.1.62 // 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.0 // indirect + github.com/minio/sha256-simd 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-multiaddr-fmt v0.1.0 // indirect - github.com/multiformats/go-multistream v0.3.3 // indirect + github.com/multiformats/go-multistream v0.5.0 // indirect github.com/multiformats/go-varint v0.0.7 // indirect - github.com/onsi/ginkgo/v2 v2.5.1 // indirect - github.com/opencontainers/runtime-spec v1.0.2 // indirect - github.com/openzipkin/zipkin-go v0.4.0 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e // indirect - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.37.0 // indirect - github.com/prometheus/procfs v0.8.0 // indirect - github.com/prometheus/statsd_exporter v0.21.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/onsi/ginkgo/v2 v2.20.2 // indirect + github.com/opencontainers/runtime-spec v1.2.0 // indirect + github.com/openzipkin/zipkin-go v0.4.3 // indirect + github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 // indirect + github.com/pion/datachannel v1.5.9 // indirect + github.com/pion/dtls/v2 v2.2.12 // indirect + github.com/pion/ice/v2 v2.3.36 // indirect + github.com/pion/interceptor v0.1.37 // 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.9 // indirect + github.com/pion/sctp v1.8.33 // indirect + github.com/pion/sdp/v3 v3.0.9 // indirect + github.com/pion/srtp/v2 v2.0.20 // indirect + github.com/pion/stun v0.6.1 // indirect + github.com/pion/transport/v2 v2.2.10 // indirect + github.com/pion/turn/v2 v2.1.6 // indirect + github.com/pion/webrtc/v3 v3.3.4 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/polydawn/refmt v0.89.0 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.60.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/prometheus/statsd_exporter v0.22.7 // indirect + github.com/quic-go/qpack v0.5.1 // indirect + github.com/quic-go/quic-go v0.48.1 // indirect + github.com/quic-go/webtransport-go v0.8.1-0.20241018022711-4ac2c9250e66 // indirect github.com/raulk/go-watchdog v1.3.0 // indirect - github.com/rs/cors v1.7.0 // indirect - github.com/samber/lo v1.36.0 // indirect - github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect + github.com/rivo/uniseg v0.4.4 // indirect + github.com/rogpeppe/go-internal v1.13.1 // indirect + github.com/rs/cors v1.11.1 // indirect + github.com/samber/lo v1.47.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect - github.com/texttheater/golang-levenshtein v0.0.0-20180516184445-d188e65d659e // indirect + github.com/texttheater/golang-levenshtein v1.0.1 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.1 // 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-gen v0.0.0-20200710004633-5379fc63235d // indirect + github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11 // indirect + github.com/whyrusleeping/cbor-gen v0.1.2 // 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/timecache v0.0.0-20160911033111-cfcb2f1abfee // indirect - go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.7.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.7.0 // indirect - go.opentelemetry.io/otel/metric v0.30.0 // indirect - go.opentelemetry.io/proto/otlp v0.16.0 // indirect - go.uber.org/atomic v1.10.0 // indirect - go.uber.org/multierr v1.8.0 // indirect - go4.org v0.0.0-20200411211856-f5505b9728dd // indirect - golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect - golang.org/x/net v0.3.0 // indirect - golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect - golang.org/x/term v0.3.0 // indirect - golang.org/x/text v0.5.0 // indirect - golang.org/x/tools v0.3.0 // indirect - golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect - google.golang.org/appengine v1.6.6 // indirect - google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1 // indirect - google.golang.org/grpc v1.46.0 // indirect - google.golang.org/protobuf v1.28.1 // indirect - gopkg.in/square/go-jose.v2 v2.5.1 // indirect + github.com/wlynxg/anet v0.0.5 // indirect + github.com/zeebo/blake3 v0.2.4 // indirect + go.opentelemetry.io/contrib/propagators/aws v1.21.1 // indirect + go.opentelemetry.io/contrib/propagators/b3 v1.21.1 // indirect + go.opentelemetry.io/contrib/propagators/jaeger v1.21.1 // indirect + go.opentelemetry.io/contrib/propagators/ot v1.21.1 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0 // indirect + go.opentelemetry.io/otel/exporters/zipkin v1.31.0 // indirect + go.opentelemetry.io/otel/metric v1.31.0 // indirect + go.opentelemetry.io/proto/otlp v1.3.1 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/mock v0.5.0 // indirect + go4.org v0.0.0-20230225012048-214862532bf5 // indirect + golang.org/x/net v0.30.0 // indirect + golang.org/x/oauth2 v0.23.0 // indirect + golang.org/x/term v0.25.0 // indirect + golang.org/x/text v0.19.0 // indirect + golang.org/x/tools v0.26.0 // indirect + golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect + gonum.org/v1/gonum v0.15.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 // indirect + google.golang.org/grpc v1.67.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - lukechampine.com/blake3 v1.1.7 // indirect + lukechampine.com/blake3 v1.3.0 // indirect ) - -go 1.18 diff --git a/go.sum b/go.sum index 061801f7e..b0a1e4cc0 100644 --- a/go.sum +++ b/go.sum @@ -34,261 +34,218 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo 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= -contrib.go.opencensus.io/exporter/prometheus v0.4.0 h1:0QfIkj9z/iVZgK31D9H9ohjjIDApI2GOPScCKwxedbs= -contrib.go.opencensus.io/exporter/prometheus v0.4.0/go.mod h1:o7cosnyfuPVK0tB8q0QmaQNhGnptITnPQB+z1+qeFB0= +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/gpu/mtl v0.0.0-20201218220906-28db891af037/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= 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/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/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/Kubuxu/go-os-helper v0.0.1 h1:EJiD2VUQyh5A9hWJLmc6iWg6yIcJ7jpBcwC8GMGXfDk= -github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y= -github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= +github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= +github.com/DataDog/zstd v1.4.5/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/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= -github.com/Shopify/sarama v1.30.0/go.mod h1:zujlQQx1kzHsh4jfV1USnptCQrHAEZ2Hk8fTKCulPVs= -github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/Shopify/toxiproxy/v2 v2.1.6-0.20210914104332-15ea381dcdae/go.mod h1:/cvHQkZ1fst0EmZnA5dFtiQdWCNCFYzb+uE2vqVgvx0= -github.com/Stebalien/go-bitfield v0.0.1 h1:X3kbSSPUaJK60wV2hjOPZwmpljr6VGCqdq4cBLhbQBo= -github.com/Stebalien/go-bitfield v0.0.1/go.mod h1:GNjFpasyUVkHMsfEOk8EFLJ9syQ6SI+XWrX9Wf2XH0s= -github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= -github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= 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-20210927113745-59d0afb8317a h1:E/8AP5dFtMhl5KPJz66Kt9G0n+7Sn41Fy1wv9/jHOrc= -github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b h1:mimo19zliBX/vSQ6PWWSL9lK8qwHozUj03+zLoEB8O0= +github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs= 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/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/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= -github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= -github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= 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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 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-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-20190605094302-a0d1e3e36d50/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/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= -github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94= 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/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= 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/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/snappy-go v1.0.0/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/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= -github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= -github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= -github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/caddyserver/certmagic v0.21.4 h1:e7VobB8rffHv8ZZpSiZtEwnLDHUwLVYLWzWSa1FfKI0= +github.com/caddyserver/certmagic v0.21.4/go.mod h1:swUXjQ1T9ZtMv95qj7/InJvWLXURU85r+CfG0T+ZbDE= +github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA= +github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4= +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/ceramicnetwork/go-dag-jose v0.1.1 h1:7pObs22egc14vSS3AfCFfS1VmaL4lQUsAK7OGC3PlKk= +github.com/ceramicnetwork/go-dag-jose v0.1.1/go.mod h1:8ptnYwY2Z2y/s5oJnNBn/UCxLg6CpramNJ2ZXF/5aNY= 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.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= +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/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= 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-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= +github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= +github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8= +github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4= +github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/pebble v1.1.2 h1:CUh2IPtR4swHlEj48Rhfzw6l/d0qA31fItcIszQVIsA= +github.com/cockroachdb/pebble v1.1.2/go.mod h1:4exszw1r40423ZsmkG/09AFEG83I0uDgfujJdbL6kYU= +github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= -github.com/containerd/cgroups v1.0.4 h1:jN/mbWBEaz+T1pi5OFtnkQ+8qnmEbAr1Oo1FRm5B0dA= -github.com/containerd/cgroups v1.0.4/go.mod h1:nLNQtsF7Sl2HxNebu77i1R0oDlhiTG+kO4JTrUzo6IA= +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-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= 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/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 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/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 h1:HVTnpeuvF6Owjd5mniCL8DEXo7uYXdQEmOP4FJbV5tg= -github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3/go.mod h1:p1d6YEZWvFzEh4KLyvBcVSnrfNDDvK2zfK/4x2v/4pE= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/crackcomm/go-gitignore v0.0.0-20241020182519-7843d2ba8fdf h1:dwGgBWn84wUS1pVikGiruW+x5XM4amhjaZO20vCjay4= +github.com/crackcomm/go-gitignore v0.0.0-20241020182519-7843d2ba8fdf/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/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/go.mod h1:rQYf4tfk5sSwFsnDg3qYaBxSjsD9S8+59vW0dKUgme4= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/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.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= -github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= -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/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.1/go.mod h1:FRmFw3uxvcpa8zG3Rxs0th+hCLIuaQg8HlNV5bjgnuU= 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/ristretto v0.0.2 h1:a5WaUrDa0qm0YrAAS1tUykT5El3kt62KNZZeMxQn3po= github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-farm v0.0.0-20190104051053-3adb47b1fb0f/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= +github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= 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 v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= -github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= -github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +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/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/elastic/gosigar v0.14.3 h1:xwkKwPia+hSfg9GqrCUKYdId102m9qTJIIr7egmK/uo= +github.com/elastic/gosigar v0.14.3/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/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= 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.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 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.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o= -github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +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.0.0 h1:DlTHqmzmvcEiKj+4RYo/imoswx/4r6iBlCMfVtrMXpQ= -github.com/flynn/noise v1.0.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= -github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +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/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= -github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= 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.2/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +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.1 h1:TRWk7se+TOjCYgRth7+1/OYLNiRNIotknkFtf/dnN7Q= -github.com/gabriel-vasile/mimetype v1.4.1/go.mod h1:05Vi0w3Y9c/lNvJOdmIwvrrAhX3rYhfQQCaf9VJcv7M= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc= +github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc= +github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= +github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= 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-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= 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-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E= +github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc= 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/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0 h1:7i2K3eKTos3Vc0enKCfnVcgHh2olr/MyfboYq7cAcFw= 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 v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/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-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +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/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.0/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.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/glog v1.2.2 h1:1+mZ9upx1Dh6FmUTFR1naJ77miKiXgALjWOZ3NVFPmY= +github.com/golang/glog v1.2.2/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-20191027212112-611e8accdfc9/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= @@ -301,10 +258,7 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt 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/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= 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= @@ -319,8 +273,9 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD 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 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 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.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -337,13 +292,12 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ 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.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +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.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= 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= @@ -355,15 +309,15 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf 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-20221203041831-ce31453925ec h1:fR20TYVVwhK4O7r7y+McjRYyaTH6/vjwJOajE+XhlzM= -github.com/google/pprof v0.0.0-20221203041831-ce31453925ec/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20241017200806-017d972448fc h1:NGyrhhFhwvRAZg02jnYVg3GBQy0qGBKmFQJwaPmpmxs= +github.com/google/pprof v0.0.0-20241017200806-017d972448fc/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/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= @@ -371,322 +325,176 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4= github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= -github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +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.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/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/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= -github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= 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/hannahhoward/go-pubsub v0.0.0-20200423002714-8d62886cc36e h1:3YKHER4nmd7b5qy5t0GWDTwSn4OyRgfAXSmo6VnryBY= -github.com/hannahhoward/go-pubsub v0.0.0-20200423002714-8d62886cc36e/go.mod h1:I8h3MITA53gN9OnWGCgaMa0JWVRdXthWw4M3CPM54OY= -github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= -github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= 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-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 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/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 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/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/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= -github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= -github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= -github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y= -github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= +github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= +github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= 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/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/ipfs/bbloom v0.0.1/go.mod h1:oqo8CVWsJFMOZqTglBG4wydCE4IQA/G2/SEofB0rjUI= +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/go-bitfield v1.0.0 h1:y/XHm2GEmD9wKngheWNNCNL0pzrWXZwCdQGv1ikXknQ= -github.com/ipfs/go-bitfield v1.0.0/go.mod h1:N/UiujQy+K+ceU1EF5EkVd1TNqevLrCQMIcAEPrdtus= -github.com/ipfs/go-bitswap v0.1.0/go.mod h1:FFJEf18E9izuCqUtHxbWEvq+reg7o4CW5wSAE1wsxj0= -github.com/ipfs/go-bitswap v0.1.2/go.mod h1:qxSWS4NXGs7jQ6zQvoPY3+NmOfHHG47mhkiLzBpJQIs= -github.com/ipfs/go-bitswap v0.5.1/go.mod h1:P+ckC87ri1xFLvk74NlXdP0Kj9RmWAh4+H78sC6Qopo= -github.com/ipfs/go-bitswap v0.6.0/go.mod h1:Hj3ZXdOC5wBJvENtdqsixmzzRukqd8EHLxZLZc3mzRA= +github.com/ipfs/boxo v0.24.3 h1:gldDPOWdM3Rz0v5LkVLtZu7A7gFNvAlWcmxhCqlHR3c= +github.com/ipfs/boxo v0.24.3/go.mod h1:h0DRzOY1IBFDHp6KNvrJLMFdSXTYID0Zf+q7X05JsNg= +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.1/go.mod h1:DK/YYcsSUIVAFNwo/KZCdIIbpN0ROH/baNLgayt4pFc= -github.com/ipfs/go-block-format v0.0.2/go.mod h1:AWR46JfpcObNfg3ok2JHDUfdiHRgWhJgCQF+KIgOPJY= -github.com/ipfs/go-block-format v0.0.3 h1:r8t66QstRp/pd/or4dpnbVfXT5Gt7lOqRvC+/dDTpMc= github.com/ipfs/go-block-format v0.0.3/go.mod h1:4LmD4ZUw0mhO+JSKdpWwrzATiEfM7WWgQ8H5l6P8MVk= -github.com/ipfs/go-blockservice v0.1.0/go.mod h1:hzmMScl1kXHg3M2BjTymbVPjv627N7sYcvYaKbop39M= -github.com/ipfs/go-blockservice v0.2.1/go.mod h1:k6SiwmgyYgs4M/qt+ww6amPeUH9EISLRBnvUurKJhi8= -github.com/ipfs/go-blockservice v0.3.0/go.mod h1:P5ppi8IHDC7O+pA0AlGTF09jruB2h+oP3wVVaZl8sfk= -github.com/ipfs/go-blockservice v0.5.0 h1:B2mwhhhVQl2ntW2EIpaWPwSCxSuqr5fFA93Ms4bYLEY= -github.com/ipfs/go-blockservice v0.5.0/go.mod h1:W6brZ5k20AehbmERplmERn8o2Ni3ZZubvAxaIUeaT6w= -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-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.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/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= -github.com/ipfs/go-cid v0.1.0/go.mod h1:rH5/Xv83Rfy8Rw6xG+id3DYAMUVmem1MowoKwdXmN2o= -github.com/ipfs/go-cid v0.3.2 h1:OGgOd+JCFM+y1DjWPmVH+2/4POtpDzwcr7VgnB7mZXc= -github.com/ipfs/go-cid v0.3.2/go.mod h1:gQ8pKqT/sUxGY+tIwy1RPpAojYu7jAyCp5Tz1svoupw= +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.0.1/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= -github.com/ipfs/go-datastore v0.0.5/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.3.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/go.mod h1:eXTcaaiN6uOlVCLS9GjJUJtlvJfM3xk23w3fyfrmmJs= github.com/ipfs/go-datastore v0.5.0/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-delegated-routing v0.7.0 h1:43FyMnKA+8XnyX68Fwg6aoGkqrf8NS5aG7p644s26PU= -github.com/ipfs/go-delegated-routing v0.7.0/go.mod h1:u4zxjUWIe7APUW5ds9CfD0tJX3vM9JhIeNqA8kE4vHE= 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-badger v0.3.0 h1:xREL3V0EH9S219kFFueOYJJTcjgNSZ2HY1iSvN7U1Ro= github.com/ipfs/go-ds-badger v0.3.0/go.mod h1:1ke6mXNqeV8K3y5Ak2bAA0osoTfmxUdupVCGm4QUIek= 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.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-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-fetcher v1.6.1 h1:UFuRVYX5AIllTiRhi5uK/iZkfhSpBCGX7L70nSZEmK8= -github.com/ipfs/go-fetcher v1.6.1/go.mod h1:27d/xMV8bodjVs9pugh/RCjjK2OZ68UgAMspMdingNo= -github.com/ipfs/go-filestore v1.2.0 h1:O2wg7wdibwxkEDcl7xkuQsPvJFRBVgVSsOJ/GP6z3yU= -github.com/ipfs/go-filestore v1.2.0/go.mod h1:HLJrCxRXquTeEEpde4lTLMaE/MYJZD7WHLkp9z6+FF8= +github.com/ipfs/go-ds-pebble v0.4.0 h1:88lgFAs2ck8jCQ8lMYRBtksEg18r9BlvTxIMnNJkZaQ= +github.com/ipfs/go-ds-pebble v0.4.0/go.mod h1:ZyYU+weIni+4NG/Yjva+cPkU3ghlsU1HA2R/VLHJ9sM= 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-graphsync v0.14.0 h1:f5KYkc8GpwwE1BrjBOWxIkRivXIw7fVqGZlnILpvbSc= -github.com/ipfs/go-graphsync v0.14.0/go.mod h1:1LDVVnNHjit8ddJOtw3Jq9epP792xWFXXL3dJWIBIkM= -github.com/ipfs/go-ipfs-blockstore v0.0.1/go.mod h1:d3WClOmRQKFnJ0Jz/jj/zmksX0ma1gROTlovZKBmN08= -github.com/ipfs/go-ipfs-blockstore v0.1.0/go.mod h1:5aD0AvHPi7mZc6Ci1WCAhiBQu2IsfTduLl+422H6Rqw= -github.com/ipfs/go-ipfs-blockstore v0.2.1/go.mod h1:jGesd8EtCM3/zPgx+qr0/feTXGUeRai6adgwC+Q+JvE= -github.com/ipfs/go-ipfs-blockstore v1.1.2/go.mod h1:w51tNR9y5+QXB0wkNcHt4O2aSZjTdqaEWaQdSxEyUOY= -github.com/ipfs/go-ipfs-blockstore v1.2.0 h1:n3WTeJ4LdICWs/0VSfjHrlqpPpl6MZ+ySd3j8qz0ykw= -github.com/ipfs/go-ipfs-blockstore v1.2.0/go.mod h1:eh8eTFLiINYNSNawfZOC7HOxNTxpB1PFuA5E1m/7exE= +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.1/go.mod h1:tWewYK0we3+rMbOh7pPFGDyypCtvGcBFymgY4rSDLAw= -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.8.1 h1:El661DBWqdqwgz7B9xwKyUpigwqk6BBBHb5B8DfJP00= -github.com/ipfs/go-ipfs-cmds v0.8.1/go.mod h1:y0bflH6m4g6ary4HniYt98UqbrVnRxmRarzeMdLIUn0= +github.com/ipfs/go-ipfs-cmds v0.14.0 h1:sxdurhAHSdQr5VrSNJjc+t92uJObSNq+gRVm/wLZGMM= +github.com/ipfs/go-ipfs-cmds v0.14.0/go.mod h1:zj2jN7bHJ4pDucRmqdq863AQYcsqdxXrfVkr9eqPfvo= 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 v0.0.1/go.mod h1:gtP9xRaZXqIQRh1HRpp595KbBEdgqWFxefeVKOV8sxo= -github.com/ipfs/go-ipfs-ds-help v0.1.1/go.mod h1:SbBafGJuGsPI/QL3j9Fc5YPLeAu+SzOkI0gFwAg+mOs= -github.com/ipfs/go-ipfs-ds-help v1.1.0 h1:yLE2w9RAsl31LtfMt91tRZcrx+e61O5mDxFRR994w4Q= -github.com/ipfs/go-ipfs-ds-help v1.1.0/go.mod h1:YR5+6EaebOhfcqVCyqemItCLthrpVNot+rsOU/5IatU= -github.com/ipfs/go-ipfs-exchange-interface v0.0.1/go.mod h1:c8MwfHjtQjPoDyiy9cFquVtVHkO9b9Ob3FG91qJnWCM= -github.com/ipfs/go-ipfs-exchange-interface v0.1.0/go.mod h1:ych7WPlyHqFvCi/uQI48zLZuAWVP5iTQPXEfVaw5WEI= -github.com/ipfs/go-ipfs-exchange-interface v0.2.0 h1:8lMSJmKogZYNo2jjhUs0izT+dck05pqUw4mWNW9Pw6Y= -github.com/ipfs/go-ipfs-exchange-interface v0.2.0/go.mod h1:z6+RhJuDQbqKguVyslSOuVDhqF9JtTrO3eptSAiW2/Y= -github.com/ipfs/go-ipfs-exchange-offline v0.0.1/go.mod h1:WhHSFCVYX36H/anEKQboAzpUws3x7UeEGkzQc3iNkM0= -github.com/ipfs/go-ipfs-exchange-offline v0.1.1/go.mod h1:vTiBRIbzSwDD0OWm+i3xeT0mO7jG2cbJYatp3HPk5XY= -github.com/ipfs/go-ipfs-exchange-offline v0.2.0/go.mod h1:HjwBeW0dvZvfOMwDP0TSKXIHf2s+ksdP4E3MLDRtLKY= +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-files v0.0.3/go.mod h1:INEFm0LL2LWXBhNJ2PMIIb2w45hpXgPjNoE7yA8Y1d4= -github.com/ipfs/go-ipfs-files v0.0.8/go.mod h1:wiN/jSG8FKyk7N0WyctKSvq3ljIa2NNTiZB55kpTdOs= -github.com/ipfs/go-ipfs-files v0.2.0 h1:z6MCYHQSZpDWpUSK59Kf0ajP1fi4gLCf6fIulVsp8A8= -github.com/ipfs/go-ipfs-files v0.2.0/go.mod h1:vT7uaQfIsprKktzbTPLnIsd+NGw9ZbYwSq0g3N74u0M= -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-pinner v0.2.1 h1:kw9hiqh2p8TatILYZ3WAfQQABby7SQARdrdA+5Z5QfY= -github.com/ipfs/go-ipfs-pinner v0.2.1/go.mod h1:l1AtLL5bovb7opnG77sh4Y10waINz3Y1ni6CvTzx7oo= -github.com/ipfs/go-ipfs-posinfo v0.0.1 h1:Esoxj+1JgSjX0+ylc0hUmJCOv6V2vFoZiETLR6OtpRs= -github.com/ipfs/go-ipfs-posinfo v0.0.1/go.mod h1:SwyeVP+jCwiDu0C313l/8jg6ZxM0qqtlt2a0vILTc1A= -github.com/ipfs/go-ipfs-pq v0.0.1/go.mod h1:LWIqQpqfRG3fNc5XsnIhz/wQ2XXGyugQwls7BgUmUfY= -github.com/ipfs/go-ipfs-pq v0.0.2 h1:e1vOOW6MuOwG2lqxcLA+wEn93i/9laCY8sXAw76jFOY= -github.com/ipfs/go-ipfs-pq v0.0.2/go.mod h1:LWIqQpqfRG3fNc5XsnIhz/wQ2XXGyugQwls7BgUmUfY= -github.com/ipfs/go-ipfs-provider v0.8.1 h1:qt670pYmcNH3BCjyXDgg07o2WsTRsOdMwYc25ukCdjQ= -github.com/ipfs/go-ipfs-provider v0.8.1/go.mod h1:qCpwpoohIRVXvNzkygzsM3qdqP/sXlrogtA5I45tClc= -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.1.0/go.mod h1:hYoUkJLyAUKhF58tysKpids8RNDPO42BVMgK5dNsoqY= -github.com/ipfs/go-ipfs-routing v0.2.1/go.mod h1:xiNNiwgjmLqPS1cimvAw6EyB9rkVDbiocA4yY+wRNLM= +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.2 h1:QCK7VtL91FH17KROVVy5KrzDx2hu68QvB2FTWk08ZQk= +github.com/ipfs/go-ipfs-redirects-file v0.1.2/go.mod h1:yIiTlLcDEM/8lS6T3FlCEXZktPPqSOyuY6dEzVqw7Fw= 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 h1:59Sswnk1MFaiq+VcaknX7aYEyGyGDAA73ilhEK2POp8= github.com/ipfs/go-ipfs-util v0.0.2/go.mod h1:CbPtkWJzjLdEcezDns2XYaehFVNXG9zrdrtMecczcsQ= -github.com/ipfs/go-ipld-cbor v0.0.2/go.mod h1:wTBtrQZA3SoFKMVkp6cn6HMRteIB1VsmHA0AQFOn7Nc= -github.com/ipfs/go-ipld-cbor v0.0.3/go.mod h1:wTBtrQZA3SoFKMVkp6cn6HMRteIB1VsmHA0AQFOn7Nc= -github.com/ipfs/go-ipld-cbor v0.0.5 h1:ovz4CHKogtG2KB/h1zUp5U0c/IzZrL435rCh5+K/5G8= -github.com/ipfs/go-ipld-cbor v0.0.5/go.mod h1:BkCduEx3XBCO6t2Sfo5BaHzuok7hbhdMm9Oh8B2Ftq4= -github.com/ipfs/go-ipld-format v0.0.1/go.mod h1:kyJtbkDALmFHv3QR6et67i35QzO3S0dCDnkOJhcZkms= -github.com/ipfs/go-ipld-format v0.0.2/go.mod h1:4B6+FM2u9OJ9zCV+kSbgFAZlOrv1Hqbf0INGQgiKf9k= -github.com/ipfs/go-ipld-format v0.2.0/go.mod h1:3l3C1uKoadTPbeNfrDi+xMInYKlx2Cvg1BuydPSdzQs= -github.com/ipfs/go-ipld-format v0.3.0/go.mod h1:co/SdBE8h99968X0hViiw1MNlh6fvxxnHpvVLnH7jSM= -github.com/ipfs/go-ipld-format v0.4.0 h1:yqJSaJftjmjc9jEOFYlpkwOLVKv68OD27jFLlSghBlQ= -github.com/ipfs/go-ipld-format v0.4.0/go.mod h1:co/SdBE8h99968X0hViiw1MNlh6fvxxnHpvVLnH7jSM= +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.2.0 h1:VHIW3HVIjcMd8m4ZLZbrYpwjzqlVUfjLM7oK4T5/YF0= +github.com/ipfs/go-ipld-cbor v0.2.0/go.mod h1:Cp8T7w1NKcu4AQJLqK0tWpd1nkgTxEVB5C6kVpLW6/0= +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.1.0/go.mod h1:86f5P/srAmh9GcIcWQR9lfFLZPrIyyXQeVlOWeeWEuI= -github.com/ipfs/go-ipld-legacy v0.1.1 h1:BvD8PEuqwBHLTKqlGFTHSwrwFOMkVESEvwIYwR2cdcc= -github.com/ipfs/go-ipld-legacy v0.1.1/go.mod h1:8AyKFCjgRPsQFf15ZQgDB8Din4DML/fOmKZkkFkrIEg= -github.com/ipfs/go-ipns v0.3.0 h1:ai791nTgVo+zTuq2bLvEGmWP1M0A6kGTXUsgv/Yq67A= -github.com/ipfs/go-ipns v0.3.0/go.mod h1:3cLT2rbvgPZGkHJoPO1YMJeh6LtkxopCkKFcio/wE24= -github.com/ipfs/go-libipfs v0.0.0-20221208220359-356ce09dd4a1 h1:p/eMmtJfOliZh/SCVv239Wxj2lCo5IN4j5bdNmeGueM= -github.com/ipfs/go-libipfs v0.0.0-20221208220359-356ce09dd4a1/go.mod h1:blLqyfvHD86wgXMJ8GR4QQWYeg1ZvFHOhX3DT340Nj8= +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-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/go.mod h1:oDCg2FkjogeFOhqqb+N39l2RpTNPL6F/StPkB3kPgcs= 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.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/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM= 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.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY= github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI= -github.com/ipfs/go-merkledag v0.2.3/go.mod h1:SQiXrtSts3KGNmgOzMICy5c0POOpUNQLvB3ClKnBAlk= -github.com/ipfs/go-merkledag v0.3.2/go.mod h1:fvkZNNZixVW6cKSZ/JfLlON5OlgTXNdRLz0p6QG/I2M= -github.com/ipfs/go-merkledag v0.5.1/go.mod h1:cLMZXx8J08idkp5+id62iVftUQV+HlYJ3PIhDfZsjA4= -github.com/ipfs/go-merkledag v0.6.0/go.mod h1:9HSEwRd5sV+lbykiYP+2NC/3o6MZbKNaa4hfNcH5iH0= -github.com/ipfs/go-merkledag v0.8.1 h1:N3yrqSre/ffvdwtHL4MXy0n7XH+VzN8DlzDrJySPa94= -github.com/ipfs/go-merkledag v0.8.1/go.mod h1:uYUlWE34GhbcTjGuUDEcdPzsEtOdnOupL64NgSRjmWI= +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-metrics-prometheus v0.0.2 h1:9i2iljLg12S78OhC6UAiXi176xvQGiZaGVF1CUVdE+s= -github.com/ipfs/go-metrics-prometheus v0.0.2/go.mod h1:ELLU99AQQNi+zX6GCGm2lAgnzdSH3u5UVlCdqSXnEks= -github.com/ipfs/go-mfs v0.2.1 h1:5jz8+ukAg/z6jTkollzxGzhkl3yxm022Za9f2nL5ab8= -github.com/ipfs/go-mfs v0.2.1/go.mod h1:Woj80iuw4ajDnIP6+seRaoHpPsc9hmL0pk/nDNDWP88= -github.com/ipfs/go-namesys v0.6.0 h1:w4+Wq9bCILnuZRT1RBBdzZQFqtJeDG1duzN8mIDnHZ0= -github.com/ipfs/go-namesys v0.6.0/go.mod h1:0L+3CHBgHxr08Cg+chVo9Ew285PGQfToThjll4g0/d4= -github.com/ipfs/go-path v0.2.1/go.mod h1:NOScsVgxfC/eIw4nz6OiGwK42PjaSJ4Y/ZFPn1Xe07I= -github.com/ipfs/go-path v0.3.0 h1:tkjga3MtpXyM5v+3EbRvOHEoo+frwi4oumw5K+KYWyA= -github.com/ipfs/go-path v0.3.0/go.mod h1:NOScsVgxfC/eIw4nz6OiGwK42PjaSJ4Y/ZFPn1Xe07I= -github.com/ipfs/go-peertaskqueue v0.1.0/go.mod h1:Jmk3IyCcfl1W3jTW3YpghSwSEC6IJ3Vzz/jUmWw8Z0U= -github.com/ipfs/go-peertaskqueue v0.7.0/go.mod h1:M/akTIE/z1jGNXMU7kFB4TeSEFvj68ow0Rrb04donIU= -github.com/ipfs/go-peertaskqueue v0.8.0 h1:JyNO144tfu9bx6Hpo119zvbEL9iQ760FHOiJYsUjqaU= -github.com/ipfs/go-peertaskqueue v0.8.0/go.mod h1:cz8hEnnARq4Du5TGqiWKgMr/BOSQ5XOgMOh1K5YYKKM= -github.com/ipfs/go-pinning-service-http-client v0.1.2 h1:jdr7KelhL9gNHTU8jbqPMwIexSZXgZzxNGkycCwmbXI= -github.com/ipfs/go-pinning-service-http-client v0.1.2/go.mod h1:6wd5mjYhXJTiWU8b4RSWPpWdlzE5/csoXV0dWWMjun4= -github.com/ipfs/go-unixfs v0.2.4/go.mod h1:SUdisfUjNoSDzzhGVxvCL9QO/nKdwXdr+gbMUdqcbYw= -github.com/ipfs/go-unixfs v0.3.1/go.mod h1:h4qfQYzghiIc8ZNFKiLMFWOTzrWIAtzYQ59W/pCFf1o= -github.com/ipfs/go-unixfs v0.4.1 h1:nmJFKvF+khK03PIWyCxxydD/nkQX315NZDcgvRqMXf0= -github.com/ipfs/go-unixfs v0.4.1/go.mod h1:2SUDFhUSzrcL408B1qpIkJJ5HznnyTzweViPXUAvkNg= -github.com/ipfs/go-unixfsnode v1.1.2/go.mod h1:5dcE2x03pyjHk4JjamXmunTMzz+VUtqvPwZjIEkfV6s= -github.com/ipfs/go-unixfsnode v1.4.0 h1:9BUxHBXrbNi8mWHc6j+5C580WJqtVw9uoeEKn4tMhwA= -github.com/ipfs/go-unixfsnode v1.4.0/go.mod h1:qc7YFFZ8tABc58p62HnIYbUMwj9chhUuFWmxSokfePo= -github.com/ipfs/go-verifcid v0.0.1/go.mod h1:5Hrva5KBeIog4A+UpqlaIU+DEstipcJYQQZc0g37pY0= -github.com/ipfs/go-verifcid v0.0.2 h1:XPnUv0XmdH+ZIhLGKg6U2vaPaRDXb9urMyNVCE7uvTs= -github.com/ipfs/go-verifcid v0.0.2/go.mod h1:40cD9x1y4OWnFXbLNJYRe7MpNvWlMn3LZAG5Wb4xnPU= -github.com/ipfs/interface-go-ipfs-core v0.8.1 h1:nuFG0YJ429Wd5gtRb3ivlblpknZ5VfDVKZkmOG2TnNQ= -github.com/ipfs/interface-go-ipfs-core v0.8.1/go.mod h1:WYC2H6Mu7aGqhlupi/CVawcs0X1Me4uRvV0rcTlo3zM= -github.com/ipld/edelweiss v0.2.0 h1:KfAZBP8eeJtrLxLhi7r3N0cBCo7JmwSRhOJp3WSpNjk= -github.com/ipld/edelweiss v0.2.0/go.mod h1:FJAzJRCep4iI8FOFlRriN9n0b7OuX3T/S9++NpBDmA4= -github.com/ipld/go-car v0.4.0 h1:U6W7F1aKF/OJMHovnOVdst2cpQE5GhmHibQkAixgNcQ= -github.com/ipld/go-car v0.4.0/go.mod h1:Uslcn4O9cBKK9wqHm/cLTFacg6RAPv6LZx2mxd2Ypl4= -github.com/ipld/go-car/v2 v2.1.1/go.mod h1:+2Yvf0Z3wzkv7NeI69i8tuZ+ft7jyjPYIWZzeVNeFcI= -github.com/ipld/go-car/v2 v2.4.0 h1:8jI6/iKlyLqRZzLz31jFWTqKvslaVzFsin305sOuqNQ= -github.com/ipld/go-car/v2 v2.4.0/go.mod h1:zjpRf0Jew9gHqSvjsKVyoq9OY9SWoEKdYCQUKVaaPT0= -github.com/ipld/go-codec-dagpb v1.3.0/go.mod h1:ga4JTU3abYApDC3pZ00BC2RSvC3qfBb9MSJkMLSwnhA= -github.com/ipld/go-codec-dagpb v1.4.1 h1:CUQJaOPRgSZ27OUPgUWtvdvvd2d17/IGGAIMOo4yYp0= -github.com/ipld/go-codec-dagpb v1.4.1/go.mod h1:XdXTO/TUD/ra9RcK/NfmwBfr1JpFxM2uRKaB9oe4LxE= -github.com/ipld/go-ipld-prime v0.9.1-0.20210324083106-dc342a9917db/go.mod h1:KvBLMr4PX1gWptgkzRjVZCrLmSGcZCb/jioOQwCqZN8= +github.com/ipfs/go-metrics-prometheus v0.0.3 h1:MVgBw30nE9eKC598ZQg+LIIbgIcx46NG8KISWLCLkMo= +github.com/ipfs/go-metrics-prometheus v0.0.3/go.mod h1:qjWVLyK+ZJrQuiyTqfgoECgKfd4b4lEtpQemAWomWc8= +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-test v0.0.4 h1:DKT66T6GBB6PsDFLoO56QZPrOmzJkqU1FZH5C9ySkew= +github.com/ipfs/go-test v0.0.4/go.mod h1:qhIM1EluEfElKKM6fnWxGn822/z9knUGM1+I/OAQNKI= +github.com/ipfs/go-unixfsnode v1.9.2 h1:0A12BYs4XOtDPJTMlwmNPlllDfqcc4yie4e919hcUXk= +github.com/ipfs/go-unixfsnode v1.9.2/go.mod h1:v1nuMFHf4QTIhFUdPMvg1nQu7AqDLvIdwyvJ531Ot1U= +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/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.14.2 h1:9ERr7KXpCC7If0rChZLhYDlyr6Bes6yRKPJnCO3hdHY= +github.com/ipld/go-car/v2 v2.14.2/go.mod h1:0iPB/825lTZLU2zPK5bVTk/R3V2612E1VI279OGSXWA= +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.0/go.mod h1:9ASQLwUFLptCov6lIYc70GRB4V7UTyLD0IJtrDJe6ZM= -github.com/ipld/go-ipld-prime v0.14.1/go.mod h1:QcE4Y9n/ZZr8Ijg5bGPT0GqYWgZ1704nH0RDcQtgTP0= -github.com/ipld/go-ipld-prime v0.16.0/go.mod h1:axSCuOCBPqrH+gvXr2w9uAOulJqBPhHPT2PjoiiU1qA= -github.com/ipld/go-ipld-prime v0.19.0 h1:5axC7rJmPc17Emw6TelxGwnzALk0PdupZ2oj2roDj04= -github.com/ipld/go-ipld-prime v0.19.0/go.mod h1:Q9j3BaVXwaA3o5JUDNvptDDr/x8+F7FG6XJ8WI3ILg4= -github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20211210234204-ce2a1c70cd73 h1:TsyATB2ZRRQGTwafJdgEUQkmjOExRV0DNokcihZxbnQ= -github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20211210234204-ce2a1c70cd73/go.mod h1:2PJ0JgxyB08t0b2WKrcuqI3di0V+5n6RS/LTUJhkoxY= -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/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/ipshipyard/p2p-forge v0.0.2 h1:86y9LxGB8sGxYQ/If5sNx+c8C/huSpBUg3UZ1uvtym8= +github.com/ipshipyard/p2p-forge v0.0.2/go.mod h1:taPeh3PDSO8Ual0/N2tIOAUXPV8gZoPF3uPXoUyiq14= 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-random v0.0.0-20190219211222-123a90aedc0c h1:uUx61FiAa1GI6ZmVd2wf2vULeQZIKG66eybjNXKYCz4= -github.com/jbenet/go-random v0.0.0-20190219211222-123a90aedc0c/go.mod h1:sdx1xVM9UuLw1tXnhJWN3piypTUO3vCIHYmG15KE/dU= -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/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= -github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= -github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= -github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= -github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc= -github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= 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/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 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.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 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.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 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/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= 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/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= @@ -694,305 +502,120 @@ github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQL 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.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM= -github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.0.6/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.1 h1:U33DW0aiEj633gHYw3LoDNfkDiYnE5Q8M/TKJn2f2jI= -github.com/klauspost/cpuid/v2 v2.2.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= +github.com/klauspost/cpuid/v2 v2.2.8/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.0-20180514024734-4a0ed625a78b/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= -github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= -github.com/koron/go-ssdp v0.0.3 h1:JivLMY45N76b4p/vsWGOKewBQu6uf39y8l+AQ7sDKx8= -github.com/koron/go-ssdp v0.0.3/go.mod h1:b2MxI6yh02pKrsyNoQUsk4+YNikaGhe4894J+Q5lDvA= +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/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 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -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/libp2p/go-addr-util v0.0.1/go.mod h1:4ac6O7n9rIAKB1dnd+s8IbbMXkt+oBpzX4/+RACcnlQ= -github.com/libp2p/go-addr-util v0.0.2/go.mod h1:Ecd6Fb3yIuLzq4bD7VcywcVSBtefcAwnUISBM3WG15E= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s= +github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= 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-conn-security-multistream v0.1.0/go.mod h1:aw6eD7LOsHEX7+2hJkDxw1MteijaVcI+/eP2/x3J1xc= -github.com/libp2p/go-conn-security-multistream v0.2.0/go.mod h1:hZN4MjlNetKD3Rq5Jb/P5ohUnFLNzEAR4DLSzpn2QLU= -github.com/libp2p/go-conn-security-multistream v0.2.1/go.mod h1:cR1d8gA0Hr59Fj6NhaTpFhJZrjSYuNmhpT2r25zYR70= 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-eventbus v0.1.0/go.mod h1:vROgu5cs5T7cv7POWlWxBaVLxfSegC5UGQf8A2eEmx4= -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.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.1.0/go.mod h1:6D/2OBauqLUoqcADOJpn9WbKqvaM07tDw68qHM0BxUM= -github.com/libp2p/go-libp2p v0.1.1/go.mod h1:I00BRo1UuUSdpuc8Q2mN7yDF/oTUTRAX6JWpTiK9Rp8= -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.14.3/go.mod h1:d12V4PdKbpL0T1/gsUNN8DfgMuRPDX8bS2QxCZlwRH0= -github.com/libp2p/go-libp2p v0.24.2 h1:iMViPIcLY0D6zr/f+1Yq9EavCZu2i7eDstsr1nEwSAk= -github.com/libp2p/go-libp2p v0.24.2/go.mod h1:WuxtL2V8yGjam03D93ZBC19tvOUiPpewYv1xdFGWu1k= -github.com/libp2p/go-libp2p-asn-util v0.2.0 h1:rg3+Os8jbnO5DxkC7K/Utdi+DkY3q/d1/1q+8WeNAsw= -github.com/libp2p/go-libp2p-asn-util v0.2.0/go.mod h1:WoaWxbHKBymSN41hWSq/lGKJEca7TNm58+gGJi2WsLI= -github.com/libp2p/go-libp2p-autonat v0.1.0/go.mod h1:1tLf2yXxiE/oKGtDwPYWTSYG3PtvYlJmg7NeVtPRqH8= -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.2/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/go.mod h1:eduNKXGTioTuQAUcZ5epXi9vMl+t4d8ugUBRQ4SqaNQ= -github.com/libp2p/go-libp2p-circuit v0.1.0/go.mod h1:Ahq4cY3V9VJcHcn1SBXjr78AbFkZeIRmfunbA7pmFh8= -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/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.2/go.mod h1:9dAcntw/n46XycV4RnlBq3BpgrmyUi9LuoTNdPrbUco= -github.com/libp2p/go-libp2p-core v0.0.3/go.mod h1:j+YQMNz9WNSkNezXOsahp9kwZBKBvxLpKD316QWSJXE= -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-flow-metrics v0.2.0 h1:EIZzjmeOE6c8Dav0sNv35vhZxATIXWZg6j/C08XmmDw= +github.com/libp2p/go-flow-metrics v0.2.0/go.mod h1:st3qqfu8+pMfh+9Mzqb2GTiwrAGjIPszEjZmtksN8Jc= +github.com/libp2p/go-libp2p v0.37.0 h1:8K3mcZgwTldydMCNOiNi/ZJrOB9BY+GlI3UxYzxBi9A= +github.com/libp2p/go-libp2p v0.37.0/go.mod h1:GOKmSN99scDuYGTwaTbQPR8Nt6dxrK3ue7OjW2NGDg4= +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-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.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.7.0/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-core v0.8.0/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-core v0.8.1/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-core v0.8.2/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-core v0.8.5/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-crypto v0.1.0/go.mod h1:sPUokVISZiy+nNuTTH/TY+leRSxnFj/2GLjtOTW90hI= -github.com/libp2p/go-libp2p-discovery v0.1.0/go.mod h1:4F/x+aldVHjHDHuX85x1zWoFTGElt8HnoDzwkFZm29g= -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/go.mod h1:+srtPIU9gDaBNu//UHvcdliKBIcr4SfDcm0/PfPJLug= -github.com/libp2p/go-libp2p-gostream v0.5.0 h1:niNGTUrFoUDP/8jxMgu97zngMO+UGYBpVpbCKwIJBls= -github.com/libp2p/go-libp2p-gostream v0.5.0/go.mod h1:rXrb0CqfcRRxa7m3RSKORQiKiWgk3IPeXWda66ZXKsA= -github.com/libp2p/go-libp2p-http v0.4.0 h1:V+f9Rhe/8GkColmXoyJyA0NVsN9F3TCLZgW2hwjoX5w= -github.com/libp2p/go-libp2p-http v0.4.0/go.mod h1:92tmLGrlBliQFDlZRpBXT3BJM7rGFONy0vsNrG/bMPg= -github.com/libp2p/go-libp2p-kad-dht v0.20.0 h1:1bcMa74JFwExCHZMFEmjtHzxX5DovhJ07EtR6UOTEpc= -github.com/libp2p/go-libp2p-kad-dht v0.20.0/go.mod h1:qPIXdiZsLczhV4/+4EO1jE8ae0YCW4ZOogc4WVIyTEU= +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.27.0 h1:1Ea32tVTPiAfaLpPMbaBWFJgbsi/JpMqC2YBuFdf32o= +github.com/libp2p/go-libp2p-kad-dht v0.27.0/go.mod h1:ixhjLuzaXSGtWsKsXTj7erySNuVC4UP7NO015cRrF14= github.com/libp2p/go-libp2p-kbucket v0.3.1/go.mod h1:oyjT5O7tS9CQurok++ERgc46YLwEpuGoFq9ubvoUOio= -github.com/libp2p/go-libp2p-kbucket v0.5.0 h1:g/7tVm8ACHDxH29BGrpsQlnNeu+6OF1A9bno/4/U1oA= -github.com/libp2p/go-libp2p-kbucket v0.5.0/go.mod h1:zGzGCpQd78b5BNTDGHNDLaTt9aDK/A02xeZp9QeFC4U= -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.4.0/go.mod h1:yCyWJE2sc6TBTnFpjvLuEJgTSw/u+MamvzILKdX7asw= -github.com/libp2p/go-libp2p-mplex v0.4.1/go.mod h1:cmy+3GfqfM1PceHTLL7zQzAAYaryDu6iPSC+CIb094g= -github.com/libp2p/go-libp2p-nat v0.0.4/go.mod h1:N9Js/zVtAXqaeT99cXgTV9e75KpnWCvVOiGzlcHmBbY= -github.com/libp2p/go-libp2p-nat v0.0.5/go.mod h1:1qubaE5bTZMJE+E/uu2URroMbzdubFz1ChgiN79yKPE= -github.com/libp2p/go-libp2p-nat v0.0.6/go.mod h1:iV59LVhB3IkFvS6S6sauVTSOrNEANnINbI/fkaLimiw= -github.com/libp2p/go-libp2p-netutil v0.1.0/go.mod h1:3Qv/aDqtMLTUyQeundkKsA+YCThNdbQD54k3TqjpbFU= -github.com/libp2p/go-libp2p-noise v0.2.0/go.mod h1:IEbYhBBzGyvdLBoxxULL/SGbJARhUeqlO8lVSREYu2Q= -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-kbucket v0.6.4 h1:OjfiYxU42TKQSB8t8WYd8MKhYhMJeO2If+NiuKfb6iQ= +github.com/libp2p/go-libp2p-kbucket v0.6.4/go.mod h1:jp6w82sczYaBsAypt5ayACcRJi0lgsba7o4TzJKEfWA= 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/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= -github.com/libp2p/go-libp2p-peerstore v0.2.7/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= -github.com/libp2p/go-libp2p-pnet v0.2.0/go.mod h1:Qqvq6JH/oMZGwqs3N1Fqhv8NVhrdYcO0BW4wssv21LA= -github.com/libp2p/go-libp2p-pubsub v0.8.2 h1:QLGUmkgKmwEVxVDYGsqc5t9CykOMY2Y21cXQHjR462I= -github.com/libp2p/go-libp2p-pubsub v0.8.2/go.mod h1:e4kT+DYjzPUYGZeWk4I+oxCSYTXizzXii5LDRRhjKSw= +github.com/libp2p/go-libp2p-pubsub v0.12.0 h1:PENNZjSfk8KYxANRlpipdS7+BfLmOl3L2E/6vSNjbdI= +github.com/libp2p/go-libp2p-pubsub v0.12.0/go.mod h1:Oi0zw9aw8/Y5GC99zt+Ef2gYAl+0nZlwdJonDyOz/sE= 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-quic-transport v0.10.0/go.mod h1:RfJbZ8IqXIhxBRm5hqUEJqjiiY8xmEuq3HUDS993MkA= -github.com/libp2p/go-libp2p-record v0.1.0/go.mod h1:ujNc8iuE5dlKWVy6wuL6dd58t0n7xI4hAIl8pE6wu5Q= 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.4.0 h1:b7y4aixQ7AwbqYfcOQ6wTw8DQvuRZeTAA0Od3YYN5yc= -github.com/libp2p/go-libp2p-routing-helpers v0.4.0/go.mod h1:dYEAgkVhqho3/YKxfOEGdFMIcWfAFNlZX8iAIihYA2E= -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.5.0/go.mod h1:sU9i6BoHE0Ve5SKz3y9WfKrh8dUat6JknzUehFx8xW4= -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/go.mod h1:Q+PFXYoiYFN5CAEG2w3gLPEzotlKsNSbKQ/lImlOWF0= +github.com/libp2p/go-libp2p-routing-helpers v0.7.4 h1:6LqS1Bzn5CfDJ4tzvP9uwh42IB7TJLNFJA6dEeGBv84= +github.com/libp2p/go-libp2p-routing-helpers v0.7.4/go.mod h1:we5WDj9tbolBXOuF1hGOkR+r7Uh1408tQbAKaT5n1LE= 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-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.2/go.mod h1:NR8ne1VwfreD5VIWIU62Agt/J18ekORFU/j1i2y8zvk= 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-libp2p-yamux v0.2.0/go.mod h1:Db2gU+XfLpm6E4rG5uGCFX6uXA8MEXOxFcRoXUODaK8= -github.com/libp2p/go-libp2p-yamux v0.2.1/go.mod h1:1FBXiHDk1VyRM1C0aez2bCfHQ4vMZKkAQzZbkSQt5fI= -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.4/go.mod h1:tfrXbyaTqqSU654GTvK3ocnSZL3BuHoeTSqhcel1wsE= -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/go.mod h1:0Oy/A9PQlwBytDRp4wSkFnzHYDKcpLot35JQ6msjvYQ= -github.com/libp2p/go-mplex v0.7.0 h1:BDhFZdlk5tbr0oyFq/xv/NPGfjbnrsDam1EvutpBDbY= -github.com/libp2p/go-mplex v0.7.0/go.mod h1:rW8ThnRcYWft/Jb2jeORBmPd6xuG3dGxWN/W168L9EU= -github.com/libp2p/go-msgio v0.0.2/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= -github.com/libp2p/go-msgio v0.0.3/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/go.mod h1:4ecVB6d9f4BDSL5fqvPiC4A3KivjWn+Venn/1ALLMWA= -github.com/libp2p/go-msgio v0.2.0 h1:W6shmB+FeynDrUVl2dgFQvzfBZcXiyqY4VmpQLu9FqU= -github.com/libp2p/go-msgio v0.2.0/go.mod h1:dBVM1gW3Jk9XqHkU4eKdGvVHdLa51hoGfll6jMJMSlY= -github.com/libp2p/go-nat v0.0.3/go.mod h1:88nUEt0k0JD45Bk93NIwDqjlhiOwOoV36GchpcVc1yI= -github.com/libp2p/go-nat v0.0.4/go.mod h1:Nmw50VAvKuk38jUBcmNh6p9lUJLoODbJRvYAa/+KSDo= -github.com/libp2p/go-nat v0.0.5/go.mod h1:B7NxsVNPZmRLvMOwiEO1scOSyjA56zxYAGv1yQgRkEU= -github.com/libp2p/go-nat v0.1.0 h1:MfVsH6DLcpa04Xr+p8hmVRG4juse0s3J8HyNWYHffXg= -github.com/libp2p/go-nat v0.1.0/go.mod h1:X7teVkwRHNInVNWQiO/tAiAVRwSr5zoRz4YSTC3uRBM= -github.com/libp2p/go-netroute v0.1.2/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk= -github.com/libp2p/go-netroute v0.1.3/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk= -github.com/libp2p/go-netroute v0.1.5/go.mod h1:V1SR3AaECRkEQCoFFzYwVYWvYIEtlxx89+O3qcpCl4A= -github.com/libp2p/go-netroute v0.1.6/go.mod h1:AqhkMh0VuWmfgtxKPp3Oc1LdU5QSWS7wl0QLhSZqXxQ= +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.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/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= -github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo= -github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= -github.com/libp2p/go-reuseport v0.0.1/go.mod h1:jn6RmB1ufnQwl0Q1f+YxAj8isJgDCQzaaxIFYDhcYEA= -github.com/libp2p/go-reuseport v0.0.2/go.mod h1:SPD+5RwGC7rcnzngoYC86GjPzjSywuQyMVAheVBD9nQ= -github.com/libp2p/go-reuseport v0.2.0 h1:18PRvIMlpY6ZK85nIAicSBuXXvrYoSw3dsBAR7zc560= -github.com/libp2p/go-reuseport v0.2.0/go.mod h1:bvVho6eLMm6Bz5hmU0LYN3ixd3nPPvtIlaURZZgOY4k= -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/go.mod h1:trPa7r/7TJK/d+0hdBLOCGvpQQVOU74OXbNCIMkufGw= -github.com/libp2p/go-sockaddr v0.0.2/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= -github.com/libp2p/go-sockaddr v0.1.0/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= -github.com/libp2p/go-sockaddr v0.1.1/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= +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-socket-activation v0.1.0 h1:OImQPhtbGlCNaF/KSTl6pBBy+chA5eBt5i9uMJNtEdY= github.com/libp2p/go-socket-activation v0.1.0/go.mod h1:gzda2dNkMG5Ti2OfWNNwW0FDIbj0g/aJJU320FcLfhk= -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/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.3/go.mod h1:9dvr03yqrPyYGIEN6Dy5UvdJZjyPFvl1S/igQ5QD1SU= -github.com/libp2p/go-testutil v0.1.0/go.mod h1:81b2n5HypcVyrCg/MJx4Wgfp/VHojytjVe/gLzZ2Ehc= -github.com/libp2p/go-ws-transport v0.1.0/go.mod h1:rjw1MG1LU9YDC6gzmwObkPd/Sqwhw7yT74kj3raBFuo= -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.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.2.3/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/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE= -github.com/libp2p/go-yamux/v2 v2.2.0/go.mod h1:3So6P6TV6r75R9jiBpiIKgU/66lOarCZjqROGxzPpPQ= -github.com/libp2p/go-yamux/v4 v4.0.0 h1:+Y80dV2Yx/kv7Y7JKu0LECyVdMXm1VUoko+VQ9rBfZQ= -github.com/libp2p/go-yamux/v4 v4.0.0/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4= +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/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= -github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= -github.com/lucas-clemente/quic-go v0.19.3/go.mod h1:ADXpNbTQjq1hIzCpB+y/k5iz4n4z4IwqoLb94Kh5Hu8= -github.com/lucas-clemente/quic-go v0.31.1 h1:O8Od7hfioqq0PMYHDyBkxU2aA7iZ2W9pjbrWuja2YR4= -github.com/lucas-clemente/quic-go v0.31.1/go.mod h1:0wFbizLgYzqHqtlyxyCaJKlE7bYgE6JQ+54TLd/Dq2g= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= -github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= 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/qpack v0.3.0 h1:UiWstOgT8+znlkDPOg2+3rIuYXJ2CnGDkGUXN6ki6hE= -github.com/marten-seemann/qpack v0.3.0/go.mod h1:cGfKPBiP4a9EQdxCwEwI/GEeWAsjSekBvx/X8mh58+g= -github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs= -github.com/marten-seemann/qtls-go1-15 v0.1.1/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I= -github.com/marten-seemann/qtls-go1-18 v0.1.3 h1:R4H2Ks8P6pAtUagjFty2p7BVHn3XiwDAl7TTQf5h7TI= -github.com/marten-seemann/qtls-go1-18 v0.1.3/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4= -github.com/marten-seemann/qtls-go1-19 v0.1.1 h1:mnbxeq3oEyQxQXwI4ReCgW9DPoPR94sNlqWoDZnjRIE= -github.com/marten-seemann/qtls-go1-19 v0.1.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI= 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/marten-seemann/webtransport-go v0.4.3 h1:vkt5o/Ci+luknRteWdYGYH1KcB7ziup+J+1PzZJIvmg= -github.com/marten-seemann/webtransport-go v0.4.3/go.mod h1:4xcfySgZMLP4aG5GBGj1egP7NlpfwgYJ1WJMvPPiVMU= 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.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 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.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.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-pointer v0.0.1 h1:n+XhsuGeVO6MEAp7xyEukFINEa+Quek5psIR/ylA6o0= -github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= +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.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/mholt/acmez/v2 v2.0.3 h1:CgDBlEwg3QBp6s45tPQmFIBrkRIkBT4rW4orMM6p4sw= +github.com/mholt/acmez/v2 v2.0.3/go.mod h1:pQ1ysaDeGrIMvJ9dfJMk5kJNkn7L2sb3UhyrX6Q91cw= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -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.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.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= -github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= +github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ= +github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ= 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= @@ -1001,20 +624,12 @@ github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc h1:PTfri+PuQmWDqERdn 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.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/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= -github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +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/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 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= @@ -1022,7 +637,6 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN 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.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= @@ -1033,212 +647,193 @@ github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYg 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.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/go.mod h1:uPbspcUPd5AfaP6ql3ujFY+QWzmBD8uLLL4bXW0XfGc= -github.com/multiformats/go-multiaddr v0.3.3/go.mod h1:lCKNGP1EQ1eZ35Za2wlqnabm9xQkib3fyB+nZXHLag0= github.com/multiformats/go-multiaddr v0.4.0/go.mod h1:YcpyLH8ZPudLxQlemYBPhSm0/oCXAT8Z4mzFpyoPyRc= -github.com/multiformats/go-multiaddr v0.8.0 h1:aqjksEcqK+iD/Foe1RRFsGZh8+XFiGo7FgUCZlpv3LU= -github.com/multiformats/go-multiaddr v0.8.0/go.mod h1:Fs50eBDWvZu+l3/9S6xAE7ZYj6yhxlvaVZjakWN7xRs= -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/go.mod h1:TJ5pr5bBO7Y1B18djPuRsVkduhQH2YqYSbxWJzYGdK0= +github.com/multiformats/go-multiaddr v0.13.0 h1:BCBzs61E3AGHcYYTv8dqRH43ZfyrqM8RXVPT8t13tLQ= +github.com/multiformats/go-multiaddr v0.13.0/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.0.1/go.mod h1:aBYjqL4T/7j4Qx+R73XSv/8JsgnRFlf0w2KGLCmXl3Q= +github.com/multiformats/go-multiaddr-dns v0.4.0 h1:P76EJ3qzBXpUXZ3twdCDx/kvagMsNo0LMFXpyms/zgU= +github.com/multiformats/go-multiaddr-dns v0.4.0/go.mod h1:7hfthtB4E4pQwirrz+J0CcDUfbWzTqEzVyYKKIKpgkc= 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/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/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc= -github.com/multiformats/go-multibase v0.1.1 h1:3ASCDsuLX8+j4kx58qnJ4YFq/JWTJpCyDW27ztsVTOI= -github.com/multiformats/go-multibase v0.1.1/go.mod h1:ZEjHE+IsUrgp5mhlEAYjMtZwK1k4haNkcaPg9aoe1a8= -github.com/multiformats/go-multicodec v0.3.0/go.mod h1:qGGaQmioCDh+TeFOnxrbU0DaIPw8yFgAZgFG0V7p1qQ= -github.com/multiformats/go-multicodec v0.3.1-0.20210902112759-1539a079fd61/go.mod h1:1Hj/eHRaVWSXiSNNfcEPcwZleTmdNP81xlxDLnWU9GQ= -github.com/multiformats/go-multicodec v0.3.1-0.20211210143421-a526f306ed2c/go.mod h1:1Hj/eHRaVWSXiSNNfcEPcwZleTmdNP81xlxDLnWU9GQ= -github.com/multiformats/go-multicodec v0.7.0 h1:rTUjGOwjlhGHbEMbPoSUJowG1spZTVsITRANCjKTUAQ= -github.com/multiformats/go-multicodec v0.7.0/go.mod h1:GUC8upxSBE4oG+q3kWZRw/+6yC1BqO550bjhWsJbZlw= +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.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.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.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.1 h1:aem8ZT0VA2nCHHk7bPJ1BjUbHNciqZC/d16Vve9l108= -github.com/multiformats/go-multihash v0.2.1/go.mod h1:WxoMcYG85AZVQUyRyo9s4wULvW5qrI9vb2Lt6evduFc= -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.1/go.mod h1:5GZPQZbkWOLOn3J2y4Y99vVW7vOfsAflxARk3x14o6k= -github.com/multiformats/go-multistream v0.2.2/go.mod h1:UIcnm7Zuo8HKG+HkWgfQsGL+/MIEhyTqbODbIUwSXKs= -github.com/multiformats/go-multistream v0.3.3 h1:d5PZpjwRgVlbwfdTDjife7XszfZd8KYWfROYFlGcR8o= -github.com/multiformats/go-multistream v0.3.3/go.mod h1:ODRoqamLUsETKS9BNcII4gcRsJBU5VAwRIv7O39cEXg= +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.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/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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 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/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= -github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= -github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= -github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= -github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= 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.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= -github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +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.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/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= 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.5.1 h1:auzK7OI497k6x4OvWq+TKAcpcSAlod0doAH72oIN0Jw= -github.com/onsi/ginkgo/v2 v2.5.1/go.mod h1:63DOGlLAH8+REH8jUGdL3YpCpu7JODesutUjdENfUAc= -github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4= +github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= 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/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.24.0 h1:+0glovB9Jd6z3VR+ScSwQqXVTIfJcGA9UBM8yzQxhqg= -github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= -github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= +github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= -github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= +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-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= -github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/openzipkin/zipkin-go v0.4.0 h1:CtfRrOVZtbDj8rt1WXjklw0kqqJQwICrCKmlfUuBUUw= -github.com/openzipkin/zipkin-go v0.4.0/go.mod h1:4c3sLeE8xjNqehmF5RpAFLPLJxXscc0R4l6Zg0P1tTQ= -github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +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/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= 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/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pion/datachannel v1.5.9 h1:LpIWAOYPyDrXtU+BW7X0Yt/vGtYxtXQ8ql7dFfYUVZA= +github.com/pion/datachannel v1.5.9/go.mod h1:kDUuk4CU4Uxp82NH4LQZbISULkX/HtzKa4P7ldf9izE= +github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= +github.com/pion/dtls/v2 v2.2.12 h1:KP7H5/c1EiVAAKUmXyCzPiQe5+bCJrpOeKg/L05dunk= +github.com/pion/dtls/v2 v2.2.12/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= +github.com/pion/ice/v2 v2.3.36 h1:SopeXiVbbcooUg2EIR8sq4b13RQ8gzrkkldOVg+bBsc= +github.com/pion/ice/v2 v2.3.36/go.mod h1:mBF7lnigdqgtB+YHkaY/Y6s6tsyRyo4u4rPGRuOjUBQ= +github.com/pion/interceptor v0.1.37 h1:aRA8Zpab/wE7/c0O3fh1PqY0AJI3fCSEM5lRWJVorwI= +github.com/pion/interceptor v0.1.37/go.mod h1:JzxbJ4umVTlZAf+/utHzNesY8tmRkM2lVmkS82TTj8Y= +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.9 h1:E2HX740TZKaqdcPmf4pw6ZZuG8u5RlMMt+l3dxeu6Wk= +github.com/pion/rtp v1.8.9/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= +github.com/pion/sctp v1.8.33 h1:dSE4wX6uTJBcNm8+YlMg7lw1wqyKHggsP5uKbdj+NZw= +github.com/pion/sctp v1.8.33/go.mod h1:beTnqSzewI53KWoG3nqB282oDMGrhNxBdb+JZnkCwRM= +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.20 h1:HNNny4s+OUmG280ETrCdgFndp4ufx3/uy85EawYEhTk= +github.com/pion/srtp/v2 v2.0.20/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.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.10 h1:ucLBLE8nuxiHfvkFKnkDQRYWYfp8ejf4YBOPfaQpw6Q= +github.com/pion/transport/v2 v2.2.10/go.mod h1:sq1kSLWs+cHW9E+2fJP95QudkzbK7wscs8yYgQToO5E= +github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= +github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0= +github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo= +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.3.4 h1:v2heQVnXTSqNRXcaFQVOhIOYkLMxOu1iJG8uy1djvkk= +github.com/pion/webrtc/v3 v3.3.4/go.mod h1:liNa+E1iwyzyXqNUwvoMRNQ10x8h8FOeJKL8RkIbamE= +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/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= -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-20190221155625-df39d6c2d992/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= -github.com/polydawn/refmt v0.0.0-20190408063855-01bf1e26dd14/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= -github.com/polydawn/refmt v0.0.0-20190807091052-3d65705ee9f1/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= -github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e h1:ZOcivgkkFRnjfoTcGsDq3UQYiBmekwLA+qg0OjyB/ls= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/polydawn/refmt v0.89.0 h1:ADJTApkvkeBZsN0tBTx8QjpD9JkmxbKp0cxfr9qszm4= +github.com/polydawn/refmt v0.89.0/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw= 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 v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= -github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU= 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.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= -github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= +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.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/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.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +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.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.28.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= +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.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA= +github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= 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.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= 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 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/prometheus/statsd_exporter v0.21.0 h1:hA05Q5RFeIjgwKIYEdFd59xu5Wwaznf33yKI+pyX6T8= -github.com/prometheus/statsd_exporter v0.21.0/go.mod h1:rbT83sZq2V+p73lHhPZfMc3MLCHmSHelCh9hSGYNLTQ= -github.com/rabbitmq/amqp091-go v1.1.0/go.mod h1:ogQDLSOACsLPsIq0NpbtiifNZi2YOz0VTJ0kHRghqbM= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +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/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= +github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= +github.com/quic-go/quic-go v0.48.1 h1:y/8xmfWI9qmGTc+lBr4jKRUWLGSlSigv847ULJ4hYXA= +github.com/quic-go/quic-go v0.48.1/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs= +github.com/quic-go/webtransport-go v0.8.1-0.20241018022711-4ac2c9250e66 h1:4WFk6u3sOT6pLa1kQ50ZVdm8BQFgJNA117cepZxtLIg= +github.com/quic-go/webtransport-go v0.8.1-0.20241018022711-4ac2c9250e66/go.mod h1:Vp72IJajgeOL6ddqrAhmp7IM9zbTcgkQxD/YdxrVwMw= 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/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= +github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= -github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/rs/zerolog v1.21.0/go.mod h1:ZPhntP/xmq1nnND05hhpAh2QMhSsA4UN3MGZ6O2J3hM= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= +github.com/rs/cors v1.11.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/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/samber/lo v1.36.0 h1:4LaOxH1mHnbDGhTVE0i1z8v/lWaQW8AIfOD3HU4mSaw= -github.com/samber/lo v1.36.0/go.mod h1:HLeWcJRRyLKp3+/XBJvOrerCQn9mhdKMHyd7IRlgeQ8= -github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc= +github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU= 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= @@ -1267,42 +862,32 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx 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/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/assertions v1.0.0 h1:UVQPSSmc3qtTi+zPPkCXvZX9VvW/xT/NsRvKfwY81a8= -github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= -github.com/smartystreets/goconvey v0.0.0-20190222223459-a17d461953aa/go.mod h1:2RVY1rIf+2J2o/IM9+vPq9RzmHDSseB7FoXiSNIUsoU= -github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= +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/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= 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.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= 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.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 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/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= 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 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= 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= @@ -1310,76 +895,78 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 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.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 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/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= +github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= -github.com/texttheater/golang-levenshtein v0.0.0-20180516184445-d188e65d659e h1:T5PdfK/M1xyrHwynxMIVMWLS7f/qHwfslZphxtGnw7s= -github.com/texttheater/golang-levenshtein v0.0.0-20180516184445-d188e65d659e/go.mod h1:XDKHRm5ThF8YJjx001LtgelzsoaEcvnA7lVWz9EeX3g= -github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M= -github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/texttheater/golang-levenshtein v1.0.1 h1:+cRNoVrfiwufQPhoMzB6N0Yf/Mqajr6t1lOv8GyGE2U= +github.com/texttheater/golang-levenshtein v1.0.1/go.mod h1:PYAKrbF5sAiq9wd+H82hs7gNaen0CplQ9uvm6+enD/8= +github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.16.0 h1:SyXa+dsSPpUlcwEDuKuEBJEz5vzTvOea+9rjyYodQFg= +github.com/tidwall/gjson v1.16.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= +github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= 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.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= +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.10.0 h1:E86YlUMYfwIacEsQGlnTvjk1IgYkyTGjPhF0RnwTCmw= -github.com/warpfork/go-wish v0.0.0-20180510122957-5ad1f5abf436/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= -github.com/warpfork/go-wish v0.0.0-20190328234359-8b3e70f8e830/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= -github.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a h1:G++j5e0OC488te356JvdhaM8YS6nMsjLAYF7JxCv07w= +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.0.0-20200123233031-1cdf64d27158/go.mod h1:Xj/M2wWU+QdTdRbu/L/1dIZY8/Wb2K9pAhtroQuxJJI= -github.com/whyrusleeping/cbor-gen v0.0.0-20200710004633-5379fc63235d h1:wSxKhvbN7kUoP0sfRS+w2tWr45qlU8409i94hHLOT8w= -github.com/whyrusleeping/cbor-gen v0.0.0-20200710004633-5379fc63235d/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= +github.com/whyrusleeping/cbor-gen v0.1.2 h1:WQFlrPhpcQl+M2/3dP5cvlTLWPVsL6LGBb9jJt6l/cA= +github.com/whyrusleeping/cbor-gen v0.1.2/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-logging v0.0.1/go.mod h1:lDPYj54zutzG1XYfHAhcc7oNXEburHQBn+Iqd4yS4vE= -github.com/whyrusleeping/go-notifier v0.0.0-20170827234753-097c5d47330f/go.mod h1:cZNvX9cFybI01GriPRMXDtczuvUhgbcYr9iCGaNlRv8= 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/mafmt v1.2.8/go.mod h1:faQJFPbLSxzD9xpA02ttW/tS9vZykNvXwGvqIpk20FA= -github.com/whyrusleeping/mdns v0.0.0-20180901202407-ef14215e6b30/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4= -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/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee h1:lYbXeSvJi5zk5GLKVuid9TVjS9a0OmLIDKTfoZBL6Ow= -github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee/go.mod h1:m2aV4LZI4Aez7dP5PMyVKEHhUyEJ/RjmPEDOpDvudHg= +github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= +github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU= +github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= -github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= -github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 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= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= +github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI= +github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE= +github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= +github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= -go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= 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= @@ -1389,110 +976,93 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 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.32.0 h1:mac9BKRqwaX6zxHPDe3pvmWpwuuIM0vuXv2juCnQevE= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.32.0/go.mod h1:5eCOqeGphOyz6TsY3ZDNjE33SM/TFAK3RGuCL2naTgY= -go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= -go.opentelemetry.io/otel v1.7.0 h1:Z2lA3Tdch0iDcrhJXDIlC94XE+bxok1F9B+4Lz/lGsM= -go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk= -go.opentelemetry.io/otel/exporters/jaeger v1.7.0 h1:wXgjiRldljksZkZrldGVe6XrG9u3kYDyQmkZwmm5dI0= -go.opentelemetry.io/otel/exporters/jaeger v1.7.0/go.mod h1:PwQAOqBgqbLQRKlj466DuD2qyMjbtcPpfPfj+AqbSBs= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.7.0 h1:7Yxsak1q4XrJ5y7XBnNwqWx9amMZvoidCctv62XOQ6Y= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.7.0/go.mod h1:M1hVZHNxcbkAlcvrOMlpQ4YOO3Awf+4N2dxkZL3xm04= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.7.0 h1:cMDtmgJ5FpRvqx9x2Aq+Mm0O6K/zcUkH73SFz20TuBw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.7.0/go.mod h1:ceUgdyfNv4h4gLxHR0WNfDiiVmZFodZhZSbOLhpxqXE= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.7.0 h1:MFAyzUPrTwLOwCi+cltN0ZVyy4phU41lwH+lyMyQTS4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.7.0/go.mod h1:E+/KKhwOSw8yoPxSSuUHG6vKppkvhN+S1Jc7Nib3k3o= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.7.0 h1:pLP0MH4MAqeTEV0g/4flxw9O8Is48uAIauAnjznbW50= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.7.0/go.mod h1:aFXT9Ng2seM9eizF+LfKiyPBGy8xIZKwhusC1gIu3hA= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.7.0 h1:8hPcgCg0rUJiKE6VWahRvjgLUrNl7rW2hffUEPKXVEM= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.7.0/go.mod h1:K4GDXPY6TjUiwbOh+DkKaEdCF8y+lvMoM6SeAPyfCCM= -go.opentelemetry.io/otel/exporters/zipkin v1.7.0 h1:X0FZj+kaIdLi29UiyrEGDhRTYsEXj9GdEW5Y39UQFEE= -go.opentelemetry.io/otel/exporters/zipkin v1.7.0/go.mod h1:9YBXeOMFLQGwNEjsxMRiWPGoJX83usGMhbCmxUbNe5I= -go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= -go.opentelemetry.io/otel/metric v0.30.0 h1:Hs8eQZ8aQgs0U49diZoaS6Uaxw3+bBE3lcMUKBFIk3c= -go.opentelemetry.io/otel/metric v0.30.0/go.mod h1:/ShZ7+TS4dHzDFmfi1kSXMhMVubNoP0oIaBp70J6UXU= -go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= -go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= -go.opentelemetry.io/otel/sdk v1.7.0 h1:4OmStpcKVOfvDOgCt7UriAPtKolwIhxpnSNI/yK+1B0= -go.opentelemetry.io/otel/sdk v1.7.0/go.mod h1:uTEOTwaqIVuTGiJN7ii13Ibp75wJmYUDe374q6cZwUU= -go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= -go.opentelemetry.io/otel/trace v1.7.0 h1:O37Iogk1lEkMRXewVtZ1BBTVn5JEp8GrJvP92bJqC6o= -go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.16.0 h1:WHzDWdXUvbc5bG2ObdrGfaNpQz7ft7QN9HHmJlbiB1E= -go.opentelemetry.io/proto/otlp v0.16.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM= +go.opentelemetry.io/contrib/propagators/autoprop v0.46.1 h1:cXTYcMjY0dsYokAuo8LbNBQxpF8VgTHdiHJJ1zlIXl4= +go.opentelemetry.io/contrib/propagators/autoprop v0.46.1/go.mod h1:WZxgny1/6+j67B1s72PLJ4bGjidoWFzSmLNfJKVt2bo= +go.opentelemetry.io/contrib/propagators/aws v1.21.1 h1:uQIQIDWb0gzyvon2ICnghpLAf9w7ADOCUiIiwCQgR2o= +go.opentelemetry.io/contrib/propagators/aws v1.21.1/go.mod h1:kCcto3ACQxm+VrkQX/NK/TkDmAd99MQhvffzyTKhzL4= +go.opentelemetry.io/contrib/propagators/b3 v1.21.1 h1:WPYiUgmw3+b7b3sQ1bFBFAf0q+Di9dvNc3AtYfnT4RQ= +go.opentelemetry.io/contrib/propagators/b3 v1.21.1/go.mod h1:EmzokPoSqsYMBVK4nRnhsfm5mbn8J1eDuz/U1UaQaWg= +go.opentelemetry.io/contrib/propagators/jaeger v1.21.1 h1:f4beMGDKiVzg9IcX7/VuWVy+oGdjx3dNJ72YehmtY5k= +go.opentelemetry.io/contrib/propagators/jaeger v1.21.1/go.mod h1:U9jhkEl8d1LL+QXY7q3kneJWJugiN3kZJV2OWz3hkBY= +go.opentelemetry.io/contrib/propagators/ot v1.21.1 h1:3TN5vkXjKYWp0YdMcnUEC/A+pBPvqz9V3nCS2xmcurk= +go.opentelemetry.io/contrib/propagators/ot v1.21.1/go.mod h1:oy0MYCbS/b3cqUDW37wBWtlwBIsutngS++Lklpgh+fc= +go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= +go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 h1:K0XaT3DwHAcV4nKLzcQvwAgSyisUghWoY20I7huthMk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0/go.mod h1:B5Ki776z/MBnVha1Nzwp5arlzBbE3+1jk+pGmaP5HME= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0 h1:FFeLy03iVTXP6ffeN2iXrxfGsZGCjVx0/4KlizjyBwU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0/go.mod h1:TMu73/k1CP8nBUpDLc71Wj/Kf7ZS9FK5b53VapRsP9o= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 h1:lUsI2TYsQw2r1IASwoROaCnjdj2cvC2+Jbxvk6nHnWU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0/go.mod h1:2HpZxxQurfGxJlJDblybejHB6RX6pmExPNe517hREw4= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0 h1:UGZ1QwZWY67Z6BmckTU+9Rxn04m2bD3gD6Mk0OIOCPk= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0/go.mod h1:fcwWuDuaObkkChiDlhEpSq9+X1C0omv+s5mBtToAQ64= +go.opentelemetry.io/otel/exporters/zipkin v1.31.0 h1:CgucL0tj3717DJnni7HVVB2wExzi8c2zJNEA2BhLMvI= +go.opentelemetry.io/otel/exporters/zipkin v1.31.0/go.mod h1:rfzOVNiSwIcWtEC2J8epwG26fiaXlYvLySJ7bwsrtAE= +go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE= +go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY= +go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk= +go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0= +go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys= +go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 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.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= -go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/dig v1.15.0 h1:vq3YWr8zRj1eFGC7Gvf907hE0eRjPTZ1d3xHadD6liE= -go.uber.org/dig v1.15.0/go.mod h1:pKHs0wMynzL6brANhB2hLMro+zalv1osARTviTcqHLM= -go.uber.org/fx v1.18.2 h1:bUNI6oShr+OVFQeU8cDNbnN7VFsu+SsjHzUF51V/GAU= -go.uber.org/fx v1.18.2/go.mod h1:g0V1KMQ66zIRk8bLu3Ea5Jt2w/cHlOIp4wdRsgh0JaY= -go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +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.18.0 h1:imUL1UiY0Mg4bqbFfsRQO5G4CGRBec/ZujWTvSVp3pw= +go.uber.org/dig v1.18.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= +go.uber.org/fx v1.23.0 h1:lIr/gYWQGfTwGcSXWXu4vP5Ws6iqnNEIY+F/aFzCKTg= +go.uber.org/fx v1.23.0/go.mod h1:o/D9n+2mLP6v1EG+qsdT1O8wKopYAsqZasju97SDFCU= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= -go.uber.org/goleak v1.1.12/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.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= +go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= 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.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= -go.uber.org/multierr v1.8.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.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= -go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= -go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= -go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +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 h1:BNJlw5kRTzdmyfh5U8F93HA2OwkP7ZGwA51eJ/0wKOU= 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-20181029021203-45a5f77698d3/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-20190530122614-20be4c3c3ed5/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-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/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-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/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-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/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-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210920023735-84f357641f63/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A= -golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +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.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= 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-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= 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= @@ -1500,9 +1070,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 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-20210615023648-acb5c1269671/go.mod h1:DVyR6MI7P4kEQgvZJSj1fQGrWIi2RzIrfYWycwheUAc= -golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= -golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY= +golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= 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= @@ -1518,30 +1087,24 @@ golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPI golang.org/x/lint v0.0.0-20200302205851-738671d3881b/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/mobile v0.0.0-20201217150744-e6ae53a27f4f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= 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.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +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.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= 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-20181023162649-9b4f9f5ad519/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-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/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-20190125091013-d26f9f9a57f3/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= @@ -1549,15 +1112,11 @@ golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn 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-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 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-20190813141303-74dc4d7220e7/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-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= @@ -1572,24 +1131,27 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/ 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-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 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-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= 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-20210428140749-89ef3d95e781/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-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/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-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.3.0 h1:VWL6FNY2bEEmsGVKabSlHu5Irp34xmMRoqb/9lF9lxk= -golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +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.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= 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= @@ -1598,9 +1160,9 @@ golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4Iltr 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-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.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= @@ -1613,48 +1175,38 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ 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.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +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.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.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-20180823144017-11551d06cbcc/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-20181026203630-95b1ffbd15a5/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-20181107165924-66b7b1311ac8/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-20181122145206-62eef0e2fa9b/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-20190302025703-b6889370fb10/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-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-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-20190524122548-abf6ff778158/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190526052359-791d8a0f4d09/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/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-20190826190057-c7b8b68b1456/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-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-20191220142924-d4481acd189f/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= @@ -1670,58 +1222,70 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w 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-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-20210112080510-489259a85091/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-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-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210317225723-c4fcb01b228e/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-20210514084401-e8d321eab015/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-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-20220412211240-33da011f77ad/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-20220704084225-05e143d24a9e/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-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20221010170243-090e33056c14/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.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.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.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.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +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.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.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= 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.5/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.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +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.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.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= 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= @@ -1745,17 +1309,13 @@ golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtn 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-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 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-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/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= @@ -1777,22 +1337,23 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.3.0 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM= -golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= +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.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= 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-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U= -golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= +golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/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.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= 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= @@ -1816,7 +1377,6 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 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 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= google.golang.org/appengine v1.6.6/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= @@ -1827,7 +1387,6 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn 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-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= 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= @@ -1846,44 +1405,34 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/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-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1 h1:b9mVrqYfq3P4bCdaLg1qtBnPzUYgglsIdjZkL/fQVOE= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 h1:T6rh4haD3GVYsgEfWExoCZA2o2FmbNyKpTuAxbEFPTg= +google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:wp2WsuBYj6j8wUdo3ToZsdxxixbvQNAHqVJrTgi5E5M= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 h1:QCqS/PdaHTSWGvupk2F/ehwHtGc0/GYkT+3GAcR1CCc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= 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.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/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.28.1/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.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.46.0 h1:oCjezcn6g6A75TGoKYBPgKmVBLexhYLM6MebdrPApP8= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= +google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= 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= @@ -1896,33 +1445,25 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj 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.27.1/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 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= 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/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= 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/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= -gopkg.in/square/go-jose.v2 v2.5.1/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/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 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= @@ -1942,16 +1483,12 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh 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= -honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= -lukechampine.com/blake3 v1.1.6/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= -lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0= -lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= -pgregory.net/rapid v0.4.7 h1:MTNRktPuv5FNqOO151TM9mDTa+XHcX6ypYeISDVD14g= -pgregory.net/rapid v0.4.7/go.mod h1:UYpPVyjFHzYBGHIxLFoupi8vwk6rXNzRY9OMvVxFIOU= +lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= +lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= +pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= +pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= 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= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= 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= diff --git a/misc/README.md b/misc/README.md index 8462532f6..28511d3fc 100644 --- a/misc/README.md +++ b/misc/README.md @@ -17,131 +17,7 @@ Description=IPFS daemon [Service] # Environment="IPFS_PATH=/data/ipfs" # optional path to ipfs init directory if not default ($HOME/.ipfs) -ExecStart=/usr/bin/ipfs daemon -Restart=on-failure - -[Install] -WantedBy=default.target -``` - -To run this in your user session, save it as `~/.config/systemd/user/ipfs.service` (creating directories as necessary). Once you run `ipfs init` to create your IPFS settings, you can control the daemon using the following commands: - -* `systemctl --user start ipfs` - start the daemon -* `systemctl --user stop ipfs` - stop the daemon -* `systemctl --user status ipfs` - get status of the daemon -* `systemctl --user enable ipfs` - enable starting the daemon at boot -* `systemctl --user disable ipfs` - disable starting the daemon at boot - -*Note:* If you want this `--user` service to run at system boot, you must [`enable-linger`](http://www.freedesktop.org/software/systemd/man/loginctl.html) on the account that runs the service: - -``` -# loginctl enable-linger [user] -``` -Read more about `--user` services here: [wiki.archlinux.org:Systemd ](https://wiki.archlinux.org/index.php/Systemd/User#Automatic_start-up_of_systemd_user_instances) - -### initd - -- Here is a full-featured sample service file: https://github.com/dylanPowers/ipfs-linux-service/blob/master/init.d/ipfs -- Use `service` or your distribution's equivalent to control the service. - -## upstart - -- And below is a very basic sample upstart job. **Note the username jbenet**. - -``` -cat /etc/init/ipfs.conf -``` -``` -description "ipfs: interplanetary filesystem" - -start on (local-filesystems and net-device-up IFACE!=lo) -stop on runlevel [!2345] - -limit nofile 524288 1048576 -limit nproc 524288 1048576 -setuid jbenet -chdir /home/jbenet -respawn -exec ipfs daemon -``` - -Another version is available here: - -```sh -ipfs cat /ipfs/QmbYCwVeA23vz6mzAiVQhJNa2JSiRH4ebef1v2e5EkDEZS/ipfs.conf >/etc/init/ipfs.conf -``` - -For both, edit to replace occurrences of `jbenet` with whatever user you want it to run as: - -```sh -sed -i s/jbenet// /etc/init/ipfs.conf -``` - -Once you run `ipfs init` to create your IPFS settings, you can control the daemon using the `init.d` commands: - -```sh -sudo service ipfs start -sudo service ipfs stop -sudo service ipfs restart -... -``` - -## launchd - -Similar to `systemd`, on macOS you can run `go-ipfs` via a user LaunchAgent. - -- Create `~/Library/LaunchAgents/io.ipfs.go-ipfs.plist`: - -```xml - - - - - KeepAlive - - Label - io.ipfs.go-ipfs - ProcessType - Background - ProgramArguments - - /bin/sh - -c - ~/go/bin/ipfs daemon - - RunAtLoad - - - -``` -The reason for running `ipfs` under a shell is to avoid needing to hard-code the user's home directory in the job. - -- To start the job, run `launchctl load ~/Library/LaunchAgents/io.ipfs.go-ipfs.plist` - -Notes: - -- To check that the job is running, run `launchctl list | grep ipfs`. -- IPFS should now start whenever you log in (and exit when you log out). -- [LaunchControl](http://www.soma-zone.com/LaunchControl/) is a GUI tool which simplifies management of LaunchAgents.## init system integration - -go-ipfs can be started by your operating system's native init system. - -- [systemd](#systemd) -- [LSB init script](#initd) -- [Upstart/startup job](#upstart) -- [launchd](#launchd) - -### systemd - -For `systemd`, the best approach is to run the daemon in a user session. Here is a sample service file: - -```systemd -[Unit] -Description=IPFS daemon - -[Service] -# Environment="IPFS_PATH=/data/ipfs" # optional path to ipfs init directory if not default ($HOME/.ipfs) -ExecStart=/usr/bin/ipfs daemon +ExecStart=/usr/local/bin/ipfs daemon Restart=on-failure [Install] diff --git a/misc/fsutil/fsutil.go b/misc/fsutil/fsutil.go new file mode 100644 index 000000000..6773ec12f --- /dev/null +++ b/misc/fsutil/fsutil.go @@ -0,0 +1,82 @@ +package fsutil + +import ( + "errors" + "fmt" + "io/fs" + "os" + "path/filepath" +) + +// DirWritable checks if a directory is writable. If the directory does +// not exist it is created with writable permission. +func DirWritable(dir string) error { + if dir == "" { + return errors.New("directory not specified") + } + + var err error + dir, err = ExpandHome(dir) + if err != nil { + return err + } + + fi, err := os.Stat(dir) + if err != nil { + if errors.Is(err, fs.ErrNotExist) { + // Directory does not exist, so create it. + err = os.Mkdir(dir, 0775) + if err == nil { + return nil + } + } + if errors.Is(err, fs.ErrPermission) { + err = fs.ErrPermission + } + return fmt.Errorf("directory not writable: %s: %w", dir, err) + } + if !fi.IsDir() { + return fmt.Errorf("not a directory: %s", dir) + } + + // Directory exists, check that a file can be written. + file, err := os.CreateTemp(dir, "writetest") + if err != nil { + if errors.Is(err, fs.ErrPermission) { + err = fs.ErrPermission + } + return fmt.Errorf("directory not writable: %s: %w", dir, err) + } + file.Close() + return os.Remove(file.Name()) +} + +// ExpandHome expands the path to include the home directory if the path is +// prefixed with `~`. If it isn't prefixed with `~`, the path is returned +// as-is. +func ExpandHome(path string) (string, error) { + if path == "" { + return path, nil + } + + if path[0] != '~' { + return path, nil + } + + if len(path) > 1 && path[1] != '/' && path[1] != '\\' { + return "", errors.New("cannot expand user-specific home dir") + } + + dir, err := os.UserHomeDir() + if err != nil { + return "", err + } + + return filepath.Join(dir, path[1:]), nil +} + +// FileExists return true if the file exists +func FileExists(filename string) bool { + _, err := os.Lstat(filename) + return !errors.Is(err, os.ErrNotExist) +} diff --git a/misc/fsutil/fsutil_test.go b/misc/fsutil/fsutil_test.go new file mode 100644 index 000000000..72834ac10 --- /dev/null +++ b/misc/fsutil/fsutil_test.go @@ -0,0 +1,92 @@ +package fsutil_test + +import ( + "io/fs" + "os" + "path/filepath" + "runtime" + "testing" + + "github.com/ipfs/kubo/misc/fsutil" + "github.com/stretchr/testify/require" +) + +func TestDirWritable(t *testing.T) { + err := fsutil.DirWritable("") + require.Error(t, err) + + err = fsutil.DirWritable("~nosuchuser/tmp") + require.Error(t, err) + + tmpDir := t.TempDir() + + wrDir := filepath.Join(tmpDir, "readwrite") + err = fsutil.DirWritable(wrDir) + require.NoError(t, err) + + // Check that DirWritable created directory. + fi, err := os.Stat(wrDir) + require.NoError(t, err) + require.True(t, fi.IsDir()) + + err = fsutil.DirWritable(wrDir) + require.NoError(t, err) + + // If running on Windows, skip read-only directory tests. + if runtime.GOOS == "windows" { + t.SkipNow() + } + + roDir := filepath.Join(tmpDir, "readonly") + require.NoError(t, os.Mkdir(roDir, 0500)) + err = fsutil.DirWritable(roDir) + require.ErrorIs(t, err, fs.ErrPermission) + + roChild := filepath.Join(roDir, "child") + err = fsutil.DirWritable(roChild) + require.ErrorIs(t, err, fs.ErrPermission) +} + +func TestFileExists(t *testing.T) { + fileName := filepath.Join(t.TempDir(), "somefile") + require.False(t, fsutil.FileExists(fileName)) + + file, err := os.Create(fileName) + require.NoError(t, err) + file.Close() + + require.True(t, fsutil.FileExists(fileName)) +} + +func TestExpandHome(t *testing.T) { + dir, err := fsutil.ExpandHome("") + require.NoError(t, err) + require.Equal(t, "", dir) + + origDir := filepath.Join("somedir", "somesub") + dir, err = fsutil.ExpandHome(origDir) + require.NoError(t, err) + require.Equal(t, origDir, dir) + + _, err = fsutil.ExpandHome(filepath.FromSlash("~nosuchuser/somedir")) + require.Error(t, err) + + homeEnv := "HOME" + if runtime.GOOS == "windows" { + homeEnv = "USERPROFILE" + } + origHome := os.Getenv(homeEnv) + defer os.Setenv(homeEnv, origHome) + homeDir := filepath.Join(t.TempDir(), "testhome") + os.Setenv(homeEnv, homeDir) + + const subDir = "mytmp" + origDir = filepath.Join("~", subDir) + dir, err = fsutil.ExpandHome(origDir) + require.NoError(t, err) + require.Equal(t, filepath.Join(homeDir, subDir), dir) + + os.Unsetenv(homeEnv) + _, err = fsutil.ExpandHome(origDir) + require.Error(t, err) +} diff --git a/misc/systemd/ipfs-hardened.service b/misc/systemd/ipfs-hardened.service index 261b82e2a..785a59829 100644 --- a/misc/systemd/ipfs-hardened.service +++ b/misc/systemd/ipfs-hardened.service @@ -8,7 +8,7 @@ # To overwrite a variable, like ExecStart you have to specify it once # blank and a second time with a new value, like: # ExecStart= -# ExecStart=/usr/bin/ipfs daemon --flag1 --flag2 +# ExecStart=/usr/local/bin/ipfs daemon --flag1 --flag2 # # For more info about custom unit files see systemd.unit(5). @@ -70,7 +70,7 @@ User=ipfs Group=ipfs StateDirectory=ipfs Environment=IPFS_PATH="${HOME}" -ExecStart=/usr/bin/ipfs daemon --init --migrate +ExecStart=/usr/local/bin/ipfs daemon --init --migrate Restart=on-failure KillSignal=SIGINT diff --git a/misc/systemd/ipfs.service b/misc/systemd/ipfs.service index 0051dffdd..ab074e22e 100644 --- a/misc/systemd/ipfs.service +++ b/misc/systemd/ipfs.service @@ -8,7 +8,7 @@ # To overwrite a variable, like ExecStart you have to specify it once # blank and a second time with a new value, like: # ExecStart= -# ExecStart=/usr/bin/ipfs daemon --flag1 --flag2 +# ExecStart=/usr/local/bin/ipfs daemon --flag1 --flag2 # # For more info about custom unit files see systemd.unit(5). @@ -41,7 +41,7 @@ User=ipfs Group=ipfs StateDirectory=ipfs Environment=IPFS_PATH="${HOME}" -ExecStart=/usr/bin/ipfs daemon --init --migrate +ExecStart=/usr/local/bin/ipfs daemon --init --migrate Restart=on-failure KillSignal=SIGINT diff --git a/p2p/listener.go b/p2p/listener.go index 9b140023b..f5942ffa0 100644 --- a/p2p/listener.go +++ b/p2p/listener.go @@ -10,38 +10,38 @@ import ( ma "github.com/multiformats/go-multiaddr" ) -// Listener listens for connections and proxies them to a target +// Listener listens for connections and proxies them to a target. type Listener interface { Protocol() protocol.ID ListenAddress() ma.Multiaddr TargetAddress() ma.Multiaddr - key() string + key() protocol.ID // close closes the listener. Does not affect child streams close() } // Listeners manages a group of Listener implementations, -// checking for conflicts and optionally dispatching connections +// checking for conflicts and optionally dispatching connections. type Listeners struct { sync.RWMutex - Listeners map[string]Listener + Listeners map[protocol.ID]Listener } func newListenersLocal() *Listeners { return &Listeners{ - Listeners: map[string]Listener{}, + Listeners: map[protocol.ID]Listener{}, } } func newListenersP2P(host p2phost.Host) *Listeners { reg := &Listeners{ - Listeners: map[string]Listener{}, + Listeners: map[protocol.ID]Listener{}, } - host.SetStreamHandlerMatch("/x/", func(p string) bool { + host.SetStreamHandlerMatch("/x/", func(p protocol.ID) bool { reg.RLock() defer reg.RUnlock() @@ -51,7 +51,7 @@ func newListenersP2P(host p2phost.Host) *Listeners { reg.RLock() defer reg.RUnlock() - l := reg.Listeners[string(stream.Protocol())] + l := reg.Listeners[stream.Protocol()] if l != nil { go l.(*remoteListener).handleStream(stream) } @@ -60,7 +60,7 @@ func newListenersP2P(host p2phost.Host) *Listeners { return reg } -// Register registers listenerInfo into this registry and starts it +// Register registers listenerInfo into this registry and starts it. func (r *Listeners) Register(l Listener) error { r.Lock() defer r.Unlock() diff --git a/p2p/local.go b/p2p/local.go index 17e1f1aee..98028c5d4 100644 --- a/p2p/local.go +++ b/p2p/local.go @@ -9,10 +9,10 @@ import ( "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/protocol" ma "github.com/multiformats/go-multiaddr" - "github.com/multiformats/go-multiaddr/net" + manet "github.com/multiformats/go-multiaddr/net" ) -// localListener manet streams and proxies them to libp2p services +// localListener manet streams and proxies them to libp2p services. type localListener struct { ctx context.Context @@ -25,7 +25,7 @@ type localListener struct { listener manet.Listener } -// ForwardLocal creates new P2P stream to a remote listener +// ForwardLocal creates new P2P stream to a remote listener. func (p2p *P2P) ForwardLocal(ctx context.Context, peer peer.ID, proto protocol.ID, bindAddr ma.Multiaddr) (Listener, error) { listener := &localListener{ ctx: ctx, @@ -52,7 +52,7 @@ func (p2p *P2P) ForwardLocal(ctx context.Context, peer peer.ID, proto protocol.I } func (l *localListener) dial(ctx context.Context) (net.Stream, error) { - cctx, cancel := context.WithTimeout(ctx, time.Second*30) //TODO: configurable? + cctx, cancel := context.WithTimeout(ctx, time.Second*30) // TODO: configurable? defer cancel() return l.p2p.peerHost.NewStream(cctx, l.peer, l.proto) @@ -76,7 +76,7 @@ func (l *localListener) setupStream(local manet.Conn) { remote, err := l.dial(l.ctx) if err != nil { local.Close() - log.Warnf("failed to dial to remote %s/%s", l.peer.Pretty(), l.proto) + log.Warnf("failed to dial to remote %s/%s", l.peer, l.proto) return } @@ -109,13 +109,13 @@ func (l *localListener) ListenAddress() ma.Multiaddr { } func (l *localListener) TargetAddress() ma.Multiaddr { - addr, err := ma.NewMultiaddr(maPrefix + l.peer.Pretty()) + addr, err := ma.NewMultiaddr(maPrefix + l.peer.String()) if err != nil { panic(err) } return addr } -func (l *localListener) key() string { - return l.ListenAddress().String() +func (l *localListener) key() protocol.ID { + return protocol.ID(l.ListenAddress().String()) } diff --git a/p2p/p2p.go b/p2p/p2p.go index c429760b0..1d0989421 100644 --- a/p2p/p2p.go +++ b/p2p/p2p.go @@ -3,13 +3,14 @@ package p2p import ( logging "github.com/ipfs/go-log" p2phost "github.com/libp2p/go-libp2p/core/host" - peer "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/peer" pstore "github.com/libp2p/go-libp2p/core/peerstore" + "github.com/libp2p/go-libp2p/core/protocol" ) var log = logging.Logger("p2p-mount") -// P2P structure holds information on currently running streams/Listeners +// P2P structure holds information on currently running streams/Listeners. type P2P struct { ListenersLocal *Listeners ListenersP2P *Listeners @@ -20,7 +21,7 @@ type P2P struct { peerstore pstore.Peerstore } -// New creates new P2P struct +// New creates new P2P struct. func New(identity peer.ID, peerHost p2phost.Host, peerstore pstore.Peerstore) *P2P { return &P2P{ identity: identity, @@ -39,8 +40,8 @@ func New(identity peer.ID, peerHost p2phost.Host, peerstore pstore.Peerstore) *P } // CheckProtoExists checks whether a proto handler is registered to -// mux handler -func (p2p *P2P) CheckProtoExists(proto string) bool { +// mux handler. +func (p2p *P2P) CheckProtoExists(proto protocol.ID) bool { protos := p2p.peerHost.Mux().Protocols() for _, p := range protos { diff --git a/p2p/remote.go b/p2p/remote.go index d10600486..b867cb313 100644 --- a/p2p/remote.go +++ b/p2p/remote.go @@ -5,14 +5,14 @@ import ( "fmt" net "github.com/libp2p/go-libp2p/core/network" - protocol "github.com/libp2p/go-libp2p/core/protocol" + "github.com/libp2p/go-libp2p/core/protocol" ma "github.com/multiformats/go-multiaddr" manet "github.com/multiformats/go-multiaddr/net" ) var maPrefix = "/" + ma.ProtocolWithCode(ma.P_IPFS).Name + "/" -// remoteListener accepts libp2p streams and proxies them to a manet host +// remoteListener accepts libp2p streams and proxies them to a manet host. type remoteListener struct { p2p *P2P @@ -27,7 +27,7 @@ type remoteListener struct { reportRemote bool } -// ForwardRemote creates new p2p listener +// ForwardRemote creates new p2p listener. func (p2p *P2P) ForwardRemote(ctx context.Context, proto protocol.ID, addr ma.Multiaddr, reportRemote bool) (Listener, error) { listener := &remoteListener{ p2p: p2p, @@ -55,13 +55,13 @@ func (l *remoteListener) handleStream(remote net.Stream) { peer := remote.Conn().RemotePeer() if l.reportRemote { - if _, err := fmt.Fprintf(local, "%s\n", peer.Pretty()); err != nil { + if _, err := fmt.Fprintf(local, "%s\n", peer); err != nil { _ = remote.Reset() return } } - peerMa, err := ma.NewMultiaddr(maPrefix + peer.Pretty()) + peerMa, err := ma.NewMultiaddr(maPrefix + peer.String()) if err != nil { _ = remote.Reset() return @@ -88,7 +88,7 @@ func (l *remoteListener) Protocol() protocol.ID { } func (l *remoteListener) ListenAddress() ma.Multiaddr { - addr, err := ma.NewMultiaddr(maPrefix + l.p2p.identity.Pretty()) + addr, err := ma.NewMultiaddr(maPrefix + l.p2p.identity.String()) if err != nil { panic(err) } @@ -101,6 +101,6 @@ func (l *remoteListener) TargetAddress() ma.Multiaddr { func (l *remoteListener) close() {} -func (l *remoteListener) key() string { - return string(l.proto) +func (l *remoteListener) key() protocol.ID { + return l.proto } diff --git a/p2p/stream.go b/p2p/stream.go index ec8bac508..5d05cba79 100644 --- a/p2p/stream.go +++ b/p2p/stream.go @@ -30,12 +30,12 @@ type Stream struct { Registry *StreamRegistry } -// close stream endpoints and deregister it +// close stream endpoints and deregister it. func (s *Stream) close() { s.Registry.Close(s) } -// reset closes stream endpoints and deregisters it +// reset closes stream endpoints and deregisters it. func (s *Stream) reset() { s.Registry.Reset(s) } @@ -71,7 +71,7 @@ type StreamRegistry struct { ifconnmgr.ConnManager } -// Register registers a stream to the registry +// Register registers a stream to the registry. func (r *StreamRegistry) Register(streamInfo *Stream) { r.Lock() defer r.Unlock() @@ -86,7 +86,7 @@ func (r *StreamRegistry) Register(streamInfo *Stream) { streamInfo.startStreaming() } -// Deregister deregisters stream from the registry +// Deregister deregisters stream from the registry. func (r *StreamRegistry) Deregister(streamID uint64) { r.Lock() defer r.Unlock() @@ -105,14 +105,14 @@ func (r *StreamRegistry) Deregister(streamID uint64) { delete(r.Streams, streamID) } -// Close stream endpoints and deregister it +// Close stream endpoints and deregister it. func (r *StreamRegistry) Close(s *Stream) { _ = s.Local.Close() _ = s.Remote.Close() s.Registry.Deregister(s.id) } -// Reset closes stream endpoints and deregisters it +// Reset closes stream endpoints and deregisters it. func (r *StreamRegistry) Reset(s *Stream) { _ = s.Local.Close() _ = s.Remote.Reset() diff --git a/package-list.json b/package-list.json deleted file mode 100644 index 7dfc5648c..000000000 --- a/package-list.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "columns": [ - "Name", - "CI/Travis", - "Coverage", - "Description" - ], - "rows": [ - "Libp2p", - ["libp2p/go-libp2p", "go-libp2p", "p2p networking library"], - ["libp2p/go-libp2p-pubsub", "go-libp2p-pubsub", "pubsub built on libp2p"], - ["libp2p/go-libp2p-kad-dht", "go-libp2p-kad-dht", "dht-backed router"], - ["libp2p/go-libp2p-pubsub-router", "go-libp2p-pubsub-router", "pubsub-backed router"], - - "Multiformats", - ["ipfs/go-cid", "go-cid", "CID implementation"], - ["multiformats/go-multiaddr", "go-multiaddr", "multiaddr implementation"], - ["multiformats/go-multihash", "go-multihash", "multihash implementation"], - ["multiformats/go-multibase", "go-multibase", "mulitbase implementation"], - - "Files", - ["ipfs/go-unixfs", "go-unixfs", "the core 'filesystem' logic"], - ["ipfs/go-mfs", "go-mfs", "a mutable filesystem editor for unixfs"], - ["ipfs/go-ipfs-posinfo", "go-ipfs-posinfo", "helper datatypes for the filestore"], - ["ipfs/go-ipfs-chunker", "go-ipfs-chunker", "file chunkers"], - - "Exchange", - ["ipfs/go-ipfs-exchange-interface", "go-ipfs-exchange-interface", "exchange service interface"], - ["ipfs/go-ipfs-exchange-offline", "go-ipfs-exchange-offline", "(dummy) offline implementation of the exchange service"], - ["ipfs/go-bitswap", "go-bitswap", "bitswap protocol implementation"], - ["ipfs/go-blockservice", "go-blockservice", "service that plugs a blockstore and an exchange together"], - - "Datastores", - ["ipfs/go-datastore", "go-datastore", "datastore interfaces, adapters, and basic implementations"], - ["ipfs/go-ipfs-ds-help", "go-ipfs-ds-help", "datastore utility functions"], - ["ipfs/go-ds-flatfs", "go-ds-flatfs", "a filesystem-based datastore"], - ["ipfs/go-ds-measure", "go-ds-measure", "a metric-collecting database adapter"], - ["ipfs/go-ds-leveldb", "go-ds-leveldb", "a leveldb based datastore"], - ["ipfs/go-ds-badger", "go-ds-badger", "a badgerdb based datastore"], - - "Namesys", - ["ipfs/go-ipns", "go-ipns", "IPNS datastructures and validation logic"], - - "Repo", - ["ipfs/go-fs-lock", "go-fs-lock", "lockfile management functions"], - ["ipfs/fs-repo-migrations", "fs-repo-migrations", "repo migrations"], - - "IPLD", - ["ipfs/go-block-format", "go-block-format", "block interfaces and implementations"], - ["ipfs/go-ipfs-blockstore", "go-ipfs-blockstore", "blockstore interfaces and implementations"], - ["ipfs/go-ipld-format", "go-ipld-format", "IPLD interfaces"], - ["ipfs/go-ipld-cbor", "go-ipld-cbor", "IPLD-CBOR implementation"], - ["ipfs/go-ipld-git", "go-ipld-git", "IPLD-Git implementation"], - ["ipfs/go-merkledag", "go-merkledag", "IPLD-Merkledag implementation (and then some)"], - - "Commands", - ["ipfs/go-ipfs-cmds", "go-ipfs-cmds", "CLI & HTTP commands library"], - ["ipfs/go-ipfs-files", "go-ipfs-files", "CLI & HTTP commands library"], - ["ipfs/go-ipfs-api", "go-ipfs-api", "an old, stable shell for the IPFS HTTP API"], - ["ipfs/go-ipfs-http-client", "go-ipfs-http-client", "a new, unstable shell for the IPFS HTTP API"], - ["ipfs/interface-go-ipfs-core", "interface-go-ipfs-core", "core go-ipfs API interface definitions"], - - "Metrics & Logging", - ["ipfs/go-metrics-interface", "go-metrics-interface", "metrics collection interfaces"], - ["ipfs/go-metrics-prometheus", "go-metrics-prometheus", "prometheus-backed metrics collector"], - ["ipfs/go-log", "go-log", "logging framework"], - - "Generics/Utils", - ["ipfs/go-ipfs-routing", "go-ipfs-routing", "routing (content, peer, value) helpers"], - ["ipfs/go-ipfs-util", "go-ipfs-util", "the kitchen sink"], - ["ipfs/go-ipfs-addr", "go-ipfs-addr", "utility functions for parsing IPFS multiaddrs"] - ] -} diff --git a/peering/peering.go b/peering/peering.go deleted file mode 100644 index 00801626f..000000000 --- a/peering/peering.go +++ /dev/null @@ -1,324 +0,0 @@ -package peering - -import ( - "context" - "errors" - "math/rand" - "strconv" - "sync" - "time" - - "github.com/ipfs/go-log" - "github.com/libp2p/go-libp2p/core/host" - "github.com/libp2p/go-libp2p/core/network" - "github.com/libp2p/go-libp2p/core/peer" - "github.com/multiformats/go-multiaddr" -) - -// Seed the random number generator. -// -// We don't need good randomness, but we do need randomness. -const ( - // maxBackoff is the maximum time between reconnect attempts. - maxBackoff = 10 * time.Minute - // The backoff will be cut off when we get within 10% of the actual max. - // If we go over the max, we'll adjust the delay down to a random value - // between 90-100% of the max backoff. - maxBackoffJitter = 10 // % - connmgrTag = "ipfs-peering" - // This needs to be sufficient to prevent two sides from simultaneously - // dialing. - initialDelay = 5 * time.Second -) - -var logger = log.Logger("peering") - -type State uint - -func (s State) String() string { - switch s { - case StateInit: - return "init" - case StateRunning: - return "running" - case StateStopped: - return "stopped" - default: - return "unkown peering state: " + strconv.FormatUint(uint64(s), 10) - } -} - -const ( - StateInit State = iota - StateRunning - StateStopped -) - -// 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 - - mu sync.Mutex - addrs []multiaddr.Multiaddr - reconnectTimer *time.Timer - - nextDelay time.Duration -} - -// 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.mu.Lock() - defer ph.mu.Unlock() - ph.addrs = addrCopy -} - -// getAddrs returns a shared slice of addresses for this peer. Do not modify. -func (ph *peerHandler) getAddrs() []multiaddr.Multiaddr { - ph.mu.Lock() - defer ph.mu.Unlock() - return ph.addrs -} - -// stop permanently stops the peer handler. -func (ph *peerHandler) stop() { - ph.cancel() - - ph.mu.Lock() - defer ph.mu.Unlock() - if ph.reconnectTimer != nil { - ph.reconnectTimer.Stop() - ph.reconnectTimer = nil - } -} - -func (ph *peerHandler) nextBackoff() time.Duration { - if ph.nextDelay < maxBackoff { - ph.nextDelay += ph.nextDelay/2 + time.Duration(rand.Int63n(int64(ph.nextDelay))) - } - - // If we've gone over the max backoff, reduce it under the max. - if ph.nextDelay > maxBackoff { - ph.nextDelay = maxBackoff - // randomize the backoff a bit (10%). - ph.nextDelay -= time.Duration(rand.Int63n(int64(maxBackoff) * maxBackoffJitter / 100)) - } - - return ph.nextDelay -} - -func (ph *peerHandler) reconnect() { - // Try connecting - addrs := ph.getAddrs() - logger.Debugw("reconnecting", "peer", ph.peer, "addrs", addrs) - - err := ph.host.Connect(ph.ctx, peer.AddrInfo{ID: ph.peer, Addrs: addrs}) - if err != nil { - logger.Debugw("failed to reconnect", "peer", ph.peer, "error", err) - // Ok, we failed. Extend the timeout. - ph.mu.Lock() - if ph.reconnectTimer != nil { - // Only counts if the reconnectTimer still exists. If not, a - // connection _was_ somehow established. - ph.reconnectTimer.Reset(ph.nextBackoff()) - } - // Otherwise, someone else has stopped us so we can assume that - // we're either connected or someone else will start us. - ph.mu.Unlock() - } - - // Always call this. We could have connected since we processed the - // error. - ph.stopIfConnected() -} - -func (ph *peerHandler) stopIfConnected() { - ph.mu.Lock() - defer ph.mu.Unlock() - - if ph.reconnectTimer != nil && ph.host.Network().Connectedness(ph.peer) == network.Connected { - logger.Debugw("successfully reconnected", "peer", ph.peer) - ph.reconnectTimer.Stop() - ph.reconnectTimer = nil - ph.nextDelay = initialDelay - } -} - -// startIfDisconnected is the inverse of stopIfConnected. -func (ph *peerHandler) startIfDisconnected() { - ph.mu.Lock() - defer ph.mu.Unlock() - - if ph.reconnectTimer == nil && ph.host.Network().Connectedness(ph.peer) != network.Connected { - logger.Debugw("disconnected from peer", "peer", ph.peer) - // Always start with a short timeout so we can stagger things a bit. - ph.reconnectTimer = time.AfterFunc(ph.nextBackoff(), ph.reconnect) - } -} - -// PeeringService maintains connections to specified peers, reconnecting on -// disconnect with a back-off. -type PeeringService struct { - host host.Host - - mu 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(host host.Host) *PeeringService { - return &PeeringService{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.mu.Lock() - defer ps.mu.Unlock() - - switch ps.state { - case StateInit: - logger.Infow("starting") - case StateRunning: - return nil - case StateStopped: - return errors.New("already stopped") - } - ps.host.Network().Notify((*netNotifee)(ps)) - ps.state = StateRunning - for _, handler := range ps.peers { - go handler.startIfDisconnected() - } - return nil -} - -// GetState get the State of the PeeringService -func (ps *PeeringService) GetState() State { - ps.mu.RLock() - defer ps.mu.RUnlock() - return ps.state -} - -// Stop stops the peering service. -func (ps *PeeringService) Stop() error { - ps.host.Network().StopNotify((*netNotifee)(ps)) - ps.mu.Lock() - defer ps.mu.Unlock() - - switch ps.state { - case StateInit, StateRunning: - logger.Infow("stopping") - 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.mu.Lock() - defer ps.mu.Unlock() - - if handler, ok := ps.peers[info.ID]; ok { - logger.Infow("updating addresses", "peer", info.ID, "addrs", info.Addrs) - handler.setAddrs(info.Addrs) - } else { - logger.Infow("peer added", "peer", info.ID, "addrs", info.Addrs) - ps.host.ConnManager().Protect(info.ID, connmgrTag) - - handler = &peerHandler{ - host: ps.host, - peer: info.ID, - addrs: info.Addrs, - nextDelay: initialDelay, - } - 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.mu.RLock() - defer ps.mu.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.mu.Lock() - defer ps.mu.Unlock() - - if handler, ok := ps.peers[id]; ok { - logger.Infow("peer removed", "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.mu.RLock() - defer ps.mu.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.mu.RLock() - defer ps.mu.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) {} diff --git a/peering/peering_test.go b/peering/peering_test.go deleted file mode 100644 index de07789c2..000000000 --- a/peering/peering_test.go +++ /dev/null @@ -1,172 +0,0 @@ -package peering - -import ( - "context" - "testing" - "time" - - "github.com/libp2p/go-libp2p" - "github.com/libp2p/go-libp2p/core/host" - "github.com/libp2p/go-libp2p/core/network" - "github.com/libp2p/go-libp2p/core/peer" - "github.com/libp2p/go-libp2p/p2p/net/connmgr" - - "github.com/stretchr/testify/require" -) - -func newNode(t *testing.T) host.Host { - cm, err := connmgr.NewConnManager(1, 100, connmgr.WithGracePeriod(0)) - require.NoError(t, err) - h, err := libp2p.New( - libp2p.ListenAddrStrings("/ip4/127.0.0.1/tcp/0"), - // We'd like to set the connection manager low water to 0, but - // that would disable the connection manager. - libp2p.ConnectionManager(cm), - ) - require.NoError(t, err) - return h -} - -func TestPeeringService(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - h1 := newNode(t) - ps1 := NewPeeringService(h1) - - h2 := newNode(t) - h3 := newNode(t) - h4 := newNode(t) - - // peer 1 -> 2 - ps1.AddPeer(peer.AddrInfo{ID: h2.ID(), Addrs: h2.Addrs()}) - require.Contains(t, ps1.ListPeers(), peer.AddrInfo{ID: h2.ID(), Addrs: h2.Addrs()}) - - // We haven't started so we shouldn't have any peers. - require.Never(t, func() bool { - return len(h1.Network().Peers()) > 0 - }, 100*time.Millisecond, 1*time.Second, "expected host 1 to have no peers") - - // Use p4 to take up the one slot we have in the connection manager. - for _, h := range []host.Host{h1, h2} { - require.NoError(t, h.Connect(ctx, peer.AddrInfo{ID: h4.ID(), Addrs: h4.Addrs()})) - h.ConnManager().TagPeer(h4.ID(), "sticky-peer", 1000) - } - - // Now start. - require.NoError(t, ps1.Start()) - // starting twice is fine. - require.NoError(t, ps1.Start()) - - // We should eventually connect. - t.Logf("waiting for h1 to connect to h2") - require.Eventually(t, func() bool { - return h1.Network().Connectedness(h2.ID()) == network.Connected - }, 30*time.Second, 10*time.Millisecond) - - // Now explicitly connect to h3. - t.Logf("waiting for h1's connection to h3 to work") - require.NoError(t, h1.Connect(ctx, peer.AddrInfo{ID: h3.ID(), Addrs: h3.Addrs()})) - require.Eventually(t, func() bool { - return h1.Network().Connectedness(h3.ID()) == network.Connected - }, 30*time.Second, 100*time.Millisecond) - - require.Len(t, h1.Network().Peers(), 3) - - // force a disconnect - h1.ConnManager().TrimOpenConns(ctx) - - // Should disconnect from h3. - t.Logf("waiting for h1's connection to h3 to disconnect") - require.Eventually(t, func() bool { - return h1.Network().Connectedness(h3.ID()) != network.Connected - }, 5*time.Second, 10*time.Millisecond) - - // Should remain connected to p2 - require.Never(t, func() bool { - return h1.Network().Connectedness(h2.ID()) != network.Connected - }, 5*time.Second, 1*time.Second) - - // Now force h2 to disconnect (we have an asymmetric peering). - conns := h2.Network().ConnsToPeer(h1.ID()) - require.NotEmpty(t, conns) - h2.ConnManager().TrimOpenConns(ctx) - - // All conns to peer should eventually close. - t.Logf("waiting for all connections to close") - for _, c := range conns { - require.Eventually(t, func() bool { - s, err := c.NewStream(context.Background()) - if s != nil { - _ = s.Reset() - } - return err != nil - }, 5*time.Second, 10*time.Millisecond) - } - - // Should eventually re-connect. - require.Eventually(t, func() bool { - return h1.Network().Connectedness(h2.ID()) == network.Connected - }, 30*time.Second, 1*time.Second) - - // Unprotect 2 from 1. - ps1.RemovePeer(h2.ID()) - require.NotContains(t, ps1.ListPeers(), peer.AddrInfo{ID: h2.ID(), Addrs: h2.Addrs()}) - - // Trim connections. - h1.ConnManager().TrimOpenConns(ctx) - - // Should disconnect - t.Logf("waiting for h1 to disconnect from h2") - require.Eventually(t, func() bool { - return h1.Network().Connectedness(h2.ID()) != network.Connected - }, 5*time.Second, 10*time.Millisecond) - - // Should never reconnect. - t.Logf("ensuring h1 is not connected to h2 again") - require.Never(t, func() bool { - return h1.Network().Connectedness(h2.ID()) == network.Connected - }, 20*time.Second, 1*time.Second) - - // Until added back - ps1.AddPeer(peer.AddrInfo{ID: h2.ID(), Addrs: h2.Addrs()}) - require.Contains(t, ps1.ListPeers(), peer.AddrInfo{ID: h2.ID(), Addrs: h2.Addrs()}) - ps1.AddPeer(peer.AddrInfo{ID: h3.ID(), Addrs: h3.Addrs()}) - require.Contains(t, ps1.ListPeers(), peer.AddrInfo{ID: h3.ID(), Addrs: h3.Addrs()}) - t.Logf("wait for h1 to connect to h2 and h3 again") - require.Eventually(t, func() bool { - return h1.Network().Connectedness(h2.ID()) == network.Connected - }, 30*time.Second, 1*time.Second) - require.Eventually(t, func() bool { - return h1.Network().Connectedness(h3.ID()) == network.Connected - }, 30*time.Second, 1*time.Second) - - // Should be able to repeatedly stop. - require.NoError(t, ps1.Stop()) - require.NoError(t, ps1.Stop()) - - // Adding and removing should work after stopping. - ps1.AddPeer(peer.AddrInfo{ID: h4.ID(), Addrs: h4.Addrs()}) - require.Contains(t, ps1.ListPeers(), peer.AddrInfo{ID: h4.ID(), Addrs: h4.Addrs()}) - ps1.RemovePeer(h2.ID()) - require.NotContains(t, ps1.ListPeers(), peer.AddrInfo{ID: h2.ID(), Addrs: h2.Addrs()}) -} - -func TestNextBackoff(t *testing.T) { - minMaxBackoff := (100 - maxBackoffJitter) / 100 * maxBackoff - for x := 0; x < 1000; x++ { - ph := peerHandler{nextDelay: time.Second} - for min, max := time.Second*3/2, time.Second*5/2; min < minMaxBackoff; min, max = min*3/2, max*5/2 { - b := ph.nextBackoff() - if b > max || b < min { - t.Errorf("expected backoff %s to be between %s and %s", b, min, max) - } - } - for i := 0; i < 100; i++ { - b := ph.nextBackoff() - if b < minMaxBackoff || b > maxBackoff { - t.Fatal("failed to stay within max bounds") - } - } - } -} diff --git a/plugin/daemon.go b/plugin/daemon.go index 04b18b33b..9f75099d6 100644 --- a/plugin/daemon.go +++ b/plugin/daemon.go @@ -1,7 +1,7 @@ package plugin import ( - coreiface "github.com/ipfs/interface-go-ipfs-core" + coreiface "github.com/ipfs/kubo/core/coreiface" ) // PluginDaemon is an interface for daemon plugins. These plugins will be run on diff --git a/plugin/datastore.go b/plugin/datastore.go index 15d93f3e6..e31a144d4 100644 --- a/plugin/datastore.go +++ b/plugin/datastore.go @@ -5,7 +5,7 @@ import ( ) // PluginDatastore is an interface that can be implemented to add handlers for -// for different datastores +// for different datastores. type PluginDatastore interface { Plugin diff --git a/plugin/ipld.go b/plugin/ipld.go index 2f783a612..d366b92cc 100644 --- a/plugin/ipld.go +++ b/plugin/ipld.go @@ -5,7 +5,7 @@ import ( ) // PluginIPLD is an interface that can be implemented to add handlers for -// for different IPLD codecs +// for different IPLD codecs. type PluginIPLD interface { Plugin diff --git a/plugin/loader/loader.go b/plugin/loader/loader.go index 0742e6a49..4ea3ac226 100644 --- a/plugin/loader/loader.go +++ b/plugin/loader/loader.go @@ -1,6 +1,8 @@ package loader import ( + "encoding/json" + "errors" "fmt" "io" "os" @@ -9,7 +11,6 @@ import ( "strings" config "github.com/ipfs/kubo/config" - cserialize "github.com/ipfs/kubo/config/serialize" "github.com/ipld/go-ipld-prime/multicodec" "github.com/ipfs/kubo/core" @@ -87,25 +88,25 @@ func (ls loaderState) String() string { // 5. Call Close to close all plugins. type PluginLoader struct { state loaderState - plugins map[string]plugin.Plugin + plugins []plugin.Plugin started []plugin.Plugin config config.Plugins repo string } -// NewPluginLoader creates new plugin loader +// NewPluginLoader creates new plugin loader. func NewPluginLoader(repo string) (*PluginLoader, error) { - loader := &PluginLoader{plugins: make(map[string]plugin.Plugin, len(preloadPlugins)), repo: repo} + loader := &PluginLoader{plugins: make([]plugin.Plugin, 0, len(preloadPlugins)), repo: repo} if repo != "" { - cfg, err := cserialize.Load(filepath.Join(repo, config.DefaultConfigFile)) - switch err { - case cserialize.ErrNotInitialized: - case nil: - loader.config = cfg.Plugins + switch plugins, err := readPluginsConfig(repo, config.DefaultConfigFile); { + case err == nil: + loader.config = plugins + case os.IsNotExist(err): default: return nil, err } } + for _, v := range preloadPlugins { if err := loader.Load(v); err != nil { return nil, err @@ -118,6 +119,33 @@ func NewPluginLoader(repo string) (*PluginLoader, error) { return loader, nil } +// readPluginsConfig reads the Plugins section of the IPFS config, avoiding +// reading anything other than the Plugin section. That way, we're free to +// make arbitrary changes to all _other_ sections in migrations. +func readPluginsConfig(repoRoot string, userConfigFile string) (config.Plugins, error) { + var cfg struct { + Plugins config.Plugins + } + + cfgPath, err := config.Filename(repoRoot, userConfigFile) + if err != nil { + return config.Plugins{}, err + } + + cfgFile, err := os.Open(cfgPath) + if err != nil { + return config.Plugins{}, err + } + defer cfgFile.Close() + + err = json.NewDecoder(cfgFile).Decode(&cfg) + if err != nil { + return config.Plugins{}, err + } + + return cfg.Plugins, nil +} + func (loader *PluginLoader) assertState(state loaderState) error { if loader.state != state { return fmt.Errorf("loader state must be %s, was %s", state, loader.state) @@ -140,18 +168,22 @@ func (loader *PluginLoader) Load(pl plugin.Plugin) error { } name := pl.Name() - if ppl, ok := loader.plugins[name]; ok { - // plugin is already loaded - return fmt.Errorf( - "plugin: %s, is duplicated in version: %s, "+ - "while trying to load dynamically: %s", - name, ppl.Version(), pl.Version()) + + for _, p := range loader.plugins { + if p.Name() == name { + // plugin is already loaded + return fmt.Errorf( + "plugin: %s, is duplicated in version: %s, "+ + "while trying to load dynamically: %s", + name, p.Version(), pl.Version()) + } } + if loader.config.Plugins[name].Disabled { log.Infof("not loading disabled plugin %s", name) return nil } - loader.plugins[name] = pl + loader.plugins = append(loader.plugins, pl) return nil } @@ -195,7 +227,7 @@ func loadDynamicPlugins(pluginDir string) ([]plugin.Plugin, error) { return nil } - if info.Mode().Perm()&0111 == 0 { + if info.Mode().Perm()&0o111 == 0 { // file is not executable let's not load it // this is to prevent loading plugins from for example non-executable // mounts, some /tmp mounts are marked as such for security @@ -214,15 +246,15 @@ func loadDynamicPlugins(pluginDir string) ([]plugin.Plugin, error) { return plugins, err } -// Initialize initializes all loaded plugins +// Initialize initializes all loaded plugins. func (loader *PluginLoader) Initialize() error { if err := loader.transition(loaderLoading, loaderInitializing); err != nil { return err } - for name, p := range loader.plugins { + for _, p := range loader.plugins { err := p.Init(&plugin.Environment{ Repo: loader.repo, - Config: loader.config.Plugins[name].Config, + Config: loader.config.Plugins[p.Name()].Config, }) if err != nil { loader.state = loaderFailed @@ -330,7 +362,7 @@ func (loader *PluginLoader) Close() error { } if errs != nil { loader.state = loaderFailed - return fmt.Errorf(strings.Join(errs, "\n")) + return errors.New(strings.Join(errs, "\n")) } loader.state = loaderClosed return nil diff --git a/plugin/loader/preload.go b/plugin/loader/preload.go index 430486211..75e21270c 100644 --- a/plugin/loader/preload.go +++ b/plugin/loader/preload.go @@ -7,6 +7,8 @@ import ( pluginfxtest "github.com/ipfs/kubo/plugin/plugins/fxtest" pluginipldgit "github.com/ipfs/kubo/plugin/plugins/git" pluginlevelds "github.com/ipfs/kubo/plugin/plugins/levelds" + pluginnopfs "github.com/ipfs/kubo/plugin/plugins/nopfs" + pluginpebbleds "github.com/ipfs/kubo/plugin/plugins/pebbleds" pluginpeerlog "github.com/ipfs/kubo/plugin/plugins/peerlog" ) @@ -20,6 +22,8 @@ func init() { Preload(pluginbadgerds.Plugins...) Preload(pluginflatfs.Plugins...) Preload(pluginlevelds.Plugins...) + Preload(pluginpebbleds.Plugins...) Preload(pluginpeerlog.Plugins...) Preload(pluginfxtest.Plugins...) + Preload(pluginnopfs.Plugins...) } diff --git a/plugin/loader/preload_list b/plugin/loader/preload_list index c18ea80cc..190cc65d7 100644 --- a/plugin/loader/preload_list +++ b/plugin/loader/preload_list @@ -9,5 +9,7 @@ iplddagjose github.com/ipfs/kubo/plugin/plugins/dagjose * badgerds github.com/ipfs/kubo/plugin/plugins/badgerds * flatfs github.com/ipfs/kubo/plugin/plugins/flatfs * levelds github.com/ipfs/kubo/plugin/plugins/levelds * +pebbleds github.com/ipfs/kubo/plugin/plugins/pebbleds * peerlog github.com/ipfs/kubo/plugin/plugins/peerlog * fxtest github.com/ipfs/kubo/plugin/plugins/fxtest * +nopfs github.com/ipfs/kubo/plugin/plugins/nopfs * diff --git a/plugin/plugins/badgerds/badgerds.go b/plugin/plugins/badgerds/badgerds.go index 3d3f69061..5f5781f8f 100644 --- a/plugin/plugins/badgerds/badgerds.go +++ b/plugin/plugins/badgerds/badgerds.go @@ -13,7 +13,7 @@ import ( badgerds "github.com/ipfs/go-ds-badger" ) -// Plugins is exported list of plugins that will be loaded +// Plugins is exported list of plugins that will be loaded. var Plugins = []plugin.Plugin{ &badgerdsPlugin{}, } @@ -47,7 +47,7 @@ type datastoreConfig struct { } // BadgerdsDatastoreConfig returns a configuration stub for a badger datastore -// from the given parameters +// from the given parameters. func (*badgerdsPlugin) DatastoreConfigParser() fsrepo.ConfigFromMap { return func(params map[string]interface{}) (fsrepo.DatastoreConfig, error) { var c datastoreConfig @@ -113,7 +113,7 @@ func (c *datastoreConfig) Create(path string) (repo.Datastore, error) { p = filepath.Join(path, p) } - err := os.MkdirAll(p, 0755) + err := os.MkdirAll(p, 0o755) if err != nil { return nil, err } diff --git a/plugin/plugins/dagjose/dagjose.go b/plugin/plugins/dagjose/dagjose.go index 943214629..4545e2e96 100644 --- a/plugin/plugins/dagjose/dagjose.go +++ b/plugin/plugins/dagjose/dagjose.go @@ -8,7 +8,7 @@ import ( mc "github.com/multiformats/go-multicodec" ) -// Plugins is exported list of plugins that will be loaded +// Plugins is exported list of plugins that will be loaded. var Plugins = []plugin.Plugin{ &dagjosePlugin{}, } diff --git a/plugin/plugins/flatfs/flatfs.go b/plugin/plugins/flatfs/flatfs.go index 40b8b37d0..397c2656c 100644 --- a/plugin/plugins/flatfs/flatfs.go +++ b/plugin/plugins/flatfs/flatfs.go @@ -11,7 +11,7 @@ import ( flatfs "github.com/ipfs/go-ds-flatfs" ) -// Plugins is exported list of plugins that will be loaded +// Plugins is exported list of plugins that will be loaded. var Plugins = []plugin.Plugin{ &flatfsPlugin{}, } @@ -42,8 +42,8 @@ type datastoreConfig struct { syncField bool } -// BadgerdsDatastoreConfig returns a configuration stub for a badger datastore -// from the given parameters +// BadgerdsDatastoreConfig returns a configuration stub for a flatfs datastore +// from the given parameters. func (*flatfsPlugin) DatastoreConfigParser() fsrepo.ConfigFromMap { return func(params map[string]interface{}) (fsrepo.DatastoreConfig, error) { var c datastoreConfig diff --git a/plugin/plugins/git/git.go b/plugin/plugins/git/git.go index 2c56d8339..baaea6c35 100644 --- a/plugin/plugins/git/git.go +++ b/plugin/plugins/git/git.go @@ -13,7 +13,7 @@ import ( mc "github.com/multiformats/go-multicodec" ) -// Plugins is exported list of plugins that will be loaded +// Plugins is exported list of plugins that will be loaded. var Plugins = []plugin.Plugin{ &gitPlugin{}, } diff --git a/plugin/plugins/levelds/levelds.go b/plugin/plugins/levelds/levelds.go index ccb7c08f8..78331730e 100644 --- a/plugin/plugins/levelds/levelds.go +++ b/plugin/plugins/levelds/levelds.go @@ -12,7 +12,7 @@ import ( ldbopts "github.com/syndtr/goleveldb/leveldb/opt" ) -// Plugins is exported list of plugins that will be loaded +// Plugins is exported list of plugins that will be loaded. var Plugins = []plugin.Plugin{ &leveldsPlugin{}, } @@ -42,8 +42,8 @@ type datastoreConfig struct { compression ldbopts.Compression } -// BadgerdsDatastoreConfig returns a configuration stub for a badger datastore -// from the given parameters +// DatastoreConfigParser returns a configuration stub for a badger datastore +// from the given parameters. func (*leveldsPlugin) DatastoreConfigParser() fsrepo.ConfigFromMap { return func(params map[string]interface{}) (fsrepo.DatastoreConfig, error) { var c datastoreConfig diff --git a/plugin/plugins/nopfs/nopfs.go b/plugin/plugins/nopfs/nopfs.go new file mode 100644 index 000000000..c32d7533f --- /dev/null +++ b/plugin/plugins/nopfs/nopfs.go @@ -0,0 +1,86 @@ +package nopfs + +import ( + "os" + "path/filepath" + + "github.com/ipfs-shipyard/nopfs" + "github.com/ipfs-shipyard/nopfs/ipfs" + "github.com/ipfs/kubo/core" + "github.com/ipfs/kubo/core/node" + "github.com/ipfs/kubo/plugin" + "go.uber.org/fx" +) + +// Plugins sets the list of plugins to be loaded. +var Plugins = []plugin.Plugin{ + &nopfsPlugin{}, +} + +// fxtestPlugin is used for testing the fx plugin. +// It merely adds an fx option that logs a debug statement, so we can verify that it works in tests. +type nopfsPlugin struct { + // Path to the IPFS repo. + repo string +} + +var _ plugin.PluginFx = (*nopfsPlugin)(nil) + +func (p *nopfsPlugin) Name() string { + return "nopfs" +} + +func (p *nopfsPlugin) Version() string { + return "0.0.10" +} + +func (p *nopfsPlugin) Init(env *plugin.Environment) error { + p.repo = env.Repo + + return nil +} + +// MakeBlocker is a factory for the blocker so that it can be provided with Fx. +func MakeBlocker(repoPath string) func() (*nopfs.Blocker, error) { + return func() (*nopfs.Blocker, error) { + defaultFiles, err := nopfs.GetDenylistFiles() + if err != nil { + return nil, err + } + + kuboFiles, err := nopfs.GetDenylistFilesInDir(filepath.Join(repoPath, "denylists")) + if err != nil { + return nil, err + } + + files := append(defaultFiles, kuboFiles...) + + return nopfs.NewBlocker(files) + } +} + +// PathResolvers returns wrapped PathResolvers for Kubo. +func PathResolvers(fetchers node.FetchersIn, blocker *nopfs.Blocker) node.PathResolversOut { + res := node.PathResolverConfig(fetchers) + return node.PathResolversOut{ + IPLDPathResolver: ipfs.WrapResolver(res.IPLDPathResolver, blocker), + UnixFSPathResolver: ipfs.WrapResolver(res.UnixFSPathResolver, blocker), + OfflineIPLDPathResolver: ipfs.WrapResolver(res.OfflineIPLDPathResolver, blocker), + OfflineUnixFSPathResolver: ipfs.WrapResolver(res.OfflineUnixFSPathResolver, blocker), + } +} + +func (p *nopfsPlugin) Options(info core.FXNodeInfo) ([]fx.Option, error) { + if os.Getenv("IPFS_CONTENT_BLOCKING_DISABLE") != "" { + return info.FXOptions, nil + } + + opts := append( + info.FXOptions, + fx.Provide(MakeBlocker(p.repo)), + fx.Decorate(ipfs.WrapBlockService), + fx.Decorate(ipfs.WrapNameSystem), + fx.Decorate(PathResolvers), + ) + return opts, nil +} diff --git a/plugin/plugins/pebbleds/pebbleds.go b/plugin/plugins/pebbleds/pebbleds.go new file mode 100644 index 000000000..965dcce13 --- /dev/null +++ b/plugin/plugins/pebbleds/pebbleds.go @@ -0,0 +1,178 @@ +package pebbleds + +import ( + "fmt" + "path/filepath" + "time" + + "github.com/cockroachdb/pebble" + pebbleds "github.com/ipfs/go-ds-pebble" + "github.com/ipfs/kubo/misc/fsutil" + "github.com/ipfs/kubo/plugin" + "github.com/ipfs/kubo/repo" + "github.com/ipfs/kubo/repo/fsrepo" +) + +// Plugins is exported list of plugins that will be loaded. +var Plugins = []plugin.Plugin{ + &pebbledsPlugin{}, +} + +type pebbledsPlugin struct{} + +var _ plugin.PluginDatastore = (*pebbledsPlugin)(nil) + +func (*pebbledsPlugin) Name() string { + return "ds-pebble" +} + +func (*pebbledsPlugin) Version() string { + return "0.1.0" +} + +func (*pebbledsPlugin) Init(_ *plugin.Environment) error { + return nil +} + +func (*pebbledsPlugin) DatastoreTypeName() string { + return "pebbleds" +} + +type datastoreConfig struct { + path string + cacheSize int64 + + // Documentation of these values: https://pkg.go.dev/github.com/cockroachdb/pebble@v1.1.2#Options + pebbleOpts *pebble.Options +} + +// PebbleDatastoreConfig returns a configuration stub for a pebble datastore +// from the given parameters. +func (*pebbledsPlugin) DatastoreConfigParser() fsrepo.ConfigFromMap { + return func(params map[string]any) (fsrepo.DatastoreConfig, error) { + var c datastoreConfig + var ok bool + + c.path, ok = params["path"].(string) + if !ok { + return nil, fmt.Errorf("'path' field is missing or not string") + } + + cacheSize, err := getConfigInt("cacheSize", params) + if err != nil { + return nil, err + } + c.cacheSize = int64(cacheSize) + + bytesPerSync, err := getConfigInt("bytesPerSync", params) + if err != nil { + return nil, err + } + disableWAL, err := getConfigBool("disableWAL", params) + if err != nil { + return nil, err + } + l0CompactionThreshold, err := getConfigInt("l0CompactionThreshold", params) + if err != nil { + return nil, err + } + l0StopWritesThreshold, err := getConfigInt("l0StopWritesThreshold", params) + if err != nil { + return nil, err + } + lBaseMaxBytes, err := getConfigInt("lBaseMaxBytes", params) + if err != nil { + return nil, err + } + maxConcurrentCompactions, err := getConfigInt("maxConcurrentCompactions", params) + if err != nil { + return nil, err + } + memTableSize, err := getConfigInt("memTableSize", params) + if err != nil { + return nil, err + } + memTableStopWritesThreshold, err := getConfigInt("memTableStopWritesThreshold", params) + if err != nil { + return nil, err + } + walBytesPerSync, err := getConfigInt("walBytesPerSync", params) + if err != nil { + return nil, err + } + walMinSyncSec, err := getConfigInt("walMinSyncIntervalSeconds", params) + if err != nil { + return nil, err + } + + if bytesPerSync != 0 || disableWAL || l0CompactionThreshold != 0 || l0StopWritesThreshold != 0 || lBaseMaxBytes != 0 || maxConcurrentCompactions != 0 || memTableSize != 0 || memTableStopWritesThreshold != 0 || walBytesPerSync != 0 || walMinSyncSec != 0 { + c.pebbleOpts = &pebble.Options{ + BytesPerSync: bytesPerSync, + DisableWAL: disableWAL, + L0CompactionThreshold: l0CompactionThreshold, + L0StopWritesThreshold: l0StopWritesThreshold, + LBaseMaxBytes: int64(lBaseMaxBytes), + MemTableSize: uint64(memTableSize), + MemTableStopWritesThreshold: memTableStopWritesThreshold, + WALBytesPerSync: walBytesPerSync, + } + if maxConcurrentCompactions != 0 { + c.pebbleOpts.MaxConcurrentCompactions = func() int { return maxConcurrentCompactions } + } + if walMinSyncSec != 0 { + c.pebbleOpts.WALMinSyncInterval = func() time.Duration { return time.Duration(walMinSyncSec) * time.Second } + } + } + + return &c, nil + } +} + +func getConfigBool(name string, params map[string]any) (bool, error) { + val, ok := params[name] + if ok { + bval, ok := val.(bool) + if !ok { + return false, fmt.Errorf("%q field was not a bool", name) + } + return bval, nil + } + return false, nil +} + +func getConfigInt(name string, params map[string]any) (int, error) { + val, ok := params[name] + if ok { + // TODO: see why val may be an int or a float64. + ival, ok := val.(int) + if !ok { + fval, ok := val.(float64) + if !ok { + return 0, fmt.Errorf("%q field was not an integer or a float64", name) + } + return int(fval), nil + } + return ival, nil + } + return 0, nil +} + +func (c *datastoreConfig) DiskSpec() fsrepo.DiskSpec { + return map[string]interface{}{ + "type": "pebbleds", + "path": c.path, + } +} + +func (c *datastoreConfig) Create(path string) (repo.Datastore, error) { + p := c.path + if !filepath.IsAbs(p) { + p = filepath.Join(path, p) + } + + if err := fsutil.DirWritable(p); err != nil { + return nil, err + } + + return pebbleds.NewDatastore(p, pebbleds.WithCacheSize(c.cacheSize), pebbleds.WithPebbleOpts(c.pebbleOpts)) +} diff --git a/plugin/plugins/peerlog/peerlog.go b/plugin/plugins/peerlog/peerlog.go index 1b71cb1ae..d55a7f0b9 100644 --- a/plugin/plugins/peerlog/peerlog.go +++ b/plugin/plugins/peerlog/peerlog.go @@ -20,7 +20,7 @@ var log = logging.Logger("plugin/peerlog") type eventType int var ( - // size of the event queue buffer + // size of the event queue buffer. eventQueueSize = 64 * 1024 // number of events to drop when busy. busyDropAmount = eventQueueSize / 8 @@ -54,7 +54,7 @@ type peerLogPlugin struct { var _ plugin.PluginDaemonInternal = (*peerLogPlugin)(nil) -// Plugins is exported list of plugins that will be loaded +// Plugins is exported list of plugins that will be loaded. var Plugins = []plugin.Plugin{ &peerLogPlugin{}, } @@ -94,7 +94,7 @@ func extractEnabled(config interface{}) bool { return enabled } -// Init initializes plugin +// Init initializes plugin. func (pl *peerLogPlugin) Init(env *plugin.Environment) error { pl.events = make(chan plEvent, eventQueueSize) pl.enabled = extractEnabled(env.Config) @@ -148,7 +148,7 @@ func (pl *peerLogPlugin) collectEvents(node *core.IpfsNode) { case e = <-pl.events: } - peerID := zap.String("peer", e.peer.Pretty()) + peerID := zap.String("peer", e.peer.String()) switch e.kind { case eventConnect: diff --git a/plugin/tracer.go b/plugin/tracer.go index b6b56939d..97aa84846 100644 --- a/plugin/tracer.go +++ b/plugin/tracer.go @@ -4,7 +4,7 @@ import ( "github.com/opentracing/opentracing-go" ) -// PluginTracer is an interface that can be implemented to add a tracer +// PluginTracer is an interface that can be implemented to add a tracer. type PluginTracer interface { Plugin diff --git a/profile/profile.go b/profile/profile.go index d15d51251..be1e5adbb 100644 --- a/profile/profile.go +++ b/profile/profile.go @@ -10,6 +10,7 @@ import ( "os" "runtime" "runtime/pprof" + "runtime/trace" "sync" "time" @@ -22,10 +23,12 @@ const ( CollectorGoroutinesPprof = "goroutines-pprof" CollectorVersion = "version" CollectorHeap = "heap" + CollectorAllocs = "allocs" CollectorBin = "bin" CollectorCPU = "cpu" CollectorMutex = "mutex" CollectorBlock = "block" + CollectorTrace = "trace" ) var ( @@ -71,6 +74,11 @@ var collectors = map[string]collector{ collectFunc: heapProfile, enabledFunc: func(opts Options) bool { return true }, }, + CollectorAllocs: { + outputFile: "allocs.pprof", + collectFunc: allocsProfile, + enabledFunc: func(opts Options) bool { return true }, + }, CollectorBin: { outputFile: "ipfs", isExecutable: true, @@ -92,6 +100,11 @@ var collectors = map[string]collector{ collectFunc: blockProfile, enabledFunc: func(opts Options) bool { return opts.ProfileDuration > 0 && opts.BlockProfileRate > 0 }, }, + CollectorTrace: { + outputFile: "trace", + collectFunc: captureTrace, + enabledFunc: func(opts Options) bool { return opts.ProfileDuration > 0 }, + }, } type Options struct { @@ -197,6 +210,10 @@ func heapProfile(ctx context.Context, _ Options, w io.Writer) error { return pprof.Lookup("heap").WriteTo(w, 0) } +func allocsProfile(ctx context.Context, _ Options, w io.Writer) error { + return pprof.Lookup("allocs").WriteTo(w, 0) +} + func versionInfo(ctx context.Context, _ Options, w io.Writer) error { return json.NewEncoder(w).Encode(version.GetVersionInfo()) } @@ -256,6 +273,15 @@ func profileCPU(ctx context.Context, opts Options, w io.Writer) error { return waitOrCancel(ctx, opts.ProfileDuration) } +func captureTrace(ctx context.Context, opts Options, w io.Writer) error { + err := trace.Start(w) + if err != nil { + return err + } + defer trace.Stop() + return waitOrCancel(ctx, opts.ProfileDuration) +} + func waitOrCancel(ctx context.Context, d time.Duration) error { timer := time.NewTimer(d) defer timer.Stop() diff --git a/profile/profile_test.go b/profile/profile_test.go index 8da00d018..6c2f5784e 100644 --- a/profile/profile_test.go +++ b/profile/profile_test.go @@ -17,10 +17,12 @@ func TestProfiler(t *testing.T) { CollectorGoroutinesPprof, CollectorVersion, CollectorHeap, + CollectorAllocs, CollectorBin, CollectorCPU, CollectorMutex, CollectorBlock, + CollectorTrace, } cases := []struct { @@ -43,10 +45,12 @@ func TestProfiler(t *testing.T) { "goroutines.pprof", "version.json", "heap.pprof", + "allocs.pprof", "ipfs", "cpu.pprof", "mutex.pprof", "block.pprof", + "trace", }, }, { @@ -63,10 +67,12 @@ func TestProfiler(t *testing.T) { "goroutines.pprof", "version.json", "heap.pprof", + "allocs.pprof", "ipfs.exe", "cpu.pprof", "mutex.pprof", "block.pprof", + "trace", }, }, { @@ -81,6 +87,7 @@ func TestProfiler(t *testing.T) { "goroutines.pprof", "version.json", "heap.pprof", + "allocs.pprof", "ipfs", }, }, @@ -96,9 +103,11 @@ func TestProfiler(t *testing.T) { "goroutines.pprof", "version.json", "heap.pprof", + "allocs.pprof", "ipfs", "cpu.pprof", "block.pprof", + "trace", }, }, { @@ -114,9 +123,11 @@ func TestProfiler(t *testing.T) { "goroutines.pprof", "version.json", "heap.pprof", + "allocs.pprof", "ipfs", "cpu.pprof", "mutex.pprof", + "trace", }, }, { diff --git a/repo/common/common.go b/repo/common/common.go index e9c56e65e..ab74ffca8 100644 --- a/repo/common/common.go +++ b/repo/common/common.go @@ -62,7 +62,7 @@ func MapSetKV(v map[string]interface{}, key string, value interface{}) error { } // Merges the right map into the left map, recursively traversing child maps -// until a non-map value is found +// until a non-map value is found. func MapMergeDeep(left, right map[string]interface{}) map[string]interface{} { // We want to alter a copy of the map, not the original result := make(map[string]interface{}) diff --git a/repo/fsrepo/config_test.go b/repo/fsrepo/config_test.go index 03af75a96..3c914ff82 100644 --- a/repo/fsrepo/config_test.go +++ b/repo/fsrepo/config_test.go @@ -2,7 +2,6 @@ package fsrepo_test import ( "encoding/json" - "os" "reflect" "testing" @@ -13,7 +12,7 @@ import ( ) // note: to test sorting of the mountpoints in the disk spec they are -// specified out of order in the test config +// specified out of order in the test config. var defaultConfig = []byte(`{ "StorageMax": "10GB", "StorageGCWatermark": 90, @@ -88,11 +87,7 @@ func TestDefaultDatastoreConfig(t *testing.T) { t.Fatal(err) } - dir, err := os.MkdirTemp("", "ipfs-datastore-config-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) // clean up + dir := t.TempDir() config := new(config.Datastore) err = json.Unmarshal(defaultConfig, config) @@ -126,11 +121,7 @@ func TestLevelDbConfig(t *testing.T) { if err != nil { t.Fatal(err) } - dir, err := os.MkdirTemp("", "ipfs-datastore-config-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) // clean up + dir := t.TempDir() spec := make(map[string]interface{}) err = json.Unmarshal(leveldbConfig, &spec) @@ -164,11 +155,7 @@ func TestFlatfsConfig(t *testing.T) { if err != nil { t.Fatal(err) } - dir, err := os.MkdirTemp("", "ipfs-datastore-config-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) // clean up + dir := t.TempDir() spec := make(map[string]interface{}) err = json.Unmarshal(flatfsConfig, &spec) @@ -202,11 +189,7 @@ func TestMeasureConfig(t *testing.T) { if err != nil { t.Fatal(err) } - dir, err := os.MkdirTemp("", "ipfs-datastore-config-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) // clean up + dir := t.TempDir() spec := make(map[string]interface{}) err = json.Unmarshal(measureConfig, &spec) diff --git a/repo/fsrepo/datastores.go b/repo/fsrepo/datastores.go index 0f60056aa..86ed0a863 100644 --- a/repo/fsrepo/datastores.go +++ b/repo/fsrepo/datastores.go @@ -14,12 +14,12 @@ import ( "github.com/ipfs/go-ds-measure" ) -// ConfigFromMap creates a new datastore config from a map +// ConfigFromMap creates a new datastore config from a map. type ConfigFromMap func(map[string]interface{}) (DatastoreConfig, error) // DatastoreConfig is an abstraction of a datastore config. A "spec" // is first converted to a DatastoreConfig and then Create() is called -// to instantiate a new datastore +// to instantiate a new datastore. type DatastoreConfig interface { // DiskSpec returns a minimal configuration of the datastore // represting what is stored on disk. Run time values are @@ -38,7 +38,7 @@ type DatastoreConfig interface { // here. type DiskSpec map[string]interface{} -// Bytes returns a minimal JSON encoding of the DiskSpec +// Bytes returns a minimal JSON encoding of the DiskSpec. func (spec DiskSpec) Bytes() []byte { b, err := json.Marshal(spec) if err != nil { @@ -48,7 +48,7 @@ func (spec DiskSpec) Bytes() []byte { return bytes.TrimSpace(b) } -// String returns a minimal JSON encoding of the DiskSpec +// String returns a minimal JSON encoding of the DiskSpec. func (spec DiskSpec) String() string { return string(spec.Bytes()) } @@ -75,7 +75,7 @@ func AddDatastoreConfigHandler(name string, dsc ConfigFromMap) error { } // AnyDatastoreConfig returns a DatastoreConfig from a spec based on -// the "type" parameter +// the "type" parameter. func AnyDatastoreConfig(params map[string]interface{}) (DatastoreConfig, error) { which, ok := params["type"].(string) if !ok { @@ -97,7 +97,7 @@ type premount struct { prefix ds.Key } -// MountDatastoreConfig returns a mount DatastoreConfig from a spec +// MountDatastoreConfig returns a mount DatastoreConfig from a spec. func MountDatastoreConfig(params map[string]interface{}) (DatastoreConfig, error) { var res mountDatastoreConfig mounts, ok := params["mounts"].([]interface{}) @@ -165,7 +165,7 @@ type memDatastoreConfig struct { cfg map[string]interface{} } -// MemDatastoreConfig returns a memory DatastoreConfig from a spec +// MemDatastoreConfig returns a memory DatastoreConfig from a spec. func MemDatastoreConfig(params map[string]interface{}) (DatastoreConfig, error) { return &memDatastoreConfig{params}, nil } @@ -183,7 +183,7 @@ type logDatastoreConfig struct { name string } -// LogDatastoreConfig returns a log DatastoreConfig from a spec +// LogDatastoreConfig returns a log DatastoreConfig from a spec. func LogDatastoreConfig(params map[string]interface{}) (DatastoreConfig, error) { childField, ok := params["child"].(map[string]interface{}) if !ok { @@ -198,7 +198,6 @@ func LogDatastoreConfig(params map[string]interface{}) (DatastoreConfig, error) return nil, fmt.Errorf("'name' field was missing or not a string") } return &logDatastoreConfig{child, name}, nil - } func (c *logDatastoreConfig) Create(path string) (repo.Datastore, error) { @@ -218,7 +217,7 @@ type measureDatastoreConfig struct { prefix string } -// MeasureDatastoreConfig returns a measure DatastoreConfig from a spec +// MeasureDatastoreConfig returns a measure DatastoreConfig from a spec. func MeasureDatastoreConfig(params map[string]interface{}) (DatastoreConfig, error) { childField, ok := params["child"].(map[string]interface{}) if !ok { diff --git a/repo/fsrepo/fsrepo.go b/repo/fsrepo/fsrepo.go index 99104b083..b21b555cf 100644 --- a/repo/fsrepo/fsrepo.go +++ b/repo/fsrepo/fsrepo.go @@ -11,32 +11,32 @@ import ( "strings" "sync" - filestore "github.com/ipfs/go-filestore" - keystore "github.com/ipfs/go-ipfs-keystore" + filestore "github.com/ipfs/boxo/filestore" + keystore "github.com/ipfs/boxo/keystore" repo "github.com/ipfs/kubo/repo" "github.com/ipfs/kubo/repo/common" dir "github.com/ipfs/kubo/thirdparty/dir" + rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager" ds "github.com/ipfs/go-datastore" measure "github.com/ipfs/go-ds-measure" lockfile "github.com/ipfs/go-fs-lock" - util "github.com/ipfs/go-ipfs-util" logging "github.com/ipfs/go-log" config "github.com/ipfs/kubo/config" serialize "github.com/ipfs/kubo/config/serialize" + "github.com/ipfs/kubo/misc/fsutil" "github.com/ipfs/kubo/repo/fsrepo/migrations" - homedir "github.com/mitchellh/go-homedir" ma "github.com/multiformats/go-multiaddr" ) // LockFile is the filename of the repo lock, relative to config dir -// TODO rename repo lock and hide name +// TODO rename repo lock and hide name. const LockFile = "repo.lock" var log = logging.Logger("fsrepo") -// RepoVersion is the version number that we are currently expecting to see -var RepoVersion = 13 +// RepoVersion is the version number that we are currently expecting to see. +var RepoVersion = 16 var migrationInstructions = `See https://github.com/ipfs/fs-repo-migrations/blob/master/run.md Sorry for the inconvenience. In the future, these will run automatically.` @@ -63,9 +63,11 @@ func (err NoRepoError) Error() string { return fmt.Sprintf("no IPFS repo found in %s.\nplease run: 'ipfs init'", err.Path) } -const apiFile = "api" -const gatewayFile = "gateway" -const swarmKeyFile = "swarm.key" +const ( + apiFile = "api" + gatewayFile = "gateway" + swarmKeyFile = "swarm.key" +) const specFn = "datastore_spec" @@ -102,11 +104,12 @@ type FSRepo struct { configFilePath string // lockfile is the file system lock to prevent others from opening // the same fsrepo path concurrently - lockfile io.Closer - config *config.Config - ds repo.Datastore - keystore keystore.Keystore - filemgr *filestore.FileManager + lockfile io.Closer + config *config.Config + userResourceOverrides rcmgr.PartialLimitConfig + ds repo.Datastore + keystore keystore.Keystore + filemgr *filestore.FileManager } var _ repo.Repo = (*FSRepo)(nil) @@ -180,6 +183,10 @@ func open(repoPath string, userConfigFilePath string) (repo.Repo, error) { return nil, err } + if err := r.openUserResourceOverrides(); err != nil { + return nil, err + } + if err := r.openDatastore(); err != nil { return nil, err } @@ -199,7 +206,7 @@ func open(repoPath string, userConfigFilePath string) (repo.Repo, error) { } func newFSRepo(rpath string, userConfigFilePath string) (*FSRepo, error) { - expPath, err := homedir.Expand(filepath.Clean(rpath)) + expPath, err := fsutil.ExpandHome(filepath.Clean(rpath)) if err != nil { return nil, err } @@ -231,7 +238,7 @@ func configIsInitialized(path string) bool { if err != nil { return false } - if !util.FileExists(configFilename) { + if !fsutil.FileExists(configFilename) { return false } return true @@ -261,7 +268,7 @@ func initSpec(path string, conf map[string]interface{}) error { return err } - if util.FileExists(fn) { + if fsutil.FileExists(fn) { return nil } @@ -271,13 +278,12 @@ func initSpec(path string, conf map[string]interface{}) error { } bytes := dsc.DiskSpec().Bytes() - return os.WriteFile(fn, bytes, 0600) + return os.WriteFile(fn, bytes, 0o600) } // Init initializes a new FSRepo at the given path with the provided config. // TODO add support for custom datastores. func Init(repoPath string, conf *config.Config) error { - // packageLock must be held to ensure that the repo is not initialized more // than once. packageLock.Lock() @@ -437,6 +443,17 @@ func (r *FSRepo) openConfig() error { return nil } +// openUserResourceOverrides will remove all overrides if the file is not present. +// It will error if the decoding fails. +func (r *FSRepo) openUserResourceOverrides() error { + // This filepath is documented in docs/libp2p-resource-management.md and be kept in sync. + err := serialize.ReadConfigFile(filepath.Join(r.path, "libp2p-resource-limit-overrides.json"), &r.userResourceOverrides) + if errors.Is(err, serialize.ErrNotInitialized) { + err = nil + } + return err +} + func (r *FSRepo) openKeystore() error { ksp := filepath.Join(r.path, "keystore") ks, err := keystore.NewFSKeystore(ksp) @@ -554,6 +571,21 @@ func (r *FSRepo) Config() (*config.Config, error) { return r.config, nil } +func (r *FSRepo) UserResourceOverrides() (rcmgr.PartialLimitConfig, error) { + // It is not necessary to hold the package lock since the repo is in an + // opened state. The package lock is _not_ meant to ensure that the repo is + // thread-safe. The package lock is only meant to guard against removal and + // coordinate the lockfile. However, we provide thread-safety to keep + // things simple. + packageLock.Lock() + defer packageLock.Unlock() + + if r.closed { + return rcmgr.PartialLimitConfig{}, errors.New("cannot access config, repo not open") + } + return r.userResourceOverrides, nil +} + func (r *FSRepo) FileManager() *filestore.FileManager { return r.filemgr } @@ -565,7 +597,7 @@ func (r *FSRepo) BackupConfig(prefix string) (string, error) { } defer temp.Close() - orig, err := os.OpenFile(r.configFilePath, os.O_RDONLY, 0600) + orig, err := os.OpenFile(r.configFilePath, os.O_RDONLY, 0o600) if err != nil { return "", err } @@ -594,7 +626,6 @@ func (r *FSRepo) BackupConfig(prefix string) (string, error) { // We need to comb SetConfig calls and replace them when possible with a // JSON map variant. func (r *FSRepo) SetConfig(updated *config.Config) error { - // packageLock is held to provide thread-safety. packageLock.Lock() defer packageLock.Unlock() @@ -693,7 +724,7 @@ func (r *FSRepo) Datastore() repo.Datastore { return d } -// GetStorageUsage computes the storage space taken by the repo in bytes +// GetStorageUsage computes the storage space taken by the repo in bytes. func (r *FSRepo) GetStorageUsage(ctx context.Context) (uint64, error) { return ds.DiskUsage(ctx, r.Datastore()) } @@ -714,8 +745,10 @@ func (r *FSRepo) SwarmKey() ([]byte, error) { return io.ReadAll(f) } -var _ io.Closer = &FSRepo{} -var _ repo.Repo = &FSRepo{} +var ( + _ io.Closer = &FSRepo{} + _ repo.Repo = &FSRepo{} +) // IsInitialized returns true if the repo is initialized at provided |path|. func IsInitialized(path string) bool { diff --git a/repo/fsrepo/fsrepo_test.go b/repo/fsrepo/fsrepo_test.go index 7c7551d20..6b30b107a 100644 --- a/repo/fsrepo/fsrepo_test.go +++ b/repo/fsrepo/fsrepo_test.go @@ -13,18 +13,9 @@ import ( config "github.com/ipfs/kubo/config" ) -// swap arg order -func testRepoPath(p string, t *testing.T) string { - name, err := os.MkdirTemp("", p) - if err != nil { - t.Fatal(err) - } - return name -} - func TestInitIdempotence(t *testing.T) { t.Parallel() - path := testRepoPath("", t) + path := t.TempDir() for i := 0; i < 10; i++ { assert.Nil(Init(path, &config.Config{Datastore: config.DefaultDatastoreConfig()}), t, "multiple calls to init should succeed") } @@ -37,8 +28,8 @@ func Remove(repoPath string) error { func TestCanManageReposIndependently(t *testing.T) { t.Parallel() - pathA := testRepoPath("a", t) - pathB := testRepoPath("b", t) + pathA := t.TempDir() + pathB := t.TempDir() t.Log("initialize two repos") assert.Nil(Init(pathA, &config.Config{Datastore: config.DefaultDatastoreConfig()}), t, "a", "should initialize successfully") @@ -65,7 +56,7 @@ func TestCanManageReposIndependently(t *testing.T) { func TestDatastoreGetNotAllowedAfterClose(t *testing.T) { t.Parallel() - path := testRepoPath("test", t) + path := t.TempDir() assert.True(!IsInitialized(path), t, "should NOT be initialized") assert.Nil(Init(path, &config.Config{Datastore: config.DefaultDatastoreConfig()}), t, "should initialize successfully") @@ -83,7 +74,7 @@ func TestDatastoreGetNotAllowedAfterClose(t *testing.T) { func TestDatastorePersistsFromRepoToRepo(t *testing.T) { t.Parallel() - path := testRepoPath("test", t) + path := t.TempDir() assert.Nil(Init(path, &config.Config{Datastore: config.DefaultDatastoreConfig()}), t) r1, err := Open(path) @@ -104,7 +95,7 @@ func TestDatastorePersistsFromRepoToRepo(t *testing.T) { func TestOpenMoreThanOnceInSameProcess(t *testing.T) { t.Parallel() - path := testRepoPath("", t) + path := t.TempDir() assert.Nil(Init(path, &config.Config{Datastore: config.DefaultDatastoreConfig()}), t) r1, err := Open(path) diff --git a/repo/fsrepo/migrations/fetch.go b/repo/fsrepo/migrations/fetch.go index 7feeac6e9..ee1deefa0 100644 --- a/repo/fsrepo/migrations/fetch.go +++ b/repo/fsrepo/migrations/fetch.go @@ -130,7 +130,7 @@ func FetchBinary(ctx context.Context, fetcher Fetcher, dist, ver, binName, out s } // Set mode of binary to executable - err = os.Chmod(out, 0755) + err = os.Chmod(out, 0o755) if err != nil { return "", err } diff --git a/repo/fsrepo/migrations/fetch_test.go b/repo/fsrepo/migrations/fetch_test.go index 2273cb5e9..6e87c966b 100644 --- a/repo/fsrepo/migrations/fetch_test.go +++ b/repo/fsrepo/migrations/fetch_test.go @@ -5,56 +5,13 @@ import ( "bytes" "context" "fmt" - "io" - "net/http" - "net/http/httptest" "os" - "path" "path/filepath" "runtime" "strings" "testing" ) -func createTestServer() *httptest.Server { - reqHandler := func(w http.ResponseWriter, r *http.Request) { - defer r.Body.Close() - if strings.Contains(r.URL.Path, "not-here") { - http.NotFound(w, r) - } else if strings.HasSuffix(r.URL.Path, "versions") { - fmt.Fprint(w, "v1.0.0\nv1.1.0\nv1.1.2\nv2.0.0-rc1\n2.0.0\nv2.0.1\n") - } else if strings.HasSuffix(r.URL.Path, ".tar.gz") { - createFakeArchive(r.URL.Path, false, w) - } else if strings.HasSuffix(r.URL.Path, "zip") { - createFakeArchive(r.URL.Path, true, w) - } else { - http.NotFound(w, r) - } - } - return httptest.NewServer(http.HandlerFunc(reqHandler)) -} - -func createFakeArchive(name string, archZip bool, w io.Writer) { - fileName := strings.Split(path.Base(name), "_")[0] - root := path.Base(path.Dir(path.Dir(name))) - - // Simulate fetching go-ipfs, which has "ipfs" as the name in the archive. - if fileName == "go-ipfs" { - fileName = "ipfs" - } - fileName = ExeName(fileName) - - var err error - if archZip { - err = writeZip(root, fileName, "FAKE DATA", w) - } else { - err = writeTarGzip(root, fileName, "FAKE DATA", w) - } - if err != nil { - panic(err) - } -} - func TestGetDistPath(t *testing.T) { os.Unsetenv(envIpfsDistPath) distPath := GetDistPathEnv("") @@ -91,12 +48,9 @@ func TestHttpFetch(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - ts := createTestServer() - defer ts.Close() + fetcher := NewHttpFetcher(testIpfsDist, testServer.URL, "", 0) - fetcher := NewHttpFetcher("", ts.URL, "", 0) - - out, err := fetcher.Fetch(ctx, "/versions") + out, err := fetcher.Fetch(ctx, "/kubo/versions") if err != nil { t.Fatal(err) } @@ -120,7 +74,7 @@ func TestHttpFetch(t *testing.T) { // Check not found _, err = fetcher.Fetch(ctx, "/no_such_file") - if err == nil || !strings.Contains(err.Error(), "404") { + if err == nil || !strings.Contains(err.Error(), "no link") { t.Fatal("expected error 404") } } @@ -131,10 +85,7 @@ func TestFetchBinary(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - ts := createTestServer() - defer ts.Close() - - fetcher := NewHttpFetcher("", ts.URL, "", 0) + fetcher := NewHttpFetcher(testIpfsDist, testServer.URL, "", 0) vers, err := DistVersions(ctx, fetcher, distFSRM, false) if err != nil { @@ -154,7 +105,7 @@ func TestFetchBinary(t *testing.T) { t.Log("downloaded and unpacked", fi.Size(), "byte file:", fi.Name()) - bin, err = FetchBinary(ctx, fetcher, "go-ipfs", "v0.3.5", "ipfs", tmpDir) + bin, err = FetchBinary(ctx, fetcher, "go-ipfs", "v1.0.0", "ipfs", tmpDir) if err != nil { t.Fatal(err) } @@ -167,12 +118,12 @@ func TestFetchBinary(t *testing.T) { t.Log("downloaded and unpacked", fi.Size(), "byte file:", fi.Name()) // Check error is destination already exists and is not directory - _, err = FetchBinary(ctx, fetcher, "go-ipfs", "v0.3.5", "ipfs", bin) + _, err = FetchBinary(ctx, fetcher, "go-ipfs", "v1.0.0", "ipfs", bin) if !os.IsExist(err) { t.Fatal("expected 'exists' error, got", err) } - _, err = FetchBinary(ctx, fetcher, "go-ipfs", "v0.3.5", "ipfs", tmpDir) + _, err = FetchBinary(ctx, fetcher, "go-ipfs", "v1.0.0", "ipfs", tmpDir) if !os.IsExist(err) { t.Error("expected 'exists' error, got:", err) } @@ -184,7 +135,7 @@ func TestFetchBinary(t *testing.T) { // Windows doesn't have read-only directories https://github.com/golang/go/issues/35042 this would need to be // tested another way if runtime.GOOS != "windows" { - err = os.Chmod(tmpDir, 0555) + err = os.Chmod(tmpDir, 0o555) if err != nil { panic(err) } @@ -192,7 +143,7 @@ func TestFetchBinary(t *testing.T) { if err != nil { panic(err) } - _, err = FetchBinary(ctx, fetcher, "go-ipfs", "v0.3.5", "ipfs", tmpDir) + _, err = FetchBinary(ctx, fetcher, "go-ipfs", "v1.0.0", "ipfs", tmpDir) if !os.IsPermission(err) { t.Error("expected 'permission' error, got:", err) } @@ -200,20 +151,20 @@ func TestFetchBinary(t *testing.T) { if err != nil { panic(err) } - err = os.Chmod(tmpDir, 0755) + err = os.Chmod(tmpDir, 0o755) if err != nil { panic(err) } } // Check error if failure to fetch due to bad dist - _, err = FetchBinary(ctx, fetcher, "not-here", "v0.3.5", "ipfs", tmpDir) - if err == nil || !strings.Contains(err.Error(), "Not Found") { + _, err = FetchBinary(ctx, fetcher, "not-here", "v1.0.0", "ipfs", tmpDir) + if err == nil || !strings.Contains(err.Error(), "no link") { t.Error("expected 'Not Found' error, got:", err) } // Check error if failure to unpack archive - _, err = FetchBinary(ctx, fetcher, "go-ipfs", "v0.3.5", "not-such-bin", tmpDir) + _, err = FetchBinary(ctx, fetcher, "go-ipfs", "v1.0.0", "not-such-bin", tmpDir) if err == nil || err.Error() != "no binary found in archive" { t.Error("expected 'no binary found in archive' error") } @@ -223,15 +174,12 @@ func TestMultiFetcher(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - ts := createTestServer() - defer ts.Close() - badFetcher := NewHttpFetcher("", "bad-url", "", 0) - fetcher := NewHttpFetcher("", ts.URL, "", 0) + fetcher := NewHttpFetcher(testIpfsDist, testServer.URL, "", 0) mf := NewMultiFetcher(badFetcher, fetcher) - vers, err := mf.Fetch(ctx, "/versions") + vers, err := mf.Fetch(ctx, "/kubo/versions") if err != nil { t.Fatal(err) } diff --git a/repo/fsrepo/migrations/fetcher.go b/repo/fsrepo/migrations/fetcher.go index c174b5e77..db7a5c0c1 100644 --- a/repo/fsrepo/migrations/fetcher.go +++ b/repo/fsrepo/migrations/fetcher.go @@ -10,12 +10,12 @@ import ( ) const ( - // Current distribution to fetch migrations from - CurrentIpfsDist = "/ipfs/Qmf4yftD4LuMo8JMNPqqw3BtUwYd2VkXMiAThuPE6usrbQ" // fs-repo-12-to-13 v1.0.0 + // Current distribution to fetch migrations from. + CurrentIpfsDist = "/ipfs/QmRzRGJEjYDfbHHaALnHBuhzzrkXGdwcPMrgd5fgM7hqbe" // fs-repo-15-to-16 v1.0.1 // Latest distribution path. Default for fetchers. LatestIpfsDist = "/ipns/dist.ipfs.tech" - // Distribution environ variable + // Distribution environ variable. envIpfsDistPath = "IPFS_DIST_PATH" ) @@ -40,7 +40,6 @@ type limitReadCloser struct { // NewMultiFetcher creates a MultiFetcher with the given Fetchers. The // Fetchers are tried in order, then passed to this function. func NewMultiFetcher(f ...Fetcher) *MultiFetcher { - mf := &MultiFetcher{ fetchers: make([]Fetcher, len(f)), } @@ -95,7 +94,7 @@ func NewLimitReadCloser(rc io.ReadCloser, limit int64) io.ReadCloser { // then returns the IPNS path. // // To get the IPFS path of the latest distribution, if not overriddin by the -// environ variable: GetDistPathEnv(CurrentIpfsDist) +// environ variable: GetDistPathEnv(CurrentIpfsDist). func GetDistPathEnv(distPath string) string { if dist := os.Getenv(envIpfsDistPath); dist != "" { return dist diff --git a/repo/fsrepo/migrations/httpfetcher.go b/repo/fsrepo/migrations/httpfetcher.go index 81e0e406d..2ae180b1e 100644 --- a/repo/fsrepo/migrations/httpfetcher.go +++ b/repo/fsrepo/migrations/httpfetcher.go @@ -2,20 +2,40 @@ package migrations import ( "context" + "errors" "fmt" "io" "net/http" - "path" + gopath "path" "strings" + + "github.com/ipfs/boxo/blockservice" + "github.com/ipfs/boxo/blockstore" + "github.com/ipfs/boxo/exchange/offline" + bsfetcher "github.com/ipfs/boxo/fetcher/impl/blockservice" + files "github.com/ipfs/boxo/files" + "github.com/ipfs/boxo/ipld/merkledag" + unixfile "github.com/ipfs/boxo/ipld/unixfs/file" + "github.com/ipfs/boxo/ipns" + "github.com/ipfs/boxo/namesys" + "github.com/ipfs/boxo/path" + "github.com/ipfs/boxo/path/resolver" + "github.com/ipfs/go-datastore" + dssync "github.com/ipfs/go-datastore/sync" + "github.com/ipfs/go-unixfsnode" + gocarv2 "github.com/ipld/go-car/v2" + dagpb "github.com/ipld/go-codec-dagpb" + madns "github.com/multiformats/go-multiaddr-dns" ) const ( - defaultGatewayURL = "https://ipfs.io" - // Default maximum download size + // default is different name than ipfs.io which is being blocked by some ISPs + defaultGatewayURL = "https://trustless-gateway.link" + // Default maximum download size. defaultFetchLimit = 1024 * 1024 * 512 ) -// HttpFetcher fetches files over HTTP +// HttpFetcher fetches files over HTTP using verifiable CAR archives. type HttpFetcher struct { //nolint distPath string gateway string @@ -25,7 +45,7 @@ type HttpFetcher struct { //nolint var _ Fetcher = (*HttpFetcher)(nil) -// NewHttpFetcher creates a new HttpFetcher +// NewHttpFetcher creates a new [HttpFetcher]. // // Specifying "" for distPath sets the default IPNS path. // Specifying "" for gateway sets the default. @@ -61,30 +81,106 @@ func NewHttpFetcher(distPath, gateway, userAgent string, fetchLimit int64) *Http // Fetch attempts to fetch the file at the given path, from the distribution // site configured for this HttpFetcher. func (f *HttpFetcher) Fetch(ctx context.Context, filePath string) ([]byte, error) { - gwURL := f.gateway + path.Join(f.distPath, filePath) - fmt.Printf("Fetching with HTTP: %q\n", gwURL) - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, gwURL, nil) + imPath, err := f.resolvePath(ctx, gopath.Join(f.distPath, filePath)) if err != nil { - return nil, fmt.Errorf("http.NewRequest error: %s", err) + return nil, fmt.Errorf("path could not be resolved: %w", err) } + rc, err := f.httpRequest(ctx, imPath, "application/vnd.ipld.car") + if err != nil { + return nil, fmt.Errorf("failed to fetch CAR: %w", err) + } + + return carStreamToFileBytes(ctx, rc, imPath) +} + +func (f *HttpFetcher) Close() error { + return nil +} + +func (f *HttpFetcher) resolvePath(ctx context.Context, pathStr string) (path.ImmutablePath, error) { + p, err := path.NewPath(pathStr) + if err != nil { + return path.ImmutablePath{}, fmt.Errorf("path is invalid: %w", err) + } + + for p.Mutable() { + // Download IPNS record and verify through the gateway, or resolve the + // DNSLink with the default DNS resolver. + name, err := ipns.NameFromString(p.Segments()[1]) + if err == nil { + p, err = f.resolveIPNS(ctx, name) + } else { + p, err = f.resolveDNSLink(ctx, p) + } + + if err != nil { + return path.ImmutablePath{}, err + } + } + + return path.NewImmutablePath(p) +} + +func (f *HttpFetcher) resolveIPNS(ctx context.Context, name ipns.Name) (path.Path, error) { + rc, err := f.httpRequest(ctx, name.AsPath(), "application/vnd.ipfs.ipns-record") + if err != nil { + return path.ImmutablePath{}, err + } + + rc = NewLimitReadCloser(rc, int64(ipns.MaxRecordSize)) + rawRecord, err := io.ReadAll(rc) + if err != nil { + return path.ImmutablePath{}, err + } + + rec, err := ipns.UnmarshalRecord(rawRecord) + if err != nil { + return path.ImmutablePath{}, err + } + + err = ipns.ValidateWithName(rec, name) + if err != nil { + return path.ImmutablePath{}, err + } + + return rec.Value() +} + +func (f *HttpFetcher) resolveDNSLink(ctx context.Context, p path.Path) (path.Path, error) { + dnsResolver := namesys.NewDNSResolver(madns.DefaultResolver.LookupTXT) + res, err := dnsResolver.Resolve(ctx, p) + if err != nil { + return nil, err + } + return res.Path, nil +} + +func (f *HttpFetcher) httpRequest(ctx context.Context, p path.Path, accept string) (io.ReadCloser, error) { + url := f.gateway + p.String() + fmt.Printf("Fetching with HTTP: %q\n", url) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + if err != nil { + return nil, fmt.Errorf("http.NewRequest error: %w", err) + } + req.Header.Set("Accept", accept) + if f.userAgent != "" { req.Header.Set("User-Agent", f.userAgent) } resp, err := http.DefaultClient.Do(req) if err != nil { - return nil, fmt.Errorf("http.DefaultClient.Do error: %s", err) + return nil, fmt.Errorf("http.DefaultClient.Do error: %w", err) } if resp.StatusCode >= 400 { defer resp.Body.Close() mes, err := io.ReadAll(resp.Body) if err != nil { - return nil, fmt.Errorf("error reading error body: %s", err) + return nil, fmt.Errorf("error reading error body: %w", err) } - return nil, fmt.Errorf("GET %s error: %s: %s", gwURL, resp.Status, string(mes)) + return nil, fmt.Errorf("GET %s error: %s: %s", url, resp.Status, string(mes)) } var rc io.ReadCloser @@ -93,11 +189,69 @@ func (f *HttpFetcher) Fetch(ctx context.Context, filePath string) ([]byte, error } else { rc = resp.Body } - defer rc.Close() - return io.ReadAll(rc) + return rc, nil } -func (f *HttpFetcher) Close() error { - return nil +func carStreamToFileBytes(ctx context.Context, r io.ReadCloser, imPath path.ImmutablePath) ([]byte, error) { + defer r.Close() + + // Create temporary block datastore and dag service. + dataStore := dssync.MutexWrap(datastore.NewMapDatastore()) + blockStore := blockstore.NewBlockstore(dataStore) + blockService := blockservice.New(blockStore, offline.Exchange(blockStore)) + dagService := merkledag.NewDAGService(blockService) + + defer dagService.Blocks.Close() + defer dataStore.Close() + + // Create CAR reader + car, err := gocarv2.NewBlockReader(r) + if err != nil { + fmt.Println(err) + return nil, fmt.Errorf("error creating car reader: %s", err) + } + + // Add all blocks to the blockstore. + for { + block, err := car.Next() + if err != nil && err != io.EOF { + return nil, fmt.Errorf("error reading block from car: %s", err) + } else if block == nil { + break + } + + err = blockStore.Put(ctx, block) + if err != nil { + return nil, fmt.Errorf("error putting block in blockstore: %s", err) + } + } + + fetcherCfg := bsfetcher.NewFetcherConfig(blockService) + fetcherCfg.PrototypeChooser = dagpb.AddSupportToChooser(bsfetcher.DefaultPrototypeChooser) + fetcher := fetcherCfg.WithReifier(unixfsnode.Reify) + resolver := resolver.NewBasicResolver(fetcher) + + cid, _, err := resolver.ResolveToLastNode(ctx, imPath) + if err != nil { + return nil, fmt.Errorf("failed to resolve: %w", err) + } + + nd, err := dagService.Get(ctx, cid) + if err != nil { + return nil, fmt.Errorf("failed to resolve: %w", err) + } + + // Make UnixFS file out of the node. + uf, err := unixfile.NewUnixfsFile(ctx, dagService, nd) + if err != nil { + return nil, fmt.Errorf("error building unixfs file: %s", err) + } + + // Check if it's a file and return. + if f, ok := uf.(files.File); ok { + return io.ReadAll(f) + } + + return nil, errors.New("unexpected unixfs node type") } diff --git a/repo/fsrepo/migrations/ipfsdir.go b/repo/fsrepo/migrations/ipfsdir.go index 1f1c320bf..8cb087d53 100644 --- a/repo/fsrepo/migrations/ipfsdir.go +++ b/repo/fsrepo/migrations/ipfsdir.go @@ -8,7 +8,7 @@ import ( "strconv" "strings" - "github.com/mitchellh/go-homedir" + "github.com/ipfs/kubo/misc/fsutil" ) const ( @@ -17,10 +17,6 @@ const ( versionFile = "version" ) -func init() { - homedir.DisableCache = true -} - // IpfsDir returns the path of the ipfs directory. If dir specified, then // returns the expanded version dir. If dir is "", then return the directory // set by IPFS_PATH, or if IPFS_PATH is not set, then return the default @@ -31,14 +27,14 @@ func IpfsDir(dir string) (string, error) { dir = os.Getenv(envIpfsPath) } if dir != "" { - dir, err = homedir.Expand(dir) + dir, err = fsutil.ExpandHome(dir) if err != nil { return "", err } return dir, nil } - home, err := homedir.Dir() + home, err := os.UserHomeDir() if err != nil { return "", err } @@ -84,7 +80,7 @@ func WriteRepoVersion(ipfsDir string, version int) error { } vFilePath := filepath.Join(ipfsDir, versionFile) - return os.WriteFile(vFilePath, []byte(fmt.Sprintf("%d\n", version)), 0644) + return os.WriteFile(vFilePath, []byte(fmt.Sprintf("%d\n", version)), 0o644) } func repoVersion(ipfsDir string) (int, error) { diff --git a/repo/fsrepo/migrations/ipfsdir_test.go b/repo/fsrepo/migrations/ipfsdir_test.go index a92a6a6d0..e4e626794 100644 --- a/repo/fsrepo/migrations/ipfsdir_test.go +++ b/repo/fsrepo/migrations/ipfsdir_test.go @@ -138,7 +138,7 @@ func testRepoVersion(t *testing.T) { t.Fatal(err) } vFilePath := filepath.Join(ipfsDir, versionFile) - err = os.WriteFile(vFilePath, []byte("bad-version-data\n"), 0644) + err = os.WriteFile(vFilePath, []byte("bad-version-data\n"), 0o644) if err != nil { panic(err) } diff --git a/repo/fsrepo/migrations/ipfsfetcher/ipfsfetcher.go b/repo/fsrepo/migrations/ipfsfetcher/ipfsfetcher.go index d13eaf148..ea020dfcc 100644 --- a/repo/fsrepo/migrations/ipfsfetcher/ipfsfetcher.go +++ b/repo/fsrepo/migrations/ipfsfetcher/ipfsfetcher.go @@ -7,17 +7,17 @@ import ( "io" "net/url" "os" - "path" + gopath "path" "strings" "sync" - files "github.com/ipfs/go-ipfs-files" - iface "github.com/ipfs/interface-go-ipfs-core" - "github.com/ipfs/interface-go-ipfs-core/options" - ipath "github.com/ipfs/interface-go-ipfs-core/path" + "github.com/ipfs/boxo/files" + "github.com/ipfs/boxo/path" "github.com/ipfs/kubo/config" "github.com/ipfs/kubo/core" "github.com/ipfs/kubo/core/coreapi" + iface "github.com/ipfs/kubo/core/coreiface" + "github.com/ipfs/kubo/core/coreiface/options" "github.com/ipfs/kubo/core/node/libp2p" "github.com/ipfs/kubo/repo/fsrepo" "github.com/ipfs/kubo/repo/fsrepo/migrations" @@ -25,7 +25,7 @@ import ( ) const ( - // Default maximum download size + // Default maximum download size. defaultFetchLimit = 1024 * 1024 * 512 tempNodeTCPAddr = "/ip4/127.0.0.1/tcp/0" @@ -46,7 +46,7 @@ type IpfsFetcher struct { ipfsTmpDir string ipfsStopFunc func() - fetched []ipath.Path + fetched []path.Path mutex sync.Mutex addrInfo peer.AddrInfo @@ -108,7 +108,7 @@ func (f *IpfsFetcher) Fetch(ctx context.Context, filePath string) ([]byte, error return nil, f.openErr } - iPath, err := parsePath(path.Join(f.distPath, filePath)) + iPath, err := parsePath(gopath.Join(f.distPath, filePath)) if err != nil { return nil, err } @@ -155,14 +155,14 @@ func (f *IpfsFetcher) AddrInfo() peer.AddrInfo { return f.addrInfo } -// FetchedPaths returns the IPFS paths of all items fetched by this fetcher -func (f *IpfsFetcher) FetchedPaths() []ipath.Path { +// FetchedPaths returns the IPFS paths of all items fetched by this fetcher. +func (f *IpfsFetcher) FetchedPaths() []path.Path { f.mutex.Lock() defer f.mutex.Unlock() return f.fetched } -func (f *IpfsFetcher) recordFetched(fetchedPath ipath.Path) { +func (f *IpfsFetcher) recordFetched(fetchedPath path.Path) { // Mutex protects against update by concurrent calls to Fetch f.mutex.Lock() defer f.mutex.Unlock() @@ -267,9 +267,8 @@ func (f *IpfsFetcher) startTempNode(ctx context.Context) error { return nil } -func parsePath(fetchPath string) (ipath.Path, error) { - ipfsPath := ipath.New(fetchPath) - if ipfsPath.IsValid() == nil { +func parsePath(fetchPath string) (path.Path, error) { + if ipfsPath, err := path.NewPath(fetchPath); err == nil { return ipfsPath, nil } @@ -280,11 +279,10 @@ func parsePath(fetchPath string) (ipath.Path, error) { switch proto := u.Scheme; proto { case "ipfs", "ipld", "ipns": - ipfsPath = ipath.New(path.Join("/", proto, u.Host, u.Path)) + return path.NewPath(gopath.Join("/", proto, u.Host, u.Path)) default: return nil, fmt.Errorf("%q is not an IPFS path", fetchPath) } - return ipfsPath, ipfsPath.IsValid() } func readIpfsConfig(repoRoot *string, userConfigFile string) (bootstrap []string, peers []peer.AddrInfo) { diff --git a/repo/fsrepo/migrations/ipfsfetcher/ipfsfetcher_test.go b/repo/fsrepo/migrations/ipfsfetcher/ipfsfetcher_test.go index f3a2a9313..7323d0172 100644 --- a/repo/fsrepo/migrations/ipfsfetcher/ipfsfetcher_test.go +++ b/repo/fsrepo/migrations/ipfsfetcher/ipfsfetcher_test.go @@ -55,7 +55,6 @@ func TestIpfsFetcher(t *testing.T) { if _, err = fetcher.Fetch(ctx, "/no_such_file"); err == nil { t.Fatal("expected error 404") } - } func TestInitIpfsFetcher(t *testing.T) { @@ -110,7 +109,7 @@ func TestInitIpfsFetcher(t *testing.T) { } func TestReadIpfsConfig(t *testing.T) { - var testConfig = ` + testConfig := ` { "Bootstrap": [ "/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt", diff --git a/repo/fsrepo/migrations/migrations.go b/repo/fsrepo/migrations/migrations.go index 7e764b731..e612b8abb 100644 --- a/repo/fsrepo/migrations/migrations.go +++ b/repo/fsrepo/migrations/migrations.go @@ -32,7 +32,7 @@ func RunMigration(ctx context.Context, fetcher Fetcher, targetVer int, ipfsDir s } fromVer, err := RepoVersion(ipfsDir) if err != nil { - return fmt.Errorf("could not get repo version: %s", err) + return fmt.Errorf("could not get repo version: %w", err) } if fromVer == targetVer { // repo already at target version number @@ -87,7 +87,7 @@ func RunMigration(ctx context.Context, fetcher Fetcher, targetVer int, ipfsDir s logger.Println("Running migration", migration, "...") err = runMigration(ctx, binPaths[migration], ipfsDir, revert, logger) if err != nil { - return fmt.Errorf("migration %s failed: %s", migration, err) + return fmt.Errorf("migration %s failed: %w", migration, err) } } logger.Printf("Success: fs-repo migrated to version %d.\n", targetVer) @@ -98,7 +98,7 @@ func RunMigration(ctx context.Context, fetcher Fetcher, targetVer int, ipfsDir s func NeedMigration(target int) (bool, error) { vnum, err := RepoVersion("") if err != nil { - return false, fmt.Errorf("could not get repo version: %s", err) + return false, fmt.Errorf("could not get repo version: %w", err) } return vnum != target, nil @@ -151,9 +151,9 @@ func ReadMigrationConfig(repoRoot string, userConfigFile string) (*config.Migrat } // GetMigrationFetcher creates one or more fetchers according to -// downloadSources, +// downloadSources,. func GetMigrationFetcher(downloadSources []string, distPath string, newIpfsFetcher func(string) Fetcher) (Fetcher, error) { - const httpUserAgent = "go-ipfs" + const httpUserAgent = "kubo/migration" const numTriesPerHTTP = 3 var fetchers []Fetcher @@ -171,7 +171,7 @@ func GetMigrationFetcher(downloadSources []string, distPath string, newIpfsFetch default: u, err := url.Parse(src) if err != nil { - return nil, fmt.Errorf("bad gateway address: %s", err) + return nil, fmt.Errorf("bad gateway address: %w", err) } switch u.Scheme { case "": @@ -293,7 +293,7 @@ func fetchMigrations(ctx context.Context, fetcher Fetcher, needed []string, dest if len(fails) != 0 { err = fmt.Errorf("failed to download migrations: %s", strings.Join(fails, " ")) if ctx.Err() != nil { - err = fmt.Errorf("%s, %s", ctx.Err(), err) + err = fmt.Errorf("%s, %w", ctx.Err(), err) } return nil, err } diff --git a/repo/fsrepo/migrations/migrations_test.go b/repo/fsrepo/migrations/migrations_test.go index 5f029e6a1..96370f864 100644 --- a/repo/fsrepo/migrations/migrations_test.go +++ b/repo/fsrepo/migrations/migrations_test.go @@ -110,9 +110,7 @@ func TestFetchMigrations(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - ts := createTestServer() - defer ts.Close() - fetcher := NewHttpFetcher(CurrentIpfsDist, ts.URL, "", 0) + fetcher := NewHttpFetcher(testIpfsDist, testServer.URL, "", 0) tmpDir := t.TempDir() @@ -162,9 +160,7 @@ func TestRunMigrations(t *testing.T) { t.Fatal(err) } - ts := createTestServer() - defer ts.Close() - fetcher := NewHttpFetcher(CurrentIpfsDist, ts.URL, "", 0) + fetcher := NewHttpFetcher(testIpfsDist, testServer.URL, "", 0) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -191,7 +187,7 @@ func createFakeBin(from, to int, tmpDir string) { panic(err) } emptyFile.Close() - err = os.Chmod(migPath, 0755) + err = os.Chmod(migPath, 0o755) if err != nil { panic(err) } diff --git a/repo/fsrepo/migrations/setup_test.go b/repo/fsrepo/migrations/setup_test.go new file mode 100644 index 000000000..2e306fda1 --- /dev/null +++ b/repo/fsrepo/migrations/setup_test.go @@ -0,0 +1,233 @@ +package migrations + +import ( + "bytes" + "context" + "fmt" + "io" + "net/http/httptest" + "os" + "path" + "path/filepath" + "strings" + "testing" + + "github.com/ipfs/boxo/blockservice" + "github.com/ipfs/boxo/exchange/offline" + "github.com/ipfs/boxo/gateway" + blocks "github.com/ipfs/go-block-format" + "github.com/ipfs/go-cid" + "github.com/ipfs/go-unixfsnode/data/builder" + "github.com/ipld/go-car/v2" + carblockstore "github.com/ipld/go-car/v2/blockstore" + "github.com/ipld/go-ipld-prime" + cidlink "github.com/ipld/go-ipld-prime/linking/cid" + "github.com/multiformats/go-multicodec" + "github.com/multiformats/go-multihash" +) + +var ( + testIpfsDist string + testServer *httptest.Server +) + +func TestMain(m *testing.M) { + // Setup test data + testDataDir := makeTestData() + defer os.RemoveAll(testDataDir) + + testCar := makeTestCar(testDataDir) + defer os.RemoveAll(testCar) + + // Setup test gateway + fd := setupTestGateway(testCar) + defer fd.Close() + + // Run tests + os.Exit(m.Run()) +} + +func makeTestData() string { + tempDir, err := os.MkdirTemp("", "kubo-migrations-test-*") + if err != nil { + panic(err) + } + + versions := []string{"v1.0.0", "v1.1.0", "v1.1.2", "v2.0.0-rc1", "2.0.0", "v2.0.1"} + packages := []string{"kubo", "go-ipfs", "fs-repo-migrations", "fs-repo-1-to-2", "fs-repo-2-to-3", "fs-repo-9-to-10", "fs-repo-10-to-11"} + + // Generate fake data + for _, name := range packages { + err = os.MkdirAll(filepath.Join(tempDir, name), 0777) + if err != nil { + panic(err) + } + + err = os.WriteFile(filepath.Join(tempDir, name, "versions"), []byte(strings.Join(versions, "\n")+"\n"), 0666) + if err != nil { + panic(err) + } + + for _, version := range versions { + filename, archName := makeArchivePath(name, name, version, "tar.gz") + createFakeArchive(filepath.Join(tempDir, filename), archName, false) + + filename, archName = makeArchivePath(name, name, version, "zip") + createFakeArchive(filepath.Join(tempDir, filename), archName, true) + } + } + + return tempDir +} + +func createFakeArchive(archName, name string, archZip bool) { + err := os.MkdirAll(filepath.Dir(archName), 0777) + if err != nil { + panic(err) + } + + fileName := strings.Split(path.Base(name), "_")[0] + root := fileName + + // Simulate fetching go-ipfs, which has "ipfs" as the name in the archive. + if fileName == "go-ipfs" || fileName == "kubo" { + fileName = "ipfs" + } + fileName = ExeName(fileName) + + if archZip { + err = writeZipFile(archName, root, fileName, "FAKE DATA") + } else { + err = writeTarGzipFile(archName, root, fileName, "FAKE DATA") + } + if err != nil { + panic(err) + } +} + +// makeTestCar makes a CAR file with the directory [testData]. This code is mostly +// sourced from https://github.com/ipld/go-car/blob/1e2f0bd2c44ee31f48a8f602b25b5671cc0c4687/cmd/car/create.go +func makeTestCar(testData string) string { + // make a cid with the right length that we eventually will patch with the root. + hasher, err := multihash.GetHasher(multihash.SHA2_256) + if err != nil { + panic(err) + } + digest := hasher.Sum([]byte{}) + hash, err := multihash.Encode(digest, multihash.SHA2_256) + if err != nil { + panic(err) + } + proxyRoot := cid.NewCidV1(uint64(multicodec.DagPb), hash) + + // Make CAR file + fd, err := os.CreateTemp("", "kubo-migrations-test-*.car") + if err != nil { + panic(err) + } + defer fd.Close() + filename := fd.Name() + + rw, err := carblockstore.OpenReadWriteFile(fd, []cid.Cid{proxyRoot}, carblockstore.WriteAsCarV1(true)) + if err != nil { + panic(err) + } + defer rw.Close() + + ctx := context.Background() + + ls := cidlink.DefaultLinkSystem() + ls.TrustedStorage = true + ls.StorageReadOpener = func(_ ipld.LinkContext, l ipld.Link) (io.Reader, error) { + cl, ok := l.(cidlink.Link) + if !ok { + return nil, fmt.Errorf("not a cidlink") + } + blk, err := rw.Get(ctx, cl.Cid) + if err != nil { + return nil, err + } + return bytes.NewBuffer(blk.RawData()), nil + } + ls.StorageWriteOpener = func(_ ipld.LinkContext) (io.Writer, ipld.BlockWriteCommitter, error) { + buf := bytes.NewBuffer(nil) + return buf, func(l ipld.Link) error { + cl, ok := l.(cidlink.Link) + if !ok { + return fmt.Errorf("not a cidlink") + } + blk, err := blocks.NewBlockWithCid(buf.Bytes(), cl.Cid) + if err != nil { + return err + } + return rw.Put(ctx, blk) + }, nil + } + + l, _, err := builder.BuildUnixFSRecursive(testData, &ls) + if err != nil { + panic(err) + } + + rcl, ok := l.(cidlink.Link) + if !ok { + panic(fmt.Errorf("could not interpret %s", l)) + } + + if err := rw.Finalize(); err != nil { + panic(err) + } + // re-open/finalize with the final root. + err = car.ReplaceRootsInFile(filename, []cid.Cid{rcl.Cid}) + if err != nil { + panic(err) + } + + return filename +} + +func setupTestGateway(testCar string) io.Closer { + blockService, roots, fd, err := newBlockServiceFromCAR(testCar) + if err != nil { + panic(err) + } + + if len(roots) != 1 { + panic("expected car with 1 root") + } + + backend, err := gateway.NewBlocksBackend(blockService) + if err != nil { + panic(err) + } + conf := gateway.Config{ + NoDNSLink: false, + DeserializedResponses: false, + } + + testIpfsDist = "/ipfs/" + roots[0].String() + testServer = httptest.NewServer(gateway.NewHandler(conf, backend)) + + return fd +} + +func newBlockServiceFromCAR(filepath string) (blockservice.BlockService, []cid.Cid, io.Closer, error) { + r, err := os.Open(filepath) + if err != nil { + return nil, nil, nil, err + } + + bs, err := carblockstore.NewReadOnly(r, nil) + if err != nil { + _ = r.Close() + return nil, nil, nil, err + } + + roots, err := bs.Roots() + if err != nil { + return nil, nil, nil, err + } + + blockService := blockservice.New(bs, offline.Exchange(bs)) + return blockService, roots, r, nil +} diff --git a/repo/fsrepo/migrations/unpack_test.go b/repo/fsrepo/migrations/unpack_test.go index be5bb4f39..e1152e2ca 100644 --- a/repo/fsrepo/migrations/unpack_test.go +++ b/repo/fsrepo/migrations/unpack_test.go @@ -35,7 +35,7 @@ func TestUnpackTgz(t *testing.T) { tmpDir := t.TempDir() badTarGzip := filepath.Join(tmpDir, "bad.tar.gz") - err := os.WriteFile(badTarGzip, []byte("bad-data\n"), 0644) + err := os.WriteFile(badTarGzip, []byte("bad-data\n"), 0o644) if err != nil { panic(err) } @@ -72,14 +72,13 @@ func TestUnpackTgz(t *testing.T) { if fi.Size() != int64(len(testData)) { t.Fatal("unpacked file size is", fi.Size(), "expected", len(testData)) } - } func TestUnpackZip(t *testing.T) { tmpDir := t.TempDir() badZip := filepath.Join(tmpDir, "bad.zip") - err := os.WriteFile(badZip, []byte("bad-data\n"), 0644) + err := os.WriteFile(badZip, []byte("bad-data\n"), 0o644) if err != nil { panic(err) } @@ -153,7 +152,7 @@ func writeTarGzip(root, fileName, data string, w io.Writer) error { if fileName != "" { hdr := &tar.Header{ Name: path.Join(root, fileName), - Mode: 0600, + Mode: 0o600, Size: int64(len(data)), } // Write header diff --git a/repo/fsrepo/migrations/versions.go b/repo/fsrepo/migrations/versions.go index af5bbbbd9..056671c07 100644 --- a/repo/fsrepo/migrations/versions.go +++ b/repo/fsrepo/migrations/versions.go @@ -57,7 +57,7 @@ func DistVersions(ctx context.Context, fetcher Fetcher, dist string, sortDesc bo vers = append(vers, ver) } if scan.Err() != nil { - return nil, fmt.Errorf("could not read versions: %s", scan.Err()) + return nil, fmt.Errorf("could not read versions: %w", scan.Err()) } if sortDesc { diff --git a/repo/fsrepo/migrations/versions_test.go b/repo/fsrepo/migrations/versions_test.go index 18de72b77..dd62f9bde 100644 --- a/repo/fsrepo/migrations/versions_test.go +++ b/repo/fsrepo/migrations/versions_test.go @@ -13,9 +13,7 @@ func TestDistVersions(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - ts := createTestServer() - defer ts.Close() - fetcher := NewHttpFetcher("", ts.URL, "", 0) + fetcher := NewHttpFetcher(testIpfsDist, testServer.URL, "", 0) vers, err := DistVersions(ctx, fetcher, testDist, true) if err != nil { @@ -32,9 +30,7 @@ func TestLatestDistVersion(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - ts := createTestServer() - defer ts.Close() - fetcher := NewHttpFetcher("", ts.URL, "", 0) + fetcher := NewHttpFetcher(testIpfsDist, testServer.URL, "", 0) latest, err := LatestDistVersion(ctx, fetcher, testDist, false) if err != nil { diff --git a/repo/fsrepo/misc.go b/repo/fsrepo/misc.go index 7824f2f4f..fa5b235e2 100644 --- a/repo/fsrepo/misc.go +++ b/repo/fsrepo/misc.go @@ -4,7 +4,7 @@ import ( "os" config "github.com/ipfs/kubo/config" - homedir "github.com/mitchellh/go-homedir" + "github.com/ipfs/kubo/misc/fsutil" ) // BestKnownPath returns the best known fsrepo path. If the ENV override is @@ -15,7 +15,7 @@ func BestKnownPath() (string, error) { if os.Getenv(config.EnvDir) != "" { ipfsPath = os.Getenv(config.EnvDir) } - ipfsPath, err := homedir.Expand(ipfsPath) + ipfsPath, err := fsutil.ExpandHome(ipfsPath) if err != nil { return "", err } diff --git a/repo/mock.go b/repo/mock.go index a50d448ed..2ac60615d 100644 --- a/repo/mock.go +++ b/repo/mock.go @@ -5,8 +5,9 @@ import ( "errors" "net" - filestore "github.com/ipfs/go-filestore" - keystore "github.com/ipfs/go-ipfs-keystore" + filestore "github.com/ipfs/boxo/filestore" + keystore "github.com/ipfs/boxo/keystore" + rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager" config "github.com/ipfs/kubo/config" ma "github.com/multiformats/go-multiaddr" @@ -14,7 +15,7 @@ import ( var errTODO = errors.New("TODO: mock repo") -// Mock is not thread-safe +// Mock is not thread-safe. type Mock struct { C config.Config D Datastore @@ -26,6 +27,14 @@ func (m *Mock) Config() (*config.Config, error) { return &m.C, nil // FIXME threadsafety } +func (m *Mock) Path() string { + return "" +} + +func (m *Mock) UserResourceOverrides() (rcmgr.PartialLimitConfig, error) { + return rcmgr.PartialLimitConfig{}, nil +} + func (m *Mock) SetConfig(updated *config.Config) error { m.C = *updated // FIXME threadsafety return nil diff --git a/repo/repo.go b/repo/repo.go index bec02049d..f345e89b4 100644 --- a/repo/repo.go +++ b/repo/repo.go @@ -6,17 +6,16 @@ import ( "io" "net" - filestore "github.com/ipfs/go-filestore" - keystore "github.com/ipfs/go-ipfs-keystore" + filestore "github.com/ipfs/boxo/filestore" + keystore "github.com/ipfs/boxo/keystore" + rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager" ds "github.com/ipfs/go-datastore" config "github.com/ipfs/kubo/config" ma "github.com/multiformats/go-multiaddr" ) -var ( - ErrApiNotRunning = errors.New("api not running") //nolint -) +var ErrApiNotRunning = errors.New("api not running") //nolint // Repo represents all persistent data of a given ipfs node. type Repo interface { @@ -24,6 +23,13 @@ type Repo interface { // to the returned config are not automatically persisted. Config() (*config.Config, error) + // Path is the repo file-system path + Path() string + + // UserResourceOverrides returns optional user resource overrides for the + // libp2p resource manager. + UserResourceOverrides() (rcmgr.PartialLimitConfig, error) + // BackupConfig creates a backup of the current configuration file using // the given prefix for naming. BackupConfig(prefix string) (string, error) diff --git a/routing/composer.go b/routing/composer.go index f2f1f65e6..3541fc7dd 100644 --- a/routing/composer.go +++ b/routing/composer.go @@ -2,6 +2,7 @@ package routing import ( "context" + "errors" "github.com/hashicorp/go-multierror" "github.com/ipfs/go-cid" @@ -11,8 +12,10 @@ import ( "github.com/multiformats/go-multihash" ) -var _ routinghelpers.ProvideManyRouter = &Composer{} -var _ routing.Routing = &Composer{} +var ( + _ routinghelpers.ProvideManyRouter = &Composer{} + _ routing.Routing = &Composer{} +) type Composer struct { GetValueRouter routing.Routing @@ -27,7 +30,6 @@ func (c *Composer) Provide(ctx context.Context, cid cid.Cid, provide bool) error err := c.ProvideRouter.Provide(ctx, cid, provide) if err != nil { log.Debug("composer: calling provide: ", cid, " error: ", err) - } return err @@ -51,7 +53,7 @@ func (c *Composer) ProvideMany(ctx context.Context, keys []multihash.Multihash) func (c *Composer) Ready() bool { log.Debug("composer: calling ready") - pmr, ok := c.ProvideRouter.(routinghelpers.ProvideManyRouter) + pmr, ok := c.ProvideRouter.(routinghelpers.ReadyAbleRouter) if !ok { return true } @@ -100,9 +102,18 @@ func (c *Composer) GetValue(ctx context.Context, key string, opts ...routing.Opt func (c *Composer) SearchValue(ctx context.Context, key string, opts ...routing.Option) (<-chan []byte, error) { log.Debug("composer: calling searchValue: ", key) ch, err := c.GetValueRouter.SearchValue(ctx, key, opts...) + + // avoid nil channels on implementations not supporting SearchValue method. + if errors.Is(err, routing.ErrNotFound) && ch == nil { + out := make(chan []byte) + close(out) + return out, err + } + if err != nil { log.Debug("composer: calling searchValue error: ", key, err) } + return ch, err } diff --git a/routing/delegated.go b/routing/delegated.go index d95053fd3..f5f98a03f 100644 --- a/routing/delegated.go +++ b/routing/delegated.go @@ -7,12 +7,11 @@ import ( "fmt" "net/http" + drclient "github.com/ipfs/boxo/routing/http/client" + "github.com/ipfs/boxo/routing/http/contentrouter" "github.com/ipfs/go-datastore" - drc "github.com/ipfs/go-delegated-routing/client" - drp "github.com/ipfs/go-delegated-routing/gen/proto" - drclient "github.com/ipfs/go-libipfs/routing/http/client" - "github.com/ipfs/go-libipfs/routing/http/contentrouter" logging "github.com/ipfs/go-log" + version "github.com/ipfs/kubo" "github.com/ipfs/kubo/config" dht "github.com/libp2p/go-libp2p-kad-dht" "github.com/libp2p/go-libp2p-kad-dht/dual" @@ -24,7 +23,6 @@ import ( "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/routing" ma "github.com/multiformats/go-multiaddr" - "github.com/multiformats/go-multicodec" "go.opencensus.io/stats/view" ) @@ -95,8 +93,6 @@ func parse(visited map[string]bool, switch cfg.Type { case config.RouterTypeHTTP: router, err = httpRoutingFromConfig(cfg.Router, extraHTTP) - case config.RouterTypeReframe: - router, err = reframeRoutingFromConfig(cfg.Router, extraHTTP) case config.RouterTypeDHT: router, err = dhtRoutingFromConfig(cfg.Router, extraDHT) case config.RouterTypeParallel: @@ -109,10 +105,11 @@ func parse(visited map[string]bool, } pr = append(pr, &routinghelpers.ParallelRouter{ - Router: ri, - IgnoreError: cr.IgnoreErrors, - Timeout: cr.Timeout.Duration, - ExecuteAfter: cr.ExecuteAfter.WithDefault(0), + Router: ri, + IgnoreError: cr.IgnoreErrors, + DoNotWaitForSearchValue: true, + Timeout: cr.Timeout.Duration, + ExecuteAfter: cr.ExecuteAfter.WithDefault(0), }) } @@ -208,6 +205,10 @@ func httpRoutingFromConfig(conf config.Router, extraHTTP *ExtraHTTPParams) (rout drclient.WithHTTPClient(delegateHTTPClient), drclient.WithIdentity(key), drclient.WithProviderInfo(addrInfo.ID, addrInfo.Addrs), + drclient.WithUserAgent(version.GetUserAgentVersion()), + drclient.WithProtocolFilter(config.DefaultHTTPRoutersFilterProtocols), + drclient.WithStreamResultsRequired(), // https://specs.ipfs.tech/routing/http-routing-v1/#streaming + drclient.WithDisabledLocalFiltering(false), // force local filtering in case remote server does not support IPIP-484 ) if err != nil { return nil, err @@ -219,73 +220,19 @@ func httpRoutingFromConfig(conf config.Router, extraHTTP *ExtraHTTPParams) (rout contentrouter.WithMaxProvideConcurrency(params.MaxProvideConcurrency), ) + err = view.Register(drclient.OpenCensusViews...) + if err != nil { + return nil, fmt.Errorf("registering HTTP delegated routing views: %w", err) + } + return &httpRoutingWrapper{ ContentRouting: cr, + PeerRouting: cr, + ValueStore: cr, ProvideManyRouter: cr, }, nil } -func reframeRoutingFromConfig(conf config.Router, extraReframe *ExtraHTTPParams) (routing.Routing, error) { - var dr drp.DelegatedRouting_Client - - params := conf.Parameters.(*config.ReframeRouterParams) - - if params.Endpoint == "" { - return nil, NewParamNeededErr("Endpoint", conf.Type) - } - - // Increase per-host connection pool since we are making lots of concurrent requests. - transport := http.DefaultTransport.(*http.Transport).Clone() - transport.MaxIdleConns = 500 - transport.MaxIdleConnsPerHost = 100 - - delegateHTTPClient := &http.Client{ - Transport: transport, - } - dr, err := drp.New_DelegatedRouting_Client(params.Endpoint, - drp.DelegatedRouting_Client_WithHTTPClient(delegateHTTPClient), - ) - if err != nil { - return nil, err - } - - var c *drc.Client - - err = view.Register(drc.DefaultViews...) - if err != nil { - return nil, fmt.Errorf("registering delegated routing views: %w", err) - } - - // this path is for tests only - if extraReframe == nil { - c, err = drc.NewClient(dr, nil, nil) - if err != nil { - return nil, err - } - } else { - prov, err := createProvider(extraReframe.PeerID, extraReframe.Addrs) - if err != nil { - return nil, err - } - - key, err := decodePrivKey(extraReframe.PrivKeyB64) - if err != nil { - return nil, err - } - - c, err = drc.NewClient(dr, prov, key) - if err != nil { - return nil, err - } - } - - crc := drc.NewContentRoutingClient(c) - return &reframeRoutingWrapper{ - Client: c, - ContentRoutingClient: crc, - }, nil -} - func decodePrivKey(keyB64 string) (ic.PrivKey, error) { pk, err := base64.StdEncoding.DecodeString(keyB64) if err != nil { @@ -317,19 +264,6 @@ func createAddrInfo(peerID string, addrs []string) (peer.AddrInfo, error) { }, nil } -func createProvider(peerID string, addrs []string) (*drc.Provider, error) { - addrInfo, err := createAddrInfo(peerID, addrs) - if err != nil { - return nil, err - } - return &drc.Provider{ - Peer: addrInfo, - ProviderProto: []drc.TransferProtocol{ - {Codec: multicodec.TransportBitswap}, - }, - }, nil -} - type ExtraDHTParams struct { BootstrapPeers []peer.AddrInfo Host host.Host diff --git a/routing/delegated_test.go b/routing/delegated_test.go index ee7543114..028f3b465 100644 --- a/routing/delegated_test.go +++ b/routing/delegated_test.go @@ -1,68 +1,27 @@ package routing import ( + "crypto/rand" "encoding/base64" "testing" "github.com/ipfs/kubo/config" - crypto "github.com/libp2p/go-libp2p/core/crypto" - peer "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/peer" "github.com/stretchr/testify/require" ) -func TestReframeRoutingFromConfig(t *testing.T) { - require := require.New(t) - - r, err := reframeRoutingFromConfig(config.Router{ - Type: config.RouterTypeReframe, - Parameters: &config.ReframeRouterParams{}, - }, nil) - - require.Nil(r) - require.EqualError(err, "configuration param 'Endpoint' is needed for reframe delegated routing types") - - r, err = reframeRoutingFromConfig(config.Router{ - Type: config.RouterTypeReframe, - Parameters: &config.ReframeRouterParams{ - Endpoint: "test", - }, - }, nil) - - require.NoError(err) - require.NotNil(r) - - priv, pub, err := crypto.GenerateKeyPair(crypto.RSA, 2048) - require.NoError(err) - - id, err := peer.IDFromPublicKey(pub) - require.NoError(err) - - privM, err := crypto.MarshalPrivateKey(priv) - require.NoError(err) - - r, err = reframeRoutingFromConfig(config.Router{ - Type: config.RouterTypeReframe, - Parameters: &config.ReframeRouterParams{ - Endpoint: "test", - }, - }, &ExtraHTTPParams{ - PeerID: id.String(), - Addrs: []string{"/ip4/0.0.0.0/tcp/4001"}, - PrivKeyB64: base64.StdEncoding.EncodeToString(privM), - }) - - require.NotNil(r) - require.NoError(err) -} - func TestParser(t *testing.T) { require := require.New(t) + pid, sk, err := generatePeerID() + require.NoError(err) + router, err := Parse(config.Routers{ "r1": config.RouterParser{ Router: config.Router{ - Type: config.RouterTypeReframe, - Parameters: &config.ReframeRouterParams{ + Type: config.RouterTypeHTTP, + Parameters: &config.HTTPRouterParams{ Endpoint: "testEndpoint", }, }, @@ -95,7 +54,10 @@ func TestParser(t *testing.T) { config.MethodNameProvide: config.Method{ RouterName: "r2", }, - }, &ExtraDHTParams{}, nil) + }, &ExtraDHTParams{}, &ExtraHTTPParams{ + PeerID: string(pid), + PrivKeyB64: sk, + }) require.NoError(err) @@ -109,27 +71,30 @@ func TestParser(t *testing.T) { func TestParserRecursive(t *testing.T) { require := require.New(t) + pid, sk, err := generatePeerID() + require.NoError(err) + router, err := Parse(config.Routers{ - "reframe1": config.RouterParser{ + "http1": config.RouterParser{ Router: config.Router{ - Type: config.RouterTypeReframe, - Parameters: &config.ReframeRouterParams{ + Type: config.RouterTypeHTTP, + Parameters: &config.HTTPRouterParams{ Endpoint: "testEndpoint1", }, }, }, - "reframe2": config.RouterParser{ + "http2": config.RouterParser{ Router: config.Router{ - Type: config.RouterTypeReframe, - Parameters: &config.ReframeRouterParams{ + Type: config.RouterTypeHTTP, + Parameters: &config.HTTPRouterParams{ Endpoint: "testEndpoint2", }, }, }, - "reframe3": config.RouterParser{ + "http3": config.RouterParser{ Router: config.Router{ - Type: config.RouterTypeReframe, - Parameters: &config.ReframeRouterParams{ + Type: config.RouterTypeHTTP, + Parameters: &config.HTTPRouterParams{ Endpoint: "testEndpoint3", }, }, @@ -140,10 +105,10 @@ func TestParserRecursive(t *testing.T) { Parameters: &config.ComposableRouterParams{ Routers: []config.ConfigRouter{ { - RouterName: "reframe1", + RouterName: "http1", }, { - RouterName: "reframe2", + RouterName: "http2", }, }, }, @@ -158,7 +123,7 @@ func TestParserRecursive(t *testing.T) { RouterName: "composable1", }, { - RouterName: "reframe3", + RouterName: "http3", }, }, }, @@ -180,13 +145,15 @@ func TestParserRecursive(t *testing.T) { config.MethodNameProvide: config.Method{ RouterName: "composable2", }, - }, &ExtraDHTParams{}, nil) + }, &ExtraDHTParams{}, &ExtraHTTPParams{ + PeerID: string(pid), + PrivKeyB64: sk, + }) require.NoError(err) _, ok := router.(*Composer) require.True(ok) - } func TestParserRecursiveLoop(t *testing.T) { @@ -237,3 +204,23 @@ func TestParserRecursiveLoop(t *testing.T) { require.ErrorContains(err, "dependency loop creating router with name \"composable2\"") } + +func generatePeerID() (string, string, error) { + sk, pk, err := crypto.GenerateEd25519Key(rand.Reader) + if err != nil { + return "", "", err + } + + bytes, err := crypto.MarshalPrivateKey(sk) + if err != nil { + return "", "", err + } + + enc := base64.StdEncoding.EncodeToString(bytes) + if err != nil { + return "", "", err + } + + pid, err := peer.IDFromPublicKey(pk) + return pid.String(), enc, err +} diff --git a/routing/wrapper.go b/routing/wrapper.go index d4215ca9c..680aef263 100644 --- a/routing/wrapper.go +++ b/routing/wrapper.go @@ -3,72 +3,29 @@ package routing import ( "context" - "github.com/ipfs/go-cid" - drc "github.com/ipfs/go-delegated-routing/client" routinghelpers "github.com/libp2p/go-libp2p-routing-helpers" - "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/routing" ) -var _ routing.Routing = &reframeRoutingWrapper{} -var _ routinghelpers.ProvideManyRouter = &reframeRoutingWrapper{} - -// reframeRoutingWrapper is a wrapper needed to construct the routing.Routing interface from -// delegated-routing library. -type reframeRoutingWrapper struct { - *drc.Client - *drc.ContentRoutingClient -} - -func (c *reframeRoutingWrapper) Provide(ctx context.Context, id cid.Cid, announce bool) error { - return c.ContentRoutingClient.Provide(ctx, id, announce) -} - -func (c *reframeRoutingWrapper) FindProvidersAsync(ctx context.Context, cid cid.Cid, count int) <-chan peer.AddrInfo { - return c.ContentRoutingClient.FindProvidersAsync(ctx, cid, count) -} - -func (c *reframeRoutingWrapper) Bootstrap(ctx context.Context) error { - return nil -} - -func (c *reframeRoutingWrapper) FindPeer(ctx context.Context, id peer.ID) (peer.AddrInfo, error) { - return peer.AddrInfo{}, routing.ErrNotSupported -} - type ProvideManyRouter interface { routinghelpers.ProvideManyRouter routing.Routing } -var _ routing.Routing = &httpRoutingWrapper{} -var _ routinghelpers.ProvideManyRouter = &httpRoutingWrapper{} +var ( + _ routing.Routing = &httpRoutingWrapper{} + _ routinghelpers.ProvideManyRouter = &httpRoutingWrapper{} +) // httpRoutingWrapper is a wrapper needed to construct the routing.Routing interface from // http delegated routing. type httpRoutingWrapper struct { routing.ContentRouting + routing.PeerRouting + routing.ValueStore routinghelpers.ProvideManyRouter } func (c *httpRoutingWrapper) Bootstrap(ctx context.Context) error { return nil } - -func (c *httpRoutingWrapper) FindPeer(ctx context.Context, id peer.ID) (peer.AddrInfo, error) { - return peer.AddrInfo{}, routing.ErrNotSupported -} - -func (c *httpRoutingWrapper) PutValue(context.Context, string, []byte, ...routing.Option) error { - return routing.ErrNotSupported -} - -func (c *httpRoutingWrapper) GetValue(context.Context, string, ...routing.Option) ([]byte, error) { - return nil, routing.ErrNotSupported -} - -func (c *httpRoutingWrapper) SearchValue(context.Context, string, ...routing.Option) (<-chan []byte, error) { - out := make(chan []byte) - close(out) - return out, routing.ErrNotSupported -} diff --git a/tar/format.go b/tar/format.go deleted file mode 100644 index 2bbf7298f..000000000 --- a/tar/format.go +++ /dev/null @@ -1,225 +0,0 @@ -package tarfmt - -import ( - "archive/tar" - "bytes" - "context" - "errors" - "io" - "strings" - - dag "github.com/ipfs/go-merkledag" - "github.com/ipfs/go-merkledag/dagutils" - path "github.com/ipfs/go-path" - importer "github.com/ipfs/go-unixfs/importer" - uio "github.com/ipfs/go-unixfs/io" - - chunker "github.com/ipfs/go-ipfs-chunker" - ipld "github.com/ipfs/go-ipld-format" - logging "github.com/ipfs/go-log" -) - -var log = logging.Logger("tarfmt") - -var blockSize = 512 -var zeroBlock = make([]byte, blockSize) - -func marshalHeader(h *tar.Header) ([]byte, error) { - buf := new(bytes.Buffer) - w := tar.NewWriter(buf) - err := w.WriteHeader(h) - if err != nil { - return nil, err - } - return buf.Bytes(), nil -} - -// ImportTar imports a tar file into the given DAGService and returns the root -// node. -func ImportTar(ctx context.Context, r io.Reader, ds ipld.DAGService) (*dag.ProtoNode, error) { - tr := tar.NewReader(r) - - root := new(dag.ProtoNode) - root.SetData([]byte("ipfs/tar")) - - e := dagutils.NewDagEditor(root, ds) - - for { - h, err := tr.Next() - if err != nil { - if err == io.EOF { - break - } - return nil, err - } - - header := new(dag.ProtoNode) - - headerBytes, err := marshalHeader(h) - if err != nil { - return nil, err - } - - header.SetData(headerBytes) - - if h.Size > 0 { - spl := chunker.NewRabin(tr, uint64(chunker.DefaultBlockSize)) - nd, err := importer.BuildDagFromReader(ds, spl) - if err != nil { - return nil, err - } - - err = header.AddNodeLink("data", nd) - if err != nil { - return nil, err - } - } - - err = ds.Add(ctx, header) - if err != nil { - return nil, err - } - - path := escapePath(h.Name) - err = e.InsertNodeAtPath(context.Background(), path, header, func() *dag.ProtoNode { return new(dag.ProtoNode) }) - if err != nil { - return nil, err - } - } - - return e.Finalize(ctx, ds) -} - -// adds a '-' to the beginning of each path element so we can use 'data' as a -// special link in the structure without having to worry about -func escapePath(pth string) string { - elems := path.SplitList(strings.Trim(pth, "/")) - for i, e := range elems { - elems[i] = "-" + e - } - return path.Join(elems) -} - -type tarReader struct { - links []*ipld.Link - ds ipld.DAGService - - childRead *tarReader - hdrBuf *bytes.Reader - fileRead *countReader - pad int - - ctx context.Context -} - -func (tr *tarReader) Read(b []byte) (int, error) { - // if we have a header to be read, it takes priority - if tr.hdrBuf != nil { - n, err := tr.hdrBuf.Read(b) - if err == io.EOF { - tr.hdrBuf = nil - return n, nil - } - return n, err - } - - // no header remaining, check for recursive - if tr.childRead != nil { - n, err := tr.childRead.Read(b) - if err == io.EOF { - tr.childRead = nil - return n, nil - } - return n, err - } - - // check for filedata to be read - if tr.fileRead != nil { - n, err := tr.fileRead.Read(b) - if err == io.EOF { - nr := tr.fileRead.n - tr.pad = (blockSize - (nr % blockSize)) % blockSize - tr.fileRead.Close() - tr.fileRead = nil - return n, nil - } - return n, err - } - - // filedata reads must be padded out to 512 byte offsets - if tr.pad > 0 { - n := copy(b, zeroBlock[:tr.pad]) - tr.pad -= n - return n, nil - } - - if len(tr.links) == 0 { - return 0, io.EOF - } - - next := tr.links[0] - tr.links = tr.links[1:] - - headerNd, err := next.GetNode(tr.ctx, tr.ds) - if err != nil { - return 0, err - } - - hndpb, ok := headerNd.(*dag.ProtoNode) - if !ok { - return 0, dag.ErrNotProtobuf - } - - tr.hdrBuf = bytes.NewReader(hndpb.Data()) - - dataNd, err := hndpb.GetLinkedProtoNode(tr.ctx, tr.ds, "data") - if err != nil && err != dag.ErrLinkNotFound { - return 0, err - } - - if err == nil { - dr, err := uio.NewDagReader(tr.ctx, dataNd, tr.ds) - if err != nil { - log.Error("dagreader error: ", err) - return 0, err - } - - tr.fileRead = &countReader{r: dr} - } else if len(headerNd.Links()) > 0 { - tr.childRead = &tarReader{ - links: headerNd.Links(), - ds: tr.ds, - ctx: tr.ctx, - } - } - - return tr.Read(b) -} - -// ExportTar exports the passed DAG as a tar file. This function is the inverse -// of ImportTar. -func ExportTar(ctx context.Context, root *dag.ProtoNode, ds ipld.DAGService) (io.Reader, error) { - if string(root.Data()) != "ipfs/tar" { - return nil, errors.New("not an IPFS tarchive") - } - return &tarReader{ - links: root.Links(), - ds: ds, - ctx: ctx, - }, nil -} - -type countReader struct { - r io.ReadCloser - n int -} - -func (r *countReader) Read(b []byte) (int, error) { - n, err := r.r.Read(b) - r.n += n - return n, err -} - -func (r *countReader) Close() error { - return r.r.Close() -} diff --git a/test/3nodetest/bin/save_profiling_data.sh b/test/3nodetest/bin/save_profiling_data.sh index 03c0cbabe..639b5d383 100644 --- a/test/3nodetest/bin/save_profiling_data.sh +++ b/test/3nodetest/bin/save_profiling_data.sh @@ -6,7 +6,7 @@ for container in 3nodetest_bootstrap_1 3nodetest_client_1 3nodetest_server_1; do done # since the nodes are executed with the --debug flag, profiling data is written -# to the the working dir. by default, the working dir is /go. +# to the working dir. by default, the working dir is /go. for container in 3nodetest_bootstrap_1 3nodetest_client_1 3nodetest_server_1; do docker cp $container:/go/ipfs.cpuprof build/profiling_data_$container diff --git a/test/bench/bench_cli_ipfs_add/main.go b/test/bench/bench_cli_ipfs_add/main.go index a089410ef..2c39ba636 100644 --- a/test/bench/bench_cli_ipfs_add/main.go +++ b/test/bench/bench_cli_ipfs_add/main.go @@ -3,6 +3,7 @@ package main import ( "flag" "fmt" + "io" "log" "os" "os/exec" @@ -11,8 +12,8 @@ import ( "github.com/ipfs/kubo/thirdparty/unit" + random "github.com/ipfs/go-test/random" config "github.com/ipfs/kubo/config" - random "github.com/jbenet/go-random" ) var ( @@ -45,12 +46,7 @@ func benchmarkAdd(amount int64) (*testing.BenchmarkResult, error) { b.SetBytes(amount) for i := 0; i < b.N; i++ { b.StopTimer() - tmpDir, err := os.MkdirTemp("", "") - if err != nil { - benchmarkError = err - b.Fatal(err) - } - defer os.RemoveAll(tmpDir) + tmpDir := b.TempDir() env := append( []string{fmt.Sprintf("%s=%s", config.EnvDir, path.Join(tmpDir, config.DefaultPathName))}, // first in order to override @@ -64,7 +60,7 @@ func benchmarkAdd(amount int64) (*testing.BenchmarkResult, error) { } } - initCmd := exec.Command("ipfs", "init", "-b=2048") + initCmd := exec.Command("ipfs", "init") setupCmd(initCmd) if err := initCmd.Run(); err != nil { benchmarkError = err @@ -79,7 +75,11 @@ func benchmarkAdd(amount int64) (*testing.BenchmarkResult, error) { } defer os.Remove(f.Name()) - if err := random.WritePseudoRandomBytes(amount, f, seed); err != nil { + randReader := &io.LimitedReader{ + R: random.NewSeededRand(seed), + N: amount, + } + if _, err := io.Copy(f, randReader); err != nil { benchmarkError = err b.Fatal(err) } diff --git a/test/bench/offline_add/main.go b/test/bench/offline_add/main.go index a15ebcffd..7c00d30f4 100644 --- a/test/bench/offline_add/main.go +++ b/test/bench/offline_add/main.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "io" "log" "os" "os/exec" @@ -10,8 +11,8 @@ import ( "github.com/ipfs/kubo/thirdparty/unit" + random "github.com/ipfs/go-test/random" config "github.com/ipfs/kubo/config" - random "github.com/jbenet/go-random" ) func main() { @@ -37,18 +38,14 @@ func benchmarkAdd(amount int64) (*testing.BenchmarkResult, error) { b.SetBytes(amount) for i := 0; i < b.N; i++ { b.StopTimer() - tmpDir, err := os.MkdirTemp("", "") - if err != nil { - b.Fatal(err) - } - defer os.RemoveAll(tmpDir) + tmpDir := b.TempDir() env := append(os.Environ(), fmt.Sprintf("%s=%s", config.EnvDir, path.Join(tmpDir, config.DefaultPathName))) setupCmd := func(cmd *exec.Cmd) { cmd.Env = env } - cmd := exec.Command("ipfs", "init", "-b=2048") + cmd := exec.Command("ipfs", "init") setupCmd(cmd) if err := cmd.Run(); err != nil { b.Fatal(err) @@ -61,7 +58,11 @@ func benchmarkAdd(amount int64) (*testing.BenchmarkResult, error) { } defer os.Remove(f.Name()) - err = random.WritePseudoRandomBytes(amount, f, seed) + randReader := &io.LimitedReader{ + R: random.NewSeededRand(seed), + N: amount, + } + _, err = io.Copy(f, randReader) if err != nil { b.Fatal(err) } diff --git a/test/bin/Rules.mk b/test/bin/Rules.mk index 02b0a4e5b..3fd0bcd92 100644 --- a/test/bin/Rules.mk +++ b/test/bin/Rules.mk @@ -5,7 +5,7 @@ TGTS_$(d) := define go-build-testdep OUT="$(CURDIR)/$@" ; \ cd "test/dependencies" ; \ - $(GOCC) build $(go-flags-with-tags) -o "$${OUT}" "$<" + $(GOCC) build $(go-flags-with-tags) -o "$${OUT}" "$<" 2>&1 endef .PHONY: github.com/ipfs/kubo/test/dependencies/pollEndpoint @@ -18,11 +18,6 @@ $(d)/go-sleep: github.com/ipfs/kubo/test/dependencies/go-sleep $(go-build-testdep) TGTS_$(d) += $(d)/go-sleep -.PHONY: github.com/ipfs/kubo/test/dependencies/graphsync-get -$(d)/graphsync-get: github.com/ipfs/kubo/test/dependencies/graphsync-get - $(go-build-testdep) -TGTS_$(d) += $(d)/graphsync-get - .PHONY: github.com/ipfs/kubo/test/dependencies/go-timeout $(d)/go-timeout: github.com/ipfs/kubo/test/dependencies/go-timeout $(go-build-testdep) diff --git a/test/cli/add_test.go b/test/cli/add_test.go new file mode 100644 index 000000000..ae652989a --- /dev/null +++ b/test/cli/add_test.go @@ -0,0 +1,118 @@ +package cli + +import ( + "testing" + + "github.com/ipfs/kubo/config" + "github.com/ipfs/kubo/test/cli/harness" + "github.com/stretchr/testify/require" +) + +func TestAdd(t *testing.T) { + t.Parallel() + + var ( + shortString = "hello world" + shortStringCidV0 = "Qmf412jQZiuVUtdgnB36FXFX7xg5V6KEbSJ4dpQuhkLyfD" // cidv0 - dag-pb - sha2-256 + shortStringCidV1 = "bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e" // cidv1 - raw - sha2-256 + shortStringCidV1NoRawLeaves = "bafybeihykld7uyxzogax6vgyvag42y7464eywpf55gxi5qpoisibh3c5wa" // cidv1 - dag-pb - sha2-256 + shortStringCidV1Sha512 = "bafkrgqbqt3gerhas23vuzrapkdeqf4vu2dwxp3srdj6hvg6nhsug2tgyn6mj3u23yx7utftq3i2ckw2fwdh5qmhid5qf3t35yvkc5e5ottlw6" + ) + + t.Run("produced cid version: implicit default (CIDv0)", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() + + cidStr := node.IPFSAddStr(shortString) + require.Equal(t, shortStringCidV0, cidStr) + }) + + t.Run("produced cid version: follows user-set configuration Import.CidVersion=0", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + node.UpdateConfig(func(cfg *config.Config) { + cfg.Import.CidVersion = *config.NewOptionalInteger(0) + }) + node.StartDaemon() + defer node.StopDaemon() + + cidStr := node.IPFSAddStr(shortString) + require.Equal(t, shortStringCidV0, cidStr) + }) + + t.Run("produced cid multihash: follows user-set configuration in Import.HashFunction", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + node.UpdateConfig(func(cfg *config.Config) { + cfg.Import.HashFunction = *config.NewOptionalString("sha2-512") + }) + node.StartDaemon() + defer node.StopDaemon() + + cidStr := node.IPFSAddStr(shortString) + require.Equal(t, shortStringCidV1Sha512, cidStr) + }) + + t.Run("produced cid version: follows user-set configuration Import.CidVersion=1", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + node.UpdateConfig(func(cfg *config.Config) { + cfg.Import.CidVersion = *config.NewOptionalInteger(1) + }) + node.StartDaemon() + defer node.StopDaemon() + + cidStr := node.IPFSAddStr(shortString) + require.Equal(t, shortStringCidV1, cidStr) + }) + + t.Run("produced cid version: command flag overrides configuration in Import.CidVersion", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + node.UpdateConfig(func(cfg *config.Config) { + cfg.Import.CidVersion = *config.NewOptionalInteger(1) + }) + node.StartDaemon() + defer node.StopDaemon() + + cidStr := node.IPFSAddStr(shortString, "--cid-version", "0") + require.Equal(t, shortStringCidV0, cidStr) + }) + + t.Run("produced unixfs raw leaves: follows user-set configuration Import.UnixFSRawLeaves", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + node.UpdateConfig(func(cfg *config.Config) { + // CIDv1 defaults to raw-leaves=true + cfg.Import.CidVersion = *config.NewOptionalInteger(1) + // disable manually + cfg.Import.UnixFSRawLeaves = config.False + }) + node.StartDaemon() + defer node.StopDaemon() + + cidStr := node.IPFSAddStr(shortString) + require.Equal(t, shortStringCidV1NoRawLeaves, cidStr) + }) + + t.Run("ipfs init --profile=legacy-cid-v0 sets config that produces legacy CIDv0", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init("--profile=legacy-cid-v0") + node.StartDaemon() + defer node.StopDaemon() + + cidStr := node.IPFSAddStr(shortString) + require.Equal(t, shortStringCidV0, cidStr) + }) + + t.Run("ipfs init --profile=test-cid-v1 produces modern CIDv1", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init("--profile=test-cid-v1") + node.StartDaemon() + defer node.StopDaemon() + + cidStr := node.IPFSAddStr(shortString) + require.Equal(t, shortStringCidV1, cidStr) + }) +} diff --git a/test/cli/backup_bootstrap_test.go b/test/cli/backup_bootstrap_test.go new file mode 100644 index 000000000..017499f3d --- /dev/null +++ b/test/cli/backup_bootstrap_test.go @@ -0,0 +1,60 @@ +package cli + +import ( + "fmt" + "testing" + "time" + + "github.com/ipfs/kubo/config" + "github.com/ipfs/kubo/test/cli/harness" + "github.com/stretchr/testify/assert" +) + +func TestBackupBootstrapPeers(t *testing.T) { + nodes := harness.NewT(t).NewNodes(3).Init() + nodes.ForEachPar(func(n *harness.Node) { + n.UpdateConfig(func(cfg *config.Config) { + cfg.Bootstrap = []string{} + cfg.Addresses.Swarm = []string{fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", harness.NewRandPort())} + cfg.Discovery.MDNS.Enabled = false + cfg.Internal.BackupBootstrapInterval = config.NewOptionalDuration(250 * time.Millisecond) + }) + }) + + // Start all nodes and ensure they all have no peers. + nodes.StartDaemons() + nodes.ForEachPar(func(n *harness.Node) { + assert.Len(t, n.Peers(), 0) + }) + + // Connect nodes 0 and 1, ensure they know each other. + nodes[0].Connect(nodes[1]) + assert.Len(t, nodes[0].Peers(), 1) + assert.Len(t, nodes[1].Peers(), 1) + assert.Len(t, nodes[2].Peers(), 0) + + // Wait a bit to ensure that 0 and 1 saved their temporary bootstrap backups. + time.Sleep(time.Millisecond * 500) + nodes.StopDaemons() + + // Start 1 and 2. 2 does not know anyone yet. + nodes[1].StartDaemon() + nodes[2].StartDaemon() + assert.Len(t, nodes[1].Peers(), 0) + assert.Len(t, nodes[2].Peers(), 0) + + // Connect 1 and 2, ensure they know each other. + nodes[1].Connect(nodes[2]) + assert.Len(t, nodes[1].Peers(), 1) + assert.Len(t, nodes[2].Peers(), 1) + + // Start 0, wait a bit. Should connect to 1, and then discover 2 via the + // backup bootstrap peers. + nodes[0].StartDaemon() + time.Sleep(time.Millisecond * 500) + + // Check if they're all connected. + assert.Len(t, nodes[0].Peers(), 2) + assert.Len(t, nodes[1].Peers(), 2) + assert.Len(t, nodes[2].Peers(), 2) +} diff --git a/test/cli/basic_commands_test.go b/test/cli/basic_commands_test.go index 30c1f1f9a..603a03d9d 100644 --- a/test/cli/basic_commands_test.go +++ b/test/cli/basic_commands_test.go @@ -88,6 +88,7 @@ func TestAllSubcommandsAcceptHelp(t *testing.T) { t.Parallel() node := harness.NewT(t).NewNode() for _, cmd := range node.IPFSCommands() { + cmd := cmd t.Run(fmt.Sprintf("command %q accepts help", cmd), func(t *testing.T) { t.Parallel() splitCmd := strings.Split(cmd, " ")[1:] @@ -115,9 +116,6 @@ func TestAllRootCommandsAreMentionedInHelpText(t *testing.T) { notInHelp := map[string]bool{ "object": true, "shutdown": true, - "tar": true, - "urlstore": true, - "dns": true, } helpMsg := strings.TrimSpace(node.IPFS("--help").Stdout.String()) @@ -149,16 +147,13 @@ func TestCommandDocsWidth(t *testing.T) { "ipfs swarm addrs listen": true, "ipfs dag resolve": true, "ipfs dag get": true, - "ipfs object stat": true, "ipfs pin remote add": true, "ipfs config show": true, "ipfs config edit": true, "ipfs pin remote rm": true, "ipfs pin remote ls": true, "ipfs pin verify": true, - "ipfs dht get": true, "ipfs pin remote service add": true, - "ipfs file ls": true, "ipfs pin update": true, "ipfs pin rm": true, "ipfs p2p": true, @@ -168,18 +163,13 @@ func TestCommandDocsWidth(t *testing.T) { "ipfs object diff": true, "ipfs object patch add-link": true, "ipfs name": true, - "ipfs object patch append-data": true, - "ipfs object patch set-data": true, - "ipfs dht put": true, "ipfs diag profile": true, "ipfs diag cmds": true, "ipfs swarm addrs local": true, "ipfs files ls": true, "ipfs stats bw": true, - "ipfs urlstore add": true, "ipfs swarm peers": true, "ipfs pubsub sub": true, - "ipfs repo fsck": true, "ipfs files write": true, "ipfs swarm limit": true, "ipfs commands completion fish": true, @@ -209,7 +199,6 @@ func TestCommandDocsWidth(t *testing.T) { for _, line := range SplitLines(res) { assert.LessOrEqualf(t, len(line), 80, "expected width %d < 80 for %q", len(line), cmd) } - }) } } @@ -225,7 +214,6 @@ func TestAllCommandsFailWhenPassedBadFlag(t *testing.T) { assert.Equal(t, 1, res.Cmd.ProcessState.ExitCode()) }) } - } func TestCommandsFlags(t *testing.T) { diff --git a/test/cli/completion_test.go b/test/cli/completion_test.go index 0c40eb02b..548cb17a2 100644 --- a/test/cli/completion_test.go +++ b/test/cli/completion_test.go @@ -29,3 +29,29 @@ func TestBashCompletion(t *testing.T) { assert.NoError(t, res.Err) }) } + +func TestZshCompletion(t *testing.T) { + t.Parallel() + h := harness.NewT(t) + node := h.NewNode() + + res := node.IPFS("commands", "completion", "zsh") + + length := len(res.Stdout.String()) + if length < 100 { + t.Fatalf("expected a long Bash completion file, but got one of length %d", length) + } + + t.Run("completion file can be loaded in bash", func(t *testing.T) { + RequiresLinux(t) + + completionFile := h.WriteToTemp(res.Stdout.String()) + res = h.Runner.Run(harness.RunRequest{ + Path: "zsh", + Args: []string{"-c", fmt.Sprintf("autoload -Uz compinit && compinit && source %s && echo -E $_comps[ipfs]", completionFile)}, + }) + + assert.NoError(t, res.Err) + assert.NotEmpty(t, res.Stdout.String()) + }) +} diff --git a/test/cli/content_blocking_test.go b/test/cli/content_blocking_test.go new file mode 100644 index 000000000..6598354d1 --- /dev/null +++ b/test/cli/content_blocking_test.go @@ -0,0 +1,365 @@ +package cli + +import ( + "context" + "fmt" + "io" + "log" + "net/http" + "net/url" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/ipfs/go-cid" + "github.com/ipfs/kubo/test/cli/harness" + carstore "github.com/ipld/go-car/v2/blockstore" + "github.com/libp2p/go-libp2p" + "github.com/libp2p/go-libp2p/core/peer" + libp2phttp "github.com/libp2p/go-libp2p/p2p/http" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestContentBlocking(t *testing.T) { + // NOTE: we can't run this with t.Parallel() because we set IPFS_NS_MAP + // and running in parallel could impact other tests + + const blockedMsg = "blocked and cannot be provided" + const statusExpl = "specific HTTP error code is expected" + const bodyExpl = "Error message informing about content block is expected" + + h := harness.NewT(t) + + // Init IPFS_PATH + node := h.NewNode().Init("--empty-repo", "--profile=test") + + // Create CIDs we use in test + h.WriteFile("parent-dir/blocked-subdir/indirectly-blocked-file.txt", "indirectly blocked file content") + allowedParentDirCID := node.IPFS("add", "--raw-leaves", "-Q", "-r", "--pin=false", filepath.Join(h.Dir, "parent-dir")).Stdout.Trimmed() + blockedSubDirCID := node.IPFS("add", "--raw-leaves", "-Q", "-r", "--pin=false", filepath.Join(h.Dir, "parent-dir", "blocked-subdir")).Stdout.Trimmed() + node.IPFS("block", "rm", blockedSubDirCID) + + h.WriteFile("directly-blocked-file.txt", "directly blocked file content") + blockedCID := node.IPFS("add", "--raw-leaves", "-Q", filepath.Join(h.Dir, "directly-blocked-file.txt")).Stdout.Trimmed() + + h.WriteFile("not-blocked-file.txt", "not blocked file content") + allowedCID := node.IPFS("add", "--raw-leaves", "-Q", filepath.Join(h.Dir, "not-blocked-file.txt")).Stdout.Trimmed() + + // Create denylist at $IPFS_PATH/denylists/test.deny + denylistTmp := h.WriteToTemp("name: test list\n---\n" + + "//QmX9dhRcQcKUw3Ws8485T5a9dtjrSCQaUAHnG4iK9i4ceM\n" + // Double hash (sha256) CID block: base58btc(sha256-multihash(QmVTF1yEejXd9iMgoRTFDxBv7HAz9kuZcQNBzHrceuK9HR)) + "//gW813G35CnLsy7gRYYHuf63hrz71U1xoLFDVeV7actx6oX\n" + // Double hash (blake3) Path block under blake3 root CID: base58btc(blake3-multihash(gW7Nhu4HrfDtphEivm3Z9NNE7gpdh5Tga8g6JNZc1S8E47/path)) + "//8526ba05eec55e28f8db5974cc891d0d92c8af69d386fc6464f1e9f372caf549\n" + // Legacy CID double-hash block: sha256(bafkqahtcnrxwg23fmqqgi33vmjwgk2dbonuca3dfm5qwg6jamnuwicq/) + "//e5b7d2ce2594e2e09901596d8e1f29fa249b74c8c9e32ea01eda5111e4d33f07\n" + // Legacy Path double-hash block: sha256(bafyaagyscufaqalqaacauaqiaejao43vmjygc5didacauaqiae/subpath) + "/ipfs/" + blockedCID + "\n" + // block specific CID + "/ipfs/" + allowedParentDirCID + "/blocked-subdir*\n" + // block only specific subpath + "/ipns/blocked-cid.example.com\n" + + "/ipns/blocked-dnslink.example.com\n") + + if err := os.MkdirAll(filepath.Join(node.Dir, "denylists"), 0o777); err != nil { + log.Panicf("failed to create denylists dir: %s", err.Error()) + } + if err := os.Rename(denylistTmp, filepath.Join(node.Dir, "denylists", "test.deny")); err != nil { + log.Panicf("failed to create test denylist: %s", err.Error()) + } + + // Add two entries to namesys resolution cache + // /ipns/blocked-cid.example.com point at a blocked CID (to confirm blocking impacts /ipns resolution) + // /ipns/blocked-dnslink.example.com with safe CID (to test blocking of /ipns/ paths) + os.Setenv("IPFS_NS_MAP", "blocked-cid.example.com:/ipfs/"+blockedCID+",blocked-dnslink.example.com/ipns/QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn") + defer os.Unsetenv("IPFS_NS_MAP") + + // Enable GatewayOverLibp2p as we want to test denylist there too + node.IPFS("config", "--json", "Experimental.GatewayOverLibp2p", "true") + + // Start daemon, it should pick up denylist from $IPFS_PATH/denylists/test.deny + node.StartDaemon() // we need online mode for GatewayOverLibp2p tests + client := node.GatewayClient() + + // First, confirm gateway works + t.Run("Gateway Allows CID that is not blocked", func(t *testing.T) { + t.Parallel() + resp := client.Get("/ipfs/" + allowedCID) + assert.Equal(t, http.StatusOK, resp.StatusCode) + assert.Equal(t, "not blocked file content", resp.Body) + }) + + // Then, does the most basic blocking case work? + t.Run("Gateway Denies directly blocked CID", func(t *testing.T) { + t.Parallel() + resp := client.Get("/ipfs/" + blockedCID) + assert.Equal(t, http.StatusGone, resp.StatusCode, statusExpl) + assert.NotEqual(t, "directly blocked file content", resp.Body) + assert.Contains(t, resp.Body, blockedMsg, bodyExpl) + }) + + // Confirm parent of blocked subpath is not blocked + t.Run("Gateway Allows parent Path that is not blocked", func(t *testing.T) { + t.Parallel() + resp := client.Get("/ipfs/" + allowedParentDirCID) + assert.Equal(t, http.StatusOK, resp.StatusCode) + }) + + // Confirm CAR responses skip blocked subpaths + t.Run("Gateway returns CAR without blocked subpath", func(t *testing.T) { + resp := client.Get("/ipfs/" + allowedParentDirCID + "/subdir?format=car") + assert.Equal(t, http.StatusOK, resp.StatusCode) + + bs, err := carstore.NewReadOnly(strings.NewReader(resp.Body), nil) + assert.NoError(t, err) + + has, err := bs.Has(context.Background(), cid.MustParse(blockedSubDirCID)) + assert.NoError(t, err) + assert.False(t, has) + }) + + /* TODO: this was already broken in 0.26, but we should fix it + t.Run("Gateway returns CAR without directly blocked CID", func(t *testing.T) { + allowedDirWithDirectlyBlockedCID := node.IPFS("add", "--raw-leaves", "-Q", "-rw", filepath.Join(h.Dir, "directly-blocked-file.txt")).Stdout.Trimmed() + resp := client.Get("/ipfs/" + allowedDirWithDirectlyBlockedCID + "?format=car") + assert.Equal(t, http.StatusOK, resp.StatusCode) + + bs, err := carstore.NewReadOnly(strings.NewReader(resp.Body), nil) + assert.NoError(t, err) + + has, err := bs.Has(context.Background(), cid.MustParse(blockedCID)) + assert.NoError(t, err) + assert.False(t, has, "Returned CAR should not include blockedCID") + }) + */ + + // Confirm CAR responses skip blocked subpaths + t.Run("Gateway returns CAR without blocked subpath", func(t *testing.T) { + resp := client.Get("/ipfs/" + allowedParentDirCID + "/subdir?format=car") + assert.Equal(t, http.StatusOK, resp.StatusCode) + + bs, err := carstore.NewReadOnly(strings.NewReader(resp.Body), nil) + assert.NoError(t, err) + + has, err := bs.Has(context.Background(), cid.MustParse(blockedSubDirCID)) + assert.NoError(t, err) + assert.False(t, has, "Returned CAR should not include blockedSubDirCID") + }) + + // Ok, now the full list of test cases we want to cover in both CLI and Gateway + testCases := []struct { + name string + path string + }{ + { + name: "directly blocked file CID", + path: "/ipfs/" + blockedCID, + }, + { + name: "indirectly blocked file (on a blocked subpath)", + path: "/ipfs/" + allowedParentDirCID + "/blocked-subdir/indirectly-blocked-file.txt", + }, + { + name: "/ipns path that resolves to a blocked CID", + path: "/ipns/blocked-cid.example.com", + }, + { + name: "/ipns Path that is blocked by DNSLink name", + path: "/ipns/blocked-dnslink.example.com", + }, + { + name: "double-hash CID block (sha256-multihash)", + path: "/ipfs/QmVTF1yEejXd9iMgoRTFDxBv7HAz9kuZcQNBzHrceuK9HR", + }, + { + name: "double-hash Path block (blake3-multihash)", + path: "/ipfs/bafyb4ieqht3b2rssdmc7sjv2cy2gfdilxkfh7623nvndziyqnawkmo266a/path", + }, + { + name: "legacy CID double-hash block (sha256)", + path: "/ipfs/bafkqahtcnrxwg23fmqqgi33vmjwgk2dbonuca3dfm5qwg6jamnuwicq", + }, + + { + name: "legacy Path double-hash block (sha256)", + path: "/ipfs/bafyaagyscufaqalqaacauaqiaejao43vmjygc5didacauaqiae/subpath", + }, + } + + // Which specific cliCmds we test against testCases + cliCmds := [][]string{ + {"block", "get"}, + {"block", "stat"}, + {"dag", "get"}, + {"dag", "export"}, + {"dag", "stat"}, + {"cat"}, + {"ls"}, + {"get"}, + {"refs"}, + } + + expectedMsg := blockedMsg + for _, testCase := range testCases { + + // Confirm that denylist is active for every command in 'cliCmds' x 'testCases' + for _, cmd := range cliCmds { + cmd := cmd + cliTestName := fmt.Sprintf("CLI '%s' denies %s", strings.Join(cmd, " "), testCase.name) + t.Run(cliTestName, func(t *testing.T) { + t.Parallel() + args := append(cmd, testCase.path) + cmd := node.RunIPFS(args...) + stdout := cmd.Stdout.Trimmed() + stderr := cmd.Stderr.Trimmed() + if !strings.Contains(stderr, expectedMsg) { + t.Errorf("Expected STDERR error message %q, but got: %q", expectedMsg, stderr) + if stdout != "" { + t.Errorf("Expected STDOUT to be empty, but got: %q", stdout) + } + } + }) + } + + // Confirm that denylist is active for every content path in 'testCases' + gwTestName := fmt.Sprintf("Gateway denies %s", testCase.name) + t.Run(gwTestName, func(t *testing.T) { + resp := client.Get(testCase.path) + assert.Equal(t, http.StatusGone, resp.StatusCode, statusExpl) + assert.Contains(t, resp.Body, blockedMsg, bodyExpl) + }) + + } + + // Extra edge cases on subdomain gateway + + t.Run("Gateway Denies /ipns Path that is blocked by DNSLink name (subdomain redirect)", func(t *testing.T) { + t.Parallel() + + gwURL, _ := url.Parse(node.GatewayURL()) + resp := client.Get("/ipns/blocked-dnslink.example.com", func(r *http.Request) { + r.Host = "localhost:" + gwURL.Port() + }) + + assert.Equal(t, http.StatusGone, resp.StatusCode, statusExpl) + assert.Contains(t, resp.Body, blockedMsg, bodyExpl) + }) + + t.Run("Gateway Denies /ipns Path that is blocked by DNSLink name (subdomain, no TLS)", func(t *testing.T) { + t.Parallel() + + gwURL, _ := url.Parse(node.GatewayURL()) + resp := client.Get("/", func(r *http.Request) { + r.Host = "blocked-dnslink.example.com.ipns.localhost:" + gwURL.Port() + }) + + assert.Equal(t, http.StatusGone, resp.StatusCode, statusExpl) + assert.Contains(t, resp.Body, blockedMsg, bodyExpl) + }) + + t.Run("Gateway Denies /ipns Path that is blocked by DNSLink name (subdomain, inlined for TLS)", func(t *testing.T) { + t.Parallel() + + gwURL, _ := url.Parse(node.GatewayURL()) + resp := client.Get("/", func(r *http.Request) { + // Inlined DNSLink to fit in single DNS label for TLS interop: + // https://specs.ipfs.tech/http-gateways/subdomain-gateway/#host-request-header + r.Host = "blocked--dnslink-example-com.ipns.localhost:" + gwURL.Port() + }) + + assert.Equal(t, http.StatusGone, resp.StatusCode, statusExpl) + assert.Contains(t, resp.Body, blockedMsg, bodyExpl) + }) + + // We need to confirm denylist is active when gateway is run in NoFetch + // mode (which usually swaps blockservice to a read-only one, and that swap + // may cause denylists to not be applied, as it is a separate code path) + t.Run("GatewayNoFetch", func(t *testing.T) { + // NOTE: we don't run this in parallel, as it requires restart with different config + + // Switch gateway to NoFetch mode + node.StopDaemon() + node.IPFS("config", "--json", "Gateway.NoFetch", "true") + node.StartDaemon() + + // update client, as the port of test node might've changed after restart + client = node.GatewayClient() + + // First, confirm gateway works + t.Run("Allows CID that is not blocked", func(t *testing.T) { + resp := client.Get("/ipfs/" + allowedCID) + assert.Equal(t, http.StatusOK, resp.StatusCode) + assert.Equal(t, "not blocked file content", resp.Body) + }) + + // Then, does the most basic blocking case work? + t.Run("Denies directly blocked CID", func(t *testing.T) { + resp := client.Get("/ipfs/" + blockedCID) + assert.Equal(t, http.StatusGone, resp.StatusCode, statusExpl) + assert.NotEqual(t, "directly blocked file content", resp.Body) + assert.Contains(t, resp.Body, blockedMsg, bodyExpl) + }) + + // Restore default + node.StopDaemon() + node.IPFS("config", "--json", "Gateway.NoFetch", "false") + node.StartDaemon() + client = node.GatewayClient() + }) + + // We need to confirm denylist is active on the + // trustless gateway exposed over libp2p + // when Experimental.GatewayOverLibp2p=true + // (https://github.com/ipfs/kubo/blob/master/docs/experimental-features.md#http-gateway-over-libp2p) + // NOTE: this type fo gateway is hardcoded to be NoFetch: it does not fetch + // data that is not in local store, so we only need to run it once: a + // simple smoke-test for allowed CID and blockedCID. + t.Run("GatewayOverLibp2p", func(t *testing.T) { + t.Parallel() + + // Create libp2p client that connects to our node over + // /http1.1 and then talks gateway semantics over the /ipfs/gateway sub-protocol + clientHost, err := libp2p.New(libp2p.NoListenAddrs) + require.NoError(t, err) + err = clientHost.Connect(context.Background(), peer.AddrInfo{ + ID: node.PeerID(), + Addrs: node.SwarmAddrs(), + }) + require.NoError(t, err) + + libp2pClient, err := (&libp2phttp.Host{StreamHost: clientHost}).NamespacedClient("/ipfs/gateway", peer.AddrInfo{ID: node.PeerID()}) + require.NoError(t, err) + + t.Run("Serves Allowed CID", func(t *testing.T) { + t.Parallel() + resp, err := libp2pClient.Get(fmt.Sprintf("/ipfs/%s?format=raw", allowedCID)) + require.NoError(t, err) + defer resp.Body.Close() + assert.Equal(t, http.StatusOK, resp.StatusCode) + body, err := io.ReadAll(resp.Body) + require.NoError(t, err) + require.Equal(t, string(body), "not blocked file content", bodyExpl) + }) + + t.Run("Denies Blocked CID", func(t *testing.T) { + t.Parallel() + resp, err := libp2pClient.Get(fmt.Sprintf("/ipfs/%s?format=raw", blockedCID)) + require.NoError(t, err) + defer resp.Body.Close() + assert.Equal(t, http.StatusGone, resp.StatusCode, statusExpl) + body, err := io.ReadAll(resp.Body) + require.NoError(t, err) + assert.NotEqual(t, string(body), "directly blocked file content") + assert.Contains(t, string(body), blockedMsg, bodyExpl) + }) + + t.Run("Denies Blocked CID as CAR", func(t *testing.T) { + t.Parallel() + resp, err := libp2pClient.Get(fmt.Sprintf("/ipfs/%s?format=car", blockedCID)) + require.NoError(t, err) + defer resp.Body.Close() + assert.Equal(t, http.StatusGone, resp.StatusCode, statusExpl) + body, err := io.ReadAll(resp.Body) + require.NoError(t, err) + assert.NotContains(t, string(body), "directly blocked file content") + assert.Contains(t, string(body), blockedMsg, bodyExpl) + }) + }) +} diff --git a/test/cli/content_routing_http_test.go b/test/cli/content_routing_http_test.go new file mode 100644 index 000000000..aea5c41ca --- /dev/null +++ b/test/cli/content_routing_http_test.go @@ -0,0 +1,119 @@ +package cli + +import ( + "context" + "net/http" + "net/http/httptest" + "os/exec" + "sync" + "testing" + "time" + + "github.com/ipfs/boxo/ipns" + "github.com/ipfs/boxo/routing/http/server" + "github.com/ipfs/boxo/routing/http/types" + "github.com/ipfs/boxo/routing/http/types/iter" + "github.com/ipfs/go-cid" + "github.com/ipfs/kubo/test/cli/harness" + "github.com/ipfs/kubo/test/cli/testutils" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/routing" + "github.com/stretchr/testify/assert" +) + +type fakeHTTPContentRouter struct { + m sync.Mutex + provideBitswapCalls int + findProvidersCalls int + findPeersCalls int +} + +func (r *fakeHTTPContentRouter) FindProviders(ctx context.Context, key cid.Cid, limit int) (iter.ResultIter[types.Record], error) { + r.m.Lock() + defer r.m.Unlock() + r.findProvidersCalls++ + return iter.FromSlice([]iter.Result[types.Record]{}), nil +} + +// nolint deprecated +func (r *fakeHTTPContentRouter) ProvideBitswap(ctx context.Context, req *server.BitswapWriteProvideRequest) (time.Duration, error) { + r.m.Lock() + defer r.m.Unlock() + r.provideBitswapCalls++ + return 0, nil +} + +func (r *fakeHTTPContentRouter) FindPeers(ctx context.Context, pid peer.ID, limit int) (iter.ResultIter[*types.PeerRecord], error) { + r.m.Lock() + defer r.m.Unlock() + r.findPeersCalls++ + return iter.FromSlice([]iter.Result[*types.PeerRecord]{}), nil +} + +func (r *fakeHTTPContentRouter) GetIPNS(ctx context.Context, name ipns.Name) (*ipns.Record, error) { + return nil, routing.ErrNotSupported +} + +func (r *fakeHTTPContentRouter) PutIPNS(ctx context.Context, name ipns.Name, rec *ipns.Record) error { + return routing.ErrNotSupported +} + +func (r *fakeHTTPContentRouter) numFindProvidersCalls() int { + r.m.Lock() + defer r.m.Unlock() + return r.findProvidersCalls +} + +// userAgentRecorder records the user agent of every HTTP request +type userAgentRecorder struct { + delegate http.Handler + userAgents []string +} + +func (r *userAgentRecorder) ServeHTTP(w http.ResponseWriter, req *http.Request) { + r.userAgents = append(r.userAgents, req.UserAgent()) + r.delegate.ServeHTTP(w, req) +} + +func TestContentRoutingHTTP(t *testing.T) { + cr := &fakeHTTPContentRouter{} + + // run the content routing HTTP server + userAgentRecorder := &userAgentRecorder{delegate: server.Handler(cr)} + server := httptest.NewServer(userAgentRecorder) + t.Cleanup(func() { server.Close() }) + + // setup the node + node := harness.NewT(t).NewNode().Init() + node.Runner.Env["IPFS_HTTP_ROUTERS"] = server.URL + node.StartDaemon() + + // compute a random CID + randStr := string(testutils.RandomBytes(100)) + res := node.PipeStrToIPFS(randStr, "add", "-qn") + wantCIDStr := res.Stdout.Trimmed() + + t.Run("fetching an uncached block results in an HTTP lookup", func(t *testing.T) { + statRes := node.Runner.Run(harness.RunRequest{ + Path: node.IPFSBin, + Args: []string{"block", "stat", wantCIDStr}, + RunFunc: (*exec.Cmd).Start, + }) + defer func() { + if err := statRes.Cmd.Process.Kill(); err != nil { + t.Logf("error killing 'block stat' cmd: %s", err) + } + }() + + // verify the content router was called + assert.Eventually(t, func() bool { + return cr.numFindProvidersCalls() > 0 + }, time.Minute, 10*time.Millisecond) + + assert.NotEmpty(t, userAgentRecorder.userAgents) + version := node.IPFS("id", "-f", "").Stdout.Trimmed() + for _, userAgent := range userAgentRecorder.userAgents { + assert.Equal(t, version, userAgent) + } + }) +} diff --git a/test/cli/daemon_test.go b/test/cli/daemon_test.go new file mode 100644 index 000000000..7a8c583a2 --- /dev/null +++ b/test/cli/daemon_test.go @@ -0,0 +1,25 @@ +package cli + +import ( + "os/exec" + "testing" + + "github.com/ipfs/kubo/test/cli/harness" +) + +func TestDaemon(t *testing.T) { + t.Parallel() + + t.Run("daemon starts if api is set to null", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + node.SetIPFSConfig("Addresses.API", nil) + node.Runner.MustRun(harness.RunRequest{ + Path: node.IPFSBin, + Args: []string{"daemon"}, + RunFunc: (*exec.Cmd).Start, // Start without waiting for completion. + }) + + node.StopDaemon() + }) +} diff --git a/test/cli/dag_test.go b/test/cli/dag_test.go new file mode 100644 index 000000000..1a3defc3c --- /dev/null +++ b/test/cli/dag_test.go @@ -0,0 +1,104 @@ +package cli + +import ( + "encoding/json" + "io" + "os" + "testing" + + "github.com/ipfs/kubo/test/cli/harness" + "github.com/ipfs/kubo/test/cli/testutils" + "github.com/stretchr/testify/assert" +) + +const ( + fixtureFile = "./fixtures/TestDagStat.car" + textOutputPath = "./fixtures/TestDagStatExpectedOutput.txt" + node1Cid = "bafyreibmdfd7c5db4kls4ty57zljfhqv36gi43l6txl44pi423wwmeskwy" + node2Cid = "bafyreie3njilzdi4ixumru4nzgecsnjtu7fzfcwhg7e6s4s5i7cnbslvn4" + fixtureCid = "bafyreifrm6uf5o4dsaacuszf35zhibyojlqclabzrms7iak67pf62jygaq" +) + +type DagStat struct { + Cid string `json:"Cid"` + Size int `json:"Size"` + NumBlocks int `json:"NumBlocks"` +} + +type Data struct { + UniqueBlocks int `json:"UniqueBlocks"` + TotalSize int `json:"TotalSize"` + SharedSize int `json:"SharedSize"` + Ratio float64 `json:"Ratio"` + DagStats []DagStat `json:"DagStats"` +} + +// The Fixture file represents a dag where 2 nodes of size = 46B each, have a common child of 7B +// when traversing the DAG from the root's children (node1 and node2) we count (46 + 7)x2 bytes (counting redundant bytes) = 106 +// since both nodes share a common child of 7 bytes we actually had to read (46)x2 + 7 = 99 bytes +// we should get a dedup ratio of 106/99 that results in approximately 1.0707071 + +func TestDag(t *testing.T) { + t.Parallel() + + t.Run("ipfs dag stat --enc=json", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init().StartDaemon() + // Import fixture + r, err := os.Open(fixtureFile) + assert.Nil(t, err) + defer r.Close() + err = node.IPFSDagImport(r, fixtureCid) + assert.NoError(t, err) + stat := node.RunIPFS("dag", "stat", "--progress=false", "--enc=json", node1Cid, node2Cid) + var data Data + err = json.Unmarshal(stat.Stdout.Bytes(), &data) + assert.NoError(t, err) + + expectedUniqueBlocks := 3 + expectedSharedSize := 7 + expectedTotalSize := 99 + expectedRatio := float64(expectedSharedSize+expectedTotalSize) / float64(expectedTotalSize) + expectedDagStatsLength := 2 + // Validate UniqueBlocks + assert.Equal(t, expectedUniqueBlocks, data.UniqueBlocks) + assert.Equal(t, expectedSharedSize, data.SharedSize) + assert.Equal(t, expectedTotalSize, data.TotalSize) + assert.Equal(t, testutils.FloatTruncate(expectedRatio, 4), testutils.FloatTruncate(data.Ratio, 4)) + + // Validate DagStats + assert.Equal(t, expectedDagStatsLength, len(data.DagStats)) + node1Output := data.DagStats[0] + node2Output := data.DagStats[1] + + assert.Equal(t, node1Output.Cid, node1Cid) + assert.Equal(t, node2Output.Cid, node2Cid) + + expectedNode1Size := (expectedTotalSize + expectedSharedSize) / 2 + expectedNode2Size := (expectedTotalSize + expectedSharedSize) / 2 + assert.Equal(t, expectedNode1Size, node1Output.Size) + assert.Equal(t, expectedNode2Size, node2Output.Size) + + expectedNode1Blocks := 2 + expectedNode2Blocks := 2 + assert.Equal(t, expectedNode1Blocks, node1Output.NumBlocks) + assert.Equal(t, expectedNode2Blocks, node2Output.NumBlocks) + }) + + t.Run("ipfs dag stat", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init().StartDaemon() + r, err := os.Open(fixtureFile) + assert.NoError(t, err) + defer r.Close() + f, err := os.Open(textOutputPath) + assert.NoError(t, err) + defer f.Close() + content, err := io.ReadAll(f) + assert.NoError(t, err) + err = node.IPFSDagImport(r, fixtureCid) + assert.NoError(t, err) + stat := node.RunIPFS("dag", "stat", "--progress=false", node1Cid, node2Cid) + assert.Equal(t, content, stat.Stdout.Bytes()) + }) +} diff --git a/test/cli/delegated_routing_http_test.go b/test/cli/delegated_routing_v1_http_client_test.go similarity index 61% rename from test/cli/delegated_routing_http_test.go rename to test/cli/delegated_routing_v1_http_client_test.go index 0b39a9b12..44e62246b 100644 --- a/test/cli/delegated_routing_http_test.go +++ b/test/cli/delegated_routing_v1_http_client_test.go @@ -15,17 +15,20 @@ func TestHTTPDelegatedRouting(t *testing.T) { t.Parallel() node := harness.NewT(t).NewNode().Init().StartDaemon() - fakeServer := func(resp string) *httptest.Server { + fakeServer := func(contentType string, resp ...string) *httptest.Server { return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - _, err := w.Write([]byte(resp)) - if err != nil { - panic(err) + w.Header().Set("Content-Type", contentType) + for _, r := range resp { + _, err := w.Write([]byte(r)) + if err != nil { + panic(err) + } } })) } findProvsCID := "baeabep4vu3ceru7nerjjbk37sxb7wmftteve4hcosmyolsbsiubw2vr6pqzj6mw7kv6tbn6nqkkldnklbjgm5tzbi4hkpkled4xlcr7xz4bq" - prov := "12D3KooWARYacCc6eoCqvsS9RW9MA2vo51CV75deoiqssx3YgyYJ" + provs := []string{"12D3KooWAobjw92XDcnQ1rRmRJDA3zAQpdPYUpZKrJxH6yccSpje", "12D3KooWARYacCc6eoCqvsS9RW9MA2vo51CV75deoiqssx3YgyYJ"} t.Run("default routing config has no routers defined", func(t *testing.T) { assert.Nil(t, node.ReadConfig().Routing.Routers) @@ -84,17 +87,25 @@ func TestHTTPDelegatedRouting(t *testing.T) { }) t.Run("adding HTTP delegated routing endpoint to Routing.Routers config works", func(t *testing.T) { - server := fakeServer(ToJSONStr(JSONObj{ - "Providers": []JSONObj{{ - "Protocol": "transport-bitswap", - "Schema": "bitswap", - "ID": prov, - "Addrs": []string{"/ip4/0.0.0.0/tcp/4001", "/ip4/0.0.0.0/tcp/4002"}, - }}, + server := fakeServer("application/json", ToJSONStr(JSONObj{ + "Providers": []JSONObj{ + { + "Schema": "bitswap", // Legacy bitswap schema. + "Protocol": "transport-bitswap", + "ID": provs[1], + "Addrs": []string{"/ip4/0.0.0.0/tcp/4001", "/ip4/0.0.0.0/tcp/4002"}, + }, + { + "Schema": "peer", + "Protocols": []string{"transport-bitswap"}, + "ID": provs[0], + "Addrs": []string{"/ip4/0.0.0.0/tcp/4001", "/ip4/0.0.0.0/tcp/4002"}, + }, + }, })) t.Cleanup(server.Close) - node.IPFS("config", "Routing.Type", "--json", `"custom"`) + node.IPFS("config", "Routing.Type", "custom") node.IPFS("config", "Routing.Routers.TestDelegatedRouter", "--json", ToJSONStr(JSONObj{ "Type": "http", "Parameters": JSONObj{ @@ -113,9 +124,43 @@ func TestHTTPDelegatedRouting(t *testing.T) { assert.Equal(t, res.Stdout.Trimmed(), server.URL) node.StartDaemon() - res = node.IPFS("routing", "findprovs", findProvsCID) - assert.Equal(t, prov, res.Stdout.Trimmed()) + assert.Equal(t, provs[1]+"\n"+provs[0], res.Stdout.Trimmed()) }) + node.StopDaemon() + + t.Run("adding HTTP delegated routing endpoint to Routing.Routers config works (streaming)", func(t *testing.T) { + server := fakeServer("application/x-ndjson", ToJSONStr(JSONObj{ + "Schema": "peer", + "Protocols": []string{"transport-bitswap"}, + "ID": provs[0], + "Addrs": []string{"/ip4/0.0.0.0/tcp/4001", "/ip4/0.0.0.0/tcp/4002"}, + }), ToJSONStr(JSONObj{ + "Schema": "bitswap", // Legacy bitswap schema. + "Protocol": "transport-bitswap", + "ID": provs[1], + "Addrs": []string{"/ip4/0.0.0.0/tcp/4001", "/ip4/0.0.0.0/tcp/4002"}, + })) + t.Cleanup(server.Close) + + node.IPFS("config", "Routing.Routers.TestDelegatedRouter", "--json", ToJSONStr(JSONObj{ + "Type": "http", + "Parameters": JSONObj{ + "Endpoint": server.URL, + }, + })) + + res := node.IPFS("config", "Routing.Routers.TestDelegatedRouter.Parameters.Endpoint") + assert.Equal(t, res.Stdout.Trimmed(), server.URL) + + node.StartDaemon() + res = node.IPFS("routing", "findprovs", findProvsCID) + assert.Equal(t, provs[0]+"\n"+provs[1], res.Stdout.Trimmed()) + }) + + t.Run("HTTP client should emit OpenCensus metrics", func(t *testing.T) { + resp := node.APIClient().Get("/debug/metrics/prometheus") + assert.Contains(t, resp.Body, "routing_http_client_length_count") + }) } diff --git a/test/cli/delegated_routing_v1_http_proxy_test.go b/test/cli/delegated_routing_v1_http_proxy_test.go new file mode 100644 index 000000000..1d80ae50a --- /dev/null +++ b/test/cli/delegated_routing_v1_http_proxy_test.go @@ -0,0 +1,147 @@ +package cli + +import ( + "testing" + + "github.com/ipfs/boxo/ipns" + "github.com/ipfs/kubo/config" + "github.com/ipfs/kubo/test/cli/harness" + "github.com/ipfs/kubo/test/cli/testutils" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestRoutingV1Proxy(t *testing.T) { + t.Parallel() + + setupNodes := func(t *testing.T) harness.Nodes { + nodes := harness.NewT(t).NewNodes(2).Init() + + // Node 0 uses DHT and exposes the Routing API. + nodes[0].UpdateConfig(func(cfg *config.Config) { + cfg.Gateway.ExposeRoutingAPI = config.True + cfg.Discovery.MDNS.Enabled = false + cfg.Routing.Type = config.NewOptionalString("dht") + }) + nodes[0].StartDaemon() + + // Node 1 uses Node 0 as Routing V1 source, no DHT. + nodes[1].UpdateConfig(func(cfg *config.Config) { + cfg.Discovery.MDNS.Enabled = false + cfg.Routing.Type = config.NewOptionalString("custom") + cfg.Routing.Methods = config.Methods{ + config.MethodNameFindPeers: {RouterName: "KuboA"}, + config.MethodNameFindProviders: {RouterName: "KuboA"}, + config.MethodNameGetIPNS: {RouterName: "KuboA"}, + config.MethodNamePutIPNS: {RouterName: "KuboA"}, + config.MethodNameProvide: {RouterName: "KuboA"}, + } + cfg.Routing.Routers = config.Routers{ + "KuboA": config.RouterParser{ + Router: config.Router{ + Type: config.RouterTypeHTTP, + Parameters: &config.HTTPRouterParams{ + Endpoint: nodes[0].GatewayURL(), + }, + }, + }, + } + }) + nodes[1].StartDaemon() + + // Connect them. + nodes.Connect() + + return nodes + } + + t.Run("Kubo can find provider for CID via Routing V1", func(t *testing.T) { + t.Parallel() + nodes := setupNodes(t) + + cidStr := nodes[0].IPFSAddStr(testutils.RandomStr(1000)) + + res := nodes[1].IPFS("routing", "findprovs", cidStr) + assert.Equal(t, nodes[0].PeerID().String(), res.Stdout.Trimmed()) + }) + + t.Run("Kubo can find peer via Routing V1", func(t *testing.T) { + t.Parallel() + nodes := setupNodes(t) + + // Start lonely node that is not connected to other nodes. + node := harness.NewT(t).NewNode().Init() + node.UpdateConfig(func(cfg *config.Config) { + cfg.Discovery.MDNS.Enabled = false + cfg.Routing.Type = config.NewOptionalString("dht") + }) + node.StartDaemon() + + // Connect Node 0 to Lonely Node. + nodes[0].Connect(node) + + // Node 1 must find Lonely Node through Node 0 Routing V1. + res := nodes[1].IPFS("routing", "findpeer", node.PeerID().String()) + assert.Equal(t, node.SwarmAddrs()[0].String(), res.Stdout.Trimmed()) + }) + + t.Run("Kubo can retrieve IPNS record via Routing V1", func(t *testing.T) { + t.Parallel() + nodes := setupNodes(t) + + nodeName := "/ipns/" + ipns.NameFromPeer(nodes[0].PeerID()).String() + + // Can't resolve the name as isn't published yet. + res := nodes[1].RunIPFS("routing", "get", nodeName) + require.Error(t, res.ExitErr) + + // Publish record on Node 0. + path := "/ipfs/" + nodes[0].IPFSAddStr(testutils.RandomStr(1000)) + nodes[0].IPFS("name", "publish", "--allow-offline", path) + + // Get record on Node 1 (no DHT). + res = nodes[1].IPFS("routing", "get", nodeName) + record, err := ipns.UnmarshalRecord(res.Stdout.Bytes()) + require.NoError(t, err) + value, err := record.Value() + require.NoError(t, err) + require.Equal(t, path, value.String()) + }) + + t.Run("Kubo can resolve IPNS name via Routing V1", func(t *testing.T) { + t.Parallel() + nodes := setupNodes(t) + + nodeName := "/ipns/" + ipns.NameFromPeer(nodes[0].PeerID()).String() + + // Can't resolve the name as isn't published yet. + res := nodes[1].RunIPFS("routing", "get", nodeName) + require.Error(t, res.ExitErr) + + // Publish name. + path := "/ipfs/" + nodes[0].IPFSAddStr(testutils.RandomStr(1000)) + nodes[0].IPFS("name", "publish", "--allow-offline", path) + + // Resolve IPNS name + res = nodes[1].IPFS("name", "resolve", nodeName) + require.Equal(t, path, res.Stdout.Trimmed()) + }) + + t.Run("Kubo can provide IPNS record via Routing V1", func(t *testing.T) { + t.Parallel() + nodes := setupNodes(t) + + // Publish something on Node 1 (no DHT). + nodeName := "/ipns/" + ipns.NameFromPeer(nodes[1].PeerID()).String() + path := "/ipfs/" + nodes[1].IPFSAddStr(testutils.RandomStr(1000)) + nodes[1].IPFS("name", "publish", "--allow-offline", path) + + // Retrieve through Node 0. + res := nodes[0].IPFS("routing", "get", nodeName) + record, err := ipns.UnmarshalRecord(res.Stdout.Bytes()) + require.NoError(t, err) + value, err := record.Value() + require.NoError(t, err) + require.Equal(t, path, value.String()) + }) +} diff --git a/test/cli/delegated_routing_v1_http_server_test.go b/test/cli/delegated_routing_v1_http_server_test.go new file mode 100644 index 000000000..f2bd98cb7 --- /dev/null +++ b/test/cli/delegated_routing_v1_http_server_test.go @@ -0,0 +1,145 @@ +package cli + +import ( + "context" + "testing" + + "github.com/google/uuid" + "github.com/ipfs/boxo/ipns" + "github.com/ipfs/boxo/routing/http/client" + "github.com/ipfs/boxo/routing/http/types" + "github.com/ipfs/boxo/routing/http/types/iter" + "github.com/ipfs/go-cid" + "github.com/ipfs/kubo/config" + "github.com/ipfs/kubo/test/cli/harness" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/stretchr/testify/assert" +) + +func TestRoutingV1Server(t *testing.T) { + t.Parallel() + + setupNodes := func(t *testing.T) harness.Nodes { + nodes := harness.NewT(t).NewNodes(5).Init() + nodes.ForEachPar(func(node *harness.Node) { + node.UpdateConfig(func(cfg *config.Config) { + cfg.Gateway.ExposeRoutingAPI = config.True + cfg.Routing.Type = config.NewOptionalString("dht") + }) + }) + nodes.StartDaemons().Connect() + return nodes + } + + t.Run("Get Providers Responds With Correct Peers", func(t *testing.T) { + t.Parallel() + nodes := setupNodes(t) + + text := "hello world " + uuid.New().String() + cidStr := nodes[2].IPFSAddStr(text) + _ = nodes[3].IPFSAddStr(text) + + cid, err := cid.Decode(cidStr) + assert.NoError(t, err) + + c, err := client.New(nodes[1].GatewayURL()) + assert.NoError(t, err) + + resultsIter, err := c.FindProviders(context.Background(), cid) + assert.NoError(t, err) + + records, err := iter.ReadAllResults(resultsIter) + assert.NoError(t, err) + + var peers []peer.ID + for _, record := range records { + assert.Equal(t, types.SchemaPeer, record.GetSchema()) + + peer, ok := record.(*types.PeerRecord) + assert.True(t, ok) + peers = append(peers, *peer.ID) + } + + assert.Contains(t, peers, nodes[2].PeerID()) + assert.Contains(t, peers, nodes[3].PeerID()) + }) + + t.Run("Get Peers Responds With Correct Peers", func(t *testing.T) { + t.Parallel() + nodes := setupNodes(t) + + c, err := client.New(nodes[1].GatewayURL()) + assert.NoError(t, err) + + resultsIter, err := c.FindPeers(context.Background(), nodes[2].PeerID()) + assert.NoError(t, err) + + records, err := iter.ReadAllResults(resultsIter) + assert.NoError(t, err) + assert.Len(t, records, 1) + assert.IsType(t, records[0].GetSchema(), records[0].GetSchema()) + assert.IsType(t, records[0], &types.PeerRecord{}) + + peer := records[0] + assert.Equal(t, nodes[2].PeerID().String(), peer.ID.String()) + assert.NotEmpty(t, peer.Addrs) + }) + + t.Run("Get IPNS Record Responds With Correct Record", func(t *testing.T) { + t.Parallel() + nodes := setupNodes(t) + + text := "hello ipns test " + uuid.New().String() + cidStr := nodes[0].IPFSAddStr(text) + nodes[0].IPFS("name", "publish", "--allow-offline", cidStr) + + // Ask for record from a different peer. + c, err := client.New(nodes[1].GatewayURL()) + assert.NoError(t, err) + + record, err := c.GetIPNS(context.Background(), ipns.NameFromPeer(nodes[0].PeerID())) + assert.NoError(t, err) + + value, err := record.Value() + assert.NoError(t, err) + assert.Equal(t, "/ipfs/"+cidStr, value.String()) + }) + + t.Run("Put IPNS Record Succeeds", func(t *testing.T) { + t.Parallel() + nodes := setupNodes(t) + + // Publish a record and confirm the /routing/v1/ipns API exposes the IPNS record + text := "hello ipns test " + uuid.New().String() + cidStr := nodes[0].IPFSAddStr(text) + nodes[0].IPFS("name", "publish", "--allow-offline", cidStr) + c, err := client.New(nodes[0].GatewayURL()) + assert.NoError(t, err) + record, err := c.GetIPNS(context.Background(), ipns.NameFromPeer(nodes[0].PeerID())) + assert.NoError(t, err) + value, err := record.Value() + assert.NoError(t, err) + assert.Equal(t, "/ipfs/"+cidStr, value.String()) + + // Start lonely node that is not connected to other nodes. + node := harness.NewT(t).NewNode().Init() + node.UpdateConfig(func(cfg *config.Config) { + cfg.Gateway.ExposeRoutingAPI = config.True + cfg.Routing.Type = config.NewOptionalString("dht") + }) + node.StartDaemon() + + // Put IPNS record in lonely node. It should be accepted as it is a valid record. + c, err = client.New(node.GatewayURL()) + assert.NoError(t, err) + err = c.PutIPNS(context.Background(), ipns.NameFromPeer(nodes[0].PeerID()), record) + assert.NoError(t, err) + + // Get the record from lonely node and double check. + record, err = c.GetIPNS(context.Background(), ipns.NameFromPeer(nodes[0].PeerID())) + assert.NoError(t, err) + value, err = record.Value() + assert.NoError(t, err) + assert.Equal(t, "/ipfs/"+cidStr, value.String()) + }) +} diff --git a/test/cli/dht_autoclient_test.go b/test/cli/dht_autoclient_test.go new file mode 100644 index 000000000..39aa5b258 --- /dev/null +++ b/test/cli/dht_autoclient_test.go @@ -0,0 +1,40 @@ +package cli + +import ( + "bytes" + "testing" + + "github.com/ipfs/kubo/test/cli/harness" + "github.com/ipfs/kubo/test/cli/testutils" + "github.com/stretchr/testify/assert" +) + +func TestDHTAutoclient(t *testing.T) { + t.Parallel() + nodes := harness.NewT(t).NewNodes(10).Init() + harness.Nodes(nodes[8:]).ForEachPar(func(node *harness.Node) { + node.IPFS("config", "Routing.Type", "autoclient") + }) + nodes.StartDaemons().Connect() + + t.Run("file added on node in client mode is retrievable from node in client mode", func(t *testing.T) { + t.Parallel() + randomBytes := testutils.RandomBytes(1000) + randomBytes = append(randomBytes, '\r') + hash := nodes[8].IPFSAdd(bytes.NewReader(randomBytes)) + + res := nodes[9].IPFS("cat", hash) + assert.Equal(t, randomBytes, []byte(res.Stdout.Trimmed())) + }) + + t.Run("file added on node in server mode is retrievable from all nodes", func(t *testing.T) { + t.Parallel() + randomBytes := testutils.RandomBytes(1000) + hash := nodes[0].IPFSAdd(bytes.NewReader(randomBytes)) + + for i := 0; i < 10; i++ { + res := nodes[i].IPFS("cat", hash) + assert.Equal(t, randomBytes, []byte(res.Stdout.Trimmed())) + } + }) +} diff --git a/test/cli/dht_opt_prov_test.go b/test/cli/dht_opt_prov_test.go new file mode 100644 index 000000000..f7b492066 --- /dev/null +++ b/test/cli/dht_opt_prov_test.go @@ -0,0 +1,30 @@ +package cli + +import ( + "testing" + + "github.com/ipfs/kubo/config" + "github.com/ipfs/kubo/test/cli/harness" + "github.com/ipfs/kubo/test/cli/testutils" + "github.com/stretchr/testify/assert" +) + +func TestDHTOptimisticProvide(t *testing.T) { + t.Parallel() + + t.Run("optimistic provide smoke test", func(t *testing.T) { + nodes := harness.NewT(t).NewNodes(2).Init() + + nodes[0].UpdateConfig(func(cfg *config.Config) { + cfg.Experimental.OptimisticProvide = true + }) + + nodes.StartDaemons().Connect() + + hash := nodes[0].IPFSAddStr(testutils.RandomStr(100)) + nodes[0].IPFS("routing", "provide", hash) + + res := nodes[1].IPFS("routing", "findprovs", "--num-providers=1", hash) + assert.Equal(t, nodes[0].PeerID().String(), res.Stdout.Trimmed()) + }) +} diff --git a/test/cli/fixtures/README.md b/test/cli/fixtures/README.md new file mode 100644 index 000000000..dab926be4 --- /dev/null +++ b/test/cli/fixtures/README.md @@ -0,0 +1,72 @@ +# Dataset Description / Sources + +TestGatewayHAMTDirectory.car generated with: + +```bash +ipfs version +# ipfs version 0.19.0 + +export HAMT_DIR=bafybeiggvykl7skb2ndlmacg2k5modvudocffxjesexlod2pfvg5yhwrqm +export IPFS_PATH=$(mktemp -d) + +# Init and start daemon, ensure we have an empty repository. +ipfs init --empty-repo +ipfs daemon &> /dev/null & +export IPFS_PID=$! + +# Retrieve the directory listing, forcing the daemon to download all required DAGs. Kill daemon. +curl -o dir.html http://127.0.0.1:8080/ipfs/$HAMT_DIR/ +kill $IPFS_PID + +# Get the list with all the downloaded refs and sanity check. +ipfs refs local > required_refs +cat required_refs | wc -l +# 962 + +# Get the list of all the files CIDs inside the directory and sanity check. +cat dir.html| pup '#content tbody .ipfs-hash attr{href}' | sed 's/\/ipfs\///g;s/\?filename=.*//g' > files_refs +cat files_refs | wc -l +# 10100 + +# Make and export our fixture. +ipfs files mkdir --cid-version 1 /fixtures +cat required_refs | xargs -I {} ipfs files cp /ipfs/{} /fixtures/{} +cat files_refs | ipfs files write --create /fixtures/files_refs +export FIXTURE_CID=$(ipfs files stat --hash /fixtures/) +echo $FIXTURE_CID +# bafybeig3yoibxe56aolixqa4zk55gp5sug3qgaztkakpndzk2b2ynobd4i +ipfs dag export $FIXTURE_CID > TestGatewayHAMTDirectory.car +``` + +TestGatewayMultiRange.car generated with: + + +```sh +ipfs version +# ipfs version 0.19.0 + +export FILE_CID=bafybeiae5abzv6j3ucqbzlpnx3pcqbr2otbnpot7d2k5pckmpymin4guau +export IPFS_PATH=$(mktemp -d) + +# Init and start daemon, ensure we have an empty repository. +ipfs init --empty-repo +ipfs daemon &> /dev/null & +export IPFS_PID=$! + +# Get a specific byte range from the file. +curl http://127.0.0.1:8080/ipfs/$FILE_CID -i -H "Range: bytes=1276-1279, 29839070-29839080" +kill $IPFS_PID + +# Get the list with all the downloaded refs and sanity check. +ipfs refs local > required_refs +cat required_refs | wc -l +# 19 + +# Make and export our fixture. +ipfs files mkdir --cid-version 1 /fixtures +cat required_refs | xargs -I {} ipfs files cp /ipfs/{} /fixtures/{} +export FIXTURE_CID=$(ipfs files stat --hash /fixtures/) +echo $FIXTURE_CID +# bafybeicgsg3lwyn3yl75lw7sn4zhyj5dxtb7wfxwscpq6yzippetmr2w3y +ipfs dag export $FIXTURE_CID > TestGatewayMultiRange.car +``` diff --git a/test/cli/fixtures/TestDagStat.car b/test/cli/fixtures/TestDagStat.car new file mode 100644 index 000000000..d8af41b4d Binary files /dev/null and b/test/cli/fixtures/TestDagStat.car differ diff --git a/test/cli/fixtures/TestDagStatExpectedOutput.txt b/test/cli/fixtures/TestDagStatExpectedOutput.txt new file mode 100644 index 000000000..9e709f4a2 --- /dev/null +++ b/test/cli/fixtures/TestDagStatExpectedOutput.txt @@ -0,0 +1,12 @@ + +CID Blocks Size +bafyreibmdfd7c5db4kls4ty57zljfhqv36gi43l6txl44pi423wwmeskwy 2 53 +bafyreie3njilzdi4ixumru4nzgecsnjtu7fzfcwhg7e6s4s5i7cnbslvn4 2 53 + +Summary +Total Size: 99 +Unique Blocks: 3 +Shared Size: 7 +Ratio: 1.070707 + + diff --git a/test/cli/fixtures/TestGatewayHAMTDirectory.car b/test/cli/fixtures/TestGatewayHAMTDirectory.car new file mode 100644 index 000000000..2cb03c6c8 Binary files /dev/null and b/test/cli/fixtures/TestGatewayHAMTDirectory.car differ diff --git a/test/cli/fixtures/TestGatewayMultiRange.car b/test/cli/fixtures/TestGatewayMultiRange.car new file mode 100644 index 000000000..993d86e33 Binary files /dev/null and b/test/cli/fixtures/TestGatewayMultiRange.car differ diff --git a/test/cli/fixtures/TestName.car b/test/cli/fixtures/TestName.car new file mode 100644 index 000000000..5e3fd57b2 Binary files /dev/null and b/test/cli/fixtures/TestName.car differ diff --git a/test/cli/gateway_range_test.go b/test/cli/gateway_range_test.go new file mode 100644 index 000000000..2d8ce1a3e --- /dev/null +++ b/test/cli/gateway_range_test.go @@ -0,0 +1,100 @@ +package cli + +import ( + "fmt" + "net/http" + "os" + "testing" + + "github.com/ipfs/kubo/test/cli/harness" + "github.com/stretchr/testify/assert" +) + +func TestGatewayHAMTDirectory(t *testing.T) { + t.Parallel() + + const ( + // The CID of the HAMT-sharded directory that has 10k items + hamtCid = "bafybeiggvykl7skb2ndlmacg2k5modvudocffxjesexlod2pfvg5yhwrqm" + + // fixtureCid is the CID of root of the DAG that is a subset of hamtCid DAG + // representing the minimal set of blocks necessary for directory listing. + // It also includes a "files_refs" file with the list of the references + // we do NOT needs to fetch (files inside the directory) + fixtureCid = "bafybeig3yoibxe56aolixqa4zk55gp5sug3qgaztkakpndzk2b2ynobd4i" + ) + + // Start node + h := harness.NewT(t) + node := h.NewNode().Init("--empty-repo", "--profile=test").StartDaemon("--offline") + client := node.GatewayClient() + + // Import fixtures + r, err := os.Open("./fixtures/TestGatewayHAMTDirectory.car") + assert.NoError(t, err) + defer r.Close() + err = node.IPFSDagImport(r, fixtureCid) + assert.NoError(t, err) + + // Fetch HAMT directory succeeds with minimal refs + resp := client.Get(fmt.Sprintf("/ipfs/%s/", hamtCid)) + assert.Equal(t, http.StatusOK, resp.StatusCode) +} + +func TestGatewayHAMTRanges(t *testing.T) { + t.Parallel() + + const ( + // fileCid is the CID of the large HAMT-sharded file. + fileCid = "bafybeiae5abzv6j3ucqbzlpnx3pcqbr2otbnpot7d2k5pckmpymin4guau" + + // fixtureCid is the CID of root of the DAG that is a subset of fileCid DAG + // representing the minimal set of blocks necessary for a simple byte range request. + fixtureCid = "bafybeicgsg3lwyn3yl75lw7sn4zhyj5dxtb7wfxwscpq6yzippetmr2w3y" + ) + + // Start node + h := harness.NewT(t) + node := h.NewNode().Init("--empty-repo", "--profile=test").StartDaemon("--offline") + client := node.GatewayClient() + + // Import fixtures + r, err := os.Open("./fixtures/TestGatewayMultiRange.car") + assert.NoError(t, err) + defer r.Close() + err = node.IPFSDagImport(r, fixtureCid) + assert.NoError(t, err) + + t.Run("Succeeds Fetching Range", func(t *testing.T) { + t.Parallel() + + resp := client.Get(fmt.Sprintf("/ipfs/%s", fileCid), func(r *http.Request) { + r.Header.Set("Range", "bytes=1276-1279") + }) + assert.Equal(t, http.StatusPartialContent, resp.StatusCode) + assert.Equal(t, "bytes 1276-1279/109266405", resp.Headers.Get("Content-Range")) + assert.Equal(t, "iana", resp.Body) + }) + + t.Run("Succeeds Fetching Second Range", func(t *testing.T) { + t.Parallel() + + resp := client.Get(fmt.Sprintf("/ipfs/%s", fileCid), func(r *http.Request) { + r.Header.Set("Range", "bytes=29839070-29839080") + }) + assert.Equal(t, http.StatusPartialContent, resp.StatusCode) + assert.Equal(t, "bytes 29839070-29839080/109266405", resp.Headers.Get("Content-Range")) + assert.Equal(t, "EXAMPLE.COM", resp.Body) + }) + + t.Run("Succeeds Fetching First Range of Multi-range Request", func(t *testing.T) { + t.Parallel() + + resp := client.Get(fmt.Sprintf("/ipfs/%s", fileCid), func(r *http.Request) { + r.Header.Set("Range", "bytes=1276-1279, 29839070-29839080") + }) + assert.Equal(t, http.StatusPartialContent, resp.StatusCode) + assert.Equal(t, "bytes 1276-1279/109266405", resp.Headers.Get("Content-Range")) + assert.Equal(t, "iana", resp.Body) + }) +} diff --git a/test/cli/gateway_test.go b/test/cli/gateway_test.go new file mode 100644 index 000000000..0a10782f9 --- /dev/null +++ b/test/cli/gateway_test.go @@ -0,0 +1,560 @@ +package cli + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "os" + "path/filepath" + "regexp" + "strconv" + "strings" + "testing" + + "github.com/ipfs/kubo/config" + "github.com/ipfs/kubo/test/cli/harness" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/multiformats/go-multiaddr" + manet "github.com/multiformats/go-multiaddr/net" + "github.com/multiformats/go-multibase" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGateway(t *testing.T) { + t.Parallel() + h := harness.NewT(t) + node := h.NewNode().Init().StartDaemon("--offline") + cid := node.IPFSAddStr("Hello Worlds!") + + peerID, err := peer.ToCid(node.PeerID()).StringOfBase(multibase.Base36) + assert.NoError(t, err) + + client := node.GatewayClient() + client.TemplateData = map[string]string{ + "CID": cid, + "PeerID": peerID, + } + + t.Run("GET IPFS path succeeds", func(t *testing.T) { + t.Parallel() + resp := client.Get("/ipfs/{{.CID}}") + assert.Equal(t, 200, resp.StatusCode) + }) + + t.Run("GET IPFS path with explicit ?filename succeeds with proper header", func(t *testing.T) { + t.Parallel() + resp := client.Get("/ipfs/{{.CID}}?filename=testтест.pdf") + assert.Equal(t, 200, resp.StatusCode) + assert.Equal(t, + `inline; filename="test____.pdf"; filename*=UTF-8''test%D1%82%D0%B5%D1%81%D1%82.pdf`, + resp.Headers.Get("Content-Disposition"), + ) + }) + + t.Run("GET IPFS path with explicit ?filename and &download=true succeeds with proper header", func(t *testing.T) { + t.Parallel() + resp := client.Get("/ipfs/{{.CID}}?filename=testтест.mp4&download=true") + assert.Equal(t, 200, resp.StatusCode) + assert.Equal(t, + `attachment; filename="test____.mp4"; filename*=UTF-8''test%D1%82%D0%B5%D1%81%D1%82.mp4`, + resp.Headers.Get("Content-Disposition"), + ) + }) + + // https://github.com/ipfs/go-ipfs/issues/4025#issuecomment-342250616 + t.Run("GET for Server Worker registration outside of an IPFS content root errors", func(t *testing.T) { + t.Parallel() + resp := client.Get("/ipfs/{{.CID}}?filename=sw.js", client.WithHeader("Service-Worker", "script")) + assert.Equal(t, 400, resp.StatusCode) + assert.Contains(t, resp.Body, "navigator.serviceWorker: registration is not allowed for this scope") + }) + + t.Run("GET IPFS directory path succeeds", func(t *testing.T) { + t.Parallel() + client := node.GatewayClient().DisableRedirects() + + pageContents := "hello i am a webpage" + fileContents := "12345" + h.WriteFile("dir/test", fileContents) + h.WriteFile("dir/dirwithindex/index.html", pageContents) + cids := node.IPFS("add", "-r", "-q", filepath.Join(h.Dir, "dir")).Stdout.Lines() + + rootCID := cids[len(cids)-1] + client.TemplateData = map[string]string{ + "IndexFileCID": cids[0], + "TestFileCID": cids[1], + "RootCID": rootCID, + } + + t.Run("GET IPFS the index file CID", func(t *testing.T) { + t.Parallel() + resp := client.Get("/ipfs/{{.IndexFileCID}}") + assert.Equal(t, 200, resp.StatusCode) + assert.Equal(t, pageContents, resp.Body) + }) + + t.Run("GET IPFS the test file CID", func(t *testing.T) { + t.Parallel() + resp := client.Get("/ipfs/{{.TestFileCID}}") + assert.Equal(t, 200, resp.StatusCode) + assert.Equal(t, fileContents, resp.Body) + }) + + t.Run("GET IPFS directory with index.html returns redirect to add trailing slash", func(t *testing.T) { + t.Parallel() + resp := client.Head("/ipfs/{{.RootCID}}/dirwithindex?query=to-remember") + assert.Equal(t, 301, resp.StatusCode) + assert.Equal(t, + fmt.Sprintf("/ipfs/%s/dirwithindex/?query=to-remember", rootCID), + resp.Headers.Get("Location"), + ) + }) + + // This enables go get to parse go-import meta tags from index.html files stored in IPFS + // https://github.com/ipfs/kubo/pull/3963 + t.Run("GET IPFS directory with index.html and no trailing slash returns expected output when go-get is passed", func(t *testing.T) { + t.Parallel() + resp := client.Get("/ipfs/{{.RootCID}}/dirwithindex?go-get=1") + assert.Equal(t, pageContents, resp.Body) + }) + + t.Run("GET IPFS directory with index.html and trailing slash returns expected output", func(t *testing.T) { + t.Parallel() + resp := client.Get("/ipfs/{{.RootCID}}/dirwithindex/?query=to-remember") + assert.Equal(t, pageContents, resp.Body) + }) + + t.Run("GET IPFS nonexistent file returns 404 (Not Found)", func(t *testing.T) { + t.Parallel() + resp := client.Get("/ipfs/{{.RootCID}}/pleaseDontAddMe") + assert.Equal(t, 404, resp.StatusCode) + }) + + t.Run("GET IPFS invalid CID returns 400 (Bad Request)", func(t *testing.T) { + t.Parallel() + resp := client.Get("/ipfs/QmInvalid/pleaseDontAddMe") + assert.Equal(t, 400, resp.StatusCode) + }) + + t.Run("GET IPFS inlined zero-length data object returns ok code (200)", func(t *testing.T) { + t.Parallel() + resp := client.Get("/ipfs/bafkqaaa") + assert.Equal(t, 200, resp.StatusCode) + assert.Equal(t, "0", resp.Resp.Header.Get("Content-Length")) + assert.Equal(t, "", resp.Body) + }) + + t.Run("GET IPFS inlined zero-length data object with byte range returns ok code (200)", func(t *testing.T) { + t.Parallel() + resp := client.Get("/ipfs/bafkqaaa", client.WithHeader("Range", "bytes=0-1048575")) + assert.Equal(t, 200, resp.StatusCode) + assert.Equal(t, "0", resp.Resp.Header.Get("Content-Length")) + assert.Equal(t, "text/plain", resp.Resp.Header.Get("Content-Type")) + }) + + t.Run("GET /ipfs/ipfs/{cid} returns redirect to the valid path", func(t *testing.T) { + t.Parallel() + resp := client.Get("/ipfs/ipfs/bafkqaaa?query=to-remember") + assert.Equal(t, 301, resp.StatusCode) + assert.Equal(t, "/ipfs/bafkqaaa?query=to-remember", resp.Resp.Header.Get("Location")) + }) + }) + + t.Run("IPNS", func(t *testing.T) { + t.Parallel() + node.IPFS("name", "publish", "--allow-offline", "--ttl", "42h", cid) + + t.Run("GET invalid IPNS root returns 500 (Internal Server Error)", func(t *testing.T) { + t.Parallel() + resp := client.Get("/ipns/QmInvalid/pleaseDontAddMe") + assert.Equal(t, 500, resp.StatusCode) + }) + + t.Run("GET IPNS path succeeds", func(t *testing.T) { + t.Parallel() + resp := client.Get("/ipns/{{.PeerID}}") + assert.Equal(t, 200, resp.StatusCode) + assert.Equal(t, "Hello Worlds!", resp.Body) + }) + + t.Run("GET IPNS path has correct Cache-Control", func(t *testing.T) { + t.Parallel() + resp := client.Get("/ipns/{{.PeerID}}") + assert.Equal(t, 200, resp.StatusCode) + cacheControl := resp.Headers.Get("Cache-Control") + assert.True(t, strings.HasPrefix(cacheControl, "public, max-age=")) + maxAge, err := strconv.Atoi(strings.TrimPrefix(cacheControl, "public, max-age=")) + assert.NoError(t, err) + assert.True(t, maxAge-151200 < 60) // MaxAge within 42h and 42h-1m + }) + + t.Run("GET /ipfs/ipns/{peerid} returns redirect to the valid path", func(t *testing.T) { + t.Parallel() + resp := client.Get("/ipfs/ipns/{{.PeerID}}?query=to-remember") + assert.Equal(t, 301, resp.StatusCode) + assert.Equal(t, fmt.Sprintf("/ipns/%s?query=to-remember", peerID), resp.Resp.Header.Get("Location")) + }) + }) + + t.Run("GET invalid IPFS path errors", func(t *testing.T) { + t.Parallel() + assert.Equal(t, 400, client.Get("/ipfs/12345").StatusCode) + }) + + t.Run("GET invalid path errors", func(t *testing.T) { + t.Parallel() + assert.Equal(t, 404, client.Get("/12345").StatusCode) + }) + + // TODO: these tests that use the API URL shouldn't be part of gateway tests... + t.Run("GET /webui returns 301 or 302", func(t *testing.T) { + t.Parallel() + resp := node.APIClient().DisableRedirects().Get("/webui") + assert.Contains(t, []int{302, 301}, resp.StatusCode) + }) + + t.Run("GET /webui/ returns 301 or 302", func(t *testing.T) { + t.Parallel() + resp := node.APIClient().DisableRedirects().Get("/webui/") + assert.Contains(t, []int{302, 301}, resp.StatusCode) + }) + + t.Run("GET /webui/ returns user-specified headers", func(t *testing.T) { + t.Parallel() + + header := "Access-Control-Allow-Origin" + values := []string{"http://localhost:3000", "https://webui.ipfs.io"} + + node := harness.NewT(t).NewNode().Init() + node.UpdateConfig(func(cfg *config.Config) { + cfg.API.HTTPHeaders = map[string][]string{header: values} + }) + node.StartDaemon() + + resp := node.APIClient().DisableRedirects().Get("/webui/") + assert.Equal(t, resp.Headers.Values(header), values) + assert.Contains(t, []int{302, 301}, resp.StatusCode) + }) + + t.Run("GET /logs returns logs", func(t *testing.T) { + t.Parallel() + apiClient := node.APIClient() + reqURL := apiClient.BuildURL("/logs") + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, reqURL, nil) + require.NoError(t, err) + + resp, err := apiClient.Client.Do(req) + require.NoError(t, err) + defer resp.Body.Close() + + // read the first line of the output and parse its JSON + dec := json.NewDecoder(resp.Body) + event := struct{ Event string }{} + err = dec.Decode(&event) + require.NoError(t, err) + + assert.Equal(t, "log API client connected", event.Event) + }) + + t.Run("POST /api/v0/version succeeds", func(t *testing.T) { + t.Parallel() + resp := node.APIClient().Post("/api/v0/version", nil) + assert.Equal(t, 200, resp.StatusCode) + + assert.Len(t, resp.Resp.TransferEncoding, 1) + assert.Equal(t, "chunked", resp.Resp.TransferEncoding[0]) + + vers := struct{ Version string }{} + err := json.Unmarshal([]byte(resp.Body), &vers) + require.NoError(t, err) + assert.NotEmpty(t, vers.Version) + }) + + t.Run("pprof", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init().StartDaemon() + apiClient := node.APIClient() + t.Run("mutex", func(t *testing.T) { + t.Parallel() + t.Run("setting the mutex fraction works (negative so it doesn't enable)", func(t *testing.T) { + t.Parallel() + resp := apiClient.Post("/debug/pprof-mutex/?fraction=-1", nil) + assert.Equal(t, 200, resp.StatusCode) + }) + t.Run("mutex endpoint doesn't accept a string as an argument", func(t *testing.T) { + t.Parallel() + resp := apiClient.Post("/debug/pprof-mutex/?fraction=that_is_a_string", nil) + assert.Equal(t, 400, resp.StatusCode) + }) + t.Run("mutex endpoint returns 405 on GET", func(t *testing.T) { + t.Parallel() + resp := apiClient.Get("/debug/pprof-mutex/?fraction=-1") + assert.Equal(t, 405, resp.StatusCode) + }) + }) + t.Run("block", func(t *testing.T) { + t.Parallel() + t.Run("setting the block profiler rate works (0 so it doesn't enable)", func(t *testing.T) { + t.Parallel() + resp := apiClient.Post("/debug/pprof-block/?rate=0", nil) + assert.Equal(t, 200, resp.StatusCode) + }) + t.Run("block profiler endpoint doesn't accept a string as an argument", func(t *testing.T) { + t.Parallel() + resp := apiClient.Post("/debug/pprof-block/?rate=that_is_a_string", nil) + assert.Equal(t, 400, resp.StatusCode) + }) + t.Run("block profiler endpoint returns 405 on GET", func(t *testing.T) { + t.Parallel() + resp := apiClient.Get("/debug/pprof-block/?rate=0") + assert.Equal(t, 405, resp.StatusCode) + }) + }) + }) + + t.Run("index content types", func(t *testing.T) { + t.Parallel() + h := harness.NewT(t) + node := h.NewNode().Init().StartDaemon() + + h.WriteFile("index/index.html", "

") + cid := node.IPFS("add", "-Q", "-r", filepath.Join(h.Dir, "index")).Stderr.Trimmed() + + apiClient := node.APIClient() + apiClient.TemplateData = map[string]string{"CID": cid} + + t.Run("GET index.html has correct content type", func(t *testing.T) { + t.Parallel() + res := apiClient.Get("/ipfs/{{.CID}}/") + assert.Equal(t, "text/html; charset=utf-8", res.Resp.Header.Get("Content-Type")) + }) + + t.Run("HEAD index.html has no content", func(t *testing.T) { + t.Parallel() + res := apiClient.Head("/ipfs/{{.CID}}/") + assert.Equal(t, "", res.Body) + assert.Equal(t, "", res.Resp.Header.Get("Content-Length")) + }) + }) + + t.Run("raw leaves node", func(t *testing.T) { + t.Parallel() + contents := "This is RAW!" + cid := node.IPFSAddStr(contents, "--raw-leaves") + assert.Equal(t, contents, client.Get("/ipfs/"+cid).Body) + }) + + t.Run("compact blocks", func(t *testing.T) { + t.Parallel() + block1 := "\x0a\x09\x08\x02\x12\x03\x66\x6f\x6f\x18\x03" + block2 := "\x0a\x04\x08\x02\x18\x06\x12\x24\x0a\x22\x12\x20\xcf\x92\xfd\xef\xcd\xc3\x4c\xac\x00\x9c" + + "\x8b\x05\xeb\x66\x2b\xe0\x61\x8d\xb9\xde\x55\xec\xd4\x27\x85\xe9\xec\x67\x12\xf8\xdf\x65" + + "\x12\x24\x0a\x22\x12\x20\xcf\x92\xfd\xef\xcd\xc3\x4c\xac\x00\x9c\x8b\x05\xeb\x66\x2b\xe0" + + "\x61\x8d\xb9\xde\x55\xec\xd4\x27\x85\xe9\xec\x67\x12\xf8\xdf\x65" + + node.PipeStrToIPFS(block1, "block", "put") + block2CID := node.PipeStrToIPFS(block2, "block", "put", "--cid-codec=dag-pb").Stdout.Trimmed() + + resp := client.Get("/ipfs/" + block2CID) + assert.Equal(t, 200, resp.StatusCode) + assert.Equal(t, "foofoo", resp.Body) + }) + + t.Run("verify gateway file", func(t *testing.T) { + t.Parallel() + r := regexp.MustCompile(`Gateway server listening on (?P.+)\s`) + matches := r.FindStringSubmatch(node.Daemon.Stdout.String()) + ma, err := multiaddr.NewMultiaddr(matches[1]) + require.NoError(t, err) + netAddr, err := manet.ToNetAddr(ma) + require.NoError(t, err) + expURL := "http://" + netAddr.String() + + b, err := os.ReadFile(filepath.Join(node.Dir, "gateway")) + require.NoError(t, err) + + assert.Equal(t, expURL, string(b)) + }) + + t.Run("verify gateway file diallable while on unspecified", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + node.UpdateConfig(func(cfg *config.Config) { + cfg.Addresses.Gateway = config.Strings{"/ip4/127.0.0.1/tcp/32563"} + }) + node.StartDaemon() + + b, err := os.ReadFile(filepath.Join(node.Dir, "gateway")) + require.NoError(t, err) + + assert.Equal(t, "http://127.0.0.1:32563", string(b)) + }) + + t.Run("NoFetch", func(t *testing.T) { + t.Parallel() + nodes := harness.NewT(t).NewNodes(2).Init() + node1 := nodes[0] + node2 := nodes[1] + + node1.UpdateConfig(func(cfg *config.Config) { + cfg.Gateway.NoFetch = true + }) + + node2PeerID, err := peer.ToCid(node2.PeerID()).StringOfBase(multibase.Base36) + assert.NoError(t, err) + + nodes.StartDaemons().Connect() + + t.Run("not present", func(t *testing.T) { + cidFoo := node2.IPFSAddStr("foo") + + t.Run("not present CID from node 1", func(t *testing.T) { + t.Parallel() + assert.Equal(t, 404, node1.GatewayClient().Get("/ipfs/"+cidFoo).StatusCode) + }) + + t.Run("not present IPNS Record from node 1", func(t *testing.T) { + t.Parallel() + assert.Equal(t, 500, node1.GatewayClient().Get("/ipns/"+node2PeerID).StatusCode) + }) + }) + + t.Run("present", func(t *testing.T) { + cidBar := node1.IPFSAddStr("bar") + + t.Run("present CID from node 1", func(t *testing.T) { + t.Parallel() + assert.Equal(t, 200, node1.GatewayClient().Get("/ipfs/"+cidBar).StatusCode) + }) + + t.Run("present IPNS Record from node 1", func(t *testing.T) { + t.Parallel() + node2.IPFS("name", "publish", "/ipfs/"+cidBar) + assert.Equal(t, 200, node1.GatewayClient().Get("/ipns/"+node2PeerID).StatusCode) + }) + }) + }) + + t.Run("DeserializedResponses", func(t *testing.T) { + type testCase struct { + globalValue config.Flag + gatewayValue config.Flag + deserializedGlobalStatusCode int + deserializedGatewayStaticCode int + message string + } + + setHost := func(r *http.Request) { + r.Host = "example.com" + } + + withAccept := func(accept string) func(r *http.Request) { + return func(r *http.Request) { + r.Header.Set("Accept", accept) + } + } + + withHostAndAccept := func(accept string) func(r *http.Request) { + return func(r *http.Request) { + setHost(r) + withAccept(accept)(r) + } + } + + makeTest := func(test *testCase) func(t *testing.T) { + return func(t *testing.T) { + t.Parallel() + + node := harness.NewT(t).NewNode().Init() + node.UpdateConfig(func(cfg *config.Config) { + cfg.Gateway.DeserializedResponses = test.globalValue + cfg.Gateway.PublicGateways = map[string]*config.GatewaySpec{ + "example.com": { + Paths: []string{"/ipfs", "/ipns"}, + DeserializedResponses: test.gatewayValue, + }, + } + }) + node.StartDaemon() + + cidFoo := node.IPFSAddStr("foo") + client := node.GatewayClient() + + deserializedPath := "/ipfs/" + cidFoo + + blockPath := deserializedPath + "?format=raw" + carPath := deserializedPath + "?format=car" + + // Global Check (Gateway.DeserializedResponses) + assert.Equal(t, http.StatusOK, client.Get(blockPath).StatusCode) + assert.Equal(t, http.StatusOK, client.Get(deserializedPath, withAccept("application/vnd.ipld.raw")).StatusCode) + + assert.Equal(t, http.StatusOK, client.Get(carPath).StatusCode) + assert.Equal(t, http.StatusOK, client.Get(deserializedPath, withAccept("application/vnd.ipld.car")).StatusCode) + + assert.Equal(t, test.deserializedGlobalStatusCode, client.Get(deserializedPath).StatusCode) + assert.Equal(t, test.deserializedGlobalStatusCode, client.Get(deserializedPath, withAccept("application/json")).StatusCode) + + // Public Gateway (example.com) Check (Gateway.PublicGateways[example.com].DeserializedResponses) + assert.Equal(t, http.StatusOK, client.Get(blockPath, setHost).StatusCode) + assert.Equal(t, http.StatusOK, client.Get(deserializedPath, withHostAndAccept("application/vnd.ipld.raw")).StatusCode) + + assert.Equal(t, http.StatusOK, client.Get(carPath, setHost).StatusCode) + assert.Equal(t, http.StatusOK, client.Get(deserializedPath, withHostAndAccept("application/vnd.ipld.car")).StatusCode) + + assert.Equal(t, test.deserializedGatewayStaticCode, client.Get(deserializedPath, setHost).StatusCode) + assert.Equal(t, test.deserializedGatewayStaticCode, client.Get(deserializedPath, withHostAndAccept("application/json")).StatusCode) + } + } + + for _, test := range []*testCase{ + {config.True, config.Default, http.StatusOK, http.StatusOK, "when Gateway.DeserializedResponses is globally enabled, leaving implicit default for Gateway.PublicGateways[example.com] should inherit the global setting (enabled)"}, + {config.False, config.Default, http.StatusNotAcceptable, http.StatusNotAcceptable, "when Gateway.DeserializedResponses is globally disabled, leaving implicit default on Gateway.PublicGateways[example.com] should inherit the global setting (disabled)"}, + {config.False, config.True, http.StatusNotAcceptable, http.StatusOK, "when Gateway.DeserializedResponses is globally disabled, explicitly enabling on Gateway.PublicGateways[example.com] should override global (enabled)"}, + {config.True, config.False, http.StatusOK, http.StatusNotAcceptable, "when Gateway.DeserializedResponses is globally enabled, explicitly disabling on Gateway.PublicGateways[example.com] should override global (disabled)"}, + } { + t.Run(test.message, makeTest(test)) + } + }) + + t.Run("DisableHTMLErrors", func(t *testing.T) { + t.Parallel() + + t.Run("Returns HTML error without DisableHTMLErrors, Accept contains text/html", func(t *testing.T) { + t.Parallel() + + node := harness.NewT(t).NewNode().Init() + node.StartDaemon() + client := node.GatewayClient() + + res := client.Get("/ipfs/invalid-thing", func(r *http.Request) { + r.Header.Set("Accept", "text/html") + }) + assert.NotEqual(t, http.StatusOK, res.StatusCode) + assert.Contains(t, res.Resp.Header.Get("Content-Type"), "text/html") + }) + + t.Run("Does not return HTML error with DisableHTMLErrors enabled, and Accept contains text/html", func(t *testing.T) { + t.Parallel() + + node := harness.NewT(t).NewNode().Init() + node.UpdateConfig(func(cfg *config.Config) { + cfg.Gateway.DisableHTMLErrors = config.True + }) + node.StartDaemon() + client := node.GatewayClient() + + res := client.Get("/ipfs/invalid-thing", func(r *http.Request) { + r.Header.Set("Accept", "text/html") + }) + assert.NotEqual(t, http.StatusOK, res.StatusCode) + assert.NotContains(t, res.Resp.Header.Get("Content-Type"), "text/html") + }) + }) +} diff --git a/test/cli/harness/buffer.go b/test/cli/harness/buffer.go index b40e160b0..ba1cddf02 100644 --- a/test/cli/harness/buffer.go +++ b/test/cli/harness/buffer.go @@ -25,11 +25,19 @@ func (b *Buffer) String() string { return b.b.String() } -// Trimmed returns the bytes as a string, with leading and trailing whitespace removed. +// Trimmed returns the bytes as a string, but with the trailing newline removed. +// This only removes a single trailing newline, not all whitespace. func (b *Buffer) Trimmed() string { b.m.Lock() defer b.m.Unlock() - return strings.TrimSpace(b.b.String()) + s := b.b.String() + if len(s) == 0 { + return s + } + if s[len(s)-1] == '\n' { + return s[:len(s)-1] + } + return s } func (b *Buffer) Bytes() []byte { diff --git a/test/cli/harness/harness.go b/test/cli/harness/harness.go index dd9f38ec3..067608cdc 100644 --- a/test/cli/harness/harness.go +++ b/test/cli/harness/harness.go @@ -11,6 +11,8 @@ import ( logging "github.com/ipfs/go-log/v2" . "github.com/ipfs/kubo/test/cli/testutils" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/multiformats/go-multiaddr" ) // Harness tracks state for a test, such as temp dirs and IFPS nodes, and cleans them up after the test. @@ -119,15 +121,19 @@ func (h *Harness) TempFile() *os.File { } // WriteFile writes a file given a filename and its contents. -// The filename should be a relative path. +// The filename must be a relative path, or this panics. func (h *Harness) WriteFile(filename, contents string) { if filepath.IsAbs(filename) { log.Panicf("%s must be a relative path", filename) } absPath := filepath.Join(h.Runner.Dir, filename) - err := os.WriteFile(absPath, []byte(contents), 0644) + err := os.MkdirAll(filepath.Dir(absPath), 0o777) if err != nil { - log.Panicf("writing '%s' ('%s'): %s", filename, absPath, err.Error()) + log.Panicf("creating intermediate dirs for %q: %s", filename, err.Error()) + } + err = os.WriteFile(absPath, []byte(contents), 0o644) + if err != nil { + log.Panicf("writing %q (%q): %s", filename, absPath, err.Error()) } } @@ -140,8 +146,7 @@ func WaitForFile(path string, timeout time.Duration) error { for { select { case <-timer.C: - end := time.Now() - return fmt.Errorf("timeout waiting for %s after %v", path, end.Sub(start)) + return fmt.Errorf("timeout waiting for %s after %v", path, time.Since(start)) case <-ticker.C: _, err := os.Stat(path) if err == nil { @@ -161,14 +166,14 @@ func (h *Harness) Mkdirs(paths ...string) { log.Panicf("%s must be a relative path when making dirs", path) } absPath := filepath.Join(h.Runner.Dir, path) - err := os.MkdirAll(absPath, 0777) + err := os.MkdirAll(absPath, 0o777) if err != nil { log.Panicf("recursively making dirs under %s: %s", absPath, err) } } } -func (h *Harness) Sh(expr string) RunResult { +func (h *Harness) Sh(expr string) *RunResult { return h.Runner.Run(RunRequest{ Path: "bash", Args: []string{"-c", expr}, @@ -185,3 +190,22 @@ func (h *Harness) Cleanup() { log.Panicf("removing temp dir %s: %s", h.Dir, err) } } + +// ExtractPeerID extracts a peer ID from the given multiaddr, and fatals if it does not contain a peer ID. +func (h *Harness) ExtractPeerID(m multiaddr.Multiaddr) peer.ID { + var peerIDStr string + multiaddr.ForEach(m, func(c multiaddr.Component) bool { + if c.Protocol().Code == multiaddr.P_P2P { + peerIDStr = c.Value() + } + return true + }) + if peerIDStr == "" { + panic(multiaddr.ErrProtocolNotFound) + } + peerID, err := peer.Decode(peerIDStr) + if err != nil { + panic(err) + } + return peerID +} diff --git a/test/cli/harness/http_client.go b/test/cli/harness/http_client.go new file mode 100644 index 000000000..83aa1bff1 --- /dev/null +++ b/test/cli/harness/http_client.go @@ -0,0 +1,116 @@ +package harness + +import ( + "io" + "net/http" + "strings" + "text/template" + "time" +) + +// HTTPClient is an HTTP client with some conveniences for testing. +// URLs are constructed from a base URL. +// The response body is buffered into a string. +// Internal errors cause panics so that tests don't need to check errors. +// The paths are evaluated as Go templates for readable string interpolation. +type HTTPClient struct { + Client *http.Client + BaseURL string + + Timeout time.Duration + TemplateData any +} + +type HTTPResponse struct { + Body string + StatusCode int + Headers http.Header + + // The raw response. The body will be closed on this response. + Resp *http.Response +} + +func (c *HTTPClient) WithHeader(k, v string) func(h *http.Request) { + return func(h *http.Request) { + h.Header.Add(k, v) + } +} + +func (c *HTTPClient) DisableRedirects() *HTTPClient { + c.Client.CheckRedirect = func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + } + return c +} + +// Do executes the request unchanged. +func (c *HTTPClient) Do(req *http.Request) *HTTPResponse { + log.Debugf("making HTTP req %s to %q with headers %+v", req.Method, req.URL.String(), req.Header) + resp, err := c.Client.Do(req) + if resp != nil && resp.Body != nil { + defer resp.Body.Close() + } + if err != nil { + panic(err) + } + bodyStr, err := io.ReadAll(resp.Body) + if err != nil { + panic(err) + } + + return &HTTPResponse{ + Body: string(bodyStr), + StatusCode: resp.StatusCode, + Headers: resp.Header, + Resp: resp, + } +} + +// BuildURL constructs a request URL from the given path by interpolating the string and then appending it to the base URL. +func (c *HTTPClient) BuildURL(urlPath string) string { + sb := &strings.Builder{} + err := template.Must(template.New("test").Parse(urlPath)).Execute(sb, c.TemplateData) + if err != nil { + panic(err) + } + renderedPath := sb.String() + return c.BaseURL + renderedPath +} + +func (c *HTTPClient) Get(urlPath string, opts ...func(*http.Request)) *HTTPResponse { + req, err := http.NewRequest(http.MethodGet, c.BuildURL(urlPath), nil) + if err != nil { + panic(err) + } + for _, o := range opts { + o(req) + } + return c.Do(req) +} + +func (c *HTTPClient) Post(urlPath string, body io.Reader, opts ...func(*http.Request)) *HTTPResponse { + req, err := http.NewRequest(http.MethodPost, c.BuildURL(urlPath), body) + if err != nil { + panic(err) + } + for _, o := range opts { + o(req) + } + return c.Do(req) +} + +func (c *HTTPClient) PostStr(urlpath, body string, opts ...func(*http.Request)) *HTTPResponse { + r := strings.NewReader(body) + return c.Post(urlpath, r, opts...) +} + +func (c *HTTPClient) Head(urlPath string, opts ...func(*http.Request)) *HTTPResponse { + req, err := http.NewRequest(http.MethodHead, c.BuildURL(urlPath), nil) + if err != nil { + panic(err) + } + for _, o := range opts { + o(req) + } + return c.Do(req) +} diff --git a/test/cli/harness/ipfs.go b/test/cli/harness/ipfs.go index 6ae7bdf94..8537e2aa2 100644 --- a/test/cli/harness/ipfs.go +++ b/test/cli/harness/ipfs.go @@ -38,9 +38,20 @@ func (n *Node) SetIPFSConfig(key string, val interface{}, flags ...string) { n.IPFS(args...) // validate the config was set correctly - var newVal string - n.GetIPFSConfig(key, &newVal) - if val != newVal { + + // Create a new value which is a pointer to the same type as the source. + var newVal any + if val != nil { + // If it is not nil grab the type with reflect. + newVal = reflect.New(reflect.TypeOf(val)).Interface() + } else { + // else just set a pointer to an any. + var anything any + newVal = &anything + } + n.GetIPFSConfig(key, newVal) + // dereference newVal using reflect to load the resulting value + if !reflect.DeepEqual(val, reflect.ValueOf(newVal).Elem().Interface()) { log.Panicf("key '%s' did not retain value '%s' after it was set, got '%s'", key, val, newVal) } } @@ -78,3 +89,22 @@ func (n *Node) IPFSAdd(content io.Reader, args ...string) string { log.Debugf("add result: %q", out) return out } + +func (n *Node) IPFSDagImport(content io.Reader, cid string, args ...string) error { + log.Debugf("node %d dag import with args: %v", n.ID, args) + fullArgs := []string{"dag", "import", "--pin-roots=false"} + fullArgs = append(fullArgs, args...) + res := n.Runner.MustRun(RunRequest{ + Path: n.IPFSBin, + Args: fullArgs, + CmdOpts: []CmdOpt{RunWithStdin(content)}, + }) + if res.Err != nil { + return res.Err + } + res = n.Runner.MustRun(RunRequest{ + Path: n.IPFSBin, + Args: []string{"block", "stat", "--offline", cid}, + }) + return res.Err +} diff --git a/test/cli/harness/log.go b/test/cli/harness/log.go new file mode 100644 index 000000000..d76bb2747 --- /dev/null +++ b/test/cli/harness/log.go @@ -0,0 +1,155 @@ +package harness + +import ( + "fmt" + "path/filepath" + "runtime" + "sort" + "strings" + "sync" + "testing" + "time" +) + +type event struct { + timestamp time.Time + msg string +} + +type events []*event + +func (e events) Len() int { return len(e) } +func (e events) Less(i, j int) bool { return e[i].timestamp.Before(e[j].timestamp) } +func (e events) Swap(i, j int) { e[i], e[j] = e[j], e[i] } + +// TestLogger is a logger for tests. +// It buffers output and only writes the output if the test fails or output is explicitly turned on. +// The purpose of this logger is to allow Go test to run with the verbose flag without printing logs. +// The verbose flag is useful since it streams test progress, but also printing logs makes the output too verbose. +// +// You can also add prefixes that are prepended to each log message, for extra logging context. +// +// This is implemented as a hierarchy of loggers, with children flushing log entries back to parents. +// This works because t.Cleanup() processes entries in LIFO order, so children always flush first. +// +// Obviously this logger should never be used in production systems. +type TestLogger struct { + parent *TestLogger + children []*TestLogger + prefixes []string + prefixesIface []any + t *testing.T + buf events + m sync.Mutex + logsEnabled bool +} + +func NewTestLogger(t *testing.T) *TestLogger { + l := &TestLogger{t: t, buf: make(events, 0)} + t.Cleanup(l.flush) + return l +} + +func (t *TestLogger) buildPrefix(timestamp time.Time) string { + d := timestamp.Format("2006-01-02T15:04:05.999999") + _, file, lineno, _ := runtime.Caller(2) + file = filepath.Base(file) + caller := fmt.Sprintf("%s:%d", file, lineno) + + if len(t.prefixes) == 0 { + return fmt.Sprintf("%s\t%s\t", d, caller) + } + + prefixes := strings.Join(t.prefixes, ":") + return fmt.Sprintf("%s\t%s\t%s: ", d, caller, prefixes) +} + +func (t *TestLogger) Log(args ...any) { + timestamp := time.Now() + e := t.buildPrefix(timestamp) + fmt.Sprint(args...) + t.add(&event{timestamp: timestamp, msg: e}) +} + +func (t *TestLogger) Logf(format string, args ...any) { + timestamp := time.Now() + e := t.buildPrefix(timestamp) + fmt.Sprintf(format, args...) + t.add(&event{timestamp: timestamp, msg: e}) +} + +func (t *TestLogger) Fatal(args ...any) { + timestamp := time.Now() + e := t.buildPrefix(timestamp) + fmt.Sprint(append([]any{"fatal: "}, args...)...) + t.add(&event{timestamp: timestamp, msg: e}) + t.t.FailNow() +} + +func (t *TestLogger) Fatalf(format string, args ...any) { + timestamp := time.Now() + e := t.buildPrefix(timestamp) + fmt.Sprintf(fmt.Sprintf("fatal: %s", format), args...) + t.add(&event{timestamp: timestamp, msg: e}) + t.t.FailNow() +} + +func (t *TestLogger) add(e *event) { + t.m.Lock() + defer t.m.Unlock() + t.buf = append(t.buf, e) +} + +func (t *TestLogger) AddPrefix(prefix string) *TestLogger { + l := &TestLogger{ + prefixes: append(t.prefixes, prefix), + prefixesIface: append(t.prefixesIface, prefix), + t: t.t, + parent: t, + logsEnabled: t.logsEnabled, + } + t.m.Lock() + defer t.m.Unlock() + + t.children = append(t.children, l) + t.t.Cleanup(l.flush) + + return l +} + +func (t *TestLogger) EnableLogs() { + t.m.Lock() + defer t.m.Unlock() + t.logsEnabled = true + if t.parent != nil { + if t.parent.logsEnabled { + t.parent.EnableLogs() + } + } + fmt.Printf("enabling %d children\n", len(t.children)) + for _, c := range t.children { + if !c.logsEnabled { + c.EnableLogs() + } + } +} + +func (t *TestLogger) flush() { + if t.t.Failed() || t.logsEnabled { + t.m.Lock() + defer t.m.Unlock() + // if this is a child, send the events to the parent + // the root parent will print all the events in sorted order + if t.parent != nil { + for _, e := range t.buf { + t.parent.add(e) + } + } else { + // we're the root, sort all the events and then print them + sort.Sort(t.buf) + fmt.Println() + fmt.Printf("Logs for test %q:\n\n", t.t.Name()) + for _, e := range t.buf { + fmt.Println(e.msg) + } + fmt.Println() + } + t.buf = nil + } +} diff --git a/test/cli/harness/node.go b/test/cli/harness/node.go index 227737eb9..af389e0ba 100644 --- a/test/cli/harness/node.go +++ b/test/cli/harness/node.go @@ -1,14 +1,17 @@ package harness import ( + "bytes" "encoding/json" "errors" "fmt" "io" + "io/fs" "net/http" "os" "os/exec" "path/filepath" + "runtime" "strconv" "strings" "syscall" @@ -18,7 +21,9 @@ import ( "github.com/ipfs/kubo/config" serial "github.com/ipfs/kubo/config/serialize" "github.com/libp2p/go-libp2p/core/peer" + rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager" "github.com/multiformats/go-multiaddr" + manet "github.com/multiformats/go-multiaddr/net" ) var log = logging.Logger("testharness") @@ -29,19 +34,20 @@ type Node struct { ID int Dir string - APIListenAddr multiaddr.Multiaddr - SwarmAddr multiaddr.Multiaddr - EnableMDNS bool + APIListenAddr multiaddr.Multiaddr + GatewayListenAddr multiaddr.Multiaddr + SwarmAddr multiaddr.Multiaddr + EnableMDNS bool IPFSBin string Runner *Runner - daemon *RunResult + Daemon *RunResult } func BuildNode(ipfsBin, baseDir string, id int) *Node { dir := filepath.Join(baseDir, strconv.Itoa(id)) - if err := os.MkdirAll(dir, 0755); err != nil { + if err := os.MkdirAll(dir, 0o755); err != nil { panic(err) } @@ -59,6 +65,35 @@ func BuildNode(ipfsBin, baseDir string, id int) *Node { } } +func (n *Node) WriteBytes(filename string, b []byte) { + f, err := os.Create(filepath.Join(n.Dir, filename)) + if err != nil { + panic(err) + } + defer f.Close() + _, err = io.Copy(f, bytes.NewReader(b)) + if err != nil { + panic(err) + } +} + +// ReadFile reads the specific file. If it is relative, it is relative the node's root dir. +func (n *Node) ReadFile(filename string) string { + f := filename + if !filepath.IsAbs(filename) { + f = filepath.Join(n.Dir, filename) + } + b, err := os.ReadFile(f) + if err != nil { + panic(err) + } + return string(b) +} + +func (n *Node) ConfigFile() string { + return filepath.Join(n.Dir, "config") +} + func (n *Node) ReadConfig() *config.Config { cfg, err := serial.Load(filepath.Join(n.Dir, "config")) if err != nil { @@ -80,23 +115,47 @@ func (n *Node) UpdateConfig(f func(cfg *config.Config)) { n.WriteConfig(cfg) } -func (n *Node) IPFS(args ...string) RunResult { +func (n *Node) ReadUserResourceOverrides() *rcmgr.PartialLimitConfig { + var r rcmgr.PartialLimitConfig + err := serial.ReadConfigFile(filepath.Join(n.Dir, "libp2p-resource-limit-overrides.json"), &r) + switch err { + case nil, serial.ErrNotInitialized: + return &r + default: + panic(err) + } +} + +func (n *Node) WriteUserSuppliedResourceOverrides(c *rcmgr.PartialLimitConfig) { + err := serial.WriteConfigFile(filepath.Join(n.Dir, "libp2p-resource-limit-overrides.json"), c) + if err != nil { + panic(err) + } +} + +func (n *Node) UpdateUserSuppliedResourceManagerOverrides(f func(overrides *rcmgr.PartialLimitConfig)) { + overrides := n.ReadUserResourceOverrides() + f(overrides) + n.WriteUserSuppliedResourceOverrides(overrides) +} + +func (n *Node) IPFS(args ...string) *RunResult { res := n.RunIPFS(args...) n.Runner.AssertNoError(res) return res } -func (n *Node) PipeStrToIPFS(s string, args ...string) RunResult { +func (n *Node) PipeStrToIPFS(s string, args ...string) *RunResult { return n.PipeToIPFS(strings.NewReader(s), args...) } -func (n *Node) PipeToIPFS(reader io.Reader, args ...string) RunResult { +func (n *Node) PipeToIPFS(reader io.Reader, args ...string) *RunResult { res := n.RunPipeToIPFS(reader, args...) n.Runner.AssertNoError(res) return res } -func (n *Node) RunPipeToIPFS(reader io.Reader, args ...string) RunResult { +func (n *Node) RunPipeToIPFS(reader io.Reader, args ...string) *RunResult { return n.Runner.Run(RunRequest{ Path: n.IPFSBin, Args: args, @@ -104,7 +163,7 @@ func (n *Node) RunPipeToIPFS(reader io.Reader, args ...string) RunResult { }) } -func (n *Node) RunIPFS(args ...string) RunResult { +func (n *Node) RunIPFS(args ...string) *RunResult { return n.Runner.Run(RunRequest{ Path: n.IPFSBin, Args: args, @@ -134,40 +193,71 @@ func (n *Node) Init(ipfsArgs ...string) *Node { n.APIListenAddr = apiAddr } + if n.GatewayListenAddr == nil { + gatewayAddr, err := multiaddr.NewMultiaddr("/ip4/127.0.0.1/tcp/0") + if err != nil { + panic(err) + } + n.GatewayListenAddr = gatewayAddr + } + n.UpdateConfig(func(cfg *config.Config) { cfg.Bootstrap = []string{} cfg.Addresses.Swarm = []string{n.SwarmAddr.String()} cfg.Addresses.API = []string{n.APIListenAddr.String()} - cfg.Addresses.Gateway = []string{""} + cfg.Addresses.Gateway = []string{n.GatewayListenAddr.String()} cfg.Swarm.DisableNatPortMap = true cfg.Discovery.MDNS.Enabled = n.EnableMDNS + cfg.Routing.LoopbackAddressesOnLanDHT = config.True }) return n } -func (n *Node) StartDaemon(ipfsArgs ...string) *Node { +// StartDaemonWithReq runs a Kubo daemon with the given request. +// This overwrites the request Path with the Kubo bin path. +// +// For example, if you want to run the daemon and see stderr and stdout to debug: +// +// node.StartDaemonWithReq(harness.RunRequest{ +// CmdOpts: []harness.CmdOpt{ +// harness.RunWithStderr(os.Stdout), +// harness.RunWithStdout(os.Stdout), +// }, +// }) +func (n *Node) StartDaemonWithReq(req RunRequest, authorization string) *Node { alive := n.IsAlive() if alive { log.Panicf("node %d is already running", n.ID) } + newReq := req + newReq.Path = n.IPFSBin + newReq.Args = append([]string{"daemon"}, req.Args...) + newReq.RunFunc = (*exec.Cmd).Start - daemonArgs := append([]string{"daemon"}, ipfsArgs...) log.Debugf("starting node %d", n.ID) - res := n.Runner.MustRun(RunRequest{ - Path: n.IPFSBin, - Args: daemonArgs, - RunFunc: (*exec.Cmd).Start, - }) + res := n.Runner.MustRun(newReq) - n.daemon = &res + n.Daemon = res log.Debugf("node %d started, checking API", n.ID) - n.WaitOnAPI() + n.WaitOnAPI(authorization) return n } +func (n *Node) StartDaemon(ipfsArgs ...string) *Node { + return n.StartDaemonWithReq(RunRequest{ + Args: ipfsArgs, + }, "") +} + +func (n *Node) StartDaemonWithAuthorization(secret string, ipfsArgs ...string) *Node { + return n.StartDaemonWithReq(RunRequest{ + Args: ipfsArgs, + }, secret) +} + func (n *Node) signalAndWait(watch <-chan struct{}, signal os.Signal, t time.Duration) bool { - err := n.daemon.Cmd.Process.Signal(signal) + err := n.Daemon.Cmd.Process.Signal(signal) if err != nil { if errors.Is(err, os.ErrProcessDone) { log.Debugf("process for node %d has already finished", n.ID) @@ -187,15 +277,24 @@ func (n *Node) signalAndWait(watch <-chan struct{}, signal os.Signal, t time.Dur func (n *Node) StopDaemon() *Node { log.Debugf("stopping node %d", n.ID) - if n.daemon == nil { + if n.Daemon == nil { log.Debugf("didn't stop node %d since no daemon present", n.ID) return n } watch := make(chan struct{}, 1) go func() { - _, _ = n.daemon.Cmd.Process.Wait() + _, _ = n.Daemon.Cmd.Process.Wait() watch <- struct{}{} }() + + // os.Interrupt does not support interrupts on Windows https://github.com/golang/go/issues/46345 + if runtime.GOOS == "windows" { + if n.signalAndWait(watch, syscall.SIGKILL, 5*time.Second) { + return n + } + log.Panicf("timed out stopping node %d with peer ID %s", n.ID, n.PeerID()) + } + log.Debugf("signaling node %d with SIGTERM", n.ID) if n.signalAndWait(watch, syscall.SIGTERM, 1*time.Second) { return n @@ -224,6 +323,15 @@ func (n *Node) APIAddr() multiaddr.Multiaddr { return ma } +func (n *Node) APIURL() string { + apiAddr := n.APIAddr() + netAddr, err := manet.ToNetAddr(apiAddr) + if err != nil { + panic(err) + } + return "http://" + netAddr.String() +} + func (n *Node) TryAPIAddr() (multiaddr.Multiaddr, error) { b, err := os.ReadFile(filepath.Join(n.Dir, "api")) if err != nil { @@ -236,12 +344,23 @@ func (n *Node) TryAPIAddr() (multiaddr.Multiaddr, error) { return ma, nil } -func (n *Node) checkAPI() bool { +func (n *Node) checkAPI(authorization string) bool { apiAddr, err := n.TryAPIAddr() if err != nil { log.Debugf("node %d API addr not available yet: %s", n.ID, err.Error()) return false } + + if unixAddr, err := apiAddr.ValueForProtocol(multiaddr.P_UNIX); err == nil { + parts := strings.SplitN(unixAddr, "/", 2) + if len(parts) < 1 { + panic("malformed unix socket address") + } + fileName := "/" + parts[1] + _, err := os.Stat(fileName) + return !errors.Is(err, fs.ErrNotExist) + } + ip, err := apiAddr.ValueForProtocol(multiaddr.P_IP4) if err != nil { panic(err) @@ -252,7 +371,16 @@ func (n *Node) checkAPI() bool { } url := fmt.Sprintf("http://%s:%s/api/v0/id", ip, port) log.Debugf("checking API for node %d at %s", n.ID, url) - httpResp, err := http.Post(url, "", nil) + + req, err := http.NewRequest(http.MethodPost, url, nil) + if err != nil { + panic(err) + } + if authorization != "" { + req.Header.Set("Authorization", authorization) + } + + httpResp, err := http.DefaultClient.Do(req) if err != nil { log.Debugf("node %d API check error: %s", err.Error()) return false @@ -301,24 +429,25 @@ func (n *Node) PeerID() peer.ID { return id } -func (n *Node) WaitOnAPI() *Node { +func (n *Node) WaitOnAPI(authorization string) *Node { log.Debugf("waiting on API for node %d", n.ID) for i := 0; i < 50; i++ { - if n.checkAPI() { + if n.checkAPI(authorization) { + log.Debugf("daemon API found, daemon stdout: %s", n.Daemon.Stdout.String()) return n } time.Sleep(400 * time.Millisecond) } - log.Panicf("node %d with peer ID %s failed to come online: \n%s\n\n%s", n.ID, n.PeerID(), n.daemon.Stderr.String(), n.daemon.Stdout.String()) + log.Panicf("node %d with peer ID %s failed to come online: \n%s\n\n%s", n.ID, n.PeerID(), n.Daemon.Stderr.String(), n.Daemon.Stdout.String()) return n } func (n *Node) IsAlive() bool { - if n.daemon == nil || n.daemon.Cmd == nil || n.daemon.Cmd.Process == nil { + if n.Daemon == nil || n.Daemon.Cmd == nil || n.Daemon.Cmd.Process == nil { return false } log.Debugf("signaling node %d daemon process for liveness check", n.ID) - err := n.daemon.Cmd.Process.Signal(syscall.Signal(0)) + err := n.Daemon.Cmd.Process.Signal(syscall.Signal(0)) if err == nil { log.Debugf("node %d daemon is alive", n.ID) return true @@ -332,8 +461,6 @@ func (n *Node) SwarmAddrs() []multiaddr.Multiaddr { Path: n.IPFSBin, Args: []string{"swarm", "addrs", "local"}, }) - ipfsProtocol := multiaddr.ProtocolWithCode(multiaddr.P_IPFS).Name - peerID := n.PeerID() out := strings.TrimSpace(res.Stdout.String()) outLines := strings.Split(out, "\n") var addrs []multiaddr.Multiaddr @@ -342,9 +469,18 @@ func (n *Node) SwarmAddrs() []multiaddr.Multiaddr { if err != nil { panic(err) } + addrs = append(addrs, ma) + } + return addrs +} +func (n *Node) SwarmAddrsWithPeerIDs() []multiaddr.Multiaddr { + ipfsProtocol := multiaddr.ProtocolWithCode(multiaddr.P_IPFS).Name + peerID := n.PeerID() + var addrs []multiaddr.Multiaddr + for _, ma := range n.SwarmAddrs() { // add the peer ID to the multiaddr if it doesn't have it - _, err = ma.ValueForProtocol(multiaddr.P_IPFS) + _, err := ma.ValueForProtocol(multiaddr.P_IPFS) if errors.Is(err, multiaddr.ErrProtocolNotFound) { comp, err := multiaddr.NewComponent(ipfsProtocol, peerID.String()) if err != nil { @@ -357,10 +493,27 @@ func (n *Node) SwarmAddrs() []multiaddr.Multiaddr { return addrs } +func (n *Node) SwarmAddrsWithoutPeerIDs() []multiaddr.Multiaddr { + var addrs []multiaddr.Multiaddr + for _, ma := range n.SwarmAddrs() { + var components []multiaddr.Multiaddr + multiaddr.ForEach(ma, func(c multiaddr.Component) bool { + if c.Protocol().Code == multiaddr.P_IPFS { + return true + } + components = append(components, &c) + return true + }) + ma = multiaddr.Join(components...) + addrs = append(addrs, ma) + } + return addrs +} + func (n *Node) Connect(other *Node) *Node { n.Runner.MustRun(RunRequest{ Path: n.IPFSBin, - Args: []string{"swarm", "connect", other.SwarmAddrs()[0].String()}, + Args: []string{"swarm", "connect", other.SwarmAddrsWithPeerIDs()[0].String()}, }) return n } @@ -370,9 +523,8 @@ func (n *Node) Peers() []multiaddr.Multiaddr { Path: n.IPFSBin, Args: []string{"swarm", "peers"}, }) - lines := strings.Split(strings.TrimSpace(res.Stdout.String()), "\n") var addrs []multiaddr.Multiaddr - for _, line := range lines { + for _, line := range res.Stdout.Lines() { ma, err := multiaddr.NewMultiaddr(line) if err != nil { panic(err) @@ -381,3 +533,60 @@ func (n *Node) Peers() []multiaddr.Multiaddr { } return addrs } + +func (n *Node) PeerWith(other *Node) { + n.UpdateConfig(func(cfg *config.Config) { + var addrs []multiaddr.Multiaddr + for _, addrStr := range other.ReadConfig().Addresses.Swarm { + ma, err := multiaddr.NewMultiaddr(addrStr) + if err != nil { + panic(err) + } + addrs = append(addrs, ma) + } + + cfg.Peering.Peers = append(cfg.Peering.Peers, peer.AddrInfo{ + ID: other.PeerID(), + Addrs: addrs, + }) + }) +} + +func (n *Node) Disconnect(other *Node) { + n.IPFS("swarm", "disconnect", "/p2p/"+other.PeerID().String()) +} + +// GatewayURL waits for the gateway file and then returns its contents or times out. +func (n *Node) GatewayURL() string { + timer := time.NewTimer(1 * time.Second) + defer timer.Stop() + for { + select { + case <-timer.C: + panic("timeout waiting for gateway file") + default: + b, err := os.ReadFile(filepath.Join(n.Dir, "gateway")) + if err == nil { + return strings.TrimSpace(string(b)) + } + if !errors.Is(err, fs.ErrNotExist) { + panic(err) + } + time.Sleep(1 * time.Millisecond) + } + } +} + +func (n *Node) GatewayClient() *HTTPClient { + return &HTTPClient{ + Client: http.DefaultClient, + BaseURL: n.GatewayURL(), + } +} + +func (n *Node) APIClient() *HTTPClient { + return &HTTPClient{ + Client: http.DefaultClient, + BaseURL: n.APIURL(), + } +} diff --git a/test/cli/harness/nodes.go b/test/cli/harness/nodes.go index b142e3d8f..113289e3c 100644 --- a/test/cli/harness/nodes.go +++ b/test/cli/harness/nodes.go @@ -1,28 +1,53 @@ package harness import ( + "sync" + + . "github.com/ipfs/kubo/test/cli/testutils" "github.com/multiformats/go-multiaddr" + "golang.org/x/sync/errgroup" ) // Nodes is a collection of Kubo nodes along with operations on groups of nodes. type Nodes []*Node func (n Nodes) Init(args ...string) Nodes { - for _, node := range n { - node.Init() - } + ForEachPar(n, func(node *Node) { node.Init(args...) }) return n } +func (n Nodes) ForEachPar(f func(*Node)) { + group := &errgroup.Group{} + for _, node := range n { + node := node + group.Go(func() error { + f(node) + return nil + }) + } + err := group.Wait() + if err != nil { + panic(err) + } +} + func (n Nodes) Connect() Nodes { + wg := sync.WaitGroup{} for i, node := range n { for j, otherNode := range n { if i == j { continue } - node.Connect(otherNode) + node := node + otherNode := otherNode + wg.Add(1) + go func() { + defer wg.Done() + node.Connect(otherNode) + }() } } + wg.Wait() for _, node := range n { firstPeer := node.Peers()[0] if _, err := firstPeer.ValueForProtocol(multiaddr.P_P2P); err != nil { @@ -32,16 +57,12 @@ func (n Nodes) Connect() Nodes { return n } -func (n Nodes) StartDaemons() Nodes { - for _, node := range n { - node.StartDaemon() - } +func (n Nodes) StartDaemons(args ...string) Nodes { + ForEachPar(n, func(node *Node) { node.StartDaemon(args...) }) return n } func (n Nodes) StopDaemons() Nodes { - for _, node := range n { - node.StopDaemon() - } + ForEachPar(n, func(node *Node) { node.StopDaemon() }) return n } diff --git a/test/cli/harness/peering.go b/test/cli/harness/peering.go new file mode 100644 index 000000000..7680eaf57 --- /dev/null +++ b/test/cli/harness/peering.go @@ -0,0 +1,36 @@ +package harness + +import ( + "fmt" + "math/rand" + "testing" + + "github.com/ipfs/kubo/config" +) + +type Peering struct { + From int + To int +} + +func NewRandPort() int { + n := rand.Int() + return 3000 + (n % 1000) +} + +func CreatePeerNodes(t *testing.T, n int, peerings []Peering) (*Harness, Nodes) { + h := NewT(t) + nodes := h.NewNodes(n).Init() + nodes.ForEachPar(func(node *Node) { + node.UpdateConfig(func(cfg *config.Config) { + cfg.Routing.Type = config.NewOptionalString("none") + cfg.Addresses.Swarm = []string{fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", NewRandPort())} + }) + }) + + for _, peering := range peerings { + nodes[peering.From].PeerWith(nodes[peering.To]) + } + + return h, nodes +} diff --git a/test/cli/harness/run.go b/test/cli/harness/run.go index 1a6b32fc2..8ca85eb63 100644 --- a/test/cli/harness/run.go +++ b/test/cli/harness/run.go @@ -14,8 +14,10 @@ type Runner struct { Verbose bool } -type CmdOpt func(*exec.Cmd) -type RunFunc func(*exec.Cmd) error +type ( + CmdOpt func(*exec.Cmd) + RunFunc func(*exec.Cmd) error +) var RunFuncStart = (*exec.Cmd).Start @@ -38,16 +40,25 @@ type RunResult struct { Cmd *exec.Cmd } +func (r *RunResult) ExitCode() int { + return r.Cmd.ProcessState.ExitCode() +} + func environToMap(environ []string) map[string]string { m := map[string]string{} for _, e := range environ { kv := strings.Split(e, "=") + // Skip environment variables that start with = + // These can occur in Windows https://github.com/golang/go/issues/61956 + if kv[0] == "" { + continue + } m[kv[0]] = kv[1] } return m } -func (r *Runner) Run(req RunRequest) RunResult { +func (r *Runner) Run(req RunRequest) *RunResult { cmd := exec.Command(req.Path, req.Args...) stdout := &Buffer{} stderr := &Buffer{} @@ -82,25 +93,23 @@ func (r *Runner) Run(req RunRequest) RunResult { result.ExitErr = exitErr } - return result + return &result } // MustRun runs the command and fails the test if the command fails. -func (r *Runner) MustRun(req RunRequest) RunResult { +func (r *Runner) MustRun(req RunRequest) *RunResult { result := r.Run(req) r.AssertNoError(result) return result } -func (r *Runner) AssertNoError(result RunResult) { +func (r *Runner) AssertNoError(result *RunResult) { if result.ExitErr != nil { log.Panicf("'%s' returned error, code: %d, err: %s\nstdout:%s\nstderr:%s\n", result.Cmd.Args, result.ExitErr.ExitCode(), result.ExitErr.Error(), result.Stdout.String(), result.Stderr.String()) - } if result.Err != nil { log.Panicf("unable to run %s: %s", result.Cmd.Path, result.Err) - } } @@ -138,3 +147,15 @@ func RunWithStdin(reader io.Reader) CmdOpt { func RunWithStdinStr(s string) CmdOpt { return RunWithStdin(strings.NewReader(s)) } + +func RunWithStdout(writer io.Writer) CmdOpt { + return func(cmd *exec.Cmd) { + cmd.Stdout = io.MultiWriter(writer, cmd.Stdout) + } +} + +func RunWithStderr(writer io.Writer) CmdOpt { + return func(cmd *exec.Cmd) { + cmd.Stderr = io.MultiWriter(writer, cmd.Stdout) + } +} diff --git a/test/cli/http_gateway_over_libp2p_test.go b/test/cli/http_gateway_over_libp2p_test.go new file mode 100644 index 000000000..f8cfe0071 --- /dev/null +++ b/test/cli/http_gateway_over_libp2p_test.go @@ -0,0 +1,116 @@ +package cli + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "testing" + + "github.com/ipfs/go-cid" + "github.com/ipfs/kubo/core/commands" + "github.com/ipfs/kubo/test/cli/harness" + "github.com/libp2p/go-libp2p" + "github.com/libp2p/go-libp2p/core/peer" + libp2phttp "github.com/libp2p/go-libp2p/p2p/http" + "github.com/multiformats/go-multiaddr" + manet "github.com/multiformats/go-multiaddr/net" + "github.com/stretchr/testify/require" +) + +func TestGatewayOverLibp2p(t *testing.T) { + t.Parallel() + nodes := harness.NewT(t).NewNodes(2).Init() + + // Setup streaming functionality + nodes.ForEachPar(func(node *harness.Node) { + node.IPFS("config", "--json", "Experimental.Libp2pStreamMounting", "true") + }) + + gwNode := nodes[0] + p2pProxyNode := nodes[1] + + nodes.StartDaemons().Connect() + + // Add data to the gateway node + cidDataOnGatewayNode := cid.MustParse(gwNode.IPFSAddStr("Hello Worlds2!")) + r := gwNode.GatewayClient().Get(fmt.Sprintf("/ipfs/%s?format=raw", cidDataOnGatewayNode)) + blockDataOnGatewayNode := []byte(r.Body) + + // Add data to the non-gateway node + cidDataNotOnGatewayNode := cid.MustParse(p2pProxyNode.IPFSAddStr("Hello Worlds!")) + r = p2pProxyNode.GatewayClient().Get(fmt.Sprintf("/ipfs/%s?format=raw", cidDataNotOnGatewayNode)) + blockDataNotOnGatewayNode := []byte(r.Body) + _ = blockDataNotOnGatewayNode + + // Setup one of the nodes as http to http-over-libp2p proxy + p2pProxyNode.IPFS("p2p", "forward", "--allow-custom-protocol", "/http/1.1", "/ip4/127.0.0.1/tcp/0", fmt.Sprintf("/p2p/%s", gwNode.PeerID())) + lsOutput := commands.P2PLsOutput{} + if err := json.Unmarshal(p2pProxyNode.IPFS("p2p", "ls", "--enc=json").Stdout.Bytes(), &lsOutput); err != nil { + t.Fatal(err) + } + require.Len(t, lsOutput.Listeners, 1) + p2pProxyNodeHTTPListenMA, err := multiaddr.NewMultiaddr(lsOutput.Listeners[0].ListenAddress) + require.NoError(t, err) + + p2pProxyNodeHTTPListenAddr, err := manet.ToNetAddr(p2pProxyNodeHTTPListenMA) + require.NoError(t, err) + + t.Run("DoesNotWorkWithoutExperimentalConfig", func(t *testing.T) { + _, err := http.Get(fmt.Sprintf("http://%s/ipfs/%s?format=raw", p2pProxyNodeHTTPListenAddr, cidDataOnGatewayNode)) + require.Error(t, err) + }) + + // Enable the experimental feature and reconnect the nodes + gwNode.IPFS("config", "--json", "Experimental.GatewayOverLibp2p", "true") + gwNode.StopDaemon().StartDaemon() + nodes.Connect() + + // Note: the bare HTTP requests here assume that the gateway is mounted at `/` + t.Run("WillNotServeRemoteContent", func(t *testing.T) { + resp, err := http.Get(fmt.Sprintf("http://%s/ipfs/%s?format=raw", p2pProxyNodeHTTPListenAddr, cidDataNotOnGatewayNode)) + require.NoError(t, err) + require.Equal(t, http.StatusNotFound, resp.StatusCode) + }) + + t.Run("WillNotServeDeserializedResponses", func(t *testing.T) { + resp, err := http.Get(fmt.Sprintf("http://%s/ipfs/%s", p2pProxyNodeHTTPListenAddr, cidDataOnGatewayNode)) + require.NoError(t, err) + require.Equal(t, http.StatusNotAcceptable, resp.StatusCode) + }) + + t.Run("ServeBlock", func(t *testing.T) { + t.Run("UsingKuboProxy", func(t *testing.T) { + resp, err := http.Get(fmt.Sprintf("http://%s/ipfs/%s?format=raw", p2pProxyNodeHTTPListenAddr, cidDataOnGatewayNode)) + require.NoError(t, err) + defer resp.Body.Close() + require.Equal(t, 200, resp.StatusCode) + body, err := io.ReadAll(resp.Body) + require.NoError(t, err) + require.Equal(t, blockDataOnGatewayNode, body) + }) + t.Run("UsingLibp2pClientWithPathDiscovery", func(t *testing.T) { + clientHost, err := libp2p.New(libp2p.NoListenAddrs) + require.NoError(t, err) + err = clientHost.Connect(context.Background(), peer.AddrInfo{ + ID: gwNode.PeerID(), + Addrs: gwNode.SwarmAddrs(), + }) + require.NoError(t, err) + + client, err := (&libp2phttp.Host{StreamHost: clientHost}).NamespacedClient("/ipfs/gateway", peer.AddrInfo{ID: gwNode.PeerID()}) + require.NoError(t, err) + + resp, err := client.Get(fmt.Sprintf("/ipfs/%s?format=raw", cidDataOnGatewayNode)) + require.NoError(t, err) + defer resp.Body.Close() + require.Equal(t, 200, resp.StatusCode) + + body, err := io.ReadAll(resp.Body) + require.NoError(t, err) + + require.Equal(t, blockDataOnGatewayNode, body) + }) + }) +} diff --git a/test/cli/init_test.go b/test/cli/init_test.go index 359856e6b..217ec64c3 100644 --- a/test/cli/init_test.go +++ b/test/cli/init_test.go @@ -33,9 +33,7 @@ func testInitAlgo(t *testing.T, initFlags []string, expOutputName string, expPee lines := []string{ fmt.Sprintf("generating %s keypair...done", expOutputName), fmt.Sprintf("peer identity: %s", node.PeerID().String()), - fmt.Sprintf("initializing IPFS node at %s", node.Dir), - "to get started, enter:", - fmt.Sprintf("\n\tipfs cat /ipfs/%s/readme\n\n", CIDWelcomeDocs), + fmt.Sprintf("initializing IPFS node at %s\n", node.Dir), } expectedInitOutput := strings.Join(lines, "\n") assert.Equal(t, expectedInitOutput, initRes.Stdout.String()) @@ -54,26 +52,28 @@ func testInitAlgo(t *testing.T, initFlags []string, expOutputName string, expPee res := node.IPFS("config", "Mounts.IPFS") assert.Equal(t, "/ipfs", res.Stdout.Trimmed()) - node.IPFS("cat", fmt.Sprintf("/ipfs/%s/readme", CIDWelcomeDocs)) + catRes := node.RunIPFS("cat", fmt.Sprintf("/ipfs/%s/readme", CIDWelcomeDocs)) + assert.NotEqual(t, 0, catRes.ExitErr.ExitCode(), "welcome readme doesn't exist") }) - t.Run("init empty repo", func(t *testing.T) { + t.Run("init without empty repo", func(t *testing.T) { t.Parallel() node := harness.NewT(t).NewNode() - initRes := node.IPFS(StrCat("init", "--empty-repo", initFlags)...) + initRes := node.IPFS(StrCat("init", "--empty-repo=false", initFlags)...) validatePeerID(t, node.PeerID(), expPeerIDPubKeyErr, expPeerIDPubKeyType) lines := []string{ fmt.Sprintf("generating %s keypair...done", expOutputName), fmt.Sprintf("peer identity: %s", node.PeerID().String()), - fmt.Sprintf("initializing IPFS node at %s\n", node.Dir), + fmt.Sprintf("initializing IPFS node at %s", node.Dir), + "to get started, enter:", + fmt.Sprintf("\n\tipfs cat /ipfs/%s/readme\n\n", CIDWelcomeDocs), } expectedEmptyInitOutput := strings.Join(lines, "\n") assert.Equal(t, expectedEmptyInitOutput, initRes.Stdout.String()) - catRes := node.RunIPFS("cat", fmt.Sprintf("/ipfs/%s/readme", CIDWelcomeDocs)) - assert.NotEqual(t, 0, catRes.ExitErr.ExitCode(), "welcome readme doesn't exist") + node.IPFS("cat", fmt.Sprintf("/ipfs/%s/readme", CIDWelcomeDocs)) idRes := node.IPFS("id", "-f", "") version := node.IPFS("version", "-n").Stdout.Trimmed() @@ -88,13 +88,12 @@ func TestInit(t *testing.T) { t.Parallel() node := harness.NewT(t).NewNode() badDir := fp.Join(node.Dir, ".badipfs") - err := os.Mkdir(badDir, 0000) + err := os.Mkdir(badDir, 0o000) require.NoError(t, err) res := node.RunIPFS("init", "--repo-dir", badDir) assert.NotEqual(t, 0, res.Cmd.ProcessState.ExitCode()) assert.Contains(t, res.Stderr.String(), "permission denied") - }) t.Run("init with ed25519", func(t *testing.T) { @@ -160,5 +159,4 @@ func TestInit(t *testing.T) { assert.NotEqual(t, 0, res.ExitErr.ExitCode()) assert.Contains(t, res.Stderr.String(), "Error: ipfs daemon is running. please stop it to run this command") }) - } diff --git a/test/cli/must.go b/test/cli/must.go new file mode 100644 index 000000000..e12598466 --- /dev/null +++ b/test/cli/must.go @@ -0,0 +1,8 @@ +package cli + +func MustVal[V any](val V, err error) V { + if err != nil { + panic(err) + } + return val +} diff --git a/test/cli/name_test.go b/test/cli/name_test.go new file mode 100644 index 000000000..42c649c09 --- /dev/null +++ b/test/cli/name_test.go @@ -0,0 +1,266 @@ +package cli + +import ( + "bytes" + "encoding/json" + "fmt" + "os" + "strings" + "testing" + + "github.com/ipfs/boxo/ipns" + "github.com/ipfs/kubo/core/commands/name" + "github.com/ipfs/kubo/test/cli/harness" + "github.com/stretchr/testify/require" +) + +func TestName(t *testing.T) { + const ( + fixturePath = "fixtures/TestName.car" + fixtureCid = "bafybeidg3uxibfrt7uqh7zd5yaodetik7wjwi4u7rwv2ndbgj6ec7lsv2a" + dagCid = "bafyreidgts62p4rtg3rtmptmbv2dt46zjzin275fr763oku3wfod3quzay" + ) + + makeDaemon := func(t *testing.T, initArgs []string) *harness.Node { + node := harness.NewT(t).NewNode().Init(append([]string{"--profile=test"}, initArgs...)...) + r, err := os.Open(fixturePath) + require.Nil(t, err) + defer r.Close() + err = node.IPFSDagImport(r, fixtureCid) + require.NoError(t, err) + return node + } + + testPublishingWithSelf := func(keyType string) { + t.Run("Publishing with self (keyType = "+keyType+")", func(t *testing.T) { + t.Parallel() + + args := []string{} + if keyType != "default" { + args = append(args, "-a="+keyType) + } + + node := makeDaemon(t, args) + name := ipns.NameFromPeer(node.PeerID()) + + t.Run("Publishing a CID", func(t *testing.T) { + publishPath := "/ipfs/" + fixtureCid + + res := node.IPFS("name", "publish", "--allow-offline", publishPath) + require.Equal(t, fmt.Sprintf("Published to %s: %s\n", name.String(), publishPath), res.Stdout.String()) + + res = node.IPFS("name", "resolve", "/ipns/"+name.String()) + require.Equal(t, publishPath+"\n", res.Stdout.String()) + }) + + t.Run("Publishing a CID with -Q option", func(t *testing.T) { + publishPath := "/ipfs/" + fixtureCid + + res := node.IPFS("name", "publish", "--allow-offline", "-Q", publishPath) + require.Equal(t, name.String()+"\n", res.Stdout.String()) + + res = node.IPFS("name", "resolve", "/ipns/"+name.String()) + require.Equal(t, publishPath+"\n", res.Stdout.String()) + }) + + t.Run("Publishing a CID+subpath", func(t *testing.T) { + publishPath := "/ipfs/" + fixtureCid + "/hello" + + res := node.IPFS("name", "publish", "--allow-offline", publishPath) + require.Equal(t, fmt.Sprintf("Published to %s: %s\n", name.String(), publishPath), res.Stdout.String()) + + res = node.IPFS("name", "resolve", "/ipns/"+name.String()) + require.Equal(t, publishPath+"\n", res.Stdout.String()) + }) + + t.Run("Publishing nothing fails", func(t *testing.T) { + res := node.RunIPFS("name", "publish") + require.Error(t, res.Err) + require.Equal(t, 1, res.ExitCode()) + require.Contains(t, res.Stderr.String(), `argument "ipfs-path" is required`) + }) + + t.Run("Publishing with IPLD works", func(t *testing.T) { + publishPath := "/ipld/" + dagCid + "/thing" + res := node.IPFS("name", "publish", "--allow-offline", publishPath) + require.Equal(t, fmt.Sprintf("Published to %s: %s\n", name.String(), publishPath), res.Stdout.String()) + + res = node.IPFS("name", "resolve", "/ipns/"+name.String()) + require.Equal(t, publishPath+"\n", res.Stdout.String()) + }) + + publishPath := "/ipfs/" + fixtureCid + res := node.IPFS("name", "publish", "--allow-offline", publishPath) + require.Equal(t, fmt.Sprintf("Published to %s: %s\n", name.String(), publishPath), res.Stdout.String()) + + t.Run("Resolving self offline succeeds (daemon off)", func(t *testing.T) { + res = node.IPFS("name", "resolve", "--offline", "/ipns/"+name.String()) + require.Equal(t, publishPath+"\n", res.Stdout.String()) + + // Test without cache. + res = node.IPFS("name", "resolve", "--offline", "-n", "/ipns/"+name.String()) + require.Equal(t, publishPath+"\n", res.Stdout.String()) + }) + + node.StartDaemon() + + t.Run("Resolving self offline succeeds (daemon on)", func(t *testing.T) { + res = node.IPFS("name", "resolve", "--offline", "/ipns/"+name.String()) + require.Equal(t, publishPath+"\n", res.Stdout.String()) + + // Test without cache. + res = node.IPFS("name", "resolve", "--offline", "-n", "/ipns/"+name.String()) + require.Equal(t, publishPath+"\n", res.Stdout.String()) + }) + }) + } + + testPublishingWithSelf("default") + testPublishingWithSelf("rsa") + testPublishingWithSelf("ed25519") + + testPublishWithKey := func(name string, keyArgs ...string) { + t.Run(name, func(t *testing.T) { + t.Parallel() + node := makeDaemon(t, nil) + + keyGenArgs := []string{"key", "gen"} + keyGenArgs = append(keyGenArgs, keyArgs...) + keyGenArgs = append(keyGenArgs, "key") + + res := node.IPFS(keyGenArgs...) + key := strings.TrimSpace(res.Stdout.String()) + + publishPath := "/ipfs/" + fixtureCid + name, err := ipns.NameFromString(key) + require.NoError(t, err) + + res = node.IPFS("name", "publish", "--allow-offline", "--key="+key, publishPath) + require.Equal(t, fmt.Sprintf("Published to %s: %s\n", name.String(), publishPath), res.Stdout.String()) + }) + } + + testPublishWithKey("Publishing with RSA (with b58mh) Key", "--ipns-base=b58mh", "--type=rsa", "--size=2048") + testPublishWithKey("Publishing with ED25519 (with b58mh) Key", "--ipns-base=b58mh", "--type=ed25519") + testPublishWithKey("Publishing with ED25519 (with base36) Key", "--ipns-base=base36", "--type=ed25519") + + t.Run("Fails to publish in offline mode", func(t *testing.T) { + t.Parallel() + node := makeDaemon(t, nil).StartDaemon("--offline") + res := node.RunIPFS("name", "publish", "/ipfs/"+fixtureCid) + require.Error(t, res.Err) + require.Equal(t, 1, res.ExitCode()) + require.Contains(t, res.Stderr.String(), `can't publish while offline`) + }) + + t.Run("Publish V2-only record", func(t *testing.T) { + t.Parallel() + + node := makeDaemon(t, nil).StartDaemon() + ipnsName := ipns.NameFromPeer(node.PeerID()).String() + ipnsPath := ipns.NamespacePrefix + ipnsName + publishPath := "/ipfs/" + fixtureCid + + res := node.IPFS("name", "publish", "--ttl=30m", "--v1compat=false", publishPath) + require.Equal(t, fmt.Sprintf("Published to %s: %s\n", ipnsName, publishPath), res.Stdout.String()) + + res = node.IPFS("name", "resolve", ipnsPath) + require.Equal(t, publishPath+"\n", res.Stdout.String()) + + res = node.IPFS("routing", "get", ipnsPath) + record := res.Stdout.Bytes() + + res = node.PipeToIPFS(bytes.NewReader(record), "name", "inspect") + out := res.Stdout.String() + require.Contains(t, out, "This record was not verified.") + require.Contains(t, out, publishPath) + require.Contains(t, out, "30m") + + res = node.PipeToIPFS(bytes.NewReader(record), "name", "inspect", "--verify="+ipnsPath) + out = res.Stdout.String() + require.Contains(t, out, "Valid: true") + require.Contains(t, out, "Signature Type: V2") + require.Contains(t, out, fmt.Sprintf("Protobuf Size: %d", len(record))) + }) + + t.Run("Publish with TTL and inspect record", func(t *testing.T) { + t.Parallel() + + node := makeDaemon(t, nil).StartDaemon() + ipnsPath := ipns.NamespacePrefix + ipns.NameFromPeer(node.PeerID()).String() + publishPath := "/ipfs/" + fixtureCid + + _ = node.IPFS("name", "publish", "--ttl=30m", publishPath) + res := node.IPFS("routing", "get", ipnsPath) + record := res.Stdout.Bytes() + + t.Run("Inspect record shows correct TTL and that it is not validated", func(t *testing.T) { + t.Parallel() + res = node.PipeToIPFS(bytes.NewReader(record), "name", "inspect") + out := res.Stdout.String() + require.Contains(t, out, "This record was not verified.") + require.Contains(t, out, publishPath) + require.Contains(t, out, "30m") + require.Contains(t, out, "Signature Type: V1+V2") + require.Contains(t, out, fmt.Sprintf("Protobuf Size: %d", len(record))) + }) + + t.Run("Inspect record shows valid with correct name", func(t *testing.T) { + t.Parallel() + res := node.PipeToIPFS(bytes.NewReader(record), "name", "inspect", "--enc=json", "--verify="+ipnsPath) + val := name.IpnsInspectResult{} + err := json.Unmarshal(res.Stdout.Bytes(), &val) + require.NoError(t, err) + require.True(t, val.Validation.Valid) + }) + + t.Run("Inspect record shows invalid with wrong name", func(t *testing.T) { + t.Parallel() + res := node.PipeToIPFS(bytes.NewReader(record), "name", "inspect", "--enc=json", "--verify=12D3KooWRirYjmmQATx2kgHBfky6DADsLP7ex1t7BRxJ6nqLs9WH") + val := name.IpnsInspectResult{} + err := json.Unmarshal(res.Stdout.Bytes(), &val) + require.NoError(t, err) + require.False(t, val.Validation.Valid) + }) + }) + + t.Run("Inspect with verification using wrong RSA key errors", func(t *testing.T) { + t.Parallel() + node := makeDaemon(t, nil).StartDaemon() + + // Prepare RSA Key 1 + res := node.IPFS("key", "gen", "--type=rsa", "--size=4096", "key1") + key1 := strings.TrimSpace(res.Stdout.String()) + name1, err := ipns.NameFromString(key1) + require.NoError(t, err) + + // Prepare RSA Key 2 + res = node.IPFS("key", "gen", "--type=rsa", "--size=4096", "key2") + key2 := strings.TrimSpace(res.Stdout.String()) + name2, err := ipns.NameFromString(key2) + require.NoError(t, err) + + // Publish using Key 1 + publishPath := "/ipfs/" + fixtureCid + res = node.IPFS("name", "publish", "--allow-offline", "--key="+key1, publishPath) + require.Equal(t, fmt.Sprintf("Published to %s: %s\n", name1.String(), publishPath), res.Stdout.String()) + + // Get IPNS Record + res = node.IPFS("routing", "get", ipns.NamespacePrefix+name1.String()) + record := res.Stdout.Bytes() + + // Validate with correct key succeeds + res = node.PipeToIPFS(bytes.NewReader(record), "name", "inspect", "--verify="+name1.String(), "--enc=json") + val := name.IpnsInspectResult{} + err = json.Unmarshal(res.Stdout.Bytes(), &val) + require.NoError(t, err) + require.True(t, val.Validation.Valid) + + // Validate with wrong key fails + res = node.PipeToIPFS(bytes.NewReader(record), "name", "inspect", "--verify="+name2.String(), "--enc=json") + val = name.IpnsInspectResult{} + err = json.Unmarshal(res.Stdout.Bytes(), &val) + require.NoError(t, err) + require.False(t, val.Validation.Valid) + }) +} diff --git a/test/cli/peering_test.go b/test/cli/peering_test.go new file mode 100644 index 000000000..9c6ab975d --- /dev/null +++ b/test/cli/peering_test.go @@ -0,0 +1,110 @@ +package cli + +import ( + "testing" + "time" + + "github.com/ipfs/kubo/test/cli/harness" + . "github.com/ipfs/kubo/test/cli/testutils" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/stretchr/testify/assert" +) + +func TestPeering(t *testing.T) { + t.Parallel() + + containsPeerID := func(p peer.ID, peers []peer.ID) bool { + for _, peerID := range peers { + if p == peerID { + return true + } + } + return false + } + + assertPeered := func(h *harness.Harness, from *harness.Node, to *harness.Node) { + assert.Eventuallyf(t, func() bool { + fromPeers := from.Peers() + if len(fromPeers) == 0 { + return false + } + var fromPeerIDs []peer.ID + for _, p := range fromPeers { + fromPeerIDs = append(fromPeerIDs, h.ExtractPeerID(p)) + } + return containsPeerID(to.PeerID(), fromPeerIDs) + }, time.Minute, 10*time.Millisecond, "%d -> %d not peered", from.ID, to.ID) + } + + assertNotPeered := func(h *harness.Harness, from *harness.Node, to *harness.Node) { + assert.Eventuallyf(t, func() bool { + fromPeers := from.Peers() + if len(fromPeers) == 0 { + return false + } + var fromPeerIDs []peer.ID + for _, p := range fromPeers { + fromPeerIDs = append(fromPeerIDs, h.ExtractPeerID(p)) + } + return !containsPeerID(to.PeerID(), fromPeerIDs) + }, 20*time.Second, 10*time.Millisecond, "%d -> %d peered", from.ID, to.ID) + } + + assertPeerings := func(h *harness.Harness, nodes []*harness.Node, peerings []harness.Peering) { + ForEachPar(peerings, func(peering harness.Peering) { + assertPeered(h, nodes[peering.From], nodes[peering.To]) + }) + } + + t.Run("bidirectional peering should work (simultaneous connect)", func(t *testing.T) { + t.Parallel() + peerings := []harness.Peering{{From: 0, To: 1}, {From: 1, To: 0}, {From: 1, To: 2}} + h, nodes := harness.CreatePeerNodes(t, 3, peerings) + + nodes.StartDaemons() + assertPeerings(h, nodes, peerings) + + nodes[0].Disconnect(nodes[1]) + assertPeerings(h, nodes, peerings) + }) + + t.Run("1 should reconnect to 2 when 2 disconnects from 1", func(t *testing.T) { + t.Parallel() + peerings := []harness.Peering{{From: 0, To: 1}, {From: 1, To: 0}, {From: 1, To: 2}} + h, nodes := harness.CreatePeerNodes(t, 3, peerings) + + nodes.StartDaemons() + assertPeerings(h, nodes, peerings) + + nodes[2].Disconnect(nodes[1]) + assertPeerings(h, nodes, peerings) + }) + + t.Run("1 will peer with 2 when it comes online", func(t *testing.T) { + t.Parallel() + peerings := []harness.Peering{{From: 0, To: 1}, {From: 1, To: 0}, {From: 1, To: 2}} + h, nodes := harness.CreatePeerNodes(t, 3, peerings) + + nodes[0].StartDaemon() + nodes[1].StartDaemon() + assertPeerings(h, nodes, []harness.Peering{{From: 0, To: 1}, {From: 1, To: 0}}) + + nodes[2].StartDaemon() + assertPeerings(h, nodes, peerings) + }) + + t.Run("1 will re-peer with 2 when it disconnects and then comes back online", func(t *testing.T) { + t.Parallel() + peerings := []harness.Peering{{From: 0, To: 1}, {From: 1, To: 0}, {From: 1, To: 2}} + h, nodes := harness.CreatePeerNodes(t, 3, peerings) + + nodes.StartDaemons() + assertPeerings(h, nodes, peerings) + + nodes[2].StopDaemon() + assertNotPeered(h, nodes[1], nodes[2]) + + nodes[2].StartDaemon() + assertPeerings(h, nodes, peerings) + }) +} diff --git a/test/cli/ping_test.go b/test/cli/ping_test.go index c4195024a..9470e67d8 100644 --- a/test/cli/ping_test.go +++ b/test/cli/ping_test.go @@ -2,6 +2,7 @@ package cli import ( "fmt" + "strings" "testing" "github.com/ipfs/kubo/test/cli/harness" @@ -29,7 +30,8 @@ func TestPing(t *testing.T) { badPeer := "QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJx" res := node1.RunIPFS("ping", "-n", "2", "--", badPeer) assert.Contains(t, res.Stdout.String(), fmt.Sprintf("Looking up peer %s", badPeer)) - assert.Contains(t, res.Stderr.String(), "Error: ping failed") + msg := res.Stderr.String() + assert.Truef(t, strings.HasPrefix(msg, "Error:"), "should fail got this instead: %q", msg) }) t.Run("self", func(t *testing.T) { diff --git a/test/cli/pinning_remote_test.go b/test/cli/pinning_remote_test.go new file mode 100644 index 000000000..fede942ba --- /dev/null +++ b/test/cli/pinning_remote_test.go @@ -0,0 +1,446 @@ +package cli + +import ( + "errors" + "fmt" + "net" + "net/http" + "testing" + "time" + + "github.com/google/uuid" + "github.com/ipfs/kubo/test/cli/harness" + "github.com/ipfs/kubo/test/cli/testutils" + "github.com/ipfs/kubo/test/cli/testutils/pinningservice" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tidwall/gjson" + "github.com/tidwall/sjson" +) + +func runPinningService(t *testing.T, authToken string) (*pinningservice.PinningService, string) { + svc := pinningservice.New() + router := pinningservice.NewRouter(authToken, svc) + server := &http.Server{Handler: router} + listener, err := net.Listen("tcp", "127.0.0.1:0") + require.NoError(t, err) + go func() { + err := server.Serve(listener) + if err != nil && !errors.Is(err, net.ErrClosed) && !errors.Is(err, http.ErrServerClosed) { + t.Logf("Serve error: %s", err) + } + }() + t.Cleanup(func() { listener.Close() }) + + return svc, fmt.Sprintf("http://%s/api/v1", listener.Addr().String()) +} + +func TestRemotePinning(t *testing.T) { + t.Parallel() + authToken := "testauthtoken" + + t.Run("MFS pinning", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + node.Runner.Env["MFS_PIN_POLL_INTERVAL"] = "10ms" + + _, svcURL := runPinningService(t, authToken) + node.IPFS("pin", "remote", "service", "add", "svc", svcURL, authToken) + node.IPFS("config", "--json", "Pinning.RemoteServices.svc.Policies.MFS.RepinInterval", `"1s"`) + node.IPFS("config", "--json", "Pinning.RemoteServices.svc.Policies.MFS.PinName", `"test_pin"`) + node.IPFS("config", "--json", "Pinning.RemoteServices.svc.Policies.MFS.Enable", "true") + + node.StartDaemon() + + node.IPFS("files", "cp", "/ipfs/bafkqaaa", "/mfs-pinning-test-"+uuid.NewString()) + node.IPFS("files", "flush") + res := node.IPFS("files", "stat", "/", "--enc=json") + hash := gjson.Get(res.Stdout.String(), "Hash").Str + + assert.Eventually(t, + func() bool { + res = node.IPFS("pin", "remote", "ls", + "--service=svc", + "--name=test_pin", + "--status=queued,pinning,pinned,failed", + "--enc=json", + ) + pinnedHash := gjson.Get(res.Stdout.String(), "Cid").Str + return hash == pinnedHash + }, + 10*time.Second, + 10*time.Millisecond, + ) + + t.Run("MFS root is repinned on CID change", func(t *testing.T) { + node.IPFS("files", "cp", "/ipfs/bafkqaaa", "/mfs-pinning-repin-test-"+uuid.NewString()) + node.IPFS("files", "flush") + res = node.IPFS("files", "stat", "/", "--enc=json") + hash := gjson.Get(res.Stdout.String(), "Hash").Str + assert.Eventually(t, + func() bool { + res := node.IPFS("pin", "remote", "ls", + "--service=svc", + "--name=test_pin", + "--status=queued,pinning,pinned,failed", + "--enc=json", + ) + pinnedHash := gjson.Get(res.Stdout.String(), "Cid").Str + return hash == pinnedHash + }, + 10*time.Second, + 10*time.Millisecond, + ) + }) + }) + + // Pinning.RemoteServices includes API.Key, so we give it the same treatment + // as Identity,PrivKey to prevent exposing it on the network + t.Run("access token security", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + node.IPFS("pin", "remote", "service", "add", "1", "http://example1.com", "testkey") + res := node.RunIPFS("config", "Pinning") + assert.Equal(t, 1, res.ExitCode()) + assert.Contains(t, res.Stderr.String(), "cannot show or change pinning services credentials") + assert.NotContains(t, res.Stdout.String(), "testkey") + + res = node.RunIPFS("config", "Pinning.RemoteServices.1.API.Key") + assert.Equal(t, 1, res.ExitCode()) + assert.Contains(t, res.Stderr.String(), "cannot show or change pinning services credentials") + assert.NotContains(t, res.Stdout.String(), "testkey") + + configShow := node.RunIPFS("config", "show").Stdout.String() + assert.NotContains(t, configShow, "testkey") + + t.Run("re-injecting config with 'ipfs config replace' preserves the API keys", func(t *testing.T) { + node.WriteBytes("config-show", []byte(configShow)) + node.IPFS("config", "replace", "config-show") + assert.Contains(t, node.ReadFile(node.ConfigFile()), "testkey") + }) + + t.Run("injecting config with 'ipfs config replace' with API keys returns an error", func(t *testing.T) { + // remove Identity.PrivKey to ensure error is triggered by Pinning.RemoteServices + configJSON := MustVal(sjson.Delete(configShow, "Identity.PrivKey")) + configJSON = MustVal(sjson.Set(configJSON, "Pinning.RemoteServices.1.API.Key", "testkey")) + node.WriteBytes("new-config", []byte(configJSON)) + res := node.RunIPFS("config", "replace", "new-config") + assert.Equal(t, 1, res.ExitCode()) + assert.Contains(t, res.Stderr.String(), "cannot change remote pinning services api info with `config replace`") + }) + }) + + t.Run("pin remote service ls --stat", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init().StartDaemon() + _, svcURL := runPinningService(t, authToken) + + node.IPFS("pin", "remote", "service", "add", "svc", svcURL, authToken) + node.IPFS("pin", "remote", "service", "add", "invalid-svc", svcURL+"/invalidpath", authToken) + + res := node.IPFS("pin", "remote", "service", "ls", "--stat") + assert.Contains(t, res.Stdout.String(), " 0/0/0/0") + + stats := node.IPFS("pin", "remote", "service", "ls", "--stat", "--enc=json").Stdout.String() + assert.Equal(t, "valid", gjson.Get(stats, `RemoteServices.#(Service == "svc").Stat.Status`).Str) + assert.Equal(t, "invalid", gjson.Get(stats, `RemoteServices.#(Service == "invalid-svc").Stat.Status`).Str) + + // no --stat returns no stat obj + t.Run("no --stat returns no stat obj", func(t *testing.T) { + res := node.IPFS("pin", "remote", "service", "ls", "--enc=json") + assert.False(t, gjson.Get(res.Stdout.String(), `RemoteServices.#(Service == "svc").Stat`).Exists()) + }) + }) + + t.Run("adding service with invalid URL fails", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init().StartDaemon() + + res := node.RunIPFS("pin", "remote", "service", "add", "svc", "invalid-service.example.com", "key") + assert.Equal(t, 1, res.ExitCode()) + assert.Contains(t, res.Stderr.String(), "service endpoint must be a valid HTTP URL") + + res = node.RunIPFS("pin", "remote", "service", "add", "svc", "xyz://invalid-service.example.com", "key") + assert.Equal(t, 1, res.ExitCode()) + assert.Contains(t, res.Stderr.String(), "service endpoint must be a valid HTTP URL") + }) + + t.Run("unauthorized pinning service calls fail", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init().StartDaemon() + _, svcURL := runPinningService(t, authToken) + + node.IPFS("pin", "remote", "service", "add", "svc", svcURL, "othertoken") + + res := node.RunIPFS("pin", "remote", "ls", "--service=svc") + assert.Equal(t, 1, res.ExitCode()) + assert.Contains(t, res.Stderr.String(), "access denied") + }) + + t.Run("pinning service calls fail when there is a wrong path", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init().StartDaemon() + _, svcURL := runPinningService(t, authToken) + node.IPFS("pin", "remote", "service", "add", "svc", svcURL+"/invalid-path", authToken) + + res := node.RunIPFS("pin", "remote", "ls", "--service=svc") + assert.Equal(t, 1, res.ExitCode()) + assert.Contains(t, res.Stderr.String(), "404") + }) + + t.Run("pinning service calls fail when DNS resolution fails", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init().StartDaemon() + node.IPFS("pin", "remote", "service", "add", "svc", "https://invalid-service.example.com", authToken) + + res := node.RunIPFS("pin", "remote", "ls", "--service=svc") + assert.Equal(t, 1, res.ExitCode()) + assert.Contains(t, res.Stderr.String(), "no such host") + }) + + t.Run("pin remote service rm", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init().StartDaemon() + node.IPFS("pin", "remote", "service", "add", "svc", "https://example.com", authToken) + node.IPFS("pin", "remote", "service", "rm", "svc") + res := node.IPFS("pin", "remote", "service", "ls") + assert.NotContains(t, res.Stdout.String(), "svc") + }) + + t.Run("remote pinning", func(t *testing.T) { + t.Parallel() + + verifyStatus := func(node *harness.Node, name, hash, status string) { + resJSON := node.IPFS("pin", "remote", "ls", + "--service=svc", + "--enc=json", + "--name="+name, + "--status="+status, + ).Stdout.String() + + assert.Equal(t, status, gjson.Get(resJSON, "Status").Str) + assert.Equal(t, hash, gjson.Get(resJSON, "Cid").Str) + assert.Equal(t, name, gjson.Get(resJSON, "Name").Str) + } + + t.Run("'ipfs pin remote add --background=true'", func(t *testing.T) { + node := harness.NewT(t).NewNode().Init().StartDaemon() + svc, svcURL := runPinningService(t, authToken) + node.IPFS("pin", "remote", "service", "add", "svc", svcURL, authToken) + + // retain a ptr to the pin that's in the DB so we can directly mutate its status + // to simulate async work + pinCh := make(chan *pinningservice.PinStatus, 1) + svc.PinAdded = func(req *pinningservice.AddPinRequest, pin *pinningservice.PinStatus) { + pinCh <- pin + } + + hash := node.IPFSAddStr("foo") + node.IPFS("pin", "remote", "add", + "--background=true", + "--service=svc", + "--name=pin1", + hash, + ) + + pin := <-pinCh + + transitionStatus := func(status string) { + pin.M.Lock() + pin.Status = status + pin.M.Unlock() + } + + verifyStatus(node, "pin1", hash, "queued") + + transitionStatus("pinning") + verifyStatus(node, "pin1", hash, "pinning") + + transitionStatus("pinned") + verifyStatus(node, "pin1", hash, "pinned") + + transitionStatus("failed") + verifyStatus(node, "pin1", hash, "failed") + }) + + t.Run("'ipfs pin remote add --background=false'", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init().StartDaemon() + svc, svcURL := runPinningService(t, authToken) + node.IPFS("pin", "remote", "service", "add", "svc", svcURL, authToken) + + svc.PinAdded = func(req *pinningservice.AddPinRequest, pin *pinningservice.PinStatus) { + pin.M.Lock() + defer pin.M.Unlock() + pin.Status = "pinned" + } + hash := node.IPFSAddStr("foo") + node.IPFS("pin", "remote", "add", + "--background=false", + "--service=svc", + "--name=pin2", + hash, + ) + verifyStatus(node, "pin2", hash, "pinned") + }) + + t.Run("'ipfs pin remote ls' with multiple statuses", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init().StartDaemon() + svc, svcURL := runPinningService(t, authToken) + node.IPFS("pin", "remote", "service", "add", "svc", svcURL, authToken) + + hash := node.IPFSAddStr("foo") + desiredStatuses := map[string]string{ + "pin-queued": "queued", + "pin-pinning": "pinning", + "pin-pinned": "pinned", + "pin-failed": "failed", + } + var pins []*pinningservice.PinStatus + svc.PinAdded = func(req *pinningservice.AddPinRequest, pin *pinningservice.PinStatus) { + pin.M.Lock() + defer pin.M.Unlock() + pins = append(pins, pin) + // this must be "pinned" for the 'pin remote add' command to return + // after 'pin remote add', we change the status to its real status + pin.Status = "pinned" + } + + for pinName := range desiredStatuses { + node.IPFS("pin", "remote", "add", + "--service=svc", + "--name="+pinName, + hash, + ) + } + for _, pin := range pins { + pin.M.Lock() + pin.Status = desiredStatuses[pin.Pin.Name] + pin.M.Unlock() + } + + res := node.IPFS("pin", "remote", "ls", + "--service=svc", + "--status=queued,pinning,pinned,failed", + "--enc=json", + ) + actualStatuses := map[string]string{} + for _, line := range res.Stdout.Lines() { + name := gjson.Get(line, "Name").Str + status := gjson.Get(line, "Status").Str + // drop statuses of other pins we didn't add + if _, ok := desiredStatuses[name]; ok { + actualStatuses[name] = status + } + } + assert.Equal(t, desiredStatuses, actualStatuses) + }) + + t.Run("'ipfs pin remote ls' by CID", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init().StartDaemon() + svc, svcURL := runPinningService(t, authToken) + node.IPFS("pin", "remote", "service", "add", "svc", svcURL, authToken) + + transitionedCh := make(chan struct{}, 1) + svc.PinAdded = func(req *pinningservice.AddPinRequest, pin *pinningservice.PinStatus) { + pin.M.Lock() + defer pin.M.Unlock() + pin.Status = "pinned" + transitionedCh <- struct{}{} + } + hash := node.IPFSAddStr(string(testutils.RandomBytes(1000))) + node.IPFS("pin", "remote", "add", "--background=false", "--service=svc", hash) + <-transitionedCh + res := node.IPFS("pin", "remote", "ls", "--service=svc", "--cid="+hash, "--enc=json").Stdout.String() + assert.Contains(t, res, hash) + }) + + t.Run("'ipfs pin remote rm --name' without --force when multiple pins match", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init().StartDaemon() + svc, svcURL := runPinningService(t, authToken) + node.IPFS("pin", "remote", "service", "add", "svc", svcURL, authToken) + + svc.PinAdded = func(req *pinningservice.AddPinRequest, pin *pinningservice.PinStatus) { + pin.M.Lock() + defer pin.M.Unlock() + pin.Status = "pinned" + } + hash := node.IPFSAddStr(string(testutils.RandomBytes(1000))) + node.IPFS("pin", "remote", "add", "--service=svc", "--name=force-test-name", hash) + node.IPFS("pin", "remote", "add", "--service=svc", "--name=force-test-name", hash) + + t.Run("fails", func(t *testing.T) { + res := node.RunIPFS("pin", "remote", "rm", "--service=svc", "--name=force-test-name") + assert.Equal(t, 1, res.ExitCode()) + assert.Contains(t, res.Stderr.String(), "Error: multiple remote pins are matching this query, add --force to confirm the bulk removal") + }) + + t.Run("matching pins are not removed", func(t *testing.T) { + lines := node.IPFS("pin", "remote", "ls", "--service=svc", "--name=force-test-name").Stdout.Lines() + assert.Contains(t, lines[0], "force-test-name") + assert.Contains(t, lines[1], "force-test-name") + }) + }) + + t.Run("'ipfs pin remote rm --name --force' remove multiple pins", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init().StartDaemon() + svc, svcURL := runPinningService(t, authToken) + node.IPFS("pin", "remote", "service", "add", "svc", svcURL, authToken) + + svc.PinAdded = func(req *pinningservice.AddPinRequest, pin *pinningservice.PinStatus) { + pin.M.Lock() + defer pin.M.Unlock() + pin.Status = "pinned" + } + hash := node.IPFSAddStr(string(testutils.RandomBytes(1000))) + node.IPFS("pin", "remote", "add", "--service=svc", "--name=force-test-name", hash) + node.IPFS("pin", "remote", "add", "--service=svc", "--name=force-test-name", hash) + + node.IPFS("pin", "remote", "rm", "--service=svc", "--name=force-test-name", "--force") + out := node.IPFS("pin", "remote", "ls", "--service=svc", "--name=force-test-name").Stdout.Trimmed() + assert.Empty(t, out) + }) + + t.Run("'ipfs pin remote rm --force' removes all pins", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init().StartDaemon() + svc, svcURL := runPinningService(t, authToken) + node.IPFS("pin", "remote", "service", "add", "svc", svcURL, authToken) + + svc.PinAdded = func(req *pinningservice.AddPinRequest, pin *pinningservice.PinStatus) { + pin.M.Lock() + defer pin.M.Unlock() + pin.Status = "pinned" + } + for i := 0; i < 4; i++ { + hash := node.IPFSAddStr(string(testutils.RandomBytes(1000))) + name := fmt.Sprintf("--name=%d", i) + node.IPFS("pin", "remote", "add", "--service=svc", "--name="+name, hash) + } + + lines := node.IPFS("pin", "remote", "ls", "--service=svc").Stdout.Lines() + assert.Len(t, lines, 4) + + node.IPFS("pin", "remote", "rm", "--service=svc", "--force") + + lines = node.IPFS("pin", "remote", "ls", "--service=svc").Stdout.Lines() + assert.Len(t, lines, 0) + }) + }) + + t.Run("'ipfs pin remote add' shows a warning message when offline", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + _, svcURL := runPinningService(t, authToken) + node.IPFS("pin", "remote", "service", "add", "svc", svcURL, authToken) + + hash := node.IPFSAddStr(string(testutils.RandomBytes(1000))) + res := node.IPFS("pin", "remote", "add", "--service=svc", "--background", hash) + warningMsg := "WARNING: the local node is offline and remote pinning may fail if there is no other provider for this CID" + assert.Contains(t, res.Stdout.String(), warningMsg) + }) +} diff --git a/test/cli/pins_test.go b/test/cli/pins_test.go index 14a3dc238..3e3325a01 100644 --- a/test/cli/pins_test.go +++ b/test/cli/pins_test.go @@ -76,7 +76,6 @@ func testPins(t *testing.T, args testPinsArgs) { for _, cid := range cids { assert.Contains(t, verboseVerifyOut, fmt.Sprintf("%s ok", cid)) } - }) t.Run("ls output should contain the cids", func(t *testing.T) { lsOut := ipfsPinLS() @@ -195,7 +194,6 @@ func TestPins(t *testing.T) { testPins(t, testPinsArgs{pinArg: "--progress", lsArg: "--stream"}) testPins(t, testPinsArgs{baseArg: "--cid-base=base32"}) testPins(t, testPinsArgs{lsArg: "--stream", baseArg: "--cid-base=base32"}) - }) t.Run("test pinning with daemon running without network", func(t *testing.T) { @@ -211,4 +209,125 @@ func TestPins(t *testing.T) { testPins(t, testPinsArgs{runDaemon: true, baseArg: "--cid-base=base32"}) testPins(t, testPinsArgs{runDaemon: true, lsArg: "--stream", baseArg: "--cid-base=base32"}) }) + + pinLs := func(node *harness.Node, args ...string) []string { + return strings.Split(node.IPFS(StrCat("pin", "ls", args)...).Stdout.Trimmed(), "\n") + } + + t.Run("test pinning with names cli text output", func(t *testing.T) { + t.Parallel() + + node := harness.NewT(t).NewNode().Init() + cidAStr := node.IPFSAddStr(RandomStr(1000), "--pin=false") + cidBStr := node.IPFSAddStr(RandomStr(1000), "--pin=false") + + _ = node.IPFS("pin", "add", "--name", "testPin", cidAStr) + + outARegular := cidAStr + " recursive" + outADetailed := outARegular + " testPin" + outBRegular := cidBStr + " recursive" + outBDetailed := outBRegular + " testPin" + + lsOut := pinLs(node, "-t=recursive") + require.Contains(t, lsOut, outARegular) + require.NotContains(t, lsOut, outADetailed) + + lsOut = pinLs(node, "-t=recursive", "--names") + require.Contains(t, lsOut, outADetailed) + require.NotContains(t, lsOut, outARegular) + + _ = node.IPFS("pin", "update", cidAStr, cidBStr) + lsOut = pinLs(node, "-t=recursive", "--names") + require.Contains(t, lsOut, outBDetailed) + require.NotContains(t, lsOut, outADetailed) + }) + + t.Run("test listing pins with names that contain specific string", func(t *testing.T) { + t.Parallel() + + node := harness.NewT(t).NewNode().Init() + cidAStr := node.IPFSAddStr(RandomStr(1000), "--pin=false") + cidBStr := node.IPFSAddStr(RandomStr(1000), "--pin=false") + cidCStr := node.IPFSAddStr(RandomStr(1000), "--pin=false") + + outA := cidAStr + " recursive testPin" + outB := cidBStr + " recursive testPin" + outC := cidCStr + " recursive randPin" + + // make sure both -n and --name work + for _, nameParam := range []string{"--name", "-n"} { + _ = node.IPFS("pin", "add", "--name", "testPin", cidAStr) + lsOut := pinLs(node, "-t=recursive", nameParam+"=test") + require.Contains(t, lsOut, outA) + lsOut = pinLs(node, "-t=recursive", nameParam+"=randomLabel") + require.NotContains(t, lsOut, outA) + + _ = node.IPFS("pin", "add", "--name", "testPin", cidBStr) + lsOut = pinLs(node, "-t=recursive", nameParam+"=test") + require.Contains(t, lsOut, outA) + require.Contains(t, lsOut, outB) + + _ = node.IPFS("pin", "add", "--name", "randPin", cidCStr) + lsOut = pinLs(node, "-t=recursive", nameParam+"=rand") + require.NotContains(t, lsOut, outA) + require.NotContains(t, lsOut, outB) + require.Contains(t, lsOut, outC) + + lsOut = pinLs(node, "-t=recursive", nameParam+"=testPin") + require.Contains(t, lsOut, outA) + require.Contains(t, lsOut, outB) + require.NotContains(t, lsOut, outC) + } + }) + + t.Run("test overwriting pin with name", func(t *testing.T) { + t.Parallel() + + node := harness.NewT(t).NewNode().Init() + cidStr := node.IPFSAddStr(RandomStr(1000), "--pin=false") + + outBefore := cidStr + " recursive A" + outAfter := cidStr + " recursive B" + + _ = node.IPFS("pin", "add", "--name", "A", cidStr) + lsOut := pinLs(node, "-t=recursive", "--names") + require.Contains(t, lsOut, outBefore) + require.NotContains(t, lsOut, outAfter) + + _ = node.IPFS("pin", "add", "--name", "B", cidStr) + lsOut = pinLs(node, "-t=recursive", "--names") + require.Contains(t, lsOut, outAfter) + require.NotContains(t, lsOut, outBefore) + }) + + // JSON that is also the wire format of /api/v0 + t.Run("test pinning with names json output", func(t *testing.T) { + t.Parallel() + + node := harness.NewT(t).NewNode().Init() + cidAStr := node.IPFSAddStr(RandomStr(1000), "--pin=false") + cidBStr := node.IPFSAddStr(RandomStr(1000), "--pin=false") + + _ = node.IPFS("pin", "add", "--name", "testPinJson", cidAStr) + + outARegular := `"` + cidAStr + `":{"Type":"recursive"` + outADetailed := outARegular + `,"Name":"testPinJson"` + outBRegular := `"` + cidBStr + `":{"Type":"recursive"` + outBDetailed := outBRegular + `,"Name":"testPinJson"` + + pinLs := func(args ...string) string { + return node.IPFS(StrCat("pin", "ls", "--enc=json", args)...).Stdout.Trimmed() + } + + lsOut := pinLs("-t=recursive") + require.Contains(t, lsOut, outARegular) + require.NotContains(t, lsOut, outADetailed) + + lsOut = pinLs("-t=recursive", "--names") + require.Contains(t, lsOut, outADetailed) + + _ = node.IPFS("pin", "update", cidAStr, cidBStr) + lsOut = pinLs("-t=recursive", "--names") + require.Contains(t, lsOut, outBDetailed) + }) } diff --git a/test/cli/provider_test.go b/test/cli/provider_test.go new file mode 100644 index 000000000..5ecf8f3ca --- /dev/null +++ b/test/cli/provider_test.go @@ -0,0 +1,165 @@ +package cli + +import ( + "bytes" + "testing" + "time" + + "github.com/ipfs/kubo/test/cli/harness" + "github.com/ipfs/kubo/test/cli/testutils" + "github.com/stretchr/testify/require" +) + +func TestProvider(t *testing.T) { + t.Parallel() + + initNodes := func(t *testing.T, n int, fn func(n *harness.Node)) harness.Nodes { + nodes := harness.NewT(t).NewNodes(n).Init() + nodes.ForEachPar(fn) + return nodes.StartDaemons().Connect() + } + + expectNoProviders := func(t *testing.T, cid string, nodes ...*harness.Node) { + for _, node := range nodes { + res := node.IPFS("routing", "findprovs", "-n=1", cid) + require.Empty(t, res.Stdout.String()) + } + } + + expectProviders := func(t *testing.T, cid, expectedProvider string, nodes ...*harness.Node) { + for _, node := range nodes { + res := node.IPFS("routing", "findprovs", "-n=1", cid) + require.Equal(t, expectedProvider, res.Stdout.Trimmed()) + } + } + + t.Run("Basic Providing", func(t *testing.T) { + t.Parallel() + + nodes := initNodes(t, 2, func(n *harness.Node) { + n.SetIPFSConfig("Experimental.StrategicProviding", false) + }) + defer nodes.StopDaemons() + + cid := nodes[0].IPFSAddStr(time.Now().String()) + expectProviders(t, cid, nodes[0].PeerID().String(), nodes[1:]...) + }) + + t.Run("Basic Strategic Providing", func(t *testing.T) { + t.Parallel() + + nodes := initNodes(t, 2, func(n *harness.Node) { + n.SetIPFSConfig("Experimental.StrategicProviding", true) + }) + defer nodes.StopDaemons() + + cid := nodes[0].IPFSAddStr(time.Now().String()) + expectNoProviders(t, cid, nodes[1:]...) + }) + + t.Run("Reprovides with 'all' strategy", func(t *testing.T) { + t.Parallel() + + nodes := initNodes(t, 2, func(n *harness.Node) { + n.SetIPFSConfig("Reprovider.Strategy", "all") + }) + defer nodes.StopDaemons() + + cid := nodes[0].IPFSAddStr(time.Now().String(), "--local") + + expectNoProviders(t, cid, nodes[1:]...) + + nodes[0].IPFS("bitswap", "reprovide") + + expectProviders(t, cid, nodes[0].PeerID().String(), nodes[1:]...) + }) + + t.Run("Reprovides with 'flat' strategy", func(t *testing.T) { + t.Parallel() + + nodes := initNodes(t, 2, func(n *harness.Node) { + n.SetIPFSConfig("Reprovider.Strategy", "flat") + }) + defer nodes.StopDaemons() + + cid := nodes[0].IPFSAddStr(time.Now().String(), "--local") + + expectNoProviders(t, cid, nodes[1:]...) + + nodes[0].IPFS("bitswap", "reprovide") + + expectProviders(t, cid, nodes[0].PeerID().String(), nodes[1:]...) + }) + + t.Run("Reprovides with 'pinned' strategy", func(t *testing.T) { + t.Parallel() + + foo := testutils.RandomBytes(1000) + bar := testutils.RandomBytes(1000) + + nodes := initNodes(t, 2, func(n *harness.Node) { + n.SetIPFSConfig("Reprovider.Strategy", "pinned") + }) + defer nodes.StopDaemons() + + cidFoo := nodes[0].IPFSAdd(bytes.NewReader(foo), "--offline", "--pin=false") + cidBar := nodes[0].IPFSAdd(bytes.NewReader(bar), "--offline", "--pin=false") + cidBarDir := nodes[0].IPFSAdd(bytes.NewReader(bar), "-Q", "--offline", "-w") + + expectNoProviders(t, cidFoo, nodes[1:]...) + expectNoProviders(t, cidBar, nodes[1:]...) + expectNoProviders(t, cidBarDir, nodes[1:]...) + + nodes[0].IPFS("bitswap", "reprovide") + + expectNoProviders(t, cidFoo, nodes[1:]...) + expectProviders(t, cidBar, nodes[0].PeerID().String(), nodes[1:]...) + expectProviders(t, cidBarDir, nodes[0].PeerID().String(), nodes[1:]...) + }) + + t.Run("Reprovides with 'roots' strategy", func(t *testing.T) { + t.Parallel() + + foo := testutils.RandomBytes(1000) + bar := testutils.RandomBytes(1000) + baz := testutils.RandomBytes(1000) + + nodes := initNodes(t, 2, func(n *harness.Node) { + n.SetIPFSConfig("Reprovider.Strategy", "roots") + }) + defer nodes.StopDaemons() + + cidFoo := nodes[0].IPFSAdd(bytes.NewReader(foo), "--offline", "--pin=false") + cidBar := nodes[0].IPFSAdd(bytes.NewReader(bar), "--offline", "--pin=false") + cidBaz := nodes[0].IPFSAdd(bytes.NewReader(baz), "--offline") + cidBarDir := nodes[0].IPFSAdd(bytes.NewReader(bar), "-Q", "--offline", "-w") + + expectNoProviders(t, cidFoo, nodes[1:]...) + expectNoProviders(t, cidBar, nodes[1:]...) + expectNoProviders(t, cidBarDir, nodes[1:]...) + + nodes[0].IPFS("bitswap", "reprovide") + + expectNoProviders(t, cidFoo, nodes[1:]...) + expectNoProviders(t, cidBar, nodes[1:]...) + expectProviders(t, cidBaz, nodes[0].PeerID().String(), nodes[1:]...) + expectProviders(t, cidBarDir, nodes[0].PeerID().String(), nodes[1:]...) + }) + + t.Run("Providing works without ticking", func(t *testing.T) { + t.Parallel() + + nodes := initNodes(t, 2, func(n *harness.Node) { + n.SetIPFSConfig("Reprovider.Interval", "0") + }) + defer nodes.StopDaemons() + + cid := nodes[0].IPFSAddStr(time.Now().String(), "--offline") + + expectNoProviders(t, cid, nodes[1:]...) + + nodes[0].IPFS("bitswap", "reprovide") + + expectProviders(t, cid, nodes[0].PeerID().String(), nodes[1:]...) + }) +} diff --git a/test/cli/rcmgr_test.go b/test/cli/rcmgr_test.go new file mode 100644 index 000000000..50ea26979 --- /dev/null +++ b/test/cli/rcmgr_test.go @@ -0,0 +1,325 @@ +package cli + +import ( + "encoding/json" + "testing" + + "github.com/ipfs/kubo/config" + "github.com/ipfs/kubo/core/node/libp2p" + "github.com/ipfs/kubo/test/cli/harness" + "github.com/ipfs/kubo/test/cli/testutils" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/protocol" + rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestRcmgr(t *testing.T) { + t.Parallel() + + t.Run("Resource manager disabled", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + node.UpdateConfig(func(cfg *config.Config) { + cfg.Swarm.ResourceMgr.Enabled = config.False + }) + + node.StartDaemon() + + t.Run("swarm resources should fail", func(t *testing.T) { + res := node.RunIPFS("swarm", "resources") + assert.Equal(t, 1, res.ExitCode()) + assert.Contains(t, res.Stderr.String(), "missing ResourceMgr") + }) + }) + + t.Run("Node with resource manager disabled", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + node.UpdateConfig(func(cfg *config.Config) { + cfg.Swarm.ResourceMgr.Enabled = config.False + }) + node.StartDaemon() + + t.Run("swarm resources should fail", func(t *testing.T) { + res := node.RunIPFS("swarm", "resources") + assert.Equal(t, 1, res.ExitCode()) + assert.Contains(t, res.Stderr.String(), "missing ResourceMgr") + }) + }) + + t.Run("Very high connmgr highwater", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + node.UpdateConfig(func(cfg *config.Config) { + cfg.Swarm.ConnMgr.HighWater = config.NewOptionalInteger(1000) + }) + node.StartDaemon() + + res := node.RunIPFS("swarm", "resources", "--enc=json") + require.Equal(t, 0, res.ExitCode()) + limits := unmarshalLimits(t, res.Stdout.Bytes()) + + rl := limits.System.ToResourceLimits() + s := rl.Build(rcmgr.BaseLimit{}) + assert.GreaterOrEqual(t, s.ConnsInbound, 2000) + assert.GreaterOrEqual(t, s.StreamsInbound, 2000) + }) + + t.Run("default configuration", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + node.UpdateConfig(func(cfg *config.Config) { + cfg.Swarm.ConnMgr.HighWater = config.NewOptionalInteger(1000) + }) + node.StartDaemon() + + t.Run("conns and streams are above 800 for default connmgr settings", func(t *testing.T) { + t.Parallel() + res := node.RunIPFS("swarm", "resources", "--enc=json") + require.Equal(t, 0, res.ExitCode()) + limits := unmarshalLimits(t, res.Stdout.Bytes()) + + if limits.System.ConnsInbound > rcmgr.DefaultLimit { + assert.GreaterOrEqual(t, limits.System.ConnsInbound, 800) + } + if limits.System.StreamsInbound > rcmgr.DefaultLimit { + assert.GreaterOrEqual(t, limits.System.StreamsInbound, 800) + } + }) + + t.Run("limits should succeed", func(t *testing.T) { + t.Parallel() + res := node.RunIPFS("swarm", "resources", "--enc=json") + assert.Equal(t, 0, res.ExitCode()) + + limits := rcmgr.PartialLimitConfig{} + err := json.Unmarshal(res.Stdout.Bytes(), &limits) + require.NoError(t, err) + + assert.NotEqual(t, limits.Transient.Memory, rcmgr.BlockAllLimit64) + assert.NotEqual(t, limits.System.Memory, rcmgr.BlockAllLimit64) + assert.NotEqual(t, limits.System.FD, rcmgr.BlockAllLimit) + assert.NotEqual(t, limits.System.Conns, rcmgr.BlockAllLimit) + assert.NotEqual(t, limits.System.ConnsInbound, rcmgr.BlockAllLimit) + assert.NotEqual(t, limits.System.ConnsOutbound, rcmgr.BlockAllLimit) + assert.NotEqual(t, limits.System.Streams, rcmgr.BlockAllLimit) + assert.NotEqual(t, limits.System.StreamsInbound, rcmgr.BlockAllLimit) + assert.NotEqual(t, limits.System.StreamsOutbound, rcmgr.BlockAllLimit) + }) + + t.Run("swarm stats works", func(t *testing.T) { + t.Parallel() + res := node.RunIPFS("swarm", "resources", "--enc=json") + require.Equal(t, 0, res.ExitCode()) + + limits := unmarshalLimits(t, res.Stdout.Bytes()) + + // every scope has the same fields, so we only inspect system + assert.Zero(t, limits.System.MemoryUsage) + assert.Zero(t, limits.System.FDUsage) + assert.Zero(t, limits.System.ConnsInboundUsage) + assert.Zero(t, limits.System.ConnsOutboundUsage) + assert.Zero(t, limits.System.StreamsInboundUsage) + assert.Zero(t, limits.System.StreamsOutboundUsage) + assert.Zero(t, limits.Transient.MemoryUsage) + }) + }) + + t.Run("smoke test unlimited System inbounds", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + node.UpdateUserSuppliedResourceManagerOverrides(func(overrides *rcmgr.PartialLimitConfig) { + overrides.System.StreamsInbound = rcmgr.Unlimited + overrides.System.ConnsInbound = rcmgr.Unlimited + }) + node.StartDaemon() + + res := node.RunIPFS("swarm", "resources", "--enc=json") + limits := unmarshalLimits(t, res.Stdout.Bytes()) + + assert.Equal(t, rcmgr.Unlimited, limits.System.ConnsInbound) + assert.Equal(t, rcmgr.Unlimited, limits.System.StreamsInbound) + }) + + t.Run("smoke test transient scope", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + node.UpdateUserSuppliedResourceManagerOverrides(func(overrides *rcmgr.PartialLimitConfig) { + overrides.Transient.Memory = 88888 + }) + node.StartDaemon() + + res := node.RunIPFS("swarm", "resources", "--enc=json") + limits := unmarshalLimits(t, res.Stdout.Bytes()) + assert.Equal(t, rcmgr.LimitVal64(88888), limits.Transient.Memory) + }) + + t.Run("smoke test service scope", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + node.UpdateUserSuppliedResourceManagerOverrides(func(overrides *rcmgr.PartialLimitConfig) { + overrides.Service = map[string]rcmgr.ResourceLimits{"foo": {Memory: 77777}} + }) + node.StartDaemon() + + res := node.RunIPFS("swarm", "resources", "--enc=json") + limits := unmarshalLimits(t, res.Stdout.Bytes()) + assert.Equal(t, rcmgr.LimitVal64(77777), limits.Services["foo"].Memory) + }) + + t.Run("smoke test protocol scope", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + node.UpdateUserSuppliedResourceManagerOverrides(func(overrides *rcmgr.PartialLimitConfig) { + overrides.Protocol = map[protocol.ID]rcmgr.ResourceLimits{"foo": {Memory: 66666}} + }) + node.StartDaemon() + + res := node.RunIPFS("swarm", "resources", "--enc=json") + limits := unmarshalLimits(t, res.Stdout.Bytes()) + assert.Equal(t, rcmgr.LimitVal64(66666), limits.Protocols["foo"].Memory) + }) + + t.Run("smoke test peer scope", func(t *testing.T) { + t.Parallel() + validPeerID, err := peer.Decode("QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN") + assert.NoError(t, err) + node := harness.NewT(t).NewNode().Init() + node.UpdateUserSuppliedResourceManagerOverrides(func(overrides *rcmgr.PartialLimitConfig) { + overrides.Peer = map[peer.ID]rcmgr.ResourceLimits{validPeerID: {Memory: 55555}} + }) + node.StartDaemon() + + res := node.RunIPFS("swarm", "resources", "--enc=json") + limits := unmarshalLimits(t, res.Stdout.Bytes()) + assert.Equal(t, rcmgr.LimitVal64(55555), limits.Peers[validPeerID].Memory) + }) + + t.Run("blocking and allowlists", func(t *testing.T) { + t.Parallel() + nodes := harness.NewT(t).NewNodes(3).Init() + node0, node1, node2 := nodes[0], nodes[1], nodes[2] + peerID1, peerID2 := node1.PeerID().String(), node2.PeerID().String() + + node0.UpdateConfig(func(cfg *config.Config) { + cfg.Swarm.ResourceMgr.Enabled = config.True + cfg.Swarm.ResourceMgr.Allowlist = []string{"/ip4/0.0.0.0/ipcidr/0/p2p/" + peerID2} + }) + node0.UpdateUserSuppliedResourceManagerOverrides(func(overrides *rcmgr.PartialLimitConfig) { + *overrides = rcmgr.PartialLimitConfig{ + System: rcmgr.ResourceLimits{ + Conns: rcmgr.BlockAllLimit, + ConnsInbound: rcmgr.BlockAllLimit, + ConnsOutbound: rcmgr.BlockAllLimit, + }, + } + }) + + nodes.StartDaemons() + + t.Run("node 0 should fail to connect to and ping node 1", func(t *testing.T) { + t.Parallel() + res := node0.Runner.Run(harness.RunRequest{ + Path: node0.IPFSBin, + Args: []string{"swarm", "connect", node1.SwarmAddrsWithPeerIDs()[0].String()}, + }) + assert.Equal(t, 1, res.ExitCode()) + testutils.AssertStringContainsOneOf(t, res.Stderr.String(), + "failed to find any peer in table", + "resource limit exceeded", + ) + + res = node0.RunIPFS("ping", "-n2", peerID1) + assert.Equal(t, 1, res.ExitCode()) + assert.Contains(t, res.Stderr.String(), "Error: ping failed") + }) + + t.Run("node 0 should connect to and ping node 2 since it is allowlisted", func(t *testing.T) { + t.Parallel() + res := node0.Runner.Run(harness.RunRequest{ + Path: node0.IPFSBin, + Args: []string{"swarm", "connect", node2.SwarmAddrsWithPeerIDs()[0].String()}, + }) + assert.Equal(t, 0, res.ExitCode()) + + res = node0.RunIPFS("ping", "-n2", peerID2) + assert.Equal(t, 0, res.ExitCode()) + }) + }) + + t.Run("daemon should refuse to start if connmgr.highwater < resources inbound", func(t *testing.T) { + t.Run("system conns", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + node.UpdateConfig(func(cfg *config.Config) { + cfg.Swarm.ConnMgr.HighWater = config.NewOptionalInteger(128) + cfg.Swarm.ConnMgr.LowWater = config.NewOptionalInteger(64) + }) + node.UpdateUserSuppliedResourceManagerOverrides(func(overrides *rcmgr.PartialLimitConfig) { + *overrides = rcmgr.PartialLimitConfig{ + System: rcmgr.ResourceLimits{Conns: 128}, + } + }) + + res := node.RunIPFS("daemon") + assert.Equal(t, 1, res.ExitCode()) + }) + t.Run("system conns inbound", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + node.UpdateConfig(func(cfg *config.Config) { + cfg.Swarm.ConnMgr.HighWater = config.NewOptionalInteger(128) + cfg.Swarm.ConnMgr.LowWater = config.NewOptionalInteger(64) + }) + node.UpdateUserSuppliedResourceManagerOverrides(func(overrides *rcmgr.PartialLimitConfig) { + *overrides = rcmgr.PartialLimitConfig{ + System: rcmgr.ResourceLimits{ConnsInbound: 128}, + } + }) + + res := node.RunIPFS("daemon") + assert.Equal(t, 1, res.ExitCode()) + }) + t.Run("system streams", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + node.UpdateConfig(func(cfg *config.Config) { + cfg.Swarm.ConnMgr.HighWater = config.NewOptionalInteger(128) + cfg.Swarm.ConnMgr.LowWater = config.NewOptionalInteger(64) + }) + node.UpdateUserSuppliedResourceManagerOverrides(func(overrides *rcmgr.PartialLimitConfig) { + *overrides = rcmgr.PartialLimitConfig{ + System: rcmgr.ResourceLimits{Streams: 128}, + } + }) + + res := node.RunIPFS("daemon") + assert.Equal(t, 1, res.ExitCode()) + }) + t.Run("system streams inbound", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + node.UpdateConfig(func(cfg *config.Config) { + cfg.Swarm.ConnMgr.HighWater = config.NewOptionalInteger(128) + cfg.Swarm.ConnMgr.LowWater = config.NewOptionalInteger(64) + }) + node.UpdateUserSuppliedResourceManagerOverrides(func(overrides *rcmgr.PartialLimitConfig) { + *overrides = rcmgr.PartialLimitConfig{ + System: rcmgr.ResourceLimits{StreamsInbound: 128}, + } + }) + + res := node.RunIPFS("daemon") + assert.Equal(t, 1, res.ExitCode()) + }) + }) +} + +func unmarshalLimits(t *testing.T, b []byte) *libp2p.LimitsConfigAndUsage { + limits := &libp2p.LimitsConfigAndUsage{} + err := json.Unmarshal(b, limits) + require.NoError(t, err) + return limits +} diff --git a/test/cli/routing_dht_test.go b/test/cli/routing_dht_test.go new file mode 100644 index 000000000..fb0d39195 --- /dev/null +++ b/test/cli/routing_dht_test.go @@ -0,0 +1,139 @@ +package cli + +import ( + "fmt" + "testing" + + "github.com/ipfs/kubo/test/cli/harness" + "github.com/ipfs/kubo/test/cli/testutils" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func testRoutingDHT(t *testing.T, enablePubsub bool) { + t.Run(fmt.Sprintf("enablePubSub=%v", enablePubsub), func(t *testing.T) { + t.Parallel() + nodes := harness.NewT(t).NewNodes(5).Init() + nodes.ForEachPar(func(node *harness.Node) { + node.IPFS("config", "Routing.Type", "dht") + }) + + var daemonArgs []string + if enablePubsub { + daemonArgs = []string{ + "--enable-pubsub-experiment", + "--enable-namesys-pubsub", + } + } + + nodes.StartDaemons(daemonArgs...).Connect() + + t.Run("ipfs routing findpeer", func(t *testing.T) { + t.Parallel() + res := nodes[1].RunIPFS("routing", "findpeer", nodes[0].PeerID().String()) + assert.Equal(t, 0, res.ExitCode()) + + swarmAddr := nodes[0].SwarmAddrsWithoutPeerIDs()[0] + require.Equal(t, swarmAddr.String(), res.Stdout.Trimmed()) + }) + + t.Run("ipfs routing get ", func(t *testing.T) { + t.Parallel() + hash := nodes[2].IPFSAddStr("hello world") + nodes[2].IPFS("name", "publish", "/ipfs/"+hash) + + res := nodes[1].IPFS("routing", "get", "/ipns/"+nodes[2].PeerID().String()) + assert.Contains(t, res.Stdout.String(), "/ipfs/"+hash) + + t.Run("put round trips (#3124)", func(t *testing.T) { + t.Parallel() + nodes[0].WriteBytes("get_result", res.Stdout.Bytes()) + res := nodes[0].IPFS("routing", "put", "/ipns/"+nodes[2].PeerID().String(), "get_result") + assert.Greater(t, len(res.Stdout.Lines()), 0, "should put to at least one node") + }) + + t.Run("put with bad keys fails (issue #5113, #4611)", func(t *testing.T) { + t.Parallel() + keys := []string{"foo", "/pk/foo", "/ipns/foo"} + for _, key := range keys { + key := key + t.Run(key, func(t *testing.T) { + t.Parallel() + res := nodes[0].RunIPFS("routing", "put", key) + assert.Equal(t, 1, res.ExitCode()) + assert.Contains(t, res.Stderr.String(), "invalid") + assert.Empty(t, res.Stdout.String()) + }) + } + }) + + t.Run("get with bad keys (issue #4611)", func(t *testing.T) { + for _, key := range []string{"foo", "/pk/foo"} { + key := key + t.Run(key, func(t *testing.T) { + t.Parallel() + res := nodes[0].RunIPFS("routing", "get", key) + assert.Equal(t, 1, res.ExitCode()) + assert.Contains(t, res.Stderr.String(), "invalid") + assert.Empty(t, res.Stdout.String()) + }) + } + }) + }) + + t.Run("ipfs routing findprovs", func(t *testing.T) { + t.Parallel() + hash := nodes[3].IPFSAddStr("some stuff") + res := nodes[4].IPFS("routing", "findprovs", hash) + assert.Equal(t, nodes[3].PeerID().String(), res.Stdout.Trimmed()) + }) + + t.Run("routing commands fail when offline", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + + // these cannot be run in parallel due to repo locking + // this seems like a bug, we should be able to run these without locking the repo + + t.Run("routing findprovs", func(t *testing.T) { + res := node.RunIPFS("routing", "findprovs", testutils.CIDEmptyDir) + assert.Equal(t, 1, res.ExitCode()) + assert.Contains(t, res.Stderr.String(), "this command must be run in online mode") + }) + + t.Run("routing findpeer", func(t *testing.T) { + res := node.RunIPFS("routing", "findpeer", testutils.CIDEmptyDir) + assert.Equal(t, 1, res.ExitCode()) + assert.Contains(t, res.Stderr.String(), "this command must be run in online mode") + }) + + t.Run("routing put", func(t *testing.T) { + node.WriteBytes("foo", []byte("foo")) + res := node.RunIPFS("routing", "put", "/ipns/"+node.PeerID().String(), "foo") + assert.Equal(t, 1, res.ExitCode()) + assert.Contains(t, res.Stderr.String(), "can't put while offline: pass `--allow-offline` to override") + }) + }) + }) +} + +func testSelfFindDHT(t *testing.T) { + t.Run("ipfs routing findpeer fails for self", func(t *testing.T) { + t.Parallel() + nodes := harness.NewT(t).NewNodes(1).Init() + nodes.ForEachPar(func(node *harness.Node) { + node.IPFS("config", "Routing.Type", "dht") + }) + + nodes.StartDaemons() + + res := nodes[0].RunIPFS("dht", "findpeer", nodes[0].PeerID().String()) + assert.Equal(t, 1, res.ExitCode()) + }) +} + +func TestRoutingDHT(t *testing.T) { + testRoutingDHT(t, false) + testRoutingDHT(t, true) + testSelfFindDHT(t) +} diff --git a/test/cli/rpc_auth_test.go b/test/cli/rpc_auth_test.go new file mode 100644 index 000000000..c30b107cf --- /dev/null +++ b/test/cli/rpc_auth_test.go @@ -0,0 +1,162 @@ +package cli + +import ( + "net/http" + "testing" + + "github.com/ipfs/kubo/client/rpc/auth" + "github.com/ipfs/kubo/config" + "github.com/ipfs/kubo/test/cli/harness" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const rpcDeniedMsg = "Kubo RPC Access Denied: Please provide a valid authorization token as defined in the API.Authorizations configuration." + +func TestRPCAuth(t *testing.T) { + t.Parallel() + + makeAndStartProtectedNode := func(t *testing.T, authorizations map[string]*config.RPCAuthScope) *harness.Node { + authorizations["test-node-starter"] = &config.RPCAuthScope{ + AuthSecret: "bearer:test-node-starter", + AllowedPaths: []string{"/api/v0"}, + } + + node := harness.NewT(t).NewNode().Init() + node.UpdateConfig(func(cfg *config.Config) { + cfg.API.Authorizations = authorizations + }) + node.StartDaemonWithAuthorization("Bearer test-node-starter") + return node + } + + makeHTTPTest := func(authSecret, header string) func(t *testing.T) { + return func(t *testing.T) { + t.Parallel() + t.Log(authSecret, header) + + node := makeAndStartProtectedNode(t, map[string]*config.RPCAuthScope{ + "userA": { + AuthSecret: authSecret, + AllowedPaths: []string{"/api/v0/id"}, + }, + }) + + apiClient := node.APIClient() + apiClient.Client = &http.Client{ + Transport: auth.NewAuthorizedRoundTripper(header, http.DefaultTransport), + } + + // Can access /id with valid token + resp := apiClient.Post("/api/v0/id", nil) + assert.Equal(t, 200, resp.StatusCode) + + // But not /config/show + resp = apiClient.Post("/api/v0/config/show", nil) + assert.Equal(t, 403, resp.StatusCode) + + // create client which sends invalid access token + invalidApiClient := node.APIClient() + invalidApiClient.Client = &http.Client{ + Transport: auth.NewAuthorizedRoundTripper("Bearer invalid", http.DefaultTransport), + } + + // Can't access /id with invalid token + errResp := invalidApiClient.Post("/api/v0/id", nil) + assert.Equal(t, 403, errResp.StatusCode) + + node.StopDaemon() + } + } + + makeCLITest := func(authSecret string) func(t *testing.T) { + return func(t *testing.T) { + t.Parallel() + + node := makeAndStartProtectedNode(t, map[string]*config.RPCAuthScope{ + "userA": { + AuthSecret: authSecret, + AllowedPaths: []string{"/api/v0/id"}, + }, + }) + + // Can access 'ipfs id' + resp := node.RunIPFS("id", "--api-auth", authSecret) + require.NoError(t, resp.Err) + + // But not 'ipfs config show' + resp = node.RunIPFS("config", "show", "--api-auth", authSecret) + require.Error(t, resp.Err) + require.Contains(t, resp.Stderr.String(), rpcDeniedMsg) + + node.StopDaemon() + } + } + + for _, testCase := range []struct { + name string + authSecret string + header string + }{ + {"Bearer (no type)", "myToken", "Bearer myToken"}, + {"Bearer", "bearer:myToken", "Bearer myToken"}, + {"Basic (user:pass)", "basic:user:pass", "Basic dXNlcjpwYXNz"}, + {"Basic (encoded)", "basic:dXNlcjpwYXNz", "Basic dXNlcjpwYXNz"}, + } { + t.Run("AllowedPaths on CLI "+testCase.name, makeCLITest(testCase.authSecret)) + t.Run("AllowedPaths on HTTP "+testCase.name, makeHTTPTest(testCase.authSecret, testCase.header)) + } + + t.Run("AllowedPaths set to /api/v0 Gives Full Access", func(t *testing.T) { + t.Parallel() + + node := makeAndStartProtectedNode(t, map[string]*config.RPCAuthScope{ + "userA": { + AuthSecret: "bearer:userAToken", + AllowedPaths: []string{"/api/v0"}, + }, + }) + + apiClient := node.APIClient() + apiClient.Client = &http.Client{ + Transport: auth.NewAuthorizedRoundTripper("Bearer userAToken", http.DefaultTransport), + } + + resp := apiClient.Post("/api/v0/id", nil) + assert.Equal(t, 200, resp.StatusCode) + + node.StopDaemon() + }) + + t.Run("API.Authorizations set to nil disables Authorization header check", func(t *testing.T) { + t.Parallel() + + node := harness.NewT(t).NewNode().Init() + node.UpdateConfig(func(cfg *config.Config) { + cfg.API.Authorizations = nil + }) + node.StartDaemon() + + apiClient := node.APIClient() + resp := apiClient.Post("/api/v0/id", nil) + assert.Equal(t, 200, resp.StatusCode) + + node.StopDaemon() + }) + + t.Run("API.Authorizations set to empty map disables Authorization header check", func(t *testing.T) { + t.Parallel() + + node := harness.NewT(t).NewNode().Init() + node.UpdateConfig(func(cfg *config.Config) { + cfg.API.Authorizations = map[string]*config.RPCAuthScope{} + }) + node.StartDaemon() + + apiClient := node.APIClient() + resp := apiClient.Post("/api/v0/id", nil) + assert.Equal(t, 200, resp.StatusCode) + + node.StopDaemon() + }) +} diff --git a/test/cli/rpc_unixsocket_test.go b/test/cli/rpc_unixsocket_test.go new file mode 100644 index 000000000..8cead7388 --- /dev/null +++ b/test/cli/rpc_unixsocket_test.go @@ -0,0 +1,51 @@ +package cli + +import ( + "context" + "path" + "testing" + + rpcapi "github.com/ipfs/kubo/client/rpc" + "github.com/ipfs/kubo/config" + "github.com/ipfs/kubo/test/cli/harness" + "github.com/multiformats/go-multiaddr" + "github.com/stretchr/testify/require" +) + +func TestRPCUnixSocket(t *testing.T) { + node := harness.NewT(t).NewNode().Init() + + sockDir := node.Dir + sockAddr := path.Join("/unix", sockDir, "sock") + + node.UpdateConfig(func(cfg *config.Config) { + //cfg.Addresses.API = append(cfg.Addresses.API, sockPath) + cfg.Addresses.API = []string{sockAddr} + }) + t.Log("Starting daemon with unix socket:", sockAddr) + node.StartDaemon() + + unixMaddr, err := multiaddr.NewMultiaddr(sockAddr) + require.NoError(t, err) + + apiClient, err := rpcapi.NewApi(unixMaddr) + require.NoError(t, err) + + var ver struct { + Version string + } + err = apiClient.Request("version").Exec(context.Background(), &ver) + require.NoError(t, err) + require.NotEmpty(t, ver) + t.Log("Got version:", ver.Version) + + var res struct { + ID string + } + err = apiClient.Request("id").Exec(context.Background(), &res) + require.NoError(t, err) + require.NotEmpty(t, res) + t.Log("Got ID:", res.ID) + + node.StopDaemon() +} diff --git a/test/cli/stats_test.go b/test/cli/stats_test.go new file mode 100644 index 000000000..05c1702b4 --- /dev/null +++ b/test/cli/stats_test.go @@ -0,0 +1,24 @@ +package cli + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/ipfs/kubo/test/cli/harness" +) + +func TestStats(t *testing.T) { + t.Parallel() + + t.Run("stats dht", func(t *testing.T) { + t.Parallel() + nodes := harness.NewT(t).NewNodes(2).Init().StartDaemons().Connect() + node1 := nodes[0] + + res := node1.IPFS("stats", "dht") + assert.NoError(t, res.Err) + assert.Equal(t, 0, len(res.Stderr.Lines())) + assert.NotEqual(t, 0, len(res.Stdout.Lines())) + }) +} diff --git a/test/cli/swarm_test.go b/test/cli/swarm_test.go new file mode 100644 index 000000000..ecb668362 --- /dev/null +++ b/test/cli/swarm_test.go @@ -0,0 +1,90 @@ +package cli + +import ( + "encoding/json" + "fmt" + "testing" + + "github.com/ipfs/kubo/test/cli/harness" + + "github.com/stretchr/testify/assert" +) + +// TODO: Migrate the rest of the sharness swarm test. +func TestSwarm(t *testing.T) { + type identifyType struct { + ID string + PublicKey string + Addresses []string + AgentVersion string + Protocols []string + } + type peer struct { + Identify identifyType + } + type expectedOutputType struct { + Peers []peer + } + + t.Parallel() + + t.Run("ipfs swarm peers returns empty peers when a node is not connected to any peers", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init().StartDaemon() + res := node.RunIPFS("swarm", "peers", "--enc=json", "--identify") + var output expectedOutputType + err := json.Unmarshal(res.Stdout.Bytes(), &output) + assert.NoError(t, err) + assert.Equal(t, 0, len(output.Peers)) + }) + t.Run("ipfs swarm peers with flag identify outputs expected identify information about connected peers", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init().StartDaemon() + otherNode := harness.NewT(t).NewNode().Init().StartDaemon() + node.Connect(otherNode) + + res := node.RunIPFS("swarm", "peers", "--enc=json", "--identify") + var output expectedOutputType + err := json.Unmarshal(res.Stdout.Bytes(), &output) + assert.NoError(t, err) + actualID := output.Peers[0].Identify.ID + actualPublicKey := output.Peers[0].Identify.PublicKey + actualAgentVersion := output.Peers[0].Identify.AgentVersion + actualAdresses := output.Peers[0].Identify.Addresses + actualProtocols := output.Peers[0].Identify.Protocols + + expectedID := otherNode.PeerID().String() + expectedAddresses := []string{fmt.Sprintf("%s/p2p/%s", otherNode.SwarmAddrs()[0], actualID)} + + assert.Equal(t, actualID, expectedID) + assert.NotNil(t, actualPublicKey) + assert.NotNil(t, actualAgentVersion) + assert.Len(t, actualAdresses, 1) + assert.Equal(t, expectedAddresses[0], actualAdresses[0]) + assert.Greater(t, len(actualProtocols), 0) + }) + + t.Run("ipfs swarm peers with flag identify outputs Identify field with data that matches calling ipfs id on a peer", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init().StartDaemon() + otherNode := harness.NewT(t).NewNode().Init().StartDaemon() + node.Connect(otherNode) + + otherNodeIDResponse := otherNode.RunIPFS("id", "--enc=json") + var otherNodeIDOutput identifyType + err := json.Unmarshal(otherNodeIDResponse.Stdout.Bytes(), &otherNodeIDOutput) + assert.NoError(t, err) + res := node.RunIPFS("swarm", "peers", "--enc=json", "--identify") + + var output expectedOutputType + err = json.Unmarshal(res.Stdout.Bytes(), &output) + assert.NoError(t, err) + outputIdentify := output.Peers[0].Identify + + assert.Equal(t, outputIdentify.ID, otherNodeIDOutput.ID) + assert.Equal(t, outputIdentify.PublicKey, otherNodeIDOutput.PublicKey) + assert.Equal(t, outputIdentify.AgentVersion, otherNodeIDOutput.AgentVersion) + assert.ElementsMatch(t, outputIdentify.Addresses, otherNodeIDOutput.Addresses) + assert.ElementsMatch(t, outputIdentify.Protocols, otherNodeIDOutput.Protocols) + }) +} diff --git a/test/cli/testutils/asserts.go b/test/cli/testutils/asserts.go new file mode 100644 index 000000000..cf840c20e --- /dev/null +++ b/test/cli/testutils/asserts.go @@ -0,0 +1,15 @@ +package testutils + +import ( + "strings" + "testing" +) + +func AssertStringContainsOneOf(t *testing.T, str string, ss ...string) { + for _, s := range ss { + if strings.Contains(str, s) { + return + } + } + t.Errorf("%q does not contain one of %v", str, ss) +} diff --git a/test/cli/testutils/floats.go b/test/cli/testutils/floats.go new file mode 100644 index 000000000..cecb7d934 --- /dev/null +++ b/test/cli/testutils/floats.go @@ -0,0 +1,9 @@ +package testutils + +func FloatTruncate(value float64, decimalPlaces int) float64 { + pow := 1.0 + for i := 0; i < decimalPlaces; i++ { + pow *= 10.0 + } + return float64(int(value*pow)) / pow +} diff --git a/test/cli/testutils/pinningservice/pinning.go b/test/cli/testutils/pinningservice/pinning.go new file mode 100644 index 000000000..6bfd4ed4e --- /dev/null +++ b/test/cli/testutils/pinningservice/pinning.go @@ -0,0 +1,401 @@ +package pinningservice + +import ( + "encoding/json" + "fmt" + "net/http" + "reflect" + "strconv" + "strings" + "sync" + "time" + + "github.com/google/uuid" + "github.com/julienschmidt/httprouter" +) + +func NewRouter(authToken string, svc *PinningService) http.Handler { + router := httprouter.New() + router.GET("/api/v1/pins", svc.listPins) + router.POST("/api/v1/pins", svc.addPin) + router.GET("/api/v1/pins/:requestID", svc.getPin) + router.POST("/api/v1/pins/:requestID", svc.replacePin) + router.DELETE("/api/v1/pins/:requestID", svc.removePin) + + handler := authHandler(authToken, router) + + return handler +} + +func authHandler(authToken string, delegate http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + authz := r.Header.Get("Authorization") + if !strings.HasPrefix(authz, "Bearer ") { + errResp(w, "invalid authorization token, must start with 'Bearer '", "", http.StatusBadRequest) + return + } + + token := strings.TrimPrefix(authz, "Bearer ") + if token != authToken { + errResp(w, "access denied", "", http.StatusUnauthorized) + return + } + + delegate.ServeHTTP(w, r) + }) +} + +func New() *PinningService { + return &PinningService{ + PinAdded: func(*AddPinRequest, *PinStatus) {}, + } +} + +// PinningService is a basic pinning service that implements the Remote Pinning API, for testing Kubo's integration with remote pinning services. +// Pins are not persisted, they are just kept in-memory, and this provides callbacks for controlling the behavior of the pinning service. +type PinningService struct { + m sync.Mutex + // PinAdded is a callback that is invoked after a new pin is added via the API. + PinAdded func(*AddPinRequest, *PinStatus) + pins []*PinStatus +} + +type Pin struct { + CID string `json:"cid"` + Name string `json:"name"` + Origins []string `json:"origins"` + Meta map[string]interface{} `json:"meta"` +} + +type PinStatus struct { + M sync.Mutex + RequestID string + Status string + Created time.Time + Pin Pin + Delegates []string + Info map[string]interface{} +} + +func (p *PinStatus) MarshalJSON() ([]byte, error) { + type pinStatusJSON struct { + RequestID string `json:"requestid"` + Status string `json:"status"` + Created time.Time `json:"created"` + Pin Pin `json:"pin"` + Delegates []string `json:"delegates"` + Info map[string]interface{} `json:"info"` + } + // lock the pin before marshaling it to protect against data races while marshaling + p.M.Lock() + pinJSON := pinStatusJSON{ + RequestID: p.RequestID, + Status: p.Status, + Created: p.Created, + Pin: p.Pin, + Delegates: p.Delegates, + Info: p.Info, + } + p.M.Unlock() + return json.Marshal(pinJSON) +} + +func (p *PinStatus) Clone() PinStatus { + return PinStatus{ + RequestID: p.RequestID, + Status: p.Status, + Created: p.Created, + Pin: p.Pin, + Delegates: p.Delegates, + Info: p.Info, + } +} + +const ( + matchExact = "exact" + matchIExact = "iexact" + matchPartial = "partial" + matchIPartial = "ipartial" + + statusQueued = "queued" + statusPinning = "pinning" + statusPinned = "pinned" + statusFailed = "failed" + + timeLayout = "2006-01-02T15:04:05.999Z" +) + +func errResp(w http.ResponseWriter, reason, details string, statusCode int) { + type errorObj struct { + Reason string `json:"reason"` + Details string `json:"details"` + } + type errorResp struct { + Error errorObj `json:"error"` + } + resp := errorResp{ + Error: errorObj{ + Reason: reason, + Details: details, + }, + } + writeJSON(w, resp, statusCode) +} + +func writeJSON(w http.ResponseWriter, val any, statusCode int) { + b, err := json.Marshal(val) + if err != nil { + w.Header().Set("Content-Type", "text/plain") + errResp(w, fmt.Sprintf("marshaling response: %s", err), "", http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(statusCode) + _, _ = w.Write(b) +} + +type AddPinRequest struct { + CID string `json:"cid"` + Name string `json:"name"` + Origins []string `json:"origins"` + Meta map[string]interface{} `json:"meta"` +} + +func (p *PinningService) addPin(writer http.ResponseWriter, req *http.Request, params httprouter.Params) { + var addReq AddPinRequest + err := json.NewDecoder(req.Body).Decode(&addReq) + if err != nil { + errResp(writer, fmt.Sprintf("unmarshaling req: %s", err), "", http.StatusBadRequest) + return + } + + pin := &PinStatus{ + RequestID: uuid.NewString(), + Status: statusQueued, + Created: time.Now(), + Pin: Pin(addReq), + } + + p.m.Lock() + p.pins = append(p.pins, pin) + p.m.Unlock() + + writeJSON(writer, &pin, http.StatusAccepted) + p.PinAdded(&addReq, pin) +} + +type ListPinsResponse struct { + Count int `json:"count"` + Results []*PinStatus `json:"results"` +} + +func (p *PinningService) listPins(writer http.ResponseWriter, req *http.Request, params httprouter.Params) { + q := req.URL.Query() + + cidStr := q.Get("cid") + name := q.Get("name") + match := q.Get("match") + status := q.Get("status") + beforeStr := q.Get("before") + afterStr := q.Get("after") + limitStr := q.Get("limit") + metaStr := q.Get("meta") + + if limitStr == "" { + limitStr = "10" + } + limit, err := strconv.Atoi(limitStr) + if err != nil { + errResp(writer, fmt.Sprintf("parsing limit: %s", err), "", http.StatusBadRequest) + return + } + + var cids []string + if cidStr != "" { + cids = strings.Split(cidStr, ",") + } + + var statuses []string + if status != "" { + statuses = strings.Split(status, ",") + } + + p.m.Lock() + defer p.m.Unlock() + var pins []*PinStatus + for _, pinStatus := range p.pins { + // clone it so we can immediately release the lock + pinStatus.M.Lock() + clonedPS := pinStatus.Clone() + pinStatus.M.Unlock() + + // cid + var matchesCID bool + if len(cids) == 0 { + matchesCID = true + } else { + for _, cid := range cids { + if cid == clonedPS.Pin.CID { + matchesCID = true + } + } + } + if !matchesCID { + continue + } + + // name + if match == "" { + match = matchExact + } + if name != "" { + switch match { + case matchExact: + if name != clonedPS.Pin.Name { + continue + } + case matchIExact: + if !strings.EqualFold(name, clonedPS.Pin.Name) { + continue + } + case matchPartial: + if !strings.Contains(clonedPS.Pin.Name, name) { + continue + } + case matchIPartial: + if !strings.Contains(strings.ToLower(clonedPS.Pin.Name), strings.ToLower(name)) { + continue + } + default: + errResp(writer, fmt.Sprintf("unknown match %q", match), "", http.StatusBadRequest) + return + } + } + + // status + var matchesStatus bool + if len(statuses) == 0 { + statuses = []string{statusPinned} + } + for _, status := range statuses { + if status == clonedPS.Status { + matchesStatus = true + } + } + if !matchesStatus { + continue + } + + // before + if beforeStr != "" { + before, err := time.Parse(timeLayout, beforeStr) + if err != nil { + errResp(writer, fmt.Sprintf("parsing before: %s", err), "", http.StatusBadRequest) + return + } + if !clonedPS.Created.Before(before) { + continue + } + } + + // after + if afterStr != "" { + after, err := time.Parse(timeLayout, afterStr) + if err != nil { + errResp(writer, fmt.Sprintf("parsing before: %s", err), "", http.StatusBadRequest) + return + } + if !clonedPS.Created.After(after) { + continue + } + } + + // meta + if metaStr != "" { + meta := map[string]interface{}{} + err := json.Unmarshal([]byte(metaStr), &meta) + if err != nil { + errResp(writer, fmt.Sprintf("parsing meta: %s", err), "", http.StatusBadRequest) + return + } + var matchesMeta bool + for k, v := range meta { + pinV, contains := clonedPS.Pin.Meta[k] + if !contains || !reflect.DeepEqual(pinV, v) { + matchesMeta = false + break + } + } + if !matchesMeta { + continue + } + } + + // add the original pin status, not the cloned one + pins = append(pins, pinStatus) + + if len(pins) == limit { + break + } + } + + out := ListPinsResponse{ + Count: len(pins), + Results: pins, + } + writeJSON(writer, out, http.StatusOK) +} + +func (p *PinningService) getPin(writer http.ResponseWriter, req *http.Request, params httprouter.Params) { + requestID := params.ByName("requestID") + p.m.Lock() + defer p.m.Unlock() + for _, pin := range p.pins { + if pin.RequestID == requestID { + writeJSON(writer, pin, http.StatusOK) + return + } + } + errResp(writer, "", "", http.StatusNotFound) +} + +func (p *PinningService) replacePin(writer http.ResponseWriter, req *http.Request, params httprouter.Params) { + requestID := params.ByName("requestID") + + var replaceReq Pin + err := json.NewDecoder(req.Body).Decode(&replaceReq) + if err != nil { + errResp(writer, fmt.Sprintf("decoding request: %s", err), "", http.StatusBadRequest) + return + } + + p.m.Lock() + defer p.m.Unlock() + for _, pin := range p.pins { + if pin.RequestID == requestID { + pin.M.Lock() + pin.Pin = replaceReq + pin.M.Unlock() + writer.WriteHeader(http.StatusAccepted) + return + } + } + errResp(writer, "", "", http.StatusNotFound) +} + +func (p *PinningService) removePin(writer http.ResponseWriter, req *http.Request, params httprouter.Params) { + requestID := params.ByName("requestID") + + p.m.Lock() + defer p.m.Unlock() + + for i, pin := range p.pins { + if pin.RequestID == requestID { + p.pins = append(p.pins[0:i], p.pins[i+1:]...) + writer.WriteHeader(http.StatusAccepted) + return + } + } + + errResp(writer, "", "", http.StatusNotFound) +} diff --git a/test/cli/testutils/random.go b/test/cli/testutils/random.go index 00bb9de49..6fa6528c3 100644 --- a/test/cli/testutils/random.go +++ b/test/cli/testutils/random.go @@ -10,3 +10,7 @@ func RandomBytes(n int) []byte { } return bytes } + +func RandomStr(n int) string { + return string(RandomBytes(n)) +} diff --git a/test/cli/testutils/random_files.go b/test/cli/testutils/random_files.go new file mode 100644 index 000000000..c7dca10d6 --- /dev/null +++ b/test/cli/testutils/random_files.go @@ -0,0 +1,118 @@ +package testutils + +import ( + "fmt" + "io" + "math/rand" + "os" + "path" + "time" +) + +var ( + AlphabetEasy = []rune("abcdefghijklmnopqrstuvwxyz01234567890-_") + AlphabetHard = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890!@#$%^&*()-_+= ;.,<>'\"[]{}() ") +) + +type RandFiles struct { + Rand *rand.Rand + FileSize int // the size per file. + FilenameSize int + Alphabet []rune // for filenames + + FanoutDepth int // how deep the hierarchy goes + FanoutFiles int // how many files per dir + FanoutDirs int // how many dirs per dir + + RandomSize bool // randomize file sizes + RandomFanout bool // randomize fanout numbers +} + +func NewRandFiles() *RandFiles { + return &RandFiles{ + Rand: rand.New(rand.NewSource(time.Now().UnixNano())), + FileSize: 4096, + FilenameSize: 16, + Alphabet: AlphabetEasy, + FanoutDepth: 2, + FanoutDirs: 5, + FanoutFiles: 10, + RandomSize: true, + } +} + +func (r *RandFiles) WriteRandomFiles(root string, depth int) error { + numfiles := r.FanoutFiles + if r.RandomFanout { + numfiles = rand.Intn(r.FanoutFiles) + 1 + } + + for i := 0; i < numfiles; i++ { + if err := r.WriteRandomFile(root); err != nil { + return err + } + } + + if depth+1 <= r.FanoutDepth { + numdirs := r.FanoutDirs + if r.RandomFanout { + numdirs = r.Rand.Intn(numdirs) + 1 + } + + for i := 0; i < numdirs; i++ { + if err := r.WriteRandomDir(root, depth+1); err != nil { + return err + } + } + } + + return nil +} + +func (r *RandFiles) RandomFilename(length int) string { + b := make([]rune, length) + for i := range b { + b[i] = r.Alphabet[r.Rand.Intn(len(r.Alphabet))] + } + return string(b) +} + +func (r *RandFiles) WriteRandomFile(root string) error { + filesize := int64(r.FileSize) + if r.RandomSize { + filesize = r.Rand.Int63n(filesize) + 1 + } + + n := rand.Intn(r.FilenameSize-4) + 4 + name := r.RandomFilename(n) + filepath := path.Join(root, name) + f, err := os.Create(filepath) + if err != nil { + return fmt.Errorf("creating random file: %w", err) + } + + if _, err := io.CopyN(f, r.Rand, filesize); err != nil { + return fmt.Errorf("copying random file: %w", err) + } + + return f.Close() +} + +func (r *RandFiles) WriteRandomDir(root string, depth int) error { + if depth > r.FanoutDepth { + return nil + } + + n := rand.Intn(r.FilenameSize-4) + 4 + name := r.RandomFilename(n) + root = path.Join(root, name) + if err := os.MkdirAll(root, 0o755); err != nil { + return fmt.Errorf("creating random dir: %w", err) + } + + err := r.WriteRandomFiles(root, depth) + if err != nil { + return fmt.Errorf("writing random files in random dir: %w", err) + } + return nil +} diff --git a/test/cli/testutils/requires.go b/test/cli/testutils/requires.go index d4b88cd6d..1462b7fee 100644 --- a/test/cli/testutils/requires.go +++ b/test/cli/testutils/requires.go @@ -7,13 +7,13 @@ import ( ) func RequiresDocker(t *testing.T) { - if os.Getenv("TEST_NO_DOCKER") == "1" { + if os.Getenv("TEST_DOCKER") != "1" { t.SkipNow() } } func RequiresFUSE(t *testing.T) { - if os.Getenv("TEST_NO_FUSE") == "1" { + if os.Getenv("TEST_FUSE") != "1" { t.SkipNow() } } @@ -25,7 +25,7 @@ func RequiresExpensive(t *testing.T) { } func RequiresPlugins(t *testing.T) { - if os.Getenv("TEST_NO_PLUGIN") == "1" { + if os.Getenv("TEST_PLUGIN") != "1" { t.SkipNow() } } diff --git a/test/cli/testutils/strings.go b/test/cli/testutils/strings.go index 529948d3f..110051e67 100644 --- a/test/cli/testutils/strings.go +++ b/test/cli/testutils/strings.go @@ -3,7 +3,14 @@ package testutils import ( "bufio" "fmt" + "net" + "net/netip" + "net/url" "strings" + "sync" + + "github.com/multiformats/go-multiaddr" + manet "github.com/multiformats/go-multiaddr/net" ) // StrCat takes a bunch of strings or string slices @@ -51,3 +58,34 @@ func SplitLines(s string) []string { } return lines } + +// URLStrToMultiaddr converts a URL string like http://localhost:80 to a multiaddr. +func URLStrToMultiaddr(u string) multiaddr.Multiaddr { + parsedURL, err := url.Parse(u) + if err != nil { + panic(err) + } + addrPort, err := netip.ParseAddrPort(parsedURL.Host) + if err != nil { + panic(err) + } + tcpAddr := net.TCPAddrFromAddrPort(addrPort) + ma, err := manet.FromNetAddr(tcpAddr) + if err != nil { + panic(err) + } + return ma +} + +// ForEachPar invokes f in a new goroutine for each element of s and waits for all to complete. +func ForEachPar[T any](s []T, f func(T)) { + wg := sync.WaitGroup{} + wg.Add(len(s)) + for _, x := range s { + go func(x T) { + defer wg.Done() + f(x) + }(x) + } + wg.Wait() +} diff --git a/test/cli/tracing_test.go b/test/cli/tracing_test.go new file mode 100644 index 000000000..6f19759be --- /dev/null +++ b/test/cli/tracing_test.go @@ -0,0 +1,89 @@ +package cli + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/ipfs/kubo/test/cli/harness" + "github.com/ipfs/kubo/test/cli/testutils" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var otelCollectorConfigYAML = ` +receivers: + otlp: + protocols: + grpc: + +processors: + batch: + +exporters: + file: + path: /traces/traces.json + +service: + pipelines: + traces: + receivers: [otlp] + processors: [batch] + exporters: [file] +` + +func TestTracing(t *testing.T) { + testutils.RequiresDocker(t) + t.Parallel() + node := harness.NewT(t).NewNode().Init() + + node.WriteBytes("collector-config.yaml", []byte(otelCollectorConfigYAML)) + + // touch traces.json and give it 777 perms in case Docker runs as a different user + node.WriteBytes("traces.json", nil) + err := os.Chmod(filepath.Join(node.Dir, "traces.json"), 0o777) + require.NoError(t, err) + + dockerBin, err := exec.LookPath("docker") + require.NoError(t, err) + node.Runner.MustRun(harness.RunRequest{ + Path: dockerBin, + Args: []string{ + "run", + "--rm", + "--detach", + "--volume", fmt.Sprintf("%s:/config.yaml", filepath.Join(node.Dir, "collector-config.yaml")), + "--volume", fmt.Sprintf("%s:/traces", node.Dir), + "--net", "host", + "--name", "ipfs-test-otel-collector", + "otel/opentelemetry-collector-contrib:0.52.0", + "--config", "/config.yaml", + }, + }) + + t.Cleanup(func() { + node.Runner.MustRun(harness.RunRequest{ + Path: dockerBin, + Args: []string{"stop", "ipfs-test-otel-collector"}, + }) + }) + + node.Runner.Env["OTEL_TRACES_EXPORTER"] = "otlp" + node.Runner.Env["OTEL_EXPORTER_OTLP_PROTOCOL"] = "grpc" + node.Runner.Env["OTEL_EXPORTER_OTLP_ENDPOINT"] = "http://localhost:4317" + node.StartDaemon() + + assert.Eventually(t, + func() bool { + b, err := os.ReadFile(filepath.Join(node.Dir, "traces.json")) + require.NoError(t, err) + return strings.Contains(string(b), "go-ipfs") + }, + 5*time.Minute, + 10*time.Millisecond, + ) +} diff --git a/test/cli/transports_test.go b/test/cli/transports_test.go new file mode 100644 index 000000000..cbef5c57d --- /dev/null +++ b/test/cli/transports_test.go @@ -0,0 +1,159 @@ +package cli + +import ( + "fmt" + "os" + "path/filepath" + "testing" + + "github.com/ipfs/kubo/config" + "github.com/ipfs/kubo/test/cli/harness" + "github.com/ipfs/kubo/test/cli/testutils" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestTransports(t *testing.T) { + disableRouting := func(nodes harness.Nodes) { + nodes.ForEachPar(func(n *harness.Node) { + n.UpdateConfig(func(cfg *config.Config) { + cfg.Routing.Type = config.NewOptionalString("none") + cfg.Bootstrap = nil + }) + }) + } + checkSingleFile := func(nodes harness.Nodes) { + s := testutils.RandomStr(100) + hash := nodes[0].IPFSAddStr(s) + nodes.ForEachPar(func(n *harness.Node) { + val := n.IPFS("cat", hash).Stdout.String() + assert.Equal(t, s, val) + }) + } + checkRandomDir := func(nodes harness.Nodes) { + randDir := filepath.Join(nodes[0].Dir, "foobar") + require.NoError(t, os.Mkdir(randDir, 0o777)) + rf := testutils.NewRandFiles() + rf.FanoutDirs = 3 + rf.FanoutFiles = 6 + require.NoError(t, rf.WriteRandomFiles(randDir, 4)) + + hash := nodes[1].IPFS("add", "-r", "-Q", randDir).Stdout.Trimmed() + nodes.ForEachPar(func(n *harness.Node) { + res := n.RunIPFS("refs", "-r", hash) + assert.Equal(t, 0, res.ExitCode()) + }) + } + + runTests := func(nodes harness.Nodes) { + checkSingleFile(nodes) + checkRandomDir(nodes) + } + + tcpNodes := func(t *testing.T) harness.Nodes { + nodes := harness.NewT(t).NewNodes(2).Init() + nodes.ForEachPar(func(n *harness.Node) { + n.UpdateConfig(func(cfg *config.Config) { + cfg.Addresses.Swarm = []string{"/ip4/127.0.0.1/tcp/0"} + cfg.Swarm.Transports.Network.QUIC = config.False + cfg.Swarm.Transports.Network.Relay = config.False + cfg.Swarm.Transports.Network.WebTransport = config.False + cfg.Swarm.Transports.Network.WebRTCDirect = config.False + cfg.Swarm.Transports.Network.Websocket = config.False + }) + }) + disableRouting(nodes) + return nodes + } + + t.Run("tcp", func(t *testing.T) { + t.Parallel() + nodes := tcpNodes(t).StartDaemons().Connect() + runTests(nodes) + }) + + t.Run("tcp with NOISE", func(t *testing.T) { + t.Parallel() + nodes := tcpNodes(t) + nodes.ForEachPar(func(n *harness.Node) { + n.UpdateConfig(func(cfg *config.Config) { + cfg.Swarm.Transports.Security.TLS = config.Disabled + }) + }) + nodes.StartDaemons().Connect() + runTests(nodes) + }) + + t.Run("QUIC", func(t *testing.T) { + t.Parallel() + nodes := harness.NewT(t).NewNodes(5).Init() + nodes.ForEachPar(func(n *harness.Node) { + n.UpdateConfig(func(cfg *config.Config) { + cfg.Addresses.Swarm = []string{"/ip4/127.0.0.1/udp/0/quic-v1"} + cfg.Swarm.Transports.Network.TCP = config.False + cfg.Swarm.Transports.Network.QUIC = config.True + cfg.Swarm.Transports.Network.WebTransport = config.False + cfg.Swarm.Transports.Network.WebRTCDirect = config.False + }) + }) + disableRouting(nodes) + nodes.StartDaemons().Connect() + runTests(nodes) + }) + + t.Run("QUIC+Webtransport", func(t *testing.T) { + t.Parallel() + nodes := harness.NewT(t).NewNodes(5).Init() + nodes.ForEachPar(func(n *harness.Node) { + n.UpdateConfig(func(cfg *config.Config) { + cfg.Addresses.Swarm = []string{"/ip4/127.0.0.1/udp/0/quic-v1/webtransport"} + cfg.Swarm.Transports.Network.TCP = config.False + cfg.Swarm.Transports.Network.QUIC = config.True + cfg.Swarm.Transports.Network.WebTransport = config.True + cfg.Swarm.Transports.Network.WebRTCDirect = config.False + }) + }) + disableRouting(nodes) + nodes.StartDaemons().Connect() + runTests(nodes) + }) + + t.Run("QUIC connects with non-dialable transports", func(t *testing.T) { + // This test targets specific Kubo internals which may change later. This checks + // if we can announce an address we do not listen on, and then are able to connect + // via a different address that is available. + t.Parallel() + nodes := harness.NewT(t).NewNodes(5).Init() + nodes.ForEachPar(func(n *harness.Node) { + n.UpdateConfig(func(cfg *config.Config) { + // We need a specific port to announce so we first generate a random port. + // We can't use 0 here to automatically assign an available port because + // that would only work with Swarm, but not for the announcing. + port := harness.NewRandPort() + quicAddr := fmt.Sprintf("/ip4/127.0.0.1/udp/%d/quic-v1", port) + cfg.Addresses.Swarm = []string{quicAddr} + cfg.Addresses.Announce = []string{quicAddr, quicAddr + "/webtransport"} + }) + }) + disableRouting(nodes) + nodes.StartDaemons().Connect() + runTests(nodes) + }) + + t.Run("WebRTC Direct", func(t *testing.T) { + t.Parallel() + nodes := harness.NewT(t).NewNodes(5).Init() + nodes.ForEachPar(func(n *harness.Node) { + n.UpdateConfig(func(cfg *config.Config) { + cfg.Addresses.Swarm = []string{"/ip4/127.0.0.1/udp/0/webrtc-direct"} + cfg.Swarm.Transports.Network.TCP = config.False + cfg.Swarm.Transports.Network.QUIC = config.False + cfg.Swarm.Transports.Network.WebTransport = config.False + cfg.Swarm.Transports.Network.WebRTCDirect = config.True + }) + }) + disableRouting(nodes) + nodes.StartDaemons().Connect() + runTests(nodes) + }) +} diff --git a/test/dependencies/go.mod b/test/dependencies/go.mod index 1a0eea226..46b022a2b 100644 --- a/test/dependencies/go.mod +++ b/test/dependencies/go.mod @@ -1,299 +1,314 @@ module github.com/ipfs/kubo/test/dependencies -go 1.18 +go 1.23 + +replace github.com/ipfs/kubo => ../../ require ( github.com/Kubuxu/gocovmerge v0.0.0-20161216165753-7ecaa51963cd - github.com/golangci/golangci-lint v1.49.0 - github.com/ipfs/go-blockservice v0.3.0 - github.com/ipfs/go-cid v0.3.2 + github.com/golangci/golangci-lint v1.60.2 github.com/ipfs/go-cidutil v0.1.0 - github.com/ipfs/go-datastore v0.5.1 - github.com/ipfs/go-graphsync v0.13.2 - github.com/ipfs/go-ipfs-blockstore v1.2.0 - github.com/ipfs/go-ipfs-exchange-offline v0.2.0 - github.com/ipfs/go-log v1.0.5 - github.com/ipfs/go-merkledag v0.8.1 - github.com/ipfs/go-unixfs v0.3.1 + github.com/ipfs/go-log/v2 v2.5.1 github.com/ipfs/hang-fds v0.1.0 - github.com/ipfs/iptb v1.4.0 - github.com/ipfs/iptb-plugins v0.3.0 - github.com/ipld/go-ipld-prime v0.19.0 + github.com/ipfs/iptb v1.4.1 + github.com/ipfs/iptb-plugins v0.5.1 github.com/jbenet/go-random v0.0.0-20190219211222-123a90aedc0c github.com/jbenet/go-random-files v0.0.0-20190219210431-31b3f20ebded - github.com/libp2p/go-libp2p v0.22.0 - github.com/multiformats/go-multiaddr v0.6.0 - github.com/multiformats/go-multihash v0.2.1 - gotest.tools/gotestsum v0.4.2 + github.com/multiformats/go-multiaddr v0.13.0 + github.com/multiformats/go-multihash v0.2.3 + gotest.tools/gotestsum v1.12.0 ) require ( - 4d63.com/gochecknoglobals v0.1.0 // indirect - github.com/Antonboom/errname v0.1.7 // indirect - github.com/Antonboom/nilnil v0.1.1 // indirect - github.com/BurntSushi/toml v1.2.0 // indirect + 4d63.com/gocheckcompilerdirectives v1.2.1 // indirect + 4d63.com/gochecknoglobals v0.2.1 // indirect + github.com/4meepo/tagalign v1.3.4 // indirect + github.com/Abirdcfly/dupword v0.0.14 // indirect + github.com/Antonboom/errname v0.1.13 // indirect + github.com/Antonboom/nilnil v0.1.9 // indirect + github.com/Antonboom/testifylint v1.4.3 // indirect + github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect + github.com/Crocmagnon/fatcontext v0.4.0 // indirect github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 // indirect - github.com/GaijinEntertainment/go-exhaustruct/v2 v2.3.0 // indirect - github.com/Masterminds/semver v1.5.0 // indirect - github.com/OpenPeeDeeP/depguard v1.1.0 // indirect - github.com/Stebalien/go-bitfield v0.0.1 // indirect - github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a // indirect + github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0 // indirect + github.com/Masterminds/semver/v3 v3.2.1 // indirect + github.com/OpenPeeDeeP/depguard/v2 v2.2.0 // indirect + github.com/alecthomas/go-check-sumtype v0.1.4 // indirect + github.com/alexkohler/nakedret/v2 v2.0.4 // indirect github.com/alexkohler/prealloc v1.0.0 // indirect github.com/alingse/asasalint v0.0.11 // indirect - github.com/ashanbrown/forbidigo v1.3.0 // indirect + github.com/ashanbrown/forbidigo v1.6.0 // indirect github.com/ashanbrown/makezero v1.1.1 // indirect - github.com/benbjohnson/clock v1.3.0 // indirect + github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bkielbasa/cyclop v1.2.0 // indirect + github.com/bitfield/gotestdox v0.2.2 // indirect + github.com/bkielbasa/cyclop v1.2.1 // indirect github.com/blizzy78/varnamelen v0.8.0 // indirect - github.com/bombsimon/wsl/v3 v3.3.0 // indirect - github.com/breml/bidichk v0.2.3 // indirect - github.com/breml/errchkjson v0.3.0 // indirect - github.com/butuzov/ireturn v0.1.1 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect - github.com/charithe/durationcheck v0.0.9 // indirect - github.com/chavacava/garif v0.0.0-20220630083739-93517212f375 // indirect - github.com/cheekybits/genny v1.0.0 // indirect - github.com/containerd/cgroups v1.0.4 // indirect - github.com/coreos/go-systemd/v22 v22.3.2 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect - github.com/curioswitch/go-reassign v0.1.2 // indirect - github.com/daixiang0/gci v0.6.3 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/bombsimon/wsl/v4 v4.4.1 // indirect + github.com/breml/bidichk v0.2.7 // indirect + github.com/breml/errchkjson v0.3.6 // indirect + github.com/butuzov/ireturn v0.3.0 // indirect + github.com/butuzov/mirror v1.2.0 // indirect + github.com/caddyserver/certmagic v0.21.4 // indirect + github.com/caddyserver/zerossl v0.1.3 // indirect + github.com/catenacyber/perfsprint v0.7.1 // indirect + github.com/ccojocar/zxcvbn-go v1.0.2 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/charithe/durationcheck v0.0.10 // indirect + github.com/chavacava/garif v0.1.0 // indirect + github.com/ckaznocha/intrange v0.1.2 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect + github.com/curioswitch/go-reassign v0.2.0 // indirect + github.com/daixiang0/gci v0.13.4 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect - github.com/denis-tingaikin/go-header v0.4.3 // indirect - github.com/docker/go-units v0.4.0 // indirect - github.com/dustin/go-humanize v1.0.0 // indirect - github.com/elastic/gosigar v0.14.2 // indirect - github.com/esimonov/ifshort v1.0.4 // indirect - github.com/ettle/strcase v0.1.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect + github.com/denis-tingaikin/go-header v0.5.0 // indirect + github.com/dnephin/pflag v1.0.7 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/ettle/strcase v0.2.0 // indirect github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 // indirect - github.com/fatih/color v1.13.0 // indirect + github.com/fatih/color v1.17.0 // indirect github.com/fatih/structtag v1.2.0 // indirect - github.com/firefart/nonamedreturns v1.0.4 // indirect - github.com/flynn/noise v1.0.0 // indirect + github.com/firefart/nonamedreturns v1.0.5 // indirect + github.com/flynn/noise v1.1.0 // indirect github.com/francoispqt/gojay v1.2.13 // indirect - github.com/fsnotify/fsnotify v1.5.4 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fzipp/gocyclo v0.6.0 // indirect - github.com/go-critic/go-critic v0.6.4 // indirect - github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect - github.com/go-toolsmith/astcast v1.0.0 // indirect - github.com/go-toolsmith/astcopy v1.0.1 // indirect - github.com/go-toolsmith/astequal v1.0.2 // indirect - github.com/go-toolsmith/astfmt v1.0.0 // indirect - github.com/go-toolsmith/astp v1.0.0 // indirect - github.com/go-toolsmith/strparse v1.0.0 // indirect - github.com/go-toolsmith/typep v1.0.2 // indirect - github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b // indirect + github.com/ghostiam/protogetter v0.3.6 // indirect + github.com/go-critic/go-critic v0.11.4 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect + github.com/go-toolsmith/astcast v1.1.0 // indirect + github.com/go-toolsmith/astcopy v1.1.0 // indirect + github.com/go-toolsmith/astequal v1.2.0 // indirect + github.com/go-toolsmith/astfmt v1.1.0 // indirect + github.com/go-toolsmith/astp v1.1.0 // indirect + github.com/go-toolsmith/strparse v1.1.0 // indirect + github.com/go-toolsmith/typep v1.1.0 // indirect + github.com/go-viper/mapstructure/v2 v2.0.0 // indirect + github.com/go-xmlfmt/xmlfmt v1.1.2 // indirect github.com/gobwas/glob v0.2.3 // indirect - github.com/godbus/dbus/v5 v5.1.0 // indirect - github.com/gofrs/flock v0.8.1 // indirect + github.com/gofrs/flock v0.12.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 // indirect github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a // indirect - github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe // indirect - github.com/golangci/gofmt v0.0.0-20220901101216-f2edd75033f2 // indirect - github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 // indirect - github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca // indirect - github.com/golangci/misspell v0.3.5 // indirect - github.com/golangci/revgrep v0.0.0-20220804021717-745bb2f7c2e6 // indirect - github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 // indirect - github.com/google/go-cmp v0.5.9 // indirect + github.com/golangci/gofmt v0.0.0-20240816233607-d8596aa466a9 // indirect + github.com/golangci/misspell v0.6.0 // indirect + github.com/golangci/modinfo v0.3.4 // indirect + github.com/golangci/plugin-module-register v0.1.1 // indirect + github.com/golangci/revgrep v0.5.3 // indirect + github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/gopacket v1.1.19 // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/gordonklaus/ineffassign v0.0.0-20210914165742-4cc7213b9bc8 // indirect - github.com/gorilla/websocket v1.5.0 // indirect + github.com/google/pprof v0.0.0-20241017200806-017d972448fc // indirect + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gordonklaus/ineffassign v0.1.0 // indirect github.com/gostaticanalysis/analysisutil v0.7.1 // indirect github.com/gostaticanalysis/comment v1.4.2 // indirect github.com/gostaticanalysis/forcetypeassert v0.1.0 // indirect github.com/gostaticanalysis/nilerr v0.1.1 // indirect - github.com/gxed/go-shellwords v1.0.3 // indirect - github.com/hannahhoward/go-pubsub v0.0.0-20200423002714-8d62886cc36e // indirect - github.com/hashicorp/errwrap v1.0.0 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-version v1.6.0 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/hashicorp/go-version v1.7.0 // indirect + github.com/hashicorp/golang-lru v1.0.2 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/hexops/gotextdiff v1.0.3 // indirect - github.com/huin/goupnp v1.0.3 // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/huin/goupnp v1.3.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/ipfs/bbloom v0.0.4 // indirect - github.com/ipfs/go-bitfield v1.0.0 // indirect - github.com/ipfs/go-block-format v0.0.3 // indirect - github.com/ipfs/go-ipfs-config v0.19.0 // indirect - github.com/ipfs/go-ipfs-ds-help v1.1.0 // indirect - github.com/ipfs/go-ipfs-exchange-interface v0.1.0 // indirect - github.com/ipfs/go-ipfs-pq v0.0.2 // indirect - github.com/ipfs/go-ipfs-util v0.0.2 // indirect - github.com/ipfs/go-ipld-cbor v0.0.5 // indirect - github.com/ipfs/go-ipld-format v0.4.0 // indirect - github.com/ipfs/go-ipld-legacy v0.1.0 // indirect - github.com/ipfs/go-log/v2 v2.5.1 // indirect + github.com/ipfs/boxo v0.24.3 // indirect + github.com/ipfs/go-block-format v0.2.0 // indirect + github.com/ipfs/go-cid v0.4.1 // indirect + github.com/ipfs/go-datastore v0.6.0 // 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-legacy v0.2.1 // indirect github.com/ipfs/go-metrics-interface v0.0.1 // indirect - github.com/ipfs/go-peertaskqueue v0.7.1 // indirect - github.com/ipfs/go-unixfsnode v1.4.0 // indirect - github.com/ipfs/go-verifcid v0.0.1 // indirect - github.com/ipfs/interface-go-ipfs-core v0.7.0 // indirect - github.com/ipld/go-codec-dagpb v1.4.0 // indirect + github.com/ipfs/kubo v0.31.0 // indirect + github.com/ipld/go-codec-dagpb v1.6.0 // indirect + github.com/ipld/go-ipld-prime v0.21.0 // indirect + github.com/ipshipyard/p2p-forge v0.0.2 // 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/jgautheron/goconst v1.5.1 // indirect + github.com/jgautheron/goconst v1.7.1 // indirect github.com/jingyugao/rowserrcheck v1.1.1 // indirect github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af // indirect - github.com/jonboulle/clockwork v0.2.0 // indirect + github.com/jjti/go-spancheck v0.6.2 // indirect github.com/julz/importas v0.1.0 // indirect - github.com/kisielk/errcheck v1.6.2 // indirect - github.com/kisielk/gotool v1.0.0 // indirect - github.com/klauspost/compress v1.15.1 // indirect - github.com/klauspost/cpuid/v2 v2.1.0 // indirect - github.com/koron/go-ssdp v0.0.3 // indirect + github.com/karamaru-alpha/copyloopvar v1.1.0 // indirect + github.com/kisielk/errcheck v1.7.0 // indirect + github.com/kkHAIKE/contextcheck v1.1.5 // indirect + github.com/klauspost/compress v1.17.11 // indirect + github.com/klauspost/cpuid/v2 v2.2.8 // indirect + github.com/koron/go-ssdp v0.0.4 // indirect github.com/kulti/thelper v0.6.3 // indirect - github.com/kunwardeep/paralleltest v1.0.6 // indirect - github.com/kyoh86/exportloopref v0.1.8 // indirect - github.com/ldez/gomoddirectives v0.2.3 // indirect - github.com/ldez/tagliatelle v0.3.1 // indirect - github.com/leonklingele/grouper v1.1.0 // indirect + github.com/kunwardeep/paralleltest v1.0.10 // indirect + github.com/kyoh86/exportloopref v0.1.11 // indirect + github.com/lasiar/canonicalheader v1.1.1 // indirect + github.com/ldez/gomoddirectives v0.2.4 // indirect + github.com/ldez/tagliatelle v0.5.0 // indirect + github.com/leonklingele/grouper v1.1.2 // indirect + github.com/libdns/libdns v0.2.2 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/libp2p/go-cidranger v1.1.0 // indirect - github.com/libp2p/go-flow-metrics v0.1.0 // indirect - github.com/libp2p/go-libp2p-asn-util v0.2.0 // indirect - github.com/libp2p/go-libp2p-core v0.20.1 // indirect - github.com/libp2p/go-libp2p-record v0.1.3 // indirect - github.com/libp2p/go-msgio v0.2.0 // indirect - github.com/libp2p/go-nat v0.1.0 // indirect - github.com/libp2p/go-netroute v0.2.0 // indirect - github.com/libp2p/go-openssl v0.1.0 // indirect - github.com/libp2p/go-reuseport v0.2.0 // indirect - github.com/libp2p/go-yamux/v3 v3.1.2 // indirect - github.com/lucas-clemente/quic-go v0.28.1 // indirect + github.com/libp2p/go-flow-metrics v0.2.0 // indirect + github.com/libp2p/go-libp2p v0.37.0 // indirect + github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect + github.com/libp2p/go-libp2p-kad-dht v0.27.0 // indirect + github.com/libp2p/go-libp2p-kbucket v0.6.4 // indirect + github.com/libp2p/go-libp2p-record v0.2.0 // indirect + github.com/libp2p/go-libp2p-routing-helpers v0.7.4 // 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/lufeee/execinquery v1.2.1 // indirect - github.com/magiconair/properties v1.8.6 // indirect - github.com/maratori/testpackage v1.1.0 // indirect - github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect - github.com/marten-seemann/qtls-go1-17 v0.1.2 // indirect - github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect - github.com/marten-seemann/qtls-go1-19 v0.1.0 // indirect - github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect - github.com/matoous/godox v0.0.0-20210227103229-6504466cf951 // indirect + github.com/macabu/inamedparam v0.1.3 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/maratori/testableexamples v1.0.0 // indirect + github.com/maratori/testpackage v1.1.1 // indirect + github.com/matoous/godox v0.0.0-20240105082147-c5b5e0e7c0c0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.16 // indirect - github.com/mattn/go-pointer v0.0.1 // indirect - github.com/mattn/go-runewidth v0.0.9 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect - github.com/mbilski/exhaustivestruct v1.2.0 // indirect - github.com/mgechev/revive v1.2.3 // indirect - github.com/miekg/dns v1.1.50 // 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.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/mattn/go-shellwords v1.0.12 // indirect + github.com/mgechev/revive v1.3.9 // indirect + github.com/mholt/acmez/v2 v2.0.3 // indirect + github.com/miekg/dns v1.1.62 // indirect + github.com/minio/sha256-simd v1.0.1 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/moricho/tparallel v0.2.1 // indirect + github.com/moricho/tparallel v0.3.2 // indirect github.com/mr-tron/base58 v1.2.0 // indirect - github.com/multiformats/go-base32 v0.0.4 // indirect - github.com/multiformats/go-base36 v0.1.0 // indirect - github.com/multiformats/go-multiaddr-dns v0.3.1 // indirect + github.com/multiformats/go-base32 v0.1.0 // indirect + github.com/multiformats/go-base36 v0.2.0 // indirect + github.com/multiformats/go-multiaddr-dns v0.4.0 // indirect github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect - github.com/multiformats/go-multibase v0.1.1 // indirect - github.com/multiformats/go-multicodec v0.6.0 // indirect - github.com/multiformats/go-multistream v0.3.3 // indirect - github.com/multiformats/go-varint v0.0.6 // indirect + github.com/multiformats/go-multibase 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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/nakabonne/nestif v0.3.1 // indirect - github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 // indirect - github.com/nishanths/exhaustive v0.8.1 // indirect + github.com/nishanths/exhaustive v0.12.0 // indirect github.com/nishanths/predeclared v0.2.2 // indirect - github.com/nxadm/tail v1.4.8 // indirect + github.com/nunnatsa/ginkgolinter v0.16.2 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect - github.com/onsi/ginkgo v1.16.5 // indirect - github.com/opencontainers/runtime-spec v1.0.2 // indirect - github.com/opentracing/opentracing-go v1.2.0 // indirect + github.com/onsi/ginkgo/v2 v2.20.2 // indirect github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect - github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.0.2 // indirect - github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e // indirect - github.com/polyfloyd/go-errorlint v1.0.2 // indirect - github.com/prometheus/client_golang v1.12.1 // indirect - github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.37.0 // indirect - github.com/prometheus/procfs v0.8.0 // indirect - github.com/quasilyte/go-ruleguard v0.3.17 // indirect - github.com/quasilyte/gogrep v0.0.0-20220120141003-628d8b3623b5 // indirect - github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pion/datachannel v1.5.9 // indirect + github.com/pion/dtls/v2 v2.2.12 // indirect + github.com/pion/ice/v2 v2.3.36 // indirect + github.com/pion/interceptor v0.1.37 // 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.9 // indirect + github.com/pion/sctp v1.8.33 // indirect + github.com/pion/sdp/v3 v3.0.9 // indirect + github.com/pion/srtp/v2 v2.0.20 // indirect + github.com/pion/stun v0.6.1 // indirect + github.com/pion/transport/v2 v2.2.10 // indirect + github.com/pion/turn/v2 v2.1.6 // indirect + github.com/pion/webrtc/v3 v3.3.4 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/polydawn/refmt v0.89.0 // indirect + github.com/polyfloyd/go-errorlint v1.6.0 // indirect + github.com/prometheus/client_golang v1.20.5 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.60.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/quasilyte/go-ruleguard v0.4.2 // indirect + github.com/quasilyte/go-ruleguard/dsl v0.3.22 // indirect + github.com/quasilyte/gogrep v0.5.0 // indirect + github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 // indirect github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect - github.com/raulk/go-watchdog v1.3.0 // indirect + github.com/quic-go/qpack v0.5.1 // indirect + github.com/quic-go/quic-go v0.48.1 // indirect + github.com/quic-go/webtransport-go v0.8.1-0.20241018022711-4ac2c9250e66 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/ryancurrah/gomodguard v1.2.4 // indirect - github.com/ryanrolds/sqlclosecheck v0.3.0 // indirect - github.com/sanposhiho/wastedassign/v2 v2.0.6 // indirect + github.com/ryancurrah/gomodguard v1.3.3 // indirect + github.com/ryanrolds/sqlclosecheck v0.5.1 // indirect + github.com/sagikazarmark/locafero v0.6.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/samber/lo v1.47.0 // indirect + github.com/sanposhiho/wastedassign/v2 v2.0.7 // indirect + github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect github.com/sashamelentyev/interfacebloat v1.1.0 // indirect - github.com/sashamelentyev/usestdlibvars v1.13.0 // indirect - github.com/securego/gosec/v2 v2.13.1 // indirect + github.com/sashamelentyev/usestdlibvars v1.27.0 // indirect + github.com/securego/gosec/v2 v2.20.1-0.20240820084340-81cda2f91fbe // indirect github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c // indirect - github.com/sirupsen/logrus v1.9.0 // indirect - github.com/sivchari/containedctx v1.0.2 // indirect - github.com/sivchari/nosnakecase v1.7.0 // indirect - github.com/sivchari/tenv v1.7.0 // indirect - github.com/sonatard/noctx v0.0.1 // indirect - github.com/sourcegraph/go-diff v0.6.1 // indirect - github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/sivchari/containedctx v1.0.3 // indirect + github.com/sivchari/tenv v1.10.0 // indirect + github.com/sonatard/noctx v0.0.2 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/sourcegraph/go-diff v0.7.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect - github.com/spf13/afero v1.8.2 // indirect - github.com/spf13/cast v1.5.0 // indirect - github.com/spf13/cobra v1.5.0 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/cobra v1.8.1 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/spf13/viper v1.12.0 // indirect + github.com/spf13/viper v1.19.0 // indirect github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect github.com/stbenjam/no-sprintf-host-port v0.1.1 // indirect - github.com/stretchr/objx v0.4.0 // indirect - github.com/stretchr/testify v1.8.0 // indirect - github.com/subosito/gotenv v1.4.0 // indirect - github.com/sylvia7788/contextcheck v1.0.6 // indirect - github.com/tdakkota/asciicheck v0.1.1 // indirect - github.com/tetafro/godot v1.4.11 // indirect - github.com/timakin/bodyclose v0.0.0-20210704033933-f49887972144 // indirect - github.com/timonwong/logrlint v0.1.0 // indirect - github.com/tomarrell/wrapcheck/v2 v2.6.2 // indirect - github.com/tommy-muehle/go-mnd/v2 v2.5.0 // indirect - github.com/ultraware/funlen v0.0.3 // indirect - github.com/ultraware/whitespace v0.0.5 // indirect - github.com/urfave/cli v1.22.2 // indirect - github.com/uudashr/gocognit v1.0.6 // indirect - github.com/whyrusleeping/cbor-gen v0.0.0-20210219115102-f37d292932f2 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/stretchr/testify v1.9.0 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/tdakkota/asciicheck v0.2.0 // indirect + github.com/tetafro/godot v1.4.16 // indirect + github.com/timakin/bodyclose v0.0.0-20240125160201-f835fa56326a // indirect + github.com/timonwong/loggercheck v0.9.4 // indirect + github.com/tomarrell/wrapcheck/v2 v2.9.0 // indirect + github.com/tommy-muehle/go-mnd/v2 v2.5.1 // indirect + github.com/ultraware/funlen v0.1.0 // indirect + github.com/ultraware/whitespace v0.1.1 // indirect + github.com/urfave/cli v1.22.16 // indirect + github.com/uudashr/gocognit v1.1.3 // indirect + github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc // indirect + github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect + github.com/wlynxg/anet v0.0.5 // indirect + github.com/xen0n/gosmopolitan v1.2.2 // indirect github.com/yagipy/maintidx v1.0.0 // indirect - github.com/yeya24/promlinter v0.2.0 // indirect - gitlab.com/bosi/decorder v0.2.3 // indirect - go.opentelemetry.io/otel v1.2.0 // indirect - go.opentelemetry.io/otel/trace v1.2.0 // indirect - go.uber.org/atomic v1.10.0 // indirect - go.uber.org/multierr v1.8.0 // indirect - go.uber.org/zap v1.22.0 // indirect - golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect - golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect - golang.org/x/exp/typeparams v0.0.0-20220613132600-b0d781184e0d // indirect - golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect - golang.org/x/net v0.0.0-20220812174116-3211cb980234 // indirect - golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect - golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect - golang.org/x/text v0.3.7 // indirect - golang.org/x/tools v0.1.12 // indirect - golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect - google.golang.org/protobuf v1.28.1 // indirect - gopkg.in/ini.v1 v1.66.6 // indirect - gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect + github.com/yeya24/promlinter v0.3.0 // indirect + github.com/ykadowak/zerologlint v0.1.5 // indirect + github.com/zeebo/blake3 v0.2.4 // indirect + gitlab.com/bosi/decorder v0.4.2 // indirect + go-simpler.org/musttag v0.12.2 // indirect + go-simpler.org/sloglint v0.7.2 // indirect + go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/otel v1.31.0 // indirect + go.opentelemetry.io/otel/metric v1.31.0 // indirect + go.opentelemetry.io/otel/trace v1.31.0 // indirect + go.uber.org/automaxprocs v1.5.3 // indirect + go.uber.org/dig v1.18.0 // indirect + go.uber.org/fx v1.23.0 // indirect + go.uber.org/mock v0.5.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/crypto v0.28.0 // indirect + golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect + golang.org/x/exp/typeparams v0.0.0-20240613232115-7f521ea00fb8 // indirect + golang.org/x/mod v0.21.0 // indirect + golang.org/x/net v0.30.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/term v0.25.0 // indirect + golang.org/x/text v0.19.0 // indirect + golang.org/x/tools v0.26.0 // indirect + gonum.org/v1/gonum v0.15.0 // indirect + google.golang.org/protobuf v1.35.1 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - honnef.co/go/tools v0.3.3 // indirect - lukechampine.com/blake3 v1.1.7 // indirect - mvdan.cc/gofumpt v0.3.1 // indirect - mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed // indirect - mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b // indirect - mvdan.cc/unparam v0.0.0-20220706161116-678bad134442 // indirect + honnef.co/go/tools v0.5.1 // indirect + lukechampine.com/blake3 v1.3.0 // indirect + mvdan.cc/gofumpt v0.7.0 // indirect + mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f // indirect ) diff --git a/test/dependencies/go.sum b/test/dependencies/go.sum index 47bd100f4..37a560dd9 100644 --- a/test/dependencies/go.sum +++ b/test/dependencies/go.sum @@ -1,470 +1,289 @@ -4d63.com/gochecknoglobals v0.1.0 h1:zeZSRqj5yCg28tCkIV/z/lWbwvNm5qnKVS15PI8nhD0= -4d63.com/gochecknoglobals v0.1.0/go.mod h1:wfdC5ZjKSPr7CybKEcgJhUOgeAQW1+7WcyK8OvUilfo= +4d63.com/gocheckcompilerdirectives v1.2.1 h1:AHcMYuw56NPjq/2y615IGg2kYkBdTvOaojYCBcRE7MA= +4d63.com/gocheckcompilerdirectives v1.2.1/go.mod h1:yjDJSxmDTtIHHCqX0ufRYZDL6vQtMG7tJdKVeWwsqvs= +4d63.com/gochecknoglobals v0.2.1 h1:1eiorGsgHOFOuoOiJDy2psSrQbRdIHrlge0IJIkUgDc= +4d63.com/gochecknoglobals v0.2.1/go.mod h1:KRE8wtJB3CXCsb1xy421JfTHIIbmT3U5ruxw2Qu8fSU= 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.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/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/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= 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/gpu/mtl v0.0.0-20201218220906-28db891af037/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= 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/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= -github.com/Antonboom/errname v0.1.7 h1:mBBDKvEYwPl4WFFNwec1CZO096G6vzK9vvDQzAwkako= -github.com/Antonboom/errname v0.1.7/go.mod h1:g0ONh16msHIPgJSGsecu1G/dcF2hlYR/0SddnIAGavU= -github.com/Antonboom/nilnil v0.1.1 h1:PHhrh5ANKFWRBh7TdYmyyq2gyT2lotnvFvvFbylF81Q= -github.com/Antonboom/nilnil v0.1.1/go.mod h1:L1jBqoWM7AOeTD+tSquifKSesRHs4ZdaxvZR+xdJEaI= +github.com/4meepo/tagalign v1.3.4 h1:P51VcvBnf04YkHzjfclN6BbsopfJR5rxs1n+5zHt+w8= +github.com/4meepo/tagalign v1.3.4/go.mod h1:M+pnkHH2vG8+qhE5bVc/zeP7HS/j910Fwa9TUSyZVI0= +github.com/Abirdcfly/dupword v0.0.14 h1:3U4ulkc8EUo+CaT105/GJ1BQwtgyj6+VaBVbAX11Ba8= +github.com/Abirdcfly/dupword v0.0.14/go.mod h1:VKDAbxdY8YbKUByLGg8EETzYSuC4crm9WwI6Y3S0cLI= +github.com/Antonboom/errname v0.1.13 h1:JHICqsewj/fNckzrfVSe+T33svwQxmjC+1ntDsHOVvM= +github.com/Antonboom/errname v0.1.13/go.mod h1:uWyefRYRN54lBg6HseYCFhs6Qjcy41Y3Jl/dVhA87Ns= +github.com/Antonboom/nilnil v0.1.9 h1:eKFMejSxPSA9eLSensFmjW2XTgTwJMjZ8hUHtV4s/SQ= +github.com/Antonboom/nilnil v0.1.9/go.mod h1:iGe2rYwCq5/Me1khrysB4nwI7swQvjclR8/YRPl5ihQ= +github.com/Antonboom/testifylint v1.4.3 h1:ohMt6AHuHgttaQ1xb6SSnxCeK4/rnK7KKzbvs7DmEck= +github.com/Antonboom/testifylint v1.4.3/go.mod h1:+8Q9+AOLsz5ZiQiiYujJKs9mNz398+M6UgslP4qgJLA= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0= -github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs= +github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/Crocmagnon/fatcontext v0.4.0 h1:4ykozu23YHA0JB6+thiuEv7iT6xq995qS1vcuWZq0tg= +github.com/Crocmagnon/fatcontext v0.4.0/go.mod h1:ZtWrXkgyfsYPzS6K3O88va6t2GEglG93vnII/F94WC0= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 h1:sHglBQTwgx+rWPdisA5ynNEsoARbiCBOyGcJM4/OzsM= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= -github.com/GaijinEntertainment/go-exhaustruct/v2 v2.3.0 h1:+r1rSv4gvYn0wmRjC8X7IAzX8QezqtFV9m0MUHFJgts= -github.com/GaijinEntertainment/go-exhaustruct/v2 v2.3.0/go.mod h1:b3g59n2Y+T5xmcxJL+UEG2f8cQploZm1mR/v6BW0mU0= -github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y= +github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0 h1:/fTUt5vmbkAcMBt4YQiuC23cV0kEsN1MVMNqeOW43cU= +github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0/go.mod h1:ONJg5sxcbsdQQ4pOW8TGdTidT2TMAUy/2Xhr8mrYaao= github.com/Kubuxu/gocovmerge v0.0.0-20161216165753-7ecaa51963cd h1:HNhzThEtZW714v8Eda8sWWRcu9WSzJC+oCyjRjvZgRA= github.com/Kubuxu/gocovmerge v0.0.0-20161216165753-7ecaa51963cd/go.mod h1:bqoB8kInrTeEtYAwaIXoSRqdwnjQmFhsfusnzyui6yY= -github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= -github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/OpenPeeDeeP/depguard v1.1.0 h1:pjK9nLPS1FwQYGGpPxoMYpe7qACHOhAWQMQzV71i49o= -github.com/OpenPeeDeeP/depguard v1.1.0/go.mod h1:JtAMzWkmFEzDPyAd+W0NHl1lvpQKTvT9jnRVsohBKpc= -github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= -github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/Stebalien/go-bitfield v0.0.1 h1:X3kbSSPUaJK60wV2hjOPZwmpljr6VGCqdq4cBLhbQBo= -github.com/Stebalien/go-bitfield v0.0.1/go.mod h1:GNjFpasyUVkHMsfEOk8EFLJ9syQ6SI+XWrX9Wf2XH0s= -github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= -github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= -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-20210927113745-59d0afb8317a h1:E/8AP5dFtMhl5KPJz66Kt9G0n+7Sn41Fy1wv9/jHOrc= -github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= +github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/OpenPeeDeeP/depguard/v2 v2.2.0 h1:vDfG60vDtIuf0MEOhmLlLLSzqaRM8EMcgJPdp74zmpA= +github.com/OpenPeeDeeP/depguard/v2 v2.2.0/go.mod h1:CIzddKRvLBC4Au5aYP/i3nyaWQ+ClszLIuVocRiCYFQ= +github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk= +github.com/alecthomas/assert/v2 v2.2.2/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= +github.com/alecthomas/go-check-sumtype v0.1.4 h1:WCvlB3l5Vq5dZQTFmodqL2g68uHiSwwlWcT5a2FGK0c= +github.com/alecthomas/go-check-sumtype v0.1.4/go.mod h1:WyYPfhfkdhyrdaligV6svFopZV8Lqdzn5pyVBaV6jhQ= +github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= +github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/alexkohler/nakedret/v2 v2.0.4 h1:yZuKmjqGi0pSmjGpOC016LtPJysIL0WEUiaXW5SUnNg= +github.com/alexkohler/nakedret/v2 v2.0.4/go.mod h1:bF5i0zF2Wo2o4X4USt9ntUWve6JbFv02Ff4vlkmS/VU= github.com/alexkohler/prealloc v1.0.0 h1:Hbq0/3fJPQhNkN0dR95AVrr6R7tou91y0uHG5pOcUuw= github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE= github.com/alingse/asasalint v0.0.11 h1:SFwnQXJ49Kx/1GghOFz1XGqHYKp21Kq1nHad/0WQRnw= github.com/alingse/asasalint v0.0.11/go.mod h1:nCaoMhw7a9kSJObvQyVzNTPBDbNpdocqrSP7t/cW5+I= 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/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= -github.com/ashanbrown/forbidigo v1.3.0 h1:VkYIwb/xxdireGAdJNZoo24O4lmnEWkactplBlWTShc= -github.com/ashanbrown/forbidigo v1.3.0/go.mod h1:vVW7PEdqEFqapJe95xHkTfB1+XvZXBFg8t0sG2FIxmI= +github.com/ashanbrown/forbidigo v1.6.0 h1:D3aewfM37Yb3pxHujIPSpTf6oQk9sc9WZi8gerOIVIY= +github.com/ashanbrown/forbidigo v1.6.0/go.mod h1:Y8j9jy9ZYAEHXdu723cUlraTqbzjKF1MUyfOKL+AjcU= github.com/ashanbrown/makezero v1.1.1 h1:iCQ87C0V0vSyO+M9E/FZYbu65auqH0lnsOkf5FcB28s= github.com/ashanbrown/makezero v1.1.1/go.mod h1:i1bJLCRSCHOcOa9Y6MyF2FTfMZMFdHvxKHxgO5Z1axI= -github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= -github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= -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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bkielbasa/cyclop v1.2.0 h1:7Jmnh0yL2DjKfw28p86YTd/B4lRGcNuu12sKE35sM7A= -github.com/bkielbasa/cyclop v1.2.0/go.mod h1:qOI0yy6A7dYC4Zgsa72Ppm9kONl0RoIlPbzot9mhmeI= +github.com/bitfield/gotestdox v0.2.2 h1:x6RcPAbBbErKLnapz1QeAlf3ospg8efBsedU93CDsnE= +github.com/bitfield/gotestdox v0.2.2/go.mod h1:D+gwtS0urjBrzguAkTM2wodsTQYFHdpx8eqRJ3N+9pY= +github.com/bkielbasa/cyclop v1.2.1 h1:AeF71HZDob1P2/pRm1so9cd1alZnrpyc4q2uP2l0gJY= +github.com/bkielbasa/cyclop v1.2.1/go.mod h1:K/dT/M0FPAiYjBgQGau7tz+3TMh4FWAEqlMhzFWCrgM= github.com/blizzy78/varnamelen v0.8.0 h1:oqSblyuQvFsW1hbBHh1zfwrKe3kcSj0rnXkKzsQ089M= github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k= -github.com/bombsimon/wsl/v3 v3.3.0 h1:Mka/+kRLoQJq7g2rggtgQsjuI/K5Efd87WX96EWFxjM= -github.com/bombsimon/wsl/v3 v3.3.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc= +github.com/bombsimon/wsl/v4 v4.4.1 h1:jfUaCkN+aUpobrMO24zwyAMwMAV5eSziCkOKEauOLdw= +github.com/bombsimon/wsl/v4 v4.4.1/go.mod h1:Xu/kDxGZTofQcDGCtQe9KCzhHphIe0fDuyWTxER9Feo= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= -github.com/breml/bidichk v0.2.3 h1:qe6ggxpTfA8E75hdjWPZ581sY3a2lnl0IRxLQFelECI= -github.com/breml/bidichk v0.2.3/go.mod h1:8u2C6DnAy0g2cEq+k/A2+tr9O1s+vHGxWn0LTc70T2A= -github.com/breml/errchkjson v0.3.0 h1:YdDqhfqMT+I1vIxPSas44P+9Z9HzJwCeAzjB8PxP1xw= -github.com/breml/errchkjson v0.3.0/go.mod h1:9Cogkyv9gcT8HREpzi3TiqBxCqDzo8awa92zSDFcofU= -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-20190605094302-a0d1e3e36d50/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/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= -github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94= -github.com/btcsuite/btcd v0.22.0-beta/go.mod h1:9n5ntfhhHQBIhUvlhDvD3Qg6fRUj4jkN0VB8L8svzOA= -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/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= -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/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= -github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/snappy-go v1.0.0/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/breml/bidichk v0.2.7 h1:dAkKQPLl/Qrk7hnP6P+E0xOodrq8Us7+U0o4UBOAlQY= +github.com/breml/bidichk v0.2.7/go.mod h1:YodjipAGI9fGcYM7II6wFvGhdMYsC5pHDlGzqvEW3tQ= +github.com/breml/errchkjson v0.3.6 h1:VLhVkqSBH96AvXEyclMR37rZslRrY2kcyq+31HCsVrA= +github.com/breml/errchkjson v0.3.6/go.mod h1:jhSDoFheAF2RSDOlCfhHO9KqhZgAYLyvHe7bRCX8f/U= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -github.com/butuzov/ireturn v0.1.1 h1:QvrO2QF2+/Cx1WA/vETCIYBKtRjc30vesdoPUNo1EbY= -github.com/butuzov/ireturn v0.1.1/go.mod h1:Wh6Zl3IMtTpaIKbmwzqi6olnM9ptYQxxVacMsOEFPoc= -github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= -github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/butuzov/ireturn v0.3.0 h1:hTjMqWw3y5JC3kpnC5vXmFJAWI/m31jaCYQqzkS6PL0= +github.com/butuzov/ireturn v0.3.0/go.mod h1:A09nIiwiqzN/IoVo9ogpa0Hzi9fex1kd9PSD6edP5ZA= +github.com/butuzov/mirror v1.2.0 h1:9YVK1qIjNspaqWutSv8gsge2e/Xpq1eqEkslEUHy5cs= +github.com/butuzov/mirror v1.2.0/go.mod h1:DqZZDtzm42wIAIyHXeN8W/qb1EPlb9Qn/if9icBOpdQ= +github.com/caddyserver/certmagic v0.21.4 h1:e7VobB8rffHv8ZZpSiZtEwnLDHUwLVYLWzWSa1FfKI0= +github.com/caddyserver/certmagic v0.21.4/go.mod h1:swUXjQ1T9ZtMv95qj7/InJvWLXURU85r+CfG0T+ZbDE= +github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA= +github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4= +github.com/catenacyber/perfsprint v0.7.1 h1:PGW5G/Kxn+YrN04cRAZKC+ZuvlVwolYMrIyyTJ/rMmc= +github.com/catenacyber/perfsprint v0.7.1/go.mod h1:/wclWYompEyjUD2FuIIDVKNkqz7IgBIWXIH3V0Zol50= +github.com/ccojocar/zxcvbn-go v1.0.2 h1:na/czXU8RrhXO4EZme6eQJLR4PzcGsahsBOAwU6I3Vg= +github.com/ccojocar/zxcvbn-go v1.0.2/go.mod h1:g1qkXtUSvHP8lhHp5GrSmTz6uWALGRMQdw6Qnz/hi60= 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/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/charithe/durationcheck v0.0.9 h1:mPP4ucLrf/rKZiIG/a9IPXHGlh8p4CzgpyTy6EEutYk= -github.com/charithe/durationcheck v0.0.9/go.mod h1:SSbRIBVfMjCi/kEB6K65XEA83D6prSM8ap1UCpNKtgg= -github.com/chavacava/garif v0.0.0-20220630083739-93517212f375 h1:E7LT642ysztPWE0dfz43cWOvMiF42DyTRC+eZIaO4yI= -github.com/chavacava/garif v0.0.0-20220630083739-93517212f375/go.mod h1:4m1Rv7xfuwWPNKXlThldNuJvutYM6J95wNuuVmn55To= -github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= -github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= -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/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= -github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= +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/charithe/durationcheck v0.0.10 h1:wgw73BiocdBDQPik+zcEoBG/ob8uyBHf2iyoHGPf5w4= +github.com/charithe/durationcheck v0.0.10/go.mod h1:bCWXb7gYRysD1CU3C+u4ceO49LoGOY1C1L6uouGNreQ= +github.com/chavacava/garif v0.1.0 h1:2JHa3hbYf5D9dsgseMKAmc/MZ109otzgNFk5s87H9Pc= +github.com/chavacava/garif v0.1.0/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+UIPD+Gww= +github.com/ckaznocha/intrange v0.1.2 h1:3Y4JAxcMntgb/wABQ6e8Q8leMd26JbX2790lIss9MTI= +github.com/ckaznocha/intrange v0.1.2/go.mod h1:RWffCw/vKBwHeOEwWdCikAtY0q4gGt8VhJZEEA5n+RE= 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/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= -github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= -github.com/containerd/cgroups v1.0.3/go.mod h1:/ofk34relqNjSGyqPrmEULrO4Sc8LJhvJmWbUCUKqj8= -github.com/containerd/cgroups v1.0.4 h1:jN/mbWBEaz+T1pi5OFtnkQ+8qnmEbAr1Oo1FRm5B0dA= -github.com/containerd/cgroups v1.0.4/go.mod h1:nLNQtsF7Sl2HxNebu77i1R0oDlhiTG+kO4JTrUzo6IA= -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.2.1-0.20180108230905-e214231b295a/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-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +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/go-systemd v0.0.0-20181012123002-c6f51f82210d h1:t5Wuyh53qYyg9eqn4BbnlIT+vmhyww0TatL+zT3uWgI= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/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.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +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/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 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 h1:HVTnpeuvF6Owjd5mniCL8DEXo7uYXdQEmOP4FJbV5tg= -github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3/go.mod h1:p1d6YEZWvFzEh4KLyvBcVSnrfNDDvK2zfK/4x2v/4pE= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cristalhq/acmd v0.7.0/go.mod h1:LG5oa43pE/BbxtfMoImHCQN++0Su7dzipdgBjMCBVDQ= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= +github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 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/curioswitch/go-reassign v0.1.2 h1:ekM07+z+VFT560Exz4mTv0/s1yU9gem6CJc/tlYpkmI= -github.com/curioswitch/go-reassign v0.1.2/go.mod h1:bFJIHgtTM3hRm2sKXSPkbwNjSFyGURQXyn4IXD2qwfQ= -github.com/daixiang0/gci v0.6.3 h1:wUAqXChk8HbwXn8AfxD9DYSCp9Bpz1L3e6Q4Roe+q9E= -github.com/daixiang0/gci v0.6.3/go.mod h1:EpVfrztufwVgQRXjnX4zuNinEpLj5OmMjtu/+MB0V0c= -github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/curioswitch/go-reassign v0.2.0 h1:G9UZyOcpk/d7Gd6mqYgd8XYWFMw/znxwGDUstnC9DIo= +github.com/curioswitch/go-reassign v0.2.0/go.mod h1:x6OpXuWvgfQaMGks2BZybTngWjT84hqJfKoO8Tt/Roc= +github.com/daixiang0/gci v0.13.4 h1:61UGkmpoAcxHM2hhNkZEf5SzwQtWJXTSws7jaPyqwlw= +github.com/daixiang0/gci v0.13.4/go.mod h1:12etP2OniiIdP4q+kjUGrC/rUagga7ODbqsom5Eo5Yk= 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/go.mod h1:rQYf4tfk5sSwFsnDg3qYaBxSjsD9S8+59vW0dKUgme4= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/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.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= -github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= -github.com/denis-tingaikin/go-header v0.4.3 h1:tEaZKAlqql6SKCY++utLmkPLd6K8IBM20Ha7UVm+mtU= -github.com/denis-tingaikin/go-header v0.4.3/go.mod h1:0wOCWuN71D5qIgE2nz9KrKmuYBAC2Mra5RassOIQ2/c= -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/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE= -github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -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/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= +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/denis-tingaikin/go-header v0.5.0 h1:SRdnP5ZKvcO9KKRP1KJrhFR3RrlGuD+42t4429eC9k8= +github.com/denis-tingaikin/go-header v0.5.0/go.mod h1:mMenU5bWrok6Wl2UsZjy+1okegmwQ3UgWl4V1D8gjlY= +github.com/dnephin/pflag v1.0.7 h1:oxONGlWxhmUct0YzKTgrpQv9AUA1wtPBn7zuSjJqptk= +github.com/dnephin/pflag v1.0.7/go.mod h1:uxE91IoWURlOiTUIA8Mq5ZZkAv3dPUfZNaT80Zm7OQE= +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/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= -github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= -github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -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/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +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/elastic/gosigar v0.14.3 h1:xwkKwPia+hSfg9GqrCUKYdId102m9qTJIIr7egmK/uo= +github.com/elastic/gosigar v0.14.3/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= 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/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/esimonov/ifshort v1.0.4 h1:6SID4yGWfRae/M7hkVDVVyppy8q/v9OuxNdmjLQStBA= -github.com/esimonov/ifshort v1.0.4/go.mod h1:Pe8zjlRrJ80+q2CxHLfEOfTwxCZ4O+MuhcHcfgNWTk0= -github.com/ettle/strcase v0.1.1 h1:htFueZyVeE1XNnMEfbqp5r67qAN/4r6ya1ysq8Q+Zcw= -github.com/ettle/strcase v0.1.1/go.mod h1:hzDLsPC7/lwKyBOywSHEP89nt2pDgdy+No1NBA9o9VY= +github.com/ettle/strcase v0.2.0 h1:fGNiVF21fHXpX1niBgk0aROov1LagYsOwV/xqKDKR/Q= +github.com/ettle/strcase v0.2.0/go.mod h1:DajmHElDSaX76ITe3/VHVyMin4LWSJN5Z909Wp+ED1A= 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.6.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= +github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= 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/firefart/nonamedreturns v1.0.4 h1:abzI1p7mAEPYuR4A+VLKn4eNDOycjYo2phmY9sfv40Y= -github.com/firefart/nonamedreturns v1.0.4/go.mod h1:TDhe/tjI1BXo48CmYbUduTV7BdIga8MAO/xbKdcVsGI= +github.com/firefart/nonamedreturns v1.0.5 h1:tM+Me2ZaXs8tfdDw3X6DOX++wMCOqzYUho6tUTYIdRA= +github.com/firefart/nonamedreturns v1.0.5/go.mod h1:gHJjDqhGM4WyPt639SOZs+G89Ko7QKH5R5BhnO6xJhw= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/flynn/noise v1.0.0 h1:DlTHqmzmvcEiKj+4RYo/imoswx/4r6iBlCMfVtrMXpQ= -github.com/flynn/noise v1.0.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= +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/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= -github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= -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.2/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= -github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= +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.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= -github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo= github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/ghostiam/protogetter v0.3.6 h1:R7qEWaSgFCsy20yYHNIJsU9ZOb8TziSRRxuAOTVKeOk= +github.com/ghostiam/protogetter v0.3.6/go.mod h1:7lpeDnEJ1ZjL/YtyoN99ljO4z0pd3H0d18/t2dPBxHw= 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-critic/go-critic v0.6.4 h1:tucuG1pvOyYgpBIrVxw0R6gwO42lNa92Aq3VaDoIs+E= -github.com/go-critic/go-critic v0.6.4/go.mod h1:qL5SOlk7NtY6sJPoVCTKDIgzNOxHkkkOCVDyi9wJe1U= +github.com/go-critic/go-critic v0.11.4 h1:O7kGOCx0NDIni4czrkRIXTnit0mkyKOCePh3My6OyEU= +github.com/go-critic/go-critic v0.11.4/go.mod h1:2QAdo4iuLik5S9YG0rT4wcZ8QxwHYkrr6/2MWAiv/vc= 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/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= -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-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/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-toolsmith/astcast v1.0.0 h1:JojxlmI6STnFVG9yOImLeGREv8W2ocNUM+iOhR6jE7g= -github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4= -github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ= -github.com/go-toolsmith/astcopy v1.0.1 h1:l09oBhAPyV74kLJ3ZO31iBU8htZGTwr9LTjuMCyL8go= -github.com/go-toolsmith/astcopy v1.0.1/go.mod h1:4TcEdbElGc9twQEYpVo/aieIXfHhiuLh4aLAck6dO7Y= -github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= -github.com/go-toolsmith/astequal v1.0.1/go.mod h1:4oGA3EZXTVItV/ipGiOx7NWkY5veFfcsOJVS2YxltLw= -github.com/go-toolsmith/astequal v1.0.2 h1:+XvaV8zNxua+9+Oa4AHmgmpo4RYAbwr/qjNppLfX2yM= -github.com/go-toolsmith/astequal v1.0.2/go.mod h1:9Ai4UglvtR+4up+bAD4+hCj7iTo4m/OXVTSLnCyTAx4= -github.com/go-toolsmith/astfmt v1.0.0 h1:A0vDDXt+vsvLEdbMFJAUBI/uTbRw1ffOPnxsILnFL6k= -github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw= -github.com/go-toolsmith/astp v1.0.0 h1:alXE75TXgcmupDsMK1fRAy0YUzLzqPVvBKoyWV+KPXg= -github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI= -github.com/go-toolsmith/pkgload v1.0.2-0.20220101231613-e814995d17c5 h1:eD9POs68PHkwrx7hAB78z1cb6PfGq/jyWn3wJywsH1o= -github.com/go-toolsmith/pkgload v1.0.2-0.20220101231613-e814995d17c5/go.mod h1:3NAwwmD4uY/yggRxoEjk/S00MIV3A+H7rrE3i87eYxM= -github.com/go-toolsmith/strparse v1.0.0 h1:Vcw78DnpCAKlM20kSbAyO4mPfJn/lyYA4BJUDxe2Jb4= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/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-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= +github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= +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-toolsmith/astcast v1.1.0 h1:+JN9xZV1A+Re+95pgnMgDboWNVnIMMQXwfBwLRPgSC8= +github.com/go-toolsmith/astcast v1.1.0/go.mod h1:qdcuFWeGGS2xX5bLM/c3U9lewg7+Zu4mr+xPwZIB4ZU= +github.com/go-toolsmith/astcopy v1.1.0 h1:YGwBN0WM+ekI/6SS6+52zLDEf8Yvp3n2seZITCUBt5s= +github.com/go-toolsmith/astcopy v1.1.0/go.mod h1:hXM6gan18VA1T/daUEHCFcYiW8Ai1tIwIzHY6srfEAw= +github.com/go-toolsmith/astequal v1.0.3/go.mod h1:9Ai4UglvtR+4up+bAD4+hCj7iTo4m/OXVTSLnCyTAx4= +github.com/go-toolsmith/astequal v1.1.0/go.mod h1:sedf7VIdCL22LD8qIvv7Nn9MuWJruQA/ysswh64lffQ= +github.com/go-toolsmith/astequal v1.2.0 h1:3Fs3CYZ1k9Vo4FzFhwwewC3CHISHDnVUPC4x0bI2+Cw= +github.com/go-toolsmith/astequal v1.2.0/go.mod h1:c8NZ3+kSFtFY/8lPso4v8LuJjdJiUFVnSuU3s0qrrDY= +github.com/go-toolsmith/astfmt v1.1.0 h1:iJVPDPp6/7AaeLJEruMsBUlOYCmvg0MoCfJprsOmcco= +github.com/go-toolsmith/astfmt v1.1.0/go.mod h1:OrcLlRwu0CuiIBp/8b5PYF9ktGVZUjlNMV634mhwuQ4= +github.com/go-toolsmith/astp v1.1.0 h1:dXPuCl6u2llURjdPLLDxJeZInAeZ0/eZwFJmqZMnpQA= +github.com/go-toolsmith/astp v1.1.0/go.mod h1:0T1xFGz9hicKs8Z5MfAqSUitoUYS30pDMsRVIDHs8CA= +github.com/go-toolsmith/pkgload v1.2.2 h1:0CtmHq/02QhxcF7E9N5LIFcYFsMR5rdovfqTtRKkgIk= +github.com/go-toolsmith/pkgload v1.2.2/go.mod h1:R2hxLNRKuAsiXCo2i5J6ZQPhnPMOVtU+f0arbFPWCus= github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8= -github.com/go-toolsmith/typep v1.0.2 h1:8xdsa1+FSIH/RhEkgnD1j2CJOy5mNllW1Q9tRiYwvlk= -github.com/go-toolsmith/typep v1.0.2/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU= -github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b h1:khEcpUM4yFcxg4/FHQWkvVRmgijNXRfzkIDHh23ggEo= -github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= +github.com/go-toolsmith/strparse v1.1.0 h1:GAioeZUK9TGxnLS+qfdqNbA4z0SSm5zVNtCQiyP2Bvw= +github.com/go-toolsmith/strparse v1.1.0/go.mod h1:7ksGy58fsaQkGQlY8WVoBFNyEPMGuJin1rfoPS4lBSQ= +github.com/go-toolsmith/typep v1.1.0 h1:fIRYDyF+JywLfqzyhdiHzRop/GQDxxNhLGQ6gFUNHus= +github.com/go-toolsmith/typep v1.1.0/go.mod h1:fVIw+7zjdsMxDA3ITWnH1yOiw1rnTQKCsF/sk2H/qig= +github.com/go-viper/mapstructure/v2 v2.0.0 h1:dhn8MZ1gZ0mzeodTG3jt5Vj/o87xZKuNAprG2mQfMfc= +github.com/go-viper/mapstructure/v2 v2.0.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/go-xmlfmt/xmlfmt v1.1.2 h1:Nea7b4icn8s57fTx1M5AI4qQT5HEM3rVUO8MuE6g80U= +github.com/go-xmlfmt/xmlfmt v1.1.2/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= +github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -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/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= -github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= -github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= +github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.0/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/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/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/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= 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.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 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 h1:23T5iq8rbUYlhpt5DB4XJkc6BU31uODLD1o1gKvZmD0= -github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4= github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM= github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk= -github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe h1:6RGUuS7EGotKx6J5HIP8ZtyMdiDscjMLfRBSPuzVVeo= -github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe/go.mod h1:gjqyPShc/m8pEMpk0a3SeagVb0kaqvhscv+i9jI5ZhQ= -github.com/golangci/gofmt v0.0.0-20220901101216-f2edd75033f2 h1:amWTbTGqOZ71ruzrdA+Nx5WA3tV1N0goTspwmKCQvBY= -github.com/golangci/gofmt v0.0.0-20220901101216-f2edd75033f2/go.mod h1:9wOXstvyDRshQ9LggQuzBCGysxs3b6Uo/1MvYCR2NMs= -github.com/golangci/golangci-lint v1.49.0 h1:I8WHOavragDttlLHtSraHn/h39C+R60bEQ5NoGcHQr8= -github.com/golangci/golangci-lint v1.49.0/go.mod h1:+V/7lLv449R6w9mQ3WdV0EKh7Je/jTylMeSwBZcLeWE= -github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 h1:MfyDlzVjl1hoaPzPD4Gpb/QgoRfSBR0jdhwGyAWwMSA= -github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg= -github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca h1:kNY3/svz5T29MYHubXix4aDDuE3RWHkPvopM/EDv/MA= -github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o= -github.com/golangci/misspell v0.3.5 h1:pLzmVdl3VxTOncgzHcvLOKirdvcx/TydsClUQXTehjo= -github.com/golangci/misspell v0.3.5/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA= -github.com/golangci/revgrep v0.0.0-20220804021717-745bb2f7c2e6 h1:DIPQnGy2Gv2FSA4B/hh8Q7xx3B7AIDk3DAMeHclH1vQ= -github.com/golangci/revgrep v0.0.0-20220804021717-745bb2f7c2e6/go.mod h1:0AKcRCkMoKvUvlf89F6O7H2LYdhr1zBh736mBItOdRs= -github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 h1:zwtduBRr5SSWhqsYNgcuWO2kFlpdOZbP0+yRjmvPGys= -github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ= +github.com/golangci/gofmt v0.0.0-20240816233607-d8596aa466a9 h1:/1322Qns6BtQxUZDTAT4SdcoxknUki7IAoK4SAXr8ME= +github.com/golangci/gofmt v0.0.0-20240816233607-d8596aa466a9/go.mod h1:Oesb/0uFAyWoaw1U1qS5zyjCg5NP9C9iwjnI4tIsXEE= +github.com/golangci/golangci-lint v1.60.2 h1:Y8aWnZCMOLY5T7Ga5hcoemyKsZZJCUmIIK3xTD3jIhc= +github.com/golangci/golangci-lint v1.60.2/go.mod h1:4UvjLpOJoQSvmyWkmO1urDR3txhL9R9sn4oM/evJ95g= +github.com/golangci/misspell v0.6.0 h1:JCle2HUTNWirNlDIAUO44hUsKhOFqGPoC4LZxlaSXDs= +github.com/golangci/misspell v0.6.0/go.mod h1:keMNyY6R9isGaSAu+4Q8NMBwMPkh15Gtc8UCVoDtAWo= +github.com/golangci/modinfo v0.3.4 h1:oU5huX3fbxqQXdfspamej74DFX0kyGLkw1ppvXoJ8GA= +github.com/golangci/modinfo v0.3.4/go.mod h1:wytF1M5xl9u0ij8YSvhkEVPP3M5Mc7XLl1pxH3B2aUM= +github.com/golangci/plugin-module-register v0.1.1 h1:TCmesur25LnyJkpsVrupv1Cdzo+2f7zX0H6Jkw1Ol6c= +github.com/golangci/plugin-module-register v0.1.1/go.mod h1:TTpqoB6KkwOJMV8u7+NyXMrkwwESJLOkfl9TxR1DGFc= +github.com/golangci/revgrep v0.5.3 h1:3tL7c1XBMtWHHqVpS5ChmiAAoe4PF/d5+ULzV9sLAzs= +github.com/golangci/revgrep v0.5.3/go.mod h1:U4R/s9dlXZsg8uJmaR1GrloUr14D7qDl8gi2iPXJH8k= +github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed h1:IURFTjxeTfNFP0hTEi1YKjB/ub8zkpaOqFFMApi2EAs= +github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed/go.mod h1:XLXN8bNw4CGRPaqgl3bv/lhz7bsGPh4/xSaMTbo2vkQ= 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.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/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.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= 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/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/pprof v0.0.0-20241017200806-017d972448fc h1:NGyrhhFhwvRAZg02jnYVg3GBQy0qGBKmFQJwaPmpmxs= +github.com/google/pprof v0.0.0-20241017200806-017d972448fc/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/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-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4= github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gordonklaus/ineffassign v0.0.0-20210914165742-4cc7213b9bc8 h1:PVRE9d4AQKmbelZ7emNig1+NT27DUmKZn5qXxfio54U= -github.com/gordonklaus/ineffassign v0.0.0-20210914165742-4cc7213b9bc8/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0= -github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= -github.com/gostaticanalysis/analysisutil v0.0.3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= -github.com/gostaticanalysis/analysisutil v0.1.0/go.mod h1:dMhHRU9KTiDcuLGdy87/2gTR8WruwYZrKdRq9m1O6uw= +github.com/gordonklaus/ineffassign v0.1.0 h1:y2Gd/9I7MdY1oEIt+n+rowjBNDcLQq3RsH5hwJd0f9s= +github.com/gordonklaus/ineffassign v0.1.0/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gostaticanalysis/analysisutil v0.7.1 h1:ZMCjoue3DtDWQ5WyU16YbjbQEQ3VuzwxALrpYd+HeKk= github.com/gostaticanalysis/analysisutil v0.7.1/go.mod h1:v21E3hY37WKMGSnbsw2S/ojApNWb6C1//mXO48CXbVc= -github.com/gostaticanalysis/comment v1.3.0/go.mod h1:xMicKDx7XRXYdVwY9f9wQpDJVnqWxw9wCauCMKp+IBI= github.com/gostaticanalysis/comment v1.4.1/go.mod h1:ih6ZxzTHLdadaiSnF5WY3dxUoXfXAlTaRzuaNDlSado= github.com/gostaticanalysis/comment v1.4.2 h1:hlnx5+S2fY9Zo9ePo4AhgYsYHbM2+eAv8m/s1JiCd6Q= github.com/gostaticanalysis/comment v1.4.2/go.mod h1:KLUTGDv6HOCotCH8h2erHKmpci2ZoR8VPu34YA2uzdM= @@ -474,318 +293,117 @@ github.com/gostaticanalysis/nilerr v0.1.1 h1:ThE+hJP0fEp4zWLkWHWcRyI2Od0p7DlgYG3 github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW0HU0GPE3+5PWN4A= github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M= github.com/gostaticanalysis/testutil v0.4.0 h1:nhdCmubdmDF6VEatUNjgUZBJKWRqugoISdUv3PPQgHY= +github.com/gostaticanalysis/testutil v0.4.0/go.mod h1:bLIoPefWXrRi/ssLFWX1dx7Repi5x3CuviD3dgAZaBU= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= -github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/gxed/go-shellwords v1.0.3 h1:2TP32H4TAklZUdz84oj95BJhVnIrRasyx2j1cqH5K38= -github.com/gxed/go-shellwords v1.0.3/go.mod h1:N7paucT91ByIjmVJHhvoarjoQnmsi3Jd3vH7VqgtMxQ= -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/hannahhoward/cbor-gen-for v0.0.0-20200817222906-ea96cece81f1/go.mod h1:jvfsLIxk0fY/2BKSQ1xf2406AKA5dwMmKKv0ADcOfN8= -github.com/hannahhoward/go-pubsub v0.0.0-20200423002714-8d62886cc36e h1:3YKHER4nmd7b5qy5t0GWDTwSn4OyRgfAXSmo6VnryBY= -github.com/hannahhoward/go-pubsub v0.0.0-20200423002714-8d62886cc36e/go.mod h1:I8h3MITA53gN9OnWGCgaMa0JWVRdXthWw4M3CPM54OY= -github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= -github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -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-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +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/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= -github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= -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.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -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/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +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 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= 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/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= -github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= -github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= -github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y= -github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= -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/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/ipfs/bbloom v0.0.1/go.mod h1:oqo8CVWsJFMOZqTglBG4wydCE4IQA/G2/SEofB0rjUI= +github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= +github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= +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/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/go-bitfield v1.0.0 h1:y/XHm2GEmD9wKngheWNNCNL0pzrWXZwCdQGv1ikXknQ= -github.com/ipfs/go-bitfield v1.0.0/go.mod h1:N/UiujQy+K+ceU1EF5EkVd1TNqevLrCQMIcAEPrdtus= -github.com/ipfs/go-bitswap v0.0.9/go.mod h1:kAPf5qgn2W2DrgAcscZ3HrM9qh4pH+X8Fkk3UPrwvis= -github.com/ipfs/go-bitswap v0.1.0/go.mod h1:FFJEf18E9izuCqUtHxbWEvq+reg7o4CW5wSAE1wsxj0= -github.com/ipfs/go-bitswap v0.1.2/go.mod h1:qxSWS4NXGs7jQ6zQvoPY3+NmOfHHG47mhkiLzBpJQIs= -github.com/ipfs/go-bitswap v0.5.1/go.mod h1:P+ckC87ri1xFLvk74NlXdP0Kj9RmWAh4+H78sC6Qopo= -github.com/ipfs/go-bitswap v0.6.0 h1:f2rc6GZtoSFhEIzQmddgGiel9xntj02Dg0ZNf2hSC+w= -github.com/ipfs/go-bitswap v0.6.0/go.mod h1:Hj3ZXdOC5wBJvENtdqsixmzzRukqd8EHLxZLZc3mzRA= -github.com/ipfs/go-block-format v0.0.1/go.mod h1:DK/YYcsSUIVAFNwo/KZCdIIbpN0ROH/baNLgayt4pFc= -github.com/ipfs/go-block-format v0.0.2/go.mod h1:AWR46JfpcObNfg3ok2JHDUfdiHRgWhJgCQF+KIgOPJY= -github.com/ipfs/go-block-format v0.0.3 h1:r8t66QstRp/pd/or4dpnbVfXT5Gt7lOqRvC+/dDTpMc= -github.com/ipfs/go-block-format v0.0.3/go.mod h1:4LmD4ZUw0mhO+JSKdpWwrzATiEfM7WWgQ8H5l6P8MVk= -github.com/ipfs/go-blockservice v0.0.7/go.mod h1:EOfb9k/Y878ZTRY/CH0x5+ATtaipfbRhbvNSdgc/7So= -github.com/ipfs/go-blockservice v0.1.0/go.mod h1:hzmMScl1kXHg3M2BjTymbVPjv627N7sYcvYaKbop39M= -github.com/ipfs/go-blockservice v0.2.1/go.mod h1:k6SiwmgyYgs4M/qt+ww6amPeUH9EISLRBnvUurKJhi8= -github.com/ipfs/go-blockservice v0.3.0 h1:cDgcZ+0P0Ih3sl8+qjFr2sVaMdysg/YZpLj5WJ8kiiw= -github.com/ipfs/go-blockservice v0.3.0/go.mod h1:P5ppi8IHDC7O+pA0AlGTF09jruB2h+oP3wVVaZl8sfk= -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/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= -github.com/ipfs/go-cid v0.1.0/go.mod h1:rH5/Xv83Rfy8Rw6xG+id3DYAMUVmem1MowoKwdXmN2o= -github.com/ipfs/go-cid v0.2.0/go.mod h1:P+HXFDF4CVhaVayiEb4wkAy7zBHxBwsJyt0Y5U6MLro= -github.com/ipfs/go-cid v0.3.2 h1:OGgOd+JCFM+y1DjWPmVH+2/4POtpDzwcr7VgnB7mZXc= -github.com/ipfs/go-cid v0.3.2/go.mod h1:gQ8pKqT/sUxGY+tIwy1RPpAojYu7jAyCp5Tz1svoupw= +github.com/ipfs/boxo v0.24.3 h1:gldDPOWdM3Rz0v5LkVLtZu7A7gFNvAlWcmxhCqlHR3c= +github.com/ipfs/boxo v0.24.3/go.mod h1:h0DRzOY1IBFDHp6KNvrJLMFdSXTYID0Zf+q7X05JsNg= +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-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.0.1/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= -github.com/ipfs/go-datastore v0.0.5/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.3.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/go.mod h1:eXTcaaiN6uOlVCLS9GjJUJtlvJfM3xk23w3fyfrmmJs= -github.com/ipfs/go-datastore v0.5.0/go.mod h1:9zhEApYMTl17C8YDp7JmU7sQZi2/wqiYh73hakZ90Bk= -github.com/ipfs/go-datastore v0.5.1 h1:WkRhLuISI+XPD0uk3OskB0fYFSyqK8Ob5ZYew9Qa1nQ= -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.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.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-badger v0.3.0/go.mod h1:1ke6mXNqeV8K3y5Ak2bAA0osoTfmxUdupVCGm4QUIek= -github.com/ipfs/go-ds-leveldb v0.0.1/go.mod h1:feO8V3kubwsEF22n0YRQCffeb79OOYIykR4L04tMOYc= -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-ds-leveldb v0.5.0/go.mod h1:d3XG9RUDzQ6V4SHi8+Xgj9j1XuEk1z82lquxrVbml/Q= -github.com/ipfs/go-graphsync v0.13.2 h1:+7IjTrdg3+3iwtPXSkLoxvhaByS3+3b9NStMAowFqkw= -github.com/ipfs/go-graphsync v0.13.2/go.mod h1:TO1Y65spARny/t37hkid5xCpQJ6vR7A7VFTEUb0Z6eA= -github.com/ipfs/go-ipfs-blockstore v0.0.1/go.mod h1:d3WClOmRQKFnJ0Jz/jj/zmksX0ma1gROTlovZKBmN08= -github.com/ipfs/go-ipfs-blockstore v0.1.0/go.mod h1:5aD0AvHPi7mZc6Ci1WCAhiBQu2IsfTduLl+422H6Rqw= -github.com/ipfs/go-ipfs-blockstore v0.2.1/go.mod h1:jGesd8EtCM3/zPgx+qr0/feTXGUeRai6adgwC+Q+JvE= -github.com/ipfs/go-ipfs-blockstore v1.1.2/go.mod h1:w51tNR9y5+QXB0wkNcHt4O2aSZjTdqaEWaQdSxEyUOY= -github.com/ipfs/go-ipfs-blockstore v1.2.0 h1:n3WTeJ4LdICWs/0VSfjHrlqpPpl6MZ+ySd3j8qz0ykw= -github.com/ipfs/go-ipfs-blockstore v1.2.0/go.mod h1:eh8eTFLiINYNSNawfZOC7HOxNTxpB1PFuA5E1m/7exE= -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.1/go.mod h1:tWewYK0we3+rMbOh7pPFGDyypCtvGcBFymgY4rSDLAw= -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-config v0.5.3/go.mod h1:nSLCFtlaL+2rbl3F+9D4gQZQbT1LjRKx7TJg/IHz6oM= -github.com/ipfs/go-ipfs-config v0.19.0 h1:OuKIL+BkOZgJ+hb4Wg/9ynCtE/BaZBWcGy8hgdMepAo= -github.com/ipfs/go-ipfs-config v0.19.0/go.mod h1:wz2lKzOjgJeYJa6zx8W9VT7mz+iSd0laBMqS/9wmX6A= -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 v0.0.1/go.mod h1:gtP9xRaZXqIQRh1HRpp595KbBEdgqWFxefeVKOV8sxo= -github.com/ipfs/go-ipfs-ds-help v0.1.1/go.mod h1:SbBafGJuGsPI/QL3j9Fc5YPLeAu+SzOkI0gFwAg+mOs= -github.com/ipfs/go-ipfs-ds-help v1.1.0 h1:yLE2w9RAsl31LtfMt91tRZcrx+e61O5mDxFRR994w4Q= -github.com/ipfs/go-ipfs-ds-help v1.1.0/go.mod h1:YR5+6EaebOhfcqVCyqemItCLthrpVNot+rsOU/5IatU= -github.com/ipfs/go-ipfs-exchange-interface v0.0.1/go.mod h1:c8MwfHjtQjPoDyiy9cFquVtVHkO9b9Ob3FG91qJnWCM= -github.com/ipfs/go-ipfs-exchange-interface v0.1.0 h1:TiMekCrOGQuWYtZO3mf4YJXDIdNgnKWZ9IE3fGlnWfo= -github.com/ipfs/go-ipfs-exchange-interface v0.1.0/go.mod h1:ych7WPlyHqFvCi/uQI48zLZuAWVP5iTQPXEfVaw5WEI= -github.com/ipfs/go-ipfs-exchange-offline v0.0.1/go.mod h1:WhHSFCVYX36H/anEKQboAzpUws3x7UeEGkzQc3iNkM0= -github.com/ipfs/go-ipfs-exchange-offline v0.1.1/go.mod h1:vTiBRIbzSwDD0OWm+i3xeT0mO7jG2cbJYatp3HPk5XY= -github.com/ipfs/go-ipfs-exchange-offline v0.2.0 h1:2PF4o4A7W656rC0RxuhUace997FTcDTcIQ6NoEtyjAI= -github.com/ipfs/go-ipfs-exchange-offline v0.2.0/go.mod h1:HjwBeW0dvZvfOMwDP0TSKXIHf2s+ksdP4E3MLDRtLKY= -github.com/ipfs/go-ipfs-files v0.0.3/go.mod h1:INEFm0LL2LWXBhNJ2PMIIb2w45hpXgPjNoE7yA8Y1d4= -github.com/ipfs/go-ipfs-files v0.0.8 h1:8o0oFJkJ8UkO/ABl8T6ac6tKF3+NIpj67aAB6ZpusRg= -github.com/ipfs/go-ipfs-files v0.0.8/go.mod h1:wiN/jSG8FKyk7N0WyctKSvq3ljIa2NNTiZB55kpTdOs= -github.com/ipfs/go-ipfs-posinfo v0.0.1 h1:Esoxj+1JgSjX0+ylc0hUmJCOv6V2vFoZiETLR6OtpRs= -github.com/ipfs/go-ipfs-posinfo v0.0.1/go.mod h1:SwyeVP+jCwiDu0C313l/8jg6ZxM0qqtlt2a0vILTc1A= -github.com/ipfs/go-ipfs-pq v0.0.1/go.mod h1:LWIqQpqfRG3fNc5XsnIhz/wQ2XXGyugQwls7BgUmUfY= -github.com/ipfs/go-ipfs-pq v0.0.2 h1:e1vOOW6MuOwG2lqxcLA+wEn93i/9laCY8sXAw76jFOY= -github.com/ipfs/go-ipfs-pq v0.0.2/go.mod h1:LWIqQpqfRG3fNc5XsnIhz/wQ2XXGyugQwls7BgUmUfY= -github.com/ipfs/go-ipfs-routing v0.0.1/go.mod h1:k76lf20iKFxQTjcJokbPM9iBXVXVZhcOwc360N4nuKs= -github.com/ipfs/go-ipfs-routing v0.1.0/go.mod h1:hYoUkJLyAUKhF58tysKpids8RNDPO42BVMgK5dNsoqY= -github.com/ipfs/go-ipfs-routing v0.2.1 h1:E+whHWhJkdN9YeoHZNj5itzc+OR292AJ2uE9FFiW0BY= -github.com/ipfs/go-ipfs-routing v0.2.1/go.mod h1:xiNNiwgjmLqPS1cimvAw6EyB9rkVDbiocA4yY+wRNLM= -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-ipld-cbor v0.0.2/go.mod h1:wTBtrQZA3SoFKMVkp6cn6HMRteIB1VsmHA0AQFOn7Nc= -github.com/ipfs/go-ipld-cbor v0.0.3/go.mod h1:wTBtrQZA3SoFKMVkp6cn6HMRteIB1VsmHA0AQFOn7Nc= -github.com/ipfs/go-ipld-cbor v0.0.4/go.mod h1:BkCduEx3XBCO6t2Sfo5BaHzuok7hbhdMm9Oh8B2Ftq4= -github.com/ipfs/go-ipld-cbor v0.0.5 h1:ovz4CHKogtG2KB/h1zUp5U0c/IzZrL435rCh5+K/5G8= -github.com/ipfs/go-ipld-cbor v0.0.5/go.mod h1:BkCduEx3XBCO6t2Sfo5BaHzuok7hbhdMm9Oh8B2Ftq4= -github.com/ipfs/go-ipld-format v0.0.1/go.mod h1:kyJtbkDALmFHv3QR6et67i35QzO3S0dCDnkOJhcZkms= -github.com/ipfs/go-ipld-format v0.0.2/go.mod h1:4B6+FM2u9OJ9zCV+kSbgFAZlOrv1Hqbf0INGQgiKf9k= -github.com/ipfs/go-ipld-format v0.2.0/go.mod h1:3l3C1uKoadTPbeNfrDi+xMInYKlx2Cvg1BuydPSdzQs= -github.com/ipfs/go-ipld-format v0.3.0/go.mod h1:co/SdBE8h99968X0hViiw1MNlh6fvxxnHpvVLnH7jSM= -github.com/ipfs/go-ipld-format v0.4.0 h1:yqJSaJftjmjc9jEOFYlpkwOLVKv68OD27jFLlSghBlQ= -github.com/ipfs/go-ipld-format v0.4.0/go.mod h1:co/SdBE8h99968X0hViiw1MNlh6fvxxnHpvVLnH7jSM= -github.com/ipfs/go-ipld-legacy v0.1.0 h1:wxkkc4k8cnvIGIjPO0waJCe7SHEyFgl+yQdafdjGrpA= -github.com/ipfs/go-ipld-legacy v0.1.0/go.mod h1:86f5P/srAmh9GcIcWQR9lfFLZPrIyyXQeVlOWeeWEuI= -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/go.mod h1:oDCg2FkjogeFOhqqb+N39l2RpTNPL6F/StPkB3kPgcs= -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.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/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM= -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-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-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-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-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-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.0.6/go.mod h1:QYPdnlvkOg7GnQRofu9XZimC5ZW5Wi3bKys/4GQQfto= -github.com/ipfs/go-merkledag v0.2.3/go.mod h1:SQiXrtSts3KGNmgOzMICy5c0POOpUNQLvB3ClKnBAlk= -github.com/ipfs/go-merkledag v0.3.2/go.mod h1:fvkZNNZixVW6cKSZ/JfLlON5OlgTXNdRLz0p6QG/I2M= -github.com/ipfs/go-merkledag v0.5.1/go.mod h1:cLMZXx8J08idkp5+id62iVftUQV+HlYJ3PIhDfZsjA4= -github.com/ipfs/go-merkledag v0.8.1 h1:N3yrqSre/ffvdwtHL4MXy0n7XH+VzN8DlzDrJySPa94= -github.com/ipfs/go-merkledag v0.8.1/go.mod h1:uYUlWE34GhbcTjGuUDEcdPzsEtOdnOupL64NgSRjmWI= 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-path v0.0.7/go.mod h1:6KTKmeRnBXgqrTvzFrPV3CamxcgvXX/4z79tfAd2Sno= -github.com/ipfs/go-peertaskqueue v0.0.4/go.mod h1:03H8fhyeMfKNFWqzYEVyMbcPUeYrqP1MX6Kd+aN+rMQ= -github.com/ipfs/go-peertaskqueue v0.1.0/go.mod h1:Jmk3IyCcfl1W3jTW3YpghSwSEC6IJ3Vzz/jUmWw8Z0U= -github.com/ipfs/go-peertaskqueue v0.7.0/go.mod h1:M/akTIE/z1jGNXMU7kFB4TeSEFvj68ow0Rrb04donIU= -github.com/ipfs/go-peertaskqueue v0.7.1 h1:7PLjon3RZwRQMgOTvYccZ+mjzkmds/7YzSWKFlBAypE= -github.com/ipfs/go-peertaskqueue v0.7.1/go.mod h1:M/akTIE/z1jGNXMU7kFB4TeSEFvj68ow0Rrb04donIU= -github.com/ipfs/go-todocounter v0.0.1/go.mod h1:l5aErvQc8qKE2r7NDMjmq5UNAvuZy0rC8BHOplkWvZ4= -github.com/ipfs/go-unixfs v0.2.4/go.mod h1:SUdisfUjNoSDzzhGVxvCL9QO/nKdwXdr+gbMUdqcbYw= -github.com/ipfs/go-unixfs v0.3.1 h1:LrfED0OGfG98ZEegO4/xiprx2O+yS+krCMQSp7zLVv8= -github.com/ipfs/go-unixfs v0.3.1/go.mod h1:h4qfQYzghiIc8ZNFKiLMFWOTzrWIAtzYQ59W/pCFf1o= -github.com/ipfs/go-unixfsnode v1.4.0 h1:9BUxHBXrbNi8mWHc6j+5C580WJqtVw9uoeEKn4tMhwA= -github.com/ipfs/go-unixfsnode v1.4.0/go.mod h1:qc7YFFZ8tABc58p62HnIYbUMwj9chhUuFWmxSokfePo= -github.com/ipfs/go-verifcid v0.0.1 h1:m2HI7zIuR5TFyQ1b79Da5N9dnnCP1vcu2QqawmWlK2E= -github.com/ipfs/go-verifcid v0.0.1/go.mod h1:5Hrva5KBeIog4A+UpqlaIU+DEstipcJYQQZc0g37pY0= +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-test v0.0.4 h1:DKT66T6GBB6PsDFLoO56QZPrOmzJkqU1FZH5C9ySkew= +github.com/ipfs/go-test v0.0.4/go.mod h1:qhIM1EluEfElKKM6fnWxGn822/z9knUGM1+I/OAQNKI= github.com/ipfs/hang-fds v0.1.0 h1:deBiFlWHsVGzJ0ZMaqscEqRM1r2O1rFZ59UiQXb1Xko= github.com/ipfs/hang-fds v0.1.0/go.mod h1:29VLWOn3ftAgNNgXg/al7b11UzuQ+w7AwtCGcTaWkbM= -github.com/ipfs/interface-go-ipfs-core v0.4.0/go.mod h1:UJBcU6iNennuI05amq3FQ7g0JHUkibHFAfhfUIy927o= -github.com/ipfs/interface-go-ipfs-core v0.7.0 h1:7tb+2upz8oCcjIyjo1atdMk+P+u7wPmI+GksBlLE8js= -github.com/ipfs/interface-go-ipfs-core v0.7.0/go.mod h1:lF27E/nnSPbylPqKVXGZghal2hzifs3MmjyiEjnc9FY= -github.com/ipfs/iptb v1.4.0 h1:YFYTrCkLMRwk/35IMyC6+yjoQSHTEcNcefBStLJzgvo= -github.com/ipfs/iptb v1.4.0/go.mod h1:1rzHpCYtNp87/+hTxG5TfCVn/yMY3dKnLn8tBiMfdmg= -github.com/ipfs/iptb-plugins v0.3.0 h1:C1rpq1o5lUZtaAOkLIox5akh6ba4uk/3RwWc6ttVxw0= -github.com/ipfs/iptb-plugins v0.3.0/go.mod h1:5QtOvckeIw4bY86gSH4fgh3p3gCSMn3FmIKr4gaBncA= -github.com/ipld/go-car/v2 v2.1.1 h1:saaKz4nC0AdfCGHLYKeXLGn8ivoPC54fyS55uyOLKwA= -github.com/ipld/go-car/v2 v2.1.1/go.mod h1:+2Yvf0Z3wzkv7NeI69i8tuZ+ft7jyjPYIWZzeVNeFcI= -github.com/ipld/go-codec-dagpb v1.3.0/go.mod h1:ga4JTU3abYApDC3pZ00BC2RSvC3qfBb9MSJkMLSwnhA= -github.com/ipld/go-codec-dagpb v1.3.1/go.mod h1:ErNNglIi5KMur/MfFE/svtgQthzVvf+43MrzLbpcIZY= -github.com/ipld/go-codec-dagpb v1.4.0 h1:VqADPIFng8G4vz5EQytmmcx/2gEgOHfBuw/kIuCgDAY= -github.com/ipld/go-codec-dagpb v1.4.0/go.mod h1:ErNNglIi5KMur/MfFE/svtgQthzVvf+43MrzLbpcIZY= -github.com/ipld/go-ipld-prime v0.9.1-0.20210324083106-dc342a9917db/go.mod h1:KvBLMr4PX1gWptgkzRjVZCrLmSGcZCb/jioOQwCqZN8= -github.com/ipld/go-ipld-prime v0.11.0/go.mod h1:+WIAkokurHmZ/KwzDOMUuoeJgaRQktHtEaLglS3ZeV8= -github.com/ipld/go-ipld-prime v0.14.0/go.mod h1:9ASQLwUFLptCov6lIYc70GRB4V7UTyLD0IJtrDJe6ZM= -github.com/ipld/go-ipld-prime v0.16.0/go.mod h1:axSCuOCBPqrH+gvXr2w9uAOulJqBPhHPT2PjoiiU1qA= -github.com/ipld/go-ipld-prime v0.17.0/go.mod h1:aYcKm5TIvGfY8P3QBKz/2gKcLxzJ1zDaD+o0bOowhgs= -github.com/ipld/go-ipld-prime v0.19.0 h1:5axC7rJmPc17Emw6TelxGwnzALk0PdupZ2oj2roDj04= -github.com/ipld/go-ipld-prime v0.19.0/go.mod h1:Q9j3BaVXwaA3o5JUDNvptDDr/x8+F7FG6XJ8WI3ILg4= -github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20211210234204-ce2a1c70cd73/go.mod h1:2PJ0JgxyB08t0b2WKrcuqI3di0V+5n6RS/LTUJhkoxY= -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/ipfs/iptb v1.4.1 h1:faXd3TKGPswbHyZecqqg6UfbES7RDjTKQb+6VFPKDUo= +github.com/ipfs/iptb v1.4.1/go.mod h1:nTsBMtVYFEu0FjC5DgrErnABm3OG9ruXkFXGJoTV5OA= +github.com/ipfs/iptb-plugins v0.5.1 h1:11PNTNEt2+SFxjUcO5qpyCTXqDj6T8Tx9pU/G4ytCIQ= +github.com/ipfs/iptb-plugins v0.5.1/go.mod h1:mscJAjRnu4g16QK6oUBn9RGpcp8ueJmLfmPxIG/At78= +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.21.0 h1:n4JmcpOlPDIxBcY037SVfpd1G+Sj1nKZah0m6QH9C2E= +github.com/ipld/go-ipld-prime v0.21.0/go.mod h1:3RLqy//ERg/y5oShXXdx5YIp50cFGOanyMctpPjsvxQ= +github.com/ipshipyard/p2p-forge v0.0.2 h1:86y9LxGB8sGxYQ/If5sNx+c8C/huSpBUg3UZ1uvtym8= +github.com/ipshipyard/p2p-forge v0.0.2/go.mod h1:taPeh3PDSO8Ual0/N2tIOAUXPV8gZoPF3uPXoUyiq14= 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/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= github.com/jbenet/go-random v0.0.0-20190219211222-123a90aedc0c h1:uUx61FiAa1GI6ZmVd2wf2vULeQZIKG66eybjNXKYCz4= github.com/jbenet/go-random v0.0.0-20190219211222-123a90aedc0c/go.mod h1:sdx1xVM9UuLw1tXnhJWN3piypTUO3vCIHYmG15KE/dU= github.com/jbenet/go-random-files v0.0.0-20190219210431-31b3f20ebded h1:fHCa28iw+qaRWZK4IqrntHxXALD5kKr/ESrpOCRRdrg= github.com/jbenet/go-random-files v0.0.0-20190219210431-31b3f20ebded/go.mod h1:FKvZrl5nnaGnTAMewcq0i7wM5zHD75e0lwlnF8q46uo= -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/jgautheron/goconst v1.5.1 h1:HxVbL1MhydKs8R8n/HE5NPvzfaYmQJA3o879lE4+WcM= -github.com/jgautheron/goconst v1.5.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= +github.com/jgautheron/goconst v1.7.1 h1:VpdAG7Ca7yvvJk5n8dMwQhfEZJh95kl/Hl9S1OI5Jkk= +github.com/jgautheron/goconst v1.7.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= github.com/jingyugao/rowserrcheck v1.1.1 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjzq7gFzUs= github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c= github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af h1:KA9BjwUk7KlCh6S9EAGWBt1oExIUv9WyNCiRz5amv48= github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/jonboulle/clockwork v0.2.0 h1:J2SLSdy7HgElq8ekSl2Mxh6vrRNFxqbXGenYH2I02Vs= -github.com/jonboulle/clockwork v0.2.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= -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/jjti/go-spancheck v0.6.2 h1:iYtoxqPMzHUPp7St+5yA8+cONdyXD3ug6KK15n7Pklk= +github.com/jjti/go-spancheck v0.6.2/go.mod h1:+X7lvIrR5ZdUTkxFYqzJ0abr8Sb5LOo80uOhWNqIrYA= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -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.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 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/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/julz/importas v0.1.0 h1:F78HnrsjY3cR7j0etXy5+TU1Zuy7Xt08X/1aJnH5xXY= github.com/julz/importas v0.1.0/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0= -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/karamaru-alpha/copyloopvar v1.1.0 h1:x7gNyKcC2vRBO1H2Mks5u1VxQtYvFiym7fCjIP8RPos= +github.com/karamaru-alpha/copyloopvar v1.1.0/go.mod h1:u7CIfztblY0jZLOQZgH3oYsJzpC2A7S6u/lfgSXHy0k= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/errcheck v1.6.2 h1:uGQ9xI8/pgc9iOoCe7kWQgRE6SBTrCGmTSf0LrEtY7c= -github.com/kisielk/errcheck v1.6.2/go.mod h1:nXw/i/MfnvRHqXa7XXmQMUB0oNFGuBrNI8d8NLy0LPw= -github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= +github.com/kisielk/errcheck v1.7.0 h1:+SbscKmWJ5mOK/bO1zS60F5I9WwZDWOfRsC4RwfwRV0= +github.com/kisielk/errcheck v1.7.0/go.mod h1:1kLL+jV4e+CFfueBmI1dSK2ADDyQnlrnrY/FqKluHJQ= 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.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.15.1 h1:y9FcTHGyrebwfP0ZZqFiaxTaiDnUrGkJkI+f583BL1A= -github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.0.6/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.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= -github.com/klauspost/cpuid/v2 v2.1.0 h1:eyi1Ad2aNJMW95zcSbmGg7Cg6cq3ADwLpMAP96d8rF0= -github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= -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.0-20180514024734-4a0ed625a78b/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= -github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= -github.com/koron/go-ssdp v0.0.2/go.mod h1:XoLfkAiA2KeZsYh4DbHxD7h3nR2AZNqVQOa+LJuqPYs= -github.com/koron/go-ssdp v0.0.3 h1:JivLMY45N76b4p/vsWGOKewBQu6uf39y8l+AQ7sDKx8= -github.com/koron/go-ssdp v0.0.3/go.mod h1:b2MxI6yh02pKrsyNoQUsk4+YNikaGhe4894J+Q5lDvA= -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/kkHAIKE/contextcheck v1.1.5 h1:CdnJh63tcDe53vG+RebdpdXJTc9atMgGqdx8LXxiilg= +github.com/kkHAIKE/contextcheck v1.1.5/go.mod h1:O930cpht4xb1YQpK+1+AgoM3mFsvxr7uyFptcnWTYUA= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= +github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= +github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +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/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 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -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= @@ -793,711 +411,277 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kulti/thelper v0.6.3 h1:ElhKf+AlItIu+xGnI990no4cE2+XaSu1ULymV2Yulxs= github.com/kulti/thelper v0.6.3/go.mod h1:DsqKShOvP40epevkFrvIwkCMNYxMeTNjdWL4dqWHZ6I= -github.com/kunwardeep/paralleltest v1.0.6 h1:FCKYMF1OF2+RveWlABsdnmsvJrei5aoyZoaGS+Ugg8g= -github.com/kunwardeep/paralleltest v1.0.6/go.mod h1:Y0Y0XISdZM5IKm3TREQMZ6iteqn1YuwCsJO/0kL9Zes= -github.com/kyoh86/exportloopref v0.1.8 h1:5Ry/at+eFdkX9Vsdw3qU4YkvGtzuVfzT4X7S77LoN/M= -github.com/kyoh86/exportloopref v0.1.8/go.mod h1:1tUcJeiioIs7VWe5gcOObrux3lb66+sBqGZrRkMwPgg= -github.com/ldez/gomoddirectives v0.2.3 h1:y7MBaisZVDYmKvt9/l1mjNCiSA1BVn34U0ObUcJwlhA= -github.com/ldez/gomoddirectives v0.2.3/go.mod h1:cpgBogWITnCfRq2qGoDkKMEVSaarhdBr6g8G04uz6d0= -github.com/ldez/tagliatelle v0.3.1 h1:3BqVVlReVUZwafJUwQ+oxbx2BEX2vUG4Yu/NOfMiKiM= -github.com/ldez/tagliatelle v0.3.1/go.mod h1:8s6WJQwEYHbKZDsp/LjArytKOG8qaMrKQQ3mFukHs88= -github.com/leonklingele/grouper v1.1.0 h1:tC2y/ygPbMFSBOs3DcyaEMKnnwH7eYKzohOtRrf0SAg= -github.com/leonklingele/grouper v1.1.0/go.mod h1:uk3I3uDfi9B6PeUjsCKi6ndcf63Uy7snXgR4yDYQVDY= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -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/go.mod h1:Ecd6Fb3yIuLzq4bD7VcywcVSBtefcAwnUISBM3WG15E= -github.com/libp2p/go-addr-util v0.1.0/go.mod h1:6I3ZYuFr2O/9D+SoyM0zEw0EF3YkldtTX406BpdQMqw= -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/kunwardeep/paralleltest v1.0.10 h1:wrodoaKYzS2mdNVnc4/w31YaXFtsc21PCTdvWJ/lDDs= +github.com/kunwardeep/paralleltest v1.0.10/go.mod h1:2C7s65hONVqY7Q5Efj5aLzRCNLjw2h4eMc9EcypGjcY= +github.com/kyoh86/exportloopref v0.1.11 h1:1Z0bcmTypkL3Q4k+IDHMWTcnCliEZcaPiIe0/ymEyhQ= +github.com/kyoh86/exportloopref v0.1.11/go.mod h1:qkV4UF1zGl6EkF1ox8L5t9SwyeBAZ3qLMd6up458uqA= +github.com/lasiar/canonicalheader v1.1.1 h1:wC+dY9ZfiqiPwAexUApFush/csSPXeIi4QqyxXmng8I= +github.com/lasiar/canonicalheader v1.1.1/go.mod h1:cXkb3Dlk6XXy+8MVQnF23CYKWlyA7kfQhSw2CcZtZb0= +github.com/ldez/gomoddirectives v0.2.4 h1:j3YjBIjEBbqZ0NKtBNzr8rtMHTOrLPeiwTkfUJZ3alg= +github.com/ldez/gomoddirectives v0.2.4/go.mod h1:oWu9i62VcQDYp9EQ0ONTfqLNh+mDLWWDO+SO0qSQw5g= +github.com/ldez/tagliatelle v0.5.0 h1:epgfuYt9v0CG3fms0pEgIMNPuFf/LpPIfjk4kyqSioo= +github.com/ldez/tagliatelle v0.5.0/go.mod h1:rj1HmWiL1MiKQuOONhd09iySTEkUuE/8+5jtPYz9xa4= +github.com/leonklingele/grouper v1.1.2 h1:o1ARBDLOmmasUaNDesWqWCIFH3u7hoFlM84YrjT3mIY= +github.com/leonklingele/grouper v1.1.2/go.mod h1:6D0M/HVkhs2yRKRFZUoGjeDy7EZTfFBE9gl4kjmIGkA= +github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s= +github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= 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-conn-security v0.0.1/go.mod h1:bGmu51N0KU9IEjX7kl2PQjgZa40JQWnayTvNMgD/vyk= -github.com/libp2p/go-conn-security-multistream v0.0.2/go.mod h1:nc9vud7inQ+d6SO0I/6dSWrdMnHnzZNHeyUQqrAJulE= -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/go.mod h1:hZN4MjlNetKD3Rq5Jb/P5ohUnFLNzEAR4DLSzpn2QLU= -github.com/libp2p/go-conn-security-multistream v0.2.1/go.mod h1:cR1d8gA0Hr59Fj6NhaTpFhJZrjSYuNmhpT2r25zYR70= -github.com/libp2p/go-conn-security-multistream v0.3.0/go.mod h1:EEP47t4fw/bTelVmEzIDqSe69hO/ip52xBEhZMLWAHM= -github.com/libp2p/go-eventbus v0.0.2/go.mod h1:Hr/yGlwxA/stuLnpMiu82lpNKpvRy3EaJxPu40XYOwk= -github.com/libp2p/go-eventbus v0.1.0/go.mod h1:vROgu5cs5T7cv7POWlWxBaVLxfSegC5UGQf8A2eEmx4= -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.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.0.30/go.mod h1:XWT8FGHlhptAv1+3V/+J5mEpzyui/5bvFsNuWYs611A= -github.com/libp2p/go-libp2p v0.1.0/go.mod h1:6D/2OBauqLUoqcADOJpn9WbKqvaM07tDw68qHM0BxUM= -github.com/libp2p/go-libp2p v0.1.1/go.mod h1:I00BRo1UuUSdpuc8Q2mN7yDF/oTUTRAX6JWpTiK9Rp8= -github.com/libp2p/go-libp2p v0.3.1/go.mod h1:e6bwxbdYH1HqWTz8faTChKGR0BjPc8p+6SyP8GTTR7Y= -github.com/libp2p/go-libp2p v0.4.0/go.mod h1:9EsEIf9p2UDuwtPd0DwJsAl0qXVxgAnuDGRvHbfATfI= -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.14.3/go.mod h1:d12V4PdKbpL0T1/gsUNN8DfgMuRPDX8bS2QxCZlwRH0= -github.com/libp2p/go-libp2p v0.19.4/go.mod h1:MIt8y481VDhUe4ErWi1a4bvt/CjjFfOq6kZTothWIXY= -github.com/libp2p/go-libp2p v0.22.0 h1:2Tce0kHOp5zASFKJbNzRElvh0iZwdtG5uZheNW8chIw= -github.com/libp2p/go-libp2p v0.22.0/go.mod h1:UDolmweypBSjQb2f7xutPnwZ/fxioLbMBxSjRksxxU4= -github.com/libp2p/go-libp2p-asn-util v0.1.0/go.mod h1:wu+AnM9Ii2KgO5jMmS1rz9dvzTdj8BXqsPR9HR0XB7I= -github.com/libp2p/go-libp2p-asn-util v0.2.0 h1:rg3+Os8jbnO5DxkC7K/Utdi+DkY3q/d1/1q+8WeNAsw= -github.com/libp2p/go-libp2p-asn-util v0.2.0/go.mod h1:WoaWxbHKBymSN41hWSq/lGKJEca7TNm58+gGJi2WsLI= -github.com/libp2p/go-libp2p-autonat v0.0.6/go.mod h1:uZneLdOkZHro35xIhpbtTzLlgYturpu4J5+0cZK3MqE= -github.com/libp2p/go-libp2p-autonat v0.1.0/go.mod h1:1tLf2yXxiE/oKGtDwPYWTSYG3PtvYlJmg7NeVtPRqH8= -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.2/go.mod h1:YxaJlpr81FhdOv3W3BTconZPfhaYivRdf53g+S2wobk= -github.com/libp2p/go-libp2p-autonat-svc v0.1.0/go.mod h1:fqi8Obl/z3R4PFVLm8xFtZ6PBL9MlV/xumymRFkKq5A= -github.com/libp2p/go-libp2p-blankhost v0.0.1/go.mod h1:Ibpbw/7cPPYwFb7PACIWdvxxv0t0XCCI10t7czjAjTc= -github.com/libp2p/go-libp2p-blankhost v0.1.1/go.mod h1:pf2fvdLJPsC1FsVrNP3DUUvMzUts2dsLLBEpo1vW1ro= -github.com/libp2p/go-libp2p-blankhost v0.1.3/go.mod h1:KML1//wiKR8vuuJO0y3LUd1uLv+tlkGTAr3jC0S5cLg= -github.com/libp2p/go-libp2p-blankhost v0.1.4/go.mod h1:oJF0saYsAXQCSfDq254GMNmLNz6ZTHTOvtF4ZydUvwU= -github.com/libp2p/go-libp2p-blankhost v0.2.0/go.mod h1:eduNKXGTioTuQAUcZ5epXi9vMl+t4d8ugUBRQ4SqaNQ= -github.com/libp2p/go-libp2p-blankhost v0.3.0/go.mod h1:urPC+7U01nCGgJ3ZsV8jdwTp6Ji9ID0dMTvq+aJ+nZU= -github.com/libp2p/go-libp2p-circuit v0.0.9/go.mod h1:uU+IBvEQzCu953/ps7bYzC/D/R0Ho2A9LfKVVCatlqU= -github.com/libp2p/go-libp2p-circuit v0.1.0/go.mod h1:Ahq4cY3V9VJcHcn1SBXjr78AbFkZeIRmfunbA7pmFh8= -github.com/libp2p/go-libp2p-circuit v0.1.1/go.mod h1:Ahq4cY3V9VJcHcn1SBXjr78AbFkZeIRmfunbA7pmFh8= -github.com/libp2p/go-libp2p-circuit v0.1.3/go.mod h1:Xqh2TjSy8DD5iV2cCOMzdynd6h8OTBGoV1AWbWor3qM= -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/go.mod h1:t/ktoFIUzM6uLQ+o1G6NuBl2ANhBKN9Bc8jRIk31MoA= -github.com/libp2p/go-libp2p-circuit v0.6.0/go.mod h1:kB8hY+zCpMeScyvFrKrGicRdid6vNXbunKE4rXATZ0M= -github.com/libp2p/go-libp2p-connmgr v0.1.1/go.mod h1:wZxh8veAmU5qdrfJ0ZBLcU8oJe9L82ciVP/fl1VHjXk= -github.com/libp2p/go-libp2p-core v0.0.1/go.mod h1:g/VxnTZ/1ygHxH3dKok7Vno1VfpvGcGip57wjTU4fco= -github.com/libp2p/go-libp2p-core v0.0.2/go.mod h1:9dAcntw/n46XycV4RnlBq3BpgrmyUi9LuoTNdPrbUco= -github.com/libp2p/go-libp2p-core v0.0.3/go.mod h1:j+YQMNz9WNSkNezXOsahp9kwZBKBvxLpKD316QWSJXE= -github.com/libp2p/go-libp2p-core v0.0.4/go.mod h1:jyuCQP356gzfCFtRKyvAbNkyeuxb7OlyhWZ3nls5d2I= -github.com/libp2p/go-libp2p-core v0.0.6/go.mod h1:0d9xmaYAVY5qmbp/fcgxHT3ZJsLjYeYPMJAUKpaCHrE= -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.3/go.mod h1:GqhyQqyIAPsxFYXHMjfXgMv03lxsvM0mFzuYA9Ib42A= -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-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.2/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/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-core v0.8.1/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-core v0.8.2/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-core v0.8.5/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-core v0.8.6/go.mod h1:dgHr0l0hIKfWpGpqAMbpo19pen9wJfdCGv51mTmdpmM= -github.com/libp2p/go-libp2p-core v0.10.0/go.mod h1:ECdxehoYosLYHgDDFa2N4yE8Y7aQRAMf0sX9mf2sbGg= -github.com/libp2p/go-libp2p-core v0.11.0/go.mod h1:ECdxehoYosLYHgDDFa2N4yE8Y7aQRAMf0sX9mf2sbGg= -github.com/libp2p/go-libp2p-core v0.12.0/go.mod h1:ECdxehoYosLYHgDDFa2N4yE8Y7aQRAMf0sX9mf2sbGg= -github.com/libp2p/go-libp2p-core v0.14.0/go.mod h1:tLasfcVdTXnixsLB0QYaT1syJOhsbrhG7q6pGrHtBg8= -github.com/libp2p/go-libp2p-core v0.15.1/go.mod h1:agSaboYM4hzB1cWekgVReqV5M4g5M+2eNNejV+1EEhs= -github.com/libp2p/go-libp2p-core v0.20.1 h1:fQz4BJyIFmSZAiTbKV8qoYhEH5Dtv/cVhZbG3Ib/+Cw= -github.com/libp2p/go-libp2p-core v0.20.1/go.mod h1:6zR8H7CvQWgYLsbG4on6oLNSGcyKaYFSEYyDt51+bIY= -github.com/libp2p/go-libp2p-crypto v0.0.1/go.mod h1:yJkNyDmO341d5wwXxDUGO0LykUVT72ImHNUqh5D/dBE= -github.com/libp2p/go-libp2p-crypto v0.0.2/go.mod h1:eETI5OUfBnvARGOHrJz2eWNyTUxEGZnBxMcbUjfIj4I= -github.com/libp2p/go-libp2p-crypto v0.1.0/go.mod h1:sPUokVISZiy+nNuTTH/TY+leRSxnFj/2GLjtOTW90hI= -github.com/libp2p/go-libp2p-daemon v0.2.2/go.mod h1:kyrpsLB2JeNYR2rvXSVWyY0iZuRIMhqzWR3im9BV6NQ= -github.com/libp2p/go-libp2p-discovery v0.0.5/go.mod h1:YtF20GUxjgoKZ4zmXj8j3Nb2TUSBHFlOCetzYdbZL5I= -github.com/libp2p/go-libp2p-discovery v0.1.0/go.mod h1:4F/x+aldVHjHDHuX85x1zWoFTGElt8HnoDzwkFZm29g= -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/go.mod h1:+srtPIU9gDaBNu//UHvcdliKBIcr4SfDcm0/PfPJLug= -github.com/libp2p/go-libp2p-host v0.0.1/go.mod h1:qWd+H1yuU0m5CwzAkvbSjqKairayEHdR5MMl7Cwa7Go= -github.com/libp2p/go-libp2p-host v0.0.3/go.mod h1:Y/qPyA6C8j2coYyos1dfRm0I8+nvd4TGrDGt4tA7JR8= -github.com/libp2p/go-libp2p-interface-connmgr v0.0.1/go.mod h1:GarlRLH0LdeWcLnYM/SaBykKFl9U5JFnbBGruAk/D5k= -github.com/libp2p/go-libp2p-interface-connmgr v0.0.4/go.mod h1:GarlRLH0LdeWcLnYM/SaBykKFl9U5JFnbBGruAk/D5k= -github.com/libp2p/go-libp2p-interface-connmgr v0.0.5/go.mod h1:GarlRLH0LdeWcLnYM/SaBykKFl9U5JFnbBGruAk/D5k= -github.com/libp2p/go-libp2p-interface-pnet v0.0.1/go.mod h1:el9jHpQAXK5dnTpKA4yfCNBZXvrzdOU75zz+C6ryp3k= -github.com/libp2p/go-libp2p-kad-dht v0.2.1/go.mod h1:k7ONOlup7HKzQ68dE6lSnp07cdxdkmnRa+6B4Fh9/w0= -github.com/libp2p/go-libp2p-kbucket v0.2.1/go.mod h1:/Rtu8tqbJ4WQ2KTCOMJhggMukOLNLNPY1EtEWWLxUvc= -github.com/libp2p/go-libp2p-loggables v0.0.1/go.mod h1:lDipDlBNYbpyqyPX/KcoO+eq0sJYEVR2JgOexcivchg= -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-metrics v0.0.1/go.mod h1:jQJ95SXXA/K1VZi13h52WZMa9ja78zjyy5rspMsC/08= -github.com/libp2p/go-libp2p-mplex v0.1.1/go.mod h1:KUQWpGkCzfV7UIpi8SKsAVxyBgz1c9R5EvxgnwLsb/I= -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.4.0/go.mod h1:yCyWJE2sc6TBTnFpjvLuEJgTSw/u+MamvzILKdX7asw= -github.com/libp2p/go-libp2p-mplex v0.4.1/go.mod h1:cmy+3GfqfM1PceHTLL7zQzAAYaryDu6iPSC+CIb094g= -github.com/libp2p/go-libp2p-mplex v0.5.0/go.mod h1:eLImPJLkj3iG5t5lq68w3Vm5NAQ5BcKwrrb2VmOYb3M= -github.com/libp2p/go-libp2p-nat v0.0.4/go.mod h1:N9Js/zVtAXqaeT99cXgTV9e75KpnWCvVOiGzlcHmBbY= -github.com/libp2p/go-libp2p-nat v0.0.5/go.mod h1:1qubaE5bTZMJE+E/uu2URroMbzdubFz1ChgiN79yKPE= -github.com/libp2p/go-libp2p-nat v0.0.6/go.mod h1:iV59LVhB3IkFvS6S6sauVTSOrNEANnINbI/fkaLimiw= -github.com/libp2p/go-libp2p-nat v0.1.0/go.mod h1:DQzAG+QbDYjN1/C3B6vXucLtz3u9rEonLVPtZVzQqks= -github.com/libp2p/go-libp2p-net v0.0.1/go.mod h1:Yt3zgmlsHOgUWSXmt5V/Jpz9upuJBE8EgNU9DrCcR8c= -github.com/libp2p/go-libp2p-net v0.0.2/go.mod h1:Yt3zgmlsHOgUWSXmt5V/Jpz9upuJBE8EgNU9DrCcR8c= -github.com/libp2p/go-libp2p-netutil v0.0.1/go.mod h1:GdusFvujWZI9Vt0X5BKqwWWmZFxecf9Gt03cKxm2f/Q= -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.2.0/go.mod h1:IEbYhBBzGyvdLBoxxULL/SGbJARhUeqlO8lVSREYu2Q= -github.com/libp2p/go-libp2p-noise v0.4.0/go.mod h1:BzzY5pyzCYSyJbQy9oD8z5oP2idsafjt4/X42h9DjZU= -github.com/libp2p/go-libp2p-peer v0.0.1/go.mod h1:nXQvOBbwVqoP+T5Y5nCjeH4sP9IX/J0AMzcDUVruVoo= -github.com/libp2p/go-libp2p-peer v0.1.1/go.mod h1:jkF12jGB4Gk/IOo+yomm+7oLWxF278F7UnrYUQ1Q8es= -github.com/libp2p/go-libp2p-peer v0.2.0/go.mod h1:RCffaCvUyW2CJmG2gAWVqwePwW7JMgxjsHm7+J5kjWY= -github.com/libp2p/go-libp2p-peerstore v0.0.1/go.mod h1:RabLyPVJLuNQ+GFyoEkfi8H4Ti6k/HtZJ7YKgtSq+20= -github.com/libp2p/go-libp2p-peerstore v0.0.6/go.mod h1:RabLyPVJLuNQ+GFyoEkfi8H4Ti6k/HtZJ7YKgtSq+20= -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.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/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= -github.com/libp2p/go-libp2p-peerstore v0.2.7/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= -github.com/libp2p/go-libp2p-peerstore v0.4.0/go.mod h1:rDJUFyzEWPpXpEwywkcTYYzDHlwza8riYMaUzaN6hX0= -github.com/libp2p/go-libp2p-peerstore v0.6.0/go.mod h1:DGEmKdXrcYpK9Jha3sS7MhqYdInxJy84bIPtSu65bKc= -github.com/libp2p/go-libp2p-pnet v0.2.0/go.mod h1:Qqvq6JH/oMZGwqs3N1Fqhv8NVhrdYcO0BW4wssv21LA= -github.com/libp2p/go-libp2p-protocol v0.0.1/go.mod h1:Af9n4PiruirSDjHycM1QuiMi/1VZNHYcK8cLgFJLZ4s= -github.com/libp2p/go-libp2p-protocol v0.1.0/go.mod h1:KQPHpAabB57XQxGrXCNvbL6UEXfQqUgC/1adR2Xtflk= -github.com/libp2p/go-libp2p-pubsub v0.1.1/go.mod h1:ZwlKzRSe1eGvSIdU5bD7+8RZN/Uzw0t1Bp9R1znpR/Q= -github.com/libp2p/go-libp2p-quic-transport v0.1.1/go.mod h1:wqG/jzhF3Pu2NrhJEvE+IE0NTHNXslOPn9JQzyCAxzU= -github.com/libp2p/go-libp2p-quic-transport v0.10.0/go.mod h1:RfJbZ8IqXIhxBRm5hqUEJqjiiY8xmEuq3HUDS993MkA= -github.com/libp2p/go-libp2p-quic-transport v0.13.0/go.mod h1:39/ZWJ1TW/jx1iFkKzzUg00W6tDJh73FC0xYudjr7Hc= -github.com/libp2p/go-libp2p-quic-transport v0.16.0/go.mod h1:1BXjVMzr+w7EkPfiHkKnwsWjPjtfaNT0q8RS3tGDvEQ= -github.com/libp2p/go-libp2p-quic-transport v0.17.0/go.mod h1:x4pw61P3/GRCcSLypcQJE/Q2+E9f4X+5aRcZLXf20LM= -github.com/libp2p/go-libp2p-record v0.0.1/go.mod h1:grzqg263Rug/sRex85QrDOLntdFAymLDLm7lxMgU79Q= -github.com/libp2p/go-libp2p-record v0.1.0/go.mod h1:ujNc8iuE5dlKWVy6wuL6dd58t0n7xI4hAIl8pE6wu5Q= -github.com/libp2p/go-libp2p-record v0.1.1/go.mod h1:VRgKajOyMVgP/F0L5g3kH7SVskp17vFi2xheb5uMJtg= -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-resource-manager v0.2.1/go.mod h1:K+eCkiapf+ey/LADO4TaMpMTP9/Qde/uLlrnRqV4PLQ= -github.com/libp2p/go-libp2p-routing v0.0.1/go.mod h1:N51q3yTr4Zdr7V8Jt2JIktVU+3xBBylx1MZeVA6t1Ys= -github.com/libp2p/go-libp2p-routing v0.1.0/go.mod h1:zfLhI1RI8RLEzmEaaPwzonRvXeeSHddONWkcTcB54nE= -github.com/libp2p/go-libp2p-secio v0.0.3/go.mod h1:hS7HQ00MgLhRO/Wyu1bTX6ctJKhVpm+j2/S2A5UqYb0= -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.0.6/go.mod h1:s5GZvzg9xXe8sbeESuFpjt8CJPTCa8mhEusweJqyFy8= -github.com/libp2p/go-libp2p-swarm v0.1.0/go.mod h1:wQVsCdjsuZoc730CgOvh5ox6K8evllckjebkdiY5ta4= -github.com/libp2p/go-libp2p-swarm v0.2.1/go.mod h1:x07b4zkMFo2EvgPV2bMTlNmdQc8i+74Jjio7xGvsTgU= -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.5.0/go.mod h1:sU9i6BoHE0Ve5SKz3y9WfKrh8dUat6JknzUehFx8xW4= -github.com/libp2p/go-libp2p-swarm v0.8.0/go.mod h1:sOMp6dPuqco0r0GHTzfVheVBh6UEL0L1lXUZ5ot2Fvc= -github.com/libp2p/go-libp2p-swarm v0.10.0/go.mod h1:71ceMcV6Rg/0rIQ97rsZWMzto1l9LnNquef+efcRbmA= -github.com/libp2p/go-libp2p-swarm v0.10.2/go.mod h1:Pdkq0QU5a+qu+oyqIV3bknMsnzk9lnNyKvB9acJ5aZs= -github.com/libp2p/go-libp2p-testing v0.0.1/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= -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/go.mod h1:Q+PFXYoiYFN5CAEG2w3gLPEzotlKsNSbKQ/lImlOWF0= -github.com/libp2p/go-libp2p-testing v0.5.0/go.mod h1:QBk8fqIL1XNcno/l3/hhaIEn4aLRijpYOR+zVjjlh+A= -github.com/libp2p/go-libp2p-testing v0.7.0/go.mod h1:OLbdn9DbgdMwv00v+tlp1l3oe2Cl+FAjoWIA2pa0X6E= -github.com/libp2p/go-libp2p-testing v0.9.0/go.mod h1:Td7kbdkWqYTJYQGTwzlgXwaqldraIanyjuRiAbK/XQU= -github.com/libp2p/go-libp2p-testing v0.9.2/go.mod h1:Td7kbdkWqYTJYQGTwzlgXwaqldraIanyjuRiAbK/XQU= -github.com/libp2p/go-libp2p-testing v0.11.0 h1:+R7FRl/U3Y00neyBSM2qgDzqz3HkWH24U9nMlascHL4= -github.com/libp2p/go-libp2p-tls v0.1.3/go.mod h1:wZfuewxOndz5RTnCAxFliGjvYSDA40sKitV4c50uI1M= -github.com/libp2p/go-libp2p-tls v0.3.0/go.mod h1:fwF5X6PWGxm6IDRwF3V8AVCCj/hOd5oFlg+wo2FxJDY= -github.com/libp2p/go-libp2p-tls v0.4.1/go.mod h1:EKCixHEysLNDlLUoKxv+3f/Lp90O2EXNjTr0UQDnrIw= -github.com/libp2p/go-libp2p-transport v0.0.1/go.mod h1:UzbUs9X+PHOSw7S3ZmeOxfnwaQY5vGDzZmKPod3N3tk= -github.com/libp2p/go-libp2p-transport v0.0.5/go.mod h1:StoY3sx6IqsP6XKoabsPnHCwqKXWUMWU7Rfcsubee/A= -github.com/libp2p/go-libp2p-transport-upgrader v0.0.4/go.mod h1:RGq+tupk+oj7PzL2kn/m1w6YXxcIAYJYeI90h6BGgUc= -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.2/go.mod h1:NR8ne1VwfreD5VIWIU62Agt/J18ekORFU/j1i2y8zvk= -github.com/libp2p/go-libp2p-transport-upgrader v0.5.0/go.mod h1:Rc+XODlB3yce7dvFV4q/RmyJGsFcCZRkeZMu/Zdg0mo= -github.com/libp2p/go-libp2p-transport-upgrader v0.7.0/go.mod h1:GIR2aTRp1J5yjVlkUoFqMkdobfob6RnAwYg/RZPhrzg= -github.com/libp2p/go-libp2p-transport-upgrader v0.7.1/go.mod h1:GIR2aTRp1J5yjVlkUoFqMkdobfob6RnAwYg/RZPhrzg= -github.com/libp2p/go-libp2p-yamux v0.1.2/go.mod h1:xUoV/RmYkg6BW/qGxA9XJyg+HzXFYkeXbnhjmnYzKp8= -github.com/libp2p/go-libp2p-yamux v0.1.3/go.mod h1:VGSQVrqkh6y4nm0189qqxMtvyBft44MOYYPpYKXiVt4= -github.com/libp2p/go-libp2p-yamux v0.2.0/go.mod h1:Db2gU+XfLpm6E4rG5uGCFX6uXA8MEXOxFcRoXUODaK8= -github.com/libp2p/go-libp2p-yamux v0.2.1/go.mod h1:1FBXiHDk1VyRM1C0aez2bCfHQ4vMZKkAQzZbkSQt5fI= -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.4/go.mod h1:tfrXbyaTqqSU654GTvK3ocnSZL3BuHoeTSqhcel1wsE= -github.com/libp2p/go-libp2p-yamux v0.8.0/go.mod h1:yTkPgN2ib8FHyU1ZcVD7aelzyAqXXwEPbyx+aSKm9h8= -github.com/libp2p/go-libp2p-yamux v0.8.1/go.mod h1:rUozF8Jah2dL9LLGyBaBeTQeARdwhefMCTQVQt6QobE= -github.com/libp2p/go-libp2p-yamux v0.9.1/go.mod h1:wRc6wvyxQINFcKe7daL4BeQ02Iyp+wxyC8WCNfngBrA= -github.com/libp2p/go-maddr-filter v0.0.1/go.mod h1:6eT12kSQMA9x2pvFQa+xesMKUBlj9VImZbj3B9FBH/Q= -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.0.4/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/go.mod h1:0Oy/A9PQlwBytDRp4wSkFnzHYDKcpLot35JQ6msjvYQ= -github.com/libp2p/go-mplex v0.4.0/go.mod h1:y26Lx+wNVtMYMaPu300Cbot5LkEZ4tJaNYeHeT9dh6E= -github.com/libp2p/go-msgio v0.0.2/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= -github.com/libp2p/go-msgio v0.0.3/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/go.mod h1:4ecVB6d9f4BDSL5fqvPiC4A3KivjWn+Venn/1ALLMWA= -github.com/libp2p/go-msgio v0.2.0 h1:W6shmB+FeynDrUVl2dgFQvzfBZcXiyqY4VmpQLu9FqU= -github.com/libp2p/go-msgio v0.2.0/go.mod h1:dBVM1gW3Jk9XqHkU4eKdGvVHdLa51hoGfll6jMJMSlY= -github.com/libp2p/go-nat v0.0.3/go.mod h1:88nUEt0k0JD45Bk93NIwDqjlhiOwOoV36GchpcVc1yI= -github.com/libp2p/go-nat v0.0.4/go.mod h1:Nmw50VAvKuk38jUBcmNh6p9lUJLoODbJRvYAa/+KSDo= -github.com/libp2p/go-nat v0.0.5/go.mod h1:B7NxsVNPZmRLvMOwiEO1scOSyjA56zxYAGv1yQgRkEU= -github.com/libp2p/go-nat v0.1.0 h1:MfVsH6DLcpa04Xr+p8hmVRG4juse0s3J8HyNWYHffXg= -github.com/libp2p/go-nat v0.1.0/go.mod h1:X7teVkwRHNInVNWQiO/tAiAVRwSr5zoRz4YSTC3uRBM= -github.com/libp2p/go-netroute v0.1.2/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk= -github.com/libp2p/go-netroute v0.1.3/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk= -github.com/libp2p/go-netroute v0.1.5/go.mod h1:V1SR3AaECRkEQCoFFzYwVYWvYIEtlxx89+O3qcpCl4A= -github.com/libp2p/go-netroute v0.1.6/go.mod h1:AqhkMh0VuWmfgtxKPp3Oc1LdU5QSWS7wl0QLhSZqXxQ= -github.com/libp2p/go-netroute v0.2.0 h1:0FpsbsvuSnAhXFnCY0VLFbJOzaK0VnP0r1QT/o4nWRE= -github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI= -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/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= -github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo= -github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= -github.com/libp2p/go-reuseport v0.0.1/go.mod h1:jn6RmB1ufnQwl0Q1f+YxAj8isJgDCQzaaxIFYDhcYEA= -github.com/libp2p/go-reuseport v0.0.2/go.mod h1:SPD+5RwGC7rcnzngoYC86GjPzjSywuQyMVAheVBD9nQ= -github.com/libp2p/go-reuseport v0.1.0/go.mod h1:bQVn9hmfcTaoo0c9v5pBhOarsU1eNOBZdaAd2hzXRKU= -github.com/libp2p/go-reuseport v0.2.0 h1:18PRvIMlpY6ZK85nIAicSBuXXvrYoSw3dsBAR7zc560= -github.com/libp2p/go-reuseport v0.2.0/go.mod h1:bvVho6eLMm6Bz5hmU0LYN3ixd3nPPvtIlaURZZgOY4k= -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/go.mod h1:trPa7r/7TJK/d+0hdBLOCGvpQQVOU74OXbNCIMkufGw= -github.com/libp2p/go-reuseport-transport v0.1.0/go.mod h1:vev0C0uMkzriDY59yFHD9v+ujJvYmDQVLowvAjEOmfw= -github.com/libp2p/go-sockaddr v0.0.2/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= -github.com/libp2p/go-sockaddr v0.1.0/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= -github.com/libp2p/go-sockaddr v0.1.1/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 v0.1.0/go.mod h1:8JAVsjeRBCWwPoZeH0W1imLOcriqXJyFvB0mR4A04sQ= -github.com/libp2p/go-stream-muxer-multistream v0.1.1/go.mod h1:zmGdfkQ1AzOECIAcccoL8L//laqawOsO03zX8Sa+eGw= -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/go.mod h1:yDh8abSIzmZtqtOt64gFJUXEryejzNb0lisTt+fAMJA= -github.com/libp2p/go-stream-muxer-multistream v0.4.0/go.mod h1:nb+dGViZleRP4XcyHuZSVrJCBl55nRBOMmiSL/dyziw= -github.com/libp2p/go-tcp-transport v0.0.4/go.mod h1:+E8HvC8ezEVOxIo3V5vCK9l1y/19K427vCzQ+xHKH/o= -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.3/go.mod h1:9dvr03yqrPyYGIEN6Dy5UvdJZjyPFvl1S/igQ5QD1SU= -github.com/libp2p/go-tcp-transport v0.4.0/go.mod h1:0y52Rwrn4076xdJYu/51/qJIdxz+EWDAOG2S45sV3VI= -github.com/libp2p/go-tcp-transport v0.5.0/go.mod h1:UPPL0DIjQqiWRwVAb+CEQlaAG0rp/mCqJfIhFcLHc4Y= -github.com/libp2p/go-tcp-transport v0.5.1/go.mod h1:UPPL0DIjQqiWRwVAb+CEQlaAG0rp/mCqJfIhFcLHc4Y= -github.com/libp2p/go-testutil v0.0.1/go.mod h1:iAcJc/DKJQanJ5ws2V+u5ywdL2n12X1WbbEG+Jjy69I= -github.com/libp2p/go-testutil v0.1.0/go.mod h1:81b2n5HypcVyrCg/MJx4Wgfp/VHojytjVe/gLzZ2Ehc= -github.com/libp2p/go-ws-transport v0.0.5/go.mod h1:Qbl4BxPfXXhhd/o0wcrgoaItHqA9tnZjoFZnxykuaXU= -github.com/libp2p/go-ws-transport v0.1.0/go.mod h1:rjw1MG1LU9YDC6gzmwObkPd/Sqwhw7yT74kj3raBFuo= -github.com/libp2p/go-ws-transport v0.1.2/go.mod h1:dsh2Ld8F+XNmzpkaAijmg5Is+e9l6/1tK/6VFOdN69Y= -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.4.0/go.mod h1:EcIEKqf/7GDjth6ksuS/6p7R49V4CBY6/E7R/iyhYUA= -github.com/libp2p/go-ws-transport v0.6.0/go.mod h1:dXqtI9e2JV9FtF1NOtWVZSKXh5zXvnuwPXfj8GPBbYU= -github.com/libp2p/go-yamux v1.2.1/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= -github.com/libp2p/go-yamux v1.2.2/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= -github.com/libp2p/go-yamux v1.2.3/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/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE= -github.com/libp2p/go-yamux/v2 v2.2.0/go.mod h1:3So6P6TV6r75R9jiBpiIKgU/66lOarCZjqROGxzPpPQ= -github.com/libp2p/go-yamux/v3 v3.0.1/go.mod h1:s2LsDhHbh+RfCsQoICSYt58U2f8ijtPANFD8BmE74Bo= -github.com/libp2p/go-yamux/v3 v3.0.2/go.mod h1:s2LsDhHbh+RfCsQoICSYt58U2f8ijtPANFD8BmE74Bo= -github.com/libp2p/go-yamux/v3 v3.1.1/go.mod h1:jeLEQgLXqE2YqX1ilAClIfCMDY+0uXQUKmmb/qp0gT4= -github.com/libp2p/go-yamux/v3 v3.1.2 h1:lNEy28MBk1HavUAlzKgShp+F6mn/ea1nDYWftZhFW9Q= -github.com/libp2p/go-yamux/v3 v3.1.2/go.mod h1:jeLEQgLXqE2YqX1ilAClIfCMDY+0uXQUKmmb/qp0gT4= -github.com/libp2p/zeroconf/v2 v2.1.1/go.mod h1:fuJqLnUwZTshS3U/bMRJ3+ow/v9oid1n0DmyYyNO1Xs= -github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= -github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= -github.com/lucas-clemente/quic-go v0.11.2/go.mod h1:PpMmPfPKO9nKJ/psF49ESTAGQSdfXxlg1otPbEB2nOw= -github.com/lucas-clemente/quic-go v0.19.3/go.mod h1:ADXpNbTQjq1hIzCpB+y/k5iz4n4z4IwqoLb94Kh5Hu8= -github.com/lucas-clemente/quic-go v0.23.0/go.mod h1:paZuzjXCE5mj6sikVLMvqXk8lJV2AsqtJ6bDhjEfxx0= -github.com/lucas-clemente/quic-go v0.25.0/go.mod h1:YtzP8bxRVCBlO77yRanE264+fY/T2U9ZlW1AaHOsMOg= -github.com/lucas-clemente/quic-go v0.27.0/go.mod h1:AzgQoPda7N+3IqMMMkywBKggIFo2KT6pfnlrQ2QieeI= -github.com/lucas-clemente/quic-go v0.27.1/go.mod h1:AzgQoPda7N+3IqMMMkywBKggIFo2KT6pfnlrQ2QieeI= -github.com/lucas-clemente/quic-go v0.28.1 h1:Uo0lvVxWg5la9gflIF9lwa39ONq85Xq2D91YNEIslzU= -github.com/lucas-clemente/quic-go v0.28.1/go.mod h1:oGz5DKK41cJt5+773+BSO9BXDsREY4HLf7+0odGAPO0= +github.com/libp2p/go-flow-metrics v0.2.0 h1:EIZzjmeOE6c8Dav0sNv35vhZxATIXWZg6j/C08XmmDw= +github.com/libp2p/go-flow-metrics v0.2.0/go.mod h1:st3qqfu8+pMfh+9Mzqb2GTiwrAGjIPszEjZmtksN8Jc= +github.com/libp2p/go-libp2p v0.37.0 h1:8K3mcZgwTldydMCNOiNi/ZJrOB9BY+GlI3UxYzxBi9A= +github.com/libp2p/go-libp2p v0.37.0/go.mod h1:GOKmSN99scDuYGTwaTbQPR8Nt6dxrK3ue7OjW2NGDg4= +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-kad-dht v0.27.0 h1:1Ea32tVTPiAfaLpPMbaBWFJgbsi/JpMqC2YBuFdf32o= +github.com/libp2p/go-libp2p-kad-dht v0.27.0/go.mod h1:ixhjLuzaXSGtWsKsXTj7erySNuVC4UP7NO015cRrF14= +github.com/libp2p/go-libp2p-kbucket v0.6.4 h1:OjfiYxU42TKQSB8t8WYd8MKhYhMJeO2If+NiuKfb6iQ= +github.com/libp2p/go-libp2p-kbucket v0.6.4/go.mod h1:jp6w82sczYaBsAypt5ayACcRJi0lgsba7o4TzJKEfWA= +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.4 h1:6LqS1Bzn5CfDJ4tzvP9uwh42IB7TJLNFJA6dEeGBv84= +github.com/libp2p/go-libp2p-routing-helpers v0.7.4/go.mod h1:we5WDj9tbolBXOuF1hGOkR+r7Uh1408tQbAKaT5n1LE= +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-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-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/lufeee/execinquery v1.2.1 h1:hf0Ems4SHcUGBxpGN7Jz78z1ppVkP/837ZlETPCEtOM= github.com/lufeee/execinquery v1.2.1/go.mod h1:EC7DrEKView09ocscGHC+apXMIaorh4xqSxS/dy8SbM= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= -github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= -github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/macabu/inamedparam v0.1.3 h1:2tk/phHkMlEL/1GNe/Yf6kkR/hkcUdAEY3L0hjYV1Mk= +github.com/macabu/inamedparam v0.1.3/go.mod h1:93FLICAIk/quk7eaPPQvbzihUdn/QkGDwIZEoLtpH6I= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/maratori/testpackage v1.1.0 h1:GJY4wlzQhuBusMF1oahQCBtUV/AQ/k69IZ68vxaac2Q= -github.com/maratori/testpackage v1.1.0/go.mod h1:PeAhzU8qkCwdGEMTEupsHJNlQu2gZopMC6RjbhmHeDc= -github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc= -github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk= -github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs= -github.com/marten-seemann/qtls-go1-15 v0.1.1/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I= -github.com/marten-seemann/qtls-go1-15 v0.1.4/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I= -github.com/marten-seemann/qtls-go1-16 v0.1.4/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk= -github.com/marten-seemann/qtls-go1-16 v0.1.5 h1:o9JrYPPco/Nukd/HpOHMHZoBDXQqoNtUCmny98/1uqQ= -github.com/marten-seemann/qtls-go1-16 v0.1.5/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk= -github.com/marten-seemann/qtls-go1-17 v0.1.0/go.mod h1:fz4HIxByo+LlWcreM4CZOYNuz3taBQ8rN2X6FqvaWo8= -github.com/marten-seemann/qtls-go1-17 v0.1.1/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s= -github.com/marten-seemann/qtls-go1-17 v0.1.2 h1:JADBlm0LYiVbuSySCHeY863dNkcpMmDR7s0bLKJeYlQ= -github.com/marten-seemann/qtls-go1-17 v0.1.2/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s= -github.com/marten-seemann/qtls-go1-18 v0.1.0-beta.1/go.mod h1:PUhIQk19LoFt2174H4+an8TYvWOGjb/hHwphBeaDHwI= -github.com/marten-seemann/qtls-go1-18 v0.1.1/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4= -github.com/marten-seemann/qtls-go1-18 v0.1.2 h1:JH6jmzbduz0ITVQ7ShevK10Av5+jBEKAHMntXmIV7kM= -github.com/marten-seemann/qtls-go1-18 v0.1.2/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4= -github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI= -github.com/marten-seemann/qtls-go1-19 v0.1.0 h1:rLFKD/9mp/uq1SYGYuVZhm83wkmU95pK5df3GufyYYU= -github.com/marten-seemann/qtls-go1-19 v0.1.0/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI= +github.com/maratori/testableexamples v1.0.0 h1:dU5alXRrD8WKSjOUnmJZuzdxWOEQ57+7s93SLMxb2vI= +github.com/maratori/testableexamples v1.0.0/go.mod h1:4rhjL1n20TUTT4vdh3RDqSizKLyXp7K2u6HgraZCGzE= +github.com/maratori/testpackage v1.1.1 h1:S58XVV5AD7HADMmD0fNnziNHqKvSdDuEKdPD1rNTU04= +github.com/maratori/testpackage v1.1.1/go.mod h1:s4gRK/ym6AMrqpOa/kEbQTV4Q4jb7WeLZzVhVVVOQMc= 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/matoous/godox v0.0.0-20210227103229-6504466cf951 h1:pWxk9e//NbPwfxat7RXkts09K+dEBJWakUWwICVqYbA= -github.com/matoous/godox v0.0.0-20210227103229-6504466cf951/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s= +github.com/matoous/godox v0.0.0-20240105082147-c5b5e0e7c0c0 h1:Ny7cm4KSWceJLYyI1sm+aFIVDWSGXLcOJ0O0UaS5wdU= +github.com/matoous/godox v0.0.0-20240105082147-c5b5e0e7c0c0/go.mod h1:jgE/3fUXiTurkdHOLT5WEkThTSuE7yxHv5iWPa80afs= github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= -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.2/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.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -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.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.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-pointer v0.0.1 h1:n+XhsuGeVO6MEAp7xyEukFINEa+Quek5psIR/ylA6o0= -github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +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.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= +github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mbilski/exhaustivestruct v1.2.0 h1:wCBmUnSYufAHO6J4AVWY6ff+oxWxsVFrwgOdMUQePUo= -github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc= -github.com/mgechev/revive v1.2.3 h1:NzIEEa9+WimQ6q2Ov7OcNeySS/IOcwtkQ8RAh0R5UJ4= -github.com/mgechev/revive v1.2.3/go.mod h1:iAWlQishqCuj4yhV24FTnKSXGpbAA+0SckXB8GQMX/Q= -github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/mgechev/revive v1.3.9 h1:18Y3R4a2USSBF+QZKFQwVkBROUda7uoBlkEuBD+YD1A= +github.com/mgechev/revive v1.3.9/go.mod h1:+uxEIr5UH0TjXWHTno3xh4u7eg6jDpXKzQccA9UGhHU= +github.com/mholt/acmez/v2 v2.0.3 h1:CgDBlEwg3QBp6s45tPQmFIBrkRIkBT4rW4orMM6p4sw= +github.com/mholt/acmez/v2 v2.0.3/go.mod h1:pQ1ysaDeGrIMvJ9dfJMk5kJNkn7L2sb3UhyrX6Q91cw= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -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.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.48/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= -github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= -github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= -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/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ= +github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ= 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.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/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= -github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= -github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +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/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -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/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/moricho/tparallel v0.2.1 h1:95FytivzT6rYzdJLdtfn6m1bfFJylOJK41+lgv/EHf4= -github.com/moricho/tparallel v0.2.1/go.mod h1:fXEIZxG2vdfl0ZF8b42f5a78EhjjD5mX8qUplsoSU4k= -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/moricho/tparallel v0.3.2 h1:odr8aZVFA3NZrNybggMkYO3rgPRcqjeQUlBBFVxKHTI= +github.com/moricho/tparallel v0.3.2/go.mod h1:OQ+K3b4Ln3l2TZveGCywybl68glfLEwFGqvnjok8b+U= 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.0.4 h1:+qMh4a2f37b4xTNs6mqitDinryCI+tfO2dRVMN9mjSE= -github.com/multiformats/go-base32 v0.0.4/go.mod h1:jNLFzjPZtp3aIARHbJRZIaPuspdH0J6q39uUM5pnABM= -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-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.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.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/go.mod h1:uPbspcUPd5AfaP6ql3ujFY+QWzmBD8uLLL4bXW0XfGc= -github.com/multiformats/go-multiaddr v0.3.3/go.mod h1:lCKNGP1EQ1eZ35Za2wlqnabm9xQkib3fyB+nZXHLag0= -github.com/multiformats/go-multiaddr v0.4.0/go.mod h1:YcpyLH8ZPudLxQlemYBPhSm0/oCXAT8Z4mzFpyoPyRc= -github.com/multiformats/go-multiaddr v0.4.1/go.mod h1:3afI9HfVW8csiF8UZqtpYRiDyew8pRX7qLIGHu9FLuM= -github.com/multiformats/go-multiaddr v0.5.0/go.mod h1:3KAxNkUqLTJ20AAwN4XVX4kZar+bR+gh4zgbfr3SNug= -github.com/multiformats/go-multiaddr v0.6.0 h1:qMnoOPj2s8xxPU5kZ57Cqdr0hHhARz7mFsPMIiYNqzg= -github.com/multiformats/go-multiaddr v0.6.0/go.mod h1:F4IpaKZuPP360tOMn2Tpyu0At8w23aRyVqeK0DbFeGM= -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.0.3/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= -github.com/multiformats/go-multiaddr-dns v0.1.0/go.mod h1:01k2RAqtoXIuPa3DCavAE9/6jc6nM0H3EgZyfUhN2oY= -github.com/multiformats/go-multiaddr-dns v0.2.0/go.mod h1:TJ5pr5bBO7Y1B18djPuRsVkduhQH2YqYSbxWJzYGdK0= -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.0.1/go.mod h1:aBYjqL4T/7j4Qx+R73XSv/8JsgnRFlf0w2KGLCmXl3Q= +github.com/multiformats/go-multiaddr v0.13.0 h1:BCBzs61E3AGHcYYTv8dqRH43ZfyrqM8RXVPT8t13tLQ= +github.com/multiformats/go-multiaddr v0.13.0/go.mod h1:sBXrNzucqkFJhvKOiwwLyqamGa/P5EIXNPLovyhQCII= +github.com/multiformats/go-multiaddr-dns v0.4.0 h1:P76EJ3qzBXpUXZ3twdCDx/kvagMsNo0LMFXpyms/zgU= +github.com/multiformats/go-multiaddr-dns v0.4.0/go.mod h1:7hfthtB4E4pQwirrz+J0CcDUfbWzTqEzVyYKKIKpgkc= 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/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/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc= -github.com/multiformats/go-multibase v0.1.1 h1:3ASCDsuLX8+j4kx58qnJ4YFq/JWTJpCyDW27ztsVTOI= -github.com/multiformats/go-multibase v0.1.1/go.mod h1:ZEjHE+IsUrgp5mhlEAYjMtZwK1k4haNkcaPg9aoe1a8= -github.com/multiformats/go-multicodec v0.3.0/go.mod h1:qGGaQmioCDh+TeFOnxrbU0DaIPw8yFgAZgFG0V7p1qQ= -github.com/multiformats/go-multicodec v0.3.1-0.20210902112759-1539a079fd61/go.mod h1:1Hj/eHRaVWSXiSNNfcEPcwZleTmdNP81xlxDLnWU9GQ= -github.com/multiformats/go-multicodec v0.3.1-0.20211210143421-a526f306ed2c/go.mod h1:1Hj/eHRaVWSXiSNNfcEPcwZleTmdNP81xlxDLnWU9GQ= -github.com/multiformats/go-multicodec v0.4.1/go.mod h1:1Hj/eHRaVWSXiSNNfcEPcwZleTmdNP81xlxDLnWU9GQ= -github.com/multiformats/go-multicodec v0.5.0/go.mod h1:DiY2HFaEp5EhEXb/iYzVAunmyX/aSFMxq2KMKfWEues= -github.com/multiformats/go-multicodec v0.6.0 h1:KhH2kSuCARyuJraYMFxrNO3DqIaYhOdS039kbhgVwpE= -github.com/multiformats/go-multicodec v0.6.0/go.mod h1:GUC8upxSBE4oG+q3kWZRw/+6yC1BqO550bjhWsJbZlw= -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-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.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.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.1 h1:aem8ZT0VA2nCHHk7bPJ1BjUbHNciqZC/d16Vve9l108= -github.com/multiformats/go-multihash v0.2.1/go.mod h1:WxoMcYG85AZVQUyRyo9s4wULvW5qrI9vb2Lt6evduFc= -github.com/multiformats/go-multistream v0.0.1/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg= -github.com/multiformats/go-multistream v0.0.4/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg= -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.1/go.mod h1:5GZPQZbkWOLOn3J2y4Y99vVW7vOfsAflxARk3x14o6k= -github.com/multiformats/go-multistream v0.2.2/go.mod h1:UIcnm7Zuo8HKG+HkWgfQsGL+/MIEhyTqbODbIUwSXKs= -github.com/multiformats/go-multistream v0.3.0/go.mod h1:ODRoqamLUsETKS9BNcII4gcRsJBU5VAwRIv7O39cEXg= -github.com/multiformats/go-multistream v0.3.3 h1:d5PZpjwRgVlbwfdTDjife7XszfZd8KYWfROYFlGcR8o= -github.com/multiformats/go-multistream v0.3.3/go.mod h1:ODRoqamLUsETKS9BNcII4gcRsJBU5VAwRIv7O39cEXg= -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/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/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.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= +github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/nakabonne/nestif v0.3.1 h1:wm28nZjhQY5HyYPx+weN3Q65k6ilSBxDb8v5S81B81U= github.com/nakabonne/nestif v0.3.1/go.mod h1:9EtoZochLn5iUprVDmDjqGKPofoUEBL8U4Ngq6aY7OE= -github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= -github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= -github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= -github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= -github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 h1:4kuARK6Y6FxaNu/BnU2OAaLF86eTVhP2hjTB6iMvItA= -github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8= 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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nishanths/exhaustive v0.8.1 h1:0QKNascWv9qIHY7zRoZSxeRr6kuk5aAT3YXLTiDmjTo= -github.com/nishanths/exhaustive v0.8.1/go.mod h1:qj+zJJUgJ76tR92+25+03oYUhzF4R7/2Wk7fGTfCHmg= +github.com/nishanths/exhaustive v0.12.0 h1:vIY9sALmw6T/yxiASewa4TQcFsVYZQQRUQJhKRf3Swg= +github.com/nishanths/exhaustive v0.12.0/go.mod h1:mEZ95wPIZW+x8kC4TgC+9YCUgiST7ecevsVDTgc2obs= github.com/nishanths/predeclared v0.2.2 h1:V2EPdZPliZymNAn79T8RkNApBjMmVKh5XRpLm/w98Vk= github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= -github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/nunnatsa/ginkgolinter v0.16.2 h1:8iLqHIZvN4fTLDC0Ke9tbSZVcyVHoBs0HIbnVSxfHJk= +github.com/nunnatsa/ginkgolinter v0.16.2/go.mod h1:4tWRinDN1FeJgU+iJANW/kz7xKN5nYRAOfJDQUS9dOQ= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -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/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -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.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY= -github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -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/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= -github.com/onsi/gomega v1.20.0 h1:8W0cWlwFkflGPLltQvLRB7ZVD5HuP6ng320w2IS245Q= -github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= -github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0= -github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= -github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= -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-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= +github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4= +github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= +github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= +github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= +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/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= -github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/otiai10/copy v1.2.0 h1:HvG945u96iNadPoG2/Ja2+AUJeW5YuFQMixq9yirC+k= github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= +github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= +github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= -github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 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/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= -github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.0.2 h1:+jQXlF3scKIcSEKkdHzXhCTDLPFi5r1wnK6yPS+49Gw= -github.com/pelletier/go-toml/v2 v2.0.2/go.mod h1:MovirKjgVRESsAvNZlAjtFwV867yGuwRkXbG66OzopI= -github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= -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/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d h1:CdDQnGF8Nq9ocOS/xlSptM1N3BbrA6/kmaep5ggwaIA= -github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw= -github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pion/datachannel v1.5.9 h1:LpIWAOYPyDrXtU+BW7X0Yt/vGtYxtXQ8ql7dFfYUVZA= +github.com/pion/datachannel v1.5.9/go.mod h1:kDUuk4CU4Uxp82NH4LQZbISULkX/HtzKa4P7ldf9izE= +github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= +github.com/pion/dtls/v2 v2.2.12 h1:KP7H5/c1EiVAAKUmXyCzPiQe5+bCJrpOeKg/L05dunk= +github.com/pion/dtls/v2 v2.2.12/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= +github.com/pion/ice/v2 v2.3.36 h1:SopeXiVbbcooUg2EIR8sq4b13RQ8gzrkkldOVg+bBsc= +github.com/pion/ice/v2 v2.3.36/go.mod h1:mBF7lnigdqgtB+YHkaY/Y6s6tsyRyo4u4rPGRuOjUBQ= +github.com/pion/interceptor v0.1.37 h1:aRA8Zpab/wE7/c0O3fh1PqY0AJI3fCSEM5lRWJVorwI= +github.com/pion/interceptor v0.1.37/go.mod h1:JzxbJ4umVTlZAf+/utHzNesY8tmRkM2lVmkS82TTj8Y= +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.9 h1:E2HX740TZKaqdcPmf4pw6ZZuG8u5RlMMt+l3dxeu6Wk= +github.com/pion/rtp v1.8.9/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= +github.com/pion/sctp v1.8.33 h1:dSE4wX6uTJBcNm8+YlMg7lw1wqyKHggsP5uKbdj+NZw= +github.com/pion/sctp v1.8.33/go.mod h1:beTnqSzewI53KWoG3nqB282oDMGrhNxBdb+JZnkCwRM= +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.20 h1:HNNny4s+OUmG280ETrCdgFndp4ufx3/uy85EawYEhTk= +github.com/pion/srtp/v2 v2.0.20/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.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.10 h1:ucLBLE8nuxiHfvkFKnkDQRYWYfp8ejf4YBOPfaQpw6Q= +github.com/pion/transport/v2 v2.2.10/go.mod h1:sq1kSLWs+cHW9E+2fJP95QudkzbK7wscs8yYgQToO5E= +github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= +github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0= +github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo= +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.3.4 h1:v2heQVnXTSqNRXcaFQVOhIOYkLMxOu1iJG8uy1djvkk= +github.com/pion/webrtc/v3 v3.3.4/go.mod h1:liNa+E1iwyzyXqNUwvoMRNQ10x8h8FOeJKL8RkIbamE= 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/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= -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-20190221155625-df39d6c2d992/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= -github.com/polydawn/refmt v0.0.0-20190408063855-01bf1e26dd14/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= -github.com/polydawn/refmt v0.0.0-20190807091052-3d65705ee9f1/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= -github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e h1:ZOcivgkkFRnjfoTcGsDq3UQYiBmekwLA+qg0OjyB/ls= -github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= -github.com/polyfloyd/go-errorlint v1.0.2 h1:kp1yvHflYhTmw5m3MmBy8SCyQkKPjwDthVuMH0ug6Yk= -github.com/polyfloyd/go-errorlint v1.0.2/go.mod h1:APVvOesVSAnne5SClsPxPdfvZTVDojXh1/G3qb5wjGI= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/polydawn/refmt v0.89.0 h1:ADJTApkvkeBZsN0tBTx8QjpD9JkmxbKp0cxfr9qszm4= +github.com/polydawn/refmt v0.89.0/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw= +github.com/polyfloyd/go-errorlint v1.6.0 h1:tftWV9DE7txiFzPpztTAwyoRLKNj9gpVm2cg8/OwcYY= +github.com/polyfloyd/go-errorlint v1.6.0/go.mod h1:HR7u8wuP1kb1NeN1zqTd1ZMlqUKPPHF+Id4vIPvDqVw= +github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= +github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= 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 v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU= -github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/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.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= -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.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -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.33.0/go.mod h1:gB3sOl7P0TvJabZpLY5uQMpUqRCPPCyRLCZYc7JZTNE= -github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= +github.com/prometheus/common v0.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA= +github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= 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.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.2.0/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 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/quasilyte/go-ruleguard v0.3.1-0.20210203134552-1b5a410e1cc8/go.mod h1:KsAh3x0e7Fkpgs+Q9pNLS5XpFSvYCEVl5gP9Pp1xp30= -github.com/quasilyte/go-ruleguard v0.3.17 h1:cDdoaSbQg11LXPDQqiCK54QmQXsEQQCTIgdcpeULGSI= -github.com/quasilyte/go-ruleguard v0.3.17/go.mod h1:sST5PvaR7yb/Az5ksX8oc88usJ4EGjmJv7cK7y3jyig= -github.com/quasilyte/go-ruleguard/dsl v0.3.0/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= -github.com/quasilyte/go-ruleguard/dsl v0.3.21/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= -github.com/quasilyte/go-ruleguard/rules v0.0.0-20201231183845-9e62ed36efe1/go.mod h1:7JTjp89EGyU1d6XfBiXihJNG37wB2VRkd125Q1u7Plc= -github.com/quasilyte/go-ruleguard/rules v0.0.0-20211022131956-028d6511ab71/go.mod h1:4cgAphtvu7Ftv7vOT2ZOYhC6CvBxZixcasr8qIOTA50= -github.com/quasilyte/gogrep v0.0.0-20220120141003-628d8b3623b5 h1:PDWGei+Rf2bBiuZIbZmM20J2ftEy9IeUCHA8HbQqed8= -github.com/quasilyte/gogrep v0.0.0-20220120141003-628d8b3623b5/go.mod h1:wSEyW6O61xRV6zb6My3HxrQ5/8ke7NE2OayqCHa3xRM= -github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95 h1:L8QM9bvf68pVdQ3bCFZMDmnt9yqcMBro1pC7F+IPYMY= -github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/quasilyte/go-ruleguard v0.4.2 h1:htXcXDK6/rO12kiTHKfHuqR4kr3Y4M0J0rOL6CH/BYs= +github.com/quasilyte/go-ruleguard v0.4.2/go.mod h1:GJLgqsLeo4qgavUoL8JeGFNS7qcisx3awV/w9eWTmNI= +github.com/quasilyte/go-ruleguard/dsl v0.3.22 h1:wd8zkOhSNr+I+8Qeciml08ivDt1pSXe60+5DqOpCjPE= +github.com/quasilyte/go-ruleguard/dsl v0.3.22/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= +github.com/quasilyte/gogrep v0.5.0 h1:eTKODPXbI8ffJMN+W2aE0+oL0z/nh8/5eNdiO34SOAo= +github.com/quasilyte/gogrep v0.5.0/go.mod h1:Cm9lpz9NZjEoL1tgZ2OgeUKPIxL1meE7eo60Z6Sk+Ng= +github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 h1:TCg2WBOl980XxGFEZSS6KlBGIV0diGdySzxATTWoqaU= +github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 h1:M8mH9eK4OUR4lu7Gd+PU1fV2/qnDNfzT635KRSObncs= github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567/go.mod h1:DWNGW8A4Y+GyBgPuaQJuWiy0XYftx4Xm/y5Jqk9I6VQ= -github.com/raulk/clock v1.1.0/go.mod h1:3MpVxdZ/ODBQDxbN+kzshf5OSZwPjtMDx6BBXBmOeY0= -github.com/raulk/go-watchdog v1.2.0/go.mod h1:lzSbAl5sh4rtI8tYHU01BWIDzgzqaQLj6RcA1i4mlqI= +github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= +github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= +github.com/quic-go/quic-go v0.48.1 h1:y/8xmfWI9qmGTc+lBr4jKRUWLGSlSigv847ULJ4hYXA= +github.com/quic-go/quic-go v0.48.1/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs= +github.com/quic-go/webtransport-go v0.8.1-0.20241018022711-4ac2c9250e66 h1:4WFk6u3sOT6pLa1kQ50ZVdm8BQFgJNA117cepZxtLIg= +github.com/quic-go/webtransport-go v0.8.1-0.20241018022711-4ac2c9250e66/go.mod h1:Vp72IJajgeOL6ddqrAhmp7IM9zbTcgkQxD/YdxrVwMw= 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/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -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.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= -github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/rs/zerolog v1.21.0/go.mod h1:ZPhntP/xmq1nnND05hhpAh2QMhSsA4UN3MGZ6O2J3hM= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= 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 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryancurrah/gomodguard v1.2.4 h1:CpMSDKan0LtNGGhPrvupAoLeObRFjND8/tU1rEOtBp4= -github.com/ryancurrah/gomodguard v1.2.4/go.mod h1:+Kem4VjWwvFpUJRJSwa16s1tBJe+vbv02+naTow2f6M= -github.com/ryanrolds/sqlclosecheck v0.3.0 h1:AZx+Bixh8zdUBxUA1NxbxVAS78vTPq4rCb8OUZI9xFw= -github.com/ryanrolds/sqlclosecheck v0.3.0/go.mod h1:1gREqxyTGR3lVtpngyFo3hZAgk0KCtEdgEkHwDbigdA= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/sanposhiho/wastedassign/v2 v2.0.6 h1:+6/hQIHKNJAUixEj6EmOngGIisyeI+T3335lYTyxRoA= -github.com/sanposhiho/wastedassign/v2 v2.0.6/go.mod h1:KyZ0MWTwxxBmfwn33zh3k1dmsbF2ud9pAAGfoLfjhtI= +github.com/ryancurrah/gomodguard v1.3.3 h1:eiSQdJVNr9KTNxY2Niij8UReSwR8Xrte3exBrAZfqpg= +github.com/ryancurrah/gomodguard v1.3.3/go.mod h1:rsKQjj4l3LXe8N344Ow7agAy5p9yjsWOtRzUMYmA0QY= +github.com/ryanrolds/sqlclosecheck v0.5.1 h1:dibWW826u0P8jNLsLN+En7+RqWWTYrjCB9fJfSfdyCU= +github.com/ryanrolds/sqlclosecheck v0.5.1/go.mod h1:2g3dUjoS6AL4huFdv6wn55WpLIDjY7ZgUR4J8HOO/XQ= +github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk= +github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc= +github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU= +github.com/sanposhiho/wastedassign/v2 v2.0.7 h1:J+6nrY4VW+gC9xFzUc+XjPD3g3wF3je/NsJFwFK7Uxc= +github.com/sanposhiho/wastedassign/v2 v2.0.7/go.mod h1:KyZ0MWTwxxBmfwn33zh3k1dmsbF2ud9pAAGfoLfjhtI= +github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= +github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= github.com/sashamelentyev/interfacebloat v1.1.0 h1:xdRdJp0irL086OyW1H/RTZTr1h/tMEOsumirXcOJqAw= github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84dxiN1iBi9+G8zZIhPVoNjQ= -github.com/sashamelentyev/usestdlibvars v1.13.0 h1:uObNudVEEHf6JbOJy5bgKJloA1bWjxR9fwgNFpPzKnI= -github.com/sashamelentyev/usestdlibvars v1.13.0/go.mod h1:D2Wb7niIYmTB+gB8z7kh8tyP5ccof1dQ+SFk+WW5NtY= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/securego/gosec/v2 v2.13.1 h1:7mU32qn2dyC81MH9L2kefnQyRMUarfDER3iQyMHcjYM= -github.com/securego/gosec/v2 v2.13.1/go.mod h1:EO1sImBMBWFjOTFzMWfTRrZW6M15gm60ljzrmy/wtHo= +github.com/sashamelentyev/usestdlibvars v1.27.0 h1:t/3jZpSXtRPRf2xr0m63i32ZrusyurIGT9E5wAvXQnI= +github.com/sashamelentyev/usestdlibvars v1.27.0/go.mod h1:9nl0jgOfHKWNFS43Ojw0i7aRoS4j6EBye3YBhmAIRF8= +github.com/securego/gosec/v2 v2.20.1-0.20240820084340-81cda2f91fbe h1:exdneYmXwZ4+VaIWv9mQ47uIHkTQSN50DYdCjXJ1cdQ= +github.com/securego/gosec/v2 v2.20.1-0.20240820084340-81cda2f91fbe/go.mod h1:iyeMMRw8QEmueUSZ2VqmkQMiDyDcobfPnG00CV/NWdE= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c h1:W65qqJCIOVP4jpqPQ0YvHYKwcMEMVWIzWC5iNQQfBTU= github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs= @@ -1524,155 +708,106 @@ github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go. 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.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= -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/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/sivchari/containedctx v1.0.2 h1:0hLQKpgC53OVF1VT7CeoFHk9YKstur1XOgfYIc1yrHI= -github.com/sivchari/containedctx v1.0.2/go.mod h1:PwZOeqm4/DLoJOqMSIJs3aKqXRX4YO+uXww087KZ7Bw= -github.com/sivchari/nosnakecase v1.7.0 h1:7QkpWIRMe8x25gckkFd2A5Pi6Ymo0qgr4JrhGt95do8= -github.com/sivchari/nosnakecase v1.7.0/go.mod h1:CwDzrzPea40/GB6uynrNLiorAlgFRvRbFSgJx2Gs+QY= -github.com/sivchari/tenv v1.7.0 h1:d4laZMBK6jpe5PWepxlV9S+LC0yXqvYHiq8E6ceoVVE= -github.com/sivchari/tenv v1.7.0/go.mod h1:64yStXKSOxDfX47NlhVwND4dHwfZDdbp2Lyl018Icvg= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/assertions v1.0.0 h1:UVQPSSmc3qtTi+zPPkCXvZX9VvW/xT/NsRvKfwY81a8= -github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= -github.com/smartystreets/goconvey v0.0.0-20190222223459-a17d461953aa/go.mod h1:2RVY1rIf+2J2o/IM9+vPq9RzmHDSseB7FoXiSNIUsoU= -github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/smola/gocompat v0.2.0/go.mod h1:1B0MlxbmoZNo3h8guHp8HztB3BSYR5itql9qtVc0ypY= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/sonatard/noctx v0.0.1 h1:VC1Qhl6Oxx9vvWo3UDgrGXYCeKCe3Wbw7qAWL6FrmTY= -github.com/sonatard/noctx v0.0.1/go.mod h1:9D2D/EoULe8Yy2joDHJj7bv3sZoq9AaSb8B4lqBjiZI= -github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sivchari/containedctx v1.0.3 h1:x+etemjbsh2fB5ewm5FeLNi5bUjK0V8n0RB+Wwfd0XE= +github.com/sivchari/containedctx v1.0.3/go.mod h1:c1RDvCbnJLtH4lLcYD/GqwiBSSf4F5Qk0xld2rBqzJ4= +github.com/sivchari/tenv v1.10.0 h1:g/hzMA+dBCKqGXgW8AV/1xIWhAvDrx0zFKNR48NFMg0= +github.com/sivchari/tenv v1.10.0/go.mod h1:tdY24masnVoZFxYrHv/nD6Tc8FbkEtAQEEziXpyMgqY= +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.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= +github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= +github.com/sonatard/noctx v0.0.2 h1:L7Dz4De2zDQhW8S0t+KUjY0MAQJd6SgVwhzNIc4ok00= +github.com/sonatard/noctx v0.0.2/go.mod h1:kzFz+CzWSjQ2OzIm46uJZoXuBpa2+0y3T36U18dWqIo= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= -github.com/sourcegraph/go-diff v0.6.1 h1:hmA1LzxW0n1c3Q4YbrFgg4P99GSnebYa3x8gr0HZqLQ= -github.com/sourcegraph/go-diff v0.6.1/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/sourcegraph/go-diff v0.7.0 h1:9uLlrd5T46OXs5qpp8L/MTltk0zikUGi0sNNyCpA8G0= +github.com/sourcegraph/go-diff v0.7.0/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= 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/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= -github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= -github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= -github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= 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/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ= -github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI= -github.com/src-d/envconfig v1.0.0/go.mod h1:Q9YQZ7BKITldTBnoxsE5gOeB5y66RyPXeue/R4aaNBc= +github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= github.com/ssgreg/nlreturn/v2 v2.2.1 h1:X4XDI7jstt3ySqGU86YGAURbxw3oTDPK9sPEi6YEwQ0= github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I= github.com/stbenjam/no-sprintf-host-port v0.1.1 h1:tYugd/yrm1O0dV+ThCbaKZh195Dfm07ysF0U6JQXczc= github.com/stbenjam/no-sprintf-host-port v0.1.1/go.mod h1:TLhvtIvONRzdmkFiio4O8LHsN9N74I+PhRquPsxpL0I= -github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= 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 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +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.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/subosito/gotenv v1.4.0 h1:yAzM1+SmVcz5R4tXGsNMu1jUl2aOJXoiWUCEwwnGrvs= -github.com/subosito/gotenv v1.4.0/go.mod h1:mZd6rFysKEcUhUHXJk0C/08wAgyDBFuwEYL7vWWGaGo= -github.com/sylvia7788/contextcheck v1.0.6 h1:o2EZgVPyMKE/Mtoqym61DInKEjwEbsmyoxg3VrmjNO4= -github.com/sylvia7788/contextcheck v1.0.6/go.mod h1:9XDxwvxyuKD+8N+a7Gs7bfWLityh5t70g/GjdEt2N2M= -github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= +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/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= -github.com/tdakkota/asciicheck v0.1.1 h1:PKzG7JUTUmVspQTDqtkX9eSiLGossXTybutHwTXuO0A= -github.com/tdakkota/asciicheck v0.1.1/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM= +github.com/tdakkota/asciicheck v0.2.0 h1:o8jvnUANo0qXtnslk2d3nMKTFNlOnJjRrNcj0j9qkHM= +github.com/tdakkota/asciicheck v0.2.0/go.mod h1:Qb7Y9EgjCLJGup51gDHFzbI08/gbGhL/UVhYIPWG2rg= github.com/tenntenn/modver v1.0.1 h1:2klLppGhDgzJrScMpkj9Ujy3rXPUspSjAcev9tSEBgA= github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0= github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpRQGxTSkNYKJ51yaw6ChIqO+Je8UqsTKN/cDag= github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY= -github.com/tetafro/godot v1.4.11 h1:BVoBIqAf/2QdbFmSwAWnaIqDivZdOV0ZRwEm6jivLKw= -github.com/tetafro/godot v1.4.11/go.mod h1:LR3CJpxDVGlYOWn3ZZg1PgNZdTUvzsZWu8xaEohUpn8= -github.com/timakin/bodyclose v0.0.0-20210704033933-f49887972144 h1:kl4KhGNsJIbDHS9/4U9yQo1UcPQM0kOMJHn29EoH/Ro= -github.com/timakin/bodyclose v0.0.0-20210704033933-f49887972144/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= -github.com/timonwong/logrlint v0.1.0 h1:phZCcypL/vtx6cGxObJgWZ5wexZF5SXFPLOM+ru0e/M= -github.com/timonwong/logrlint v0.1.0/go.mod h1:Zleg4Gw+kRxNej+Ra7o+tEaW5k1qthTaYKU7rSD39LU= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tomarrell/wrapcheck/v2 v2.6.2 h1:3dI6YNcrJTQ/CJQ6M/DUkc0gnqYSIk6o0rChn9E/D0M= -github.com/tomarrell/wrapcheck/v2 v2.6.2/go.mod h1:ao7l5p0aOlUNJKI0qVwB4Yjlqutd0IvAB9Rdwyilxvg= -github.com/tommy-muehle/go-mnd/v2 v2.5.0 h1:iAj0a8e6+dXSL7Liq0aXPox36FiN1dBbjA6lt9fl65s= -github.com/tommy-muehle/go-mnd/v2 v2.5.0/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/ultraware/funlen v0.0.3 h1:5ylVWm8wsNwH5aWo9438pwvsK0QiqVuUrt9bn7S/iLA= -github.com/ultraware/funlen v0.0.3/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA= -github.com/ultraware/whitespace v0.0.5 h1:hh+/cpIcopyMYbZNVov9iSxvJU3OYQg78Sfaqzi/CzI= -github.com/ultraware/whitespace v0.0.5/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo= -github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli/v2 v2.0.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= -github.com/uudashr/gocognit v1.0.6 h1:2Cgi6MweCsdB6kpcVQp7EW4U23iBFQWfTXiWlyp842Y= -github.com/uudashr/gocognit v1.0.6/go.mod h1:nAIUuVBnYU7pcninia3BHOvQkpQCeO76Uscky5BOwcY= +github.com/tetafro/godot v1.4.16 h1:4ChfhveiNLk4NveAZ9Pu2AN8QZ2nkUGFuadM9lrr5D0= +github.com/tetafro/godot v1.4.16/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio= +github.com/timakin/bodyclose v0.0.0-20240125160201-f835fa56326a h1:A6uKudFIfAEpoPdaal3aSqGxBzLyU8TqyXImLwo6dIo= +github.com/timakin/bodyclose v0.0.0-20240125160201-f835fa56326a/go.mod h1:mkjARE7Yr8qU23YcGMSALbIxTQ9r9QBVahQOBRfU460= +github.com/timonwong/loggercheck v0.9.4 h1:HKKhqrjcVj8sxL7K77beXh0adEm6DLjV/QOGeMXEVi4= +github.com/timonwong/loggercheck v0.9.4/go.mod h1:caz4zlPcgvpEkXgVnAJGowHAMW2NwHaNlpS8xDbVhTg= +github.com/tomarrell/wrapcheck/v2 v2.9.0 h1:801U2YCAjLhdN8zhZ/7tdjB3EnAoRlJHt/s+9hijLQ4= +github.com/tomarrell/wrapcheck/v2 v2.9.0/go.mod h1:g9vNIyhb5/9TQgumxQyOEqDHsmGYcGsVMOx/xGkqdMo= +github.com/tommy-muehle/go-mnd/v2 v2.5.1 h1:NowYhSdyE/1zwK9QCLeRb6USWdoif80Ie+v+yU8u1Zw= +github.com/tommy-muehle/go-mnd/v2 v2.5.1/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw= +github.com/ultraware/funlen v0.1.0 h1:BuqclbkY6pO+cvxoq7OsktIXZpgBSkYTQtmwhAK81vI= +github.com/ultraware/funlen v0.1.0/go.mod h1:XJqmOQja6DpxarLj6Jj1U7JuoS8PvL4nEqDaQhy22p4= +github.com/ultraware/whitespace v0.1.1 h1:bTPOGejYFulW3PkcrqkeQwOd6NKOOXvmGD9bo/Gk8VQ= +github.com/ultraware/whitespace v0.1.1/go.mod h1:XcP1RLD81eV4BW8UhQlpaR+SDc2givTvyI8a586WjW8= +github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli v1.22.16 h1:MH0k6uJxdwdeWQTwhSO42Pwr4YLrNLwBtg1MRgTqPdQ= +github.com/urfave/cli v1.22.16/go.mod h1:EeJR6BKodywf4zciqrdw6hpCPk68JO9z5LazXZMn5Po= +github.com/uudashr/gocognit v1.1.3 h1:l+a111VcDbKfynh+airAy/DJQKaXh2m9vkoysMPSZyM= +github.com/uudashr/gocognit v1.1.3/go.mod h1:aKH8/e8xbTRBwjbCkwZ8qt4l2EpKXl31KMHgSS+lZ2U= 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/warpfork/go-testmark v0.3.0/go.mod h1:jhEf8FVxd+F17juRubpmut64NEG6I2rgkUhlcqqXwE0= -github.com/warpfork/go-testmark v0.10.0 h1:E86YlUMYfwIacEsQGlnTvjk1IgYkyTGjPhF0RnwTCmw= -github.com/warpfork/go-testmark v0.10.0/go.mod h1:jhEf8FVxd+F17juRubpmut64NEG6I2rgkUhlcqqXwE0= -github.com/warpfork/go-wish v0.0.0-20180510122957-5ad1f5abf436/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= -github.com/warpfork/go-wish v0.0.0-20190328234359-8b3e70f8e830/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= -github.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a h1:G++j5e0OC488te356JvdhaM8YS6nMsjLAYF7JxCv07w= -github.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= +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-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.0.0-20200123233031-1cdf64d27158/go.mod h1:Xj/M2wWU+QdTdRbu/L/1dIZY8/Wb2K9pAhtroQuxJJI= -github.com/whyrusleeping/cbor-gen v0.0.0-20200710004633-5379fc63235d/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= -github.com/whyrusleeping/cbor-gen v0.0.0-20210219115102-f37d292932f2 h1:bsUlNhdmbtlfdLVXAVfuvKQ01RnWAM09TVrJkI7NZs4= -github.com/whyrusleeping/cbor-gen v0.0.0-20210219115102-f37d292932f2/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= -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-ctrlnet v0.0.0-20180313164037-f564fbbdaa95/go.mod h1:SJqKCCPXRfBFCwXjfNT/skfsceF7+MBFLI2OrvuRA7g= +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/go-notifier v0.0.0-20170827234753-097c5d47330f/go.mod h1:cZNvX9cFybI01GriPRMXDtczuvUhgbcYr9iCGaNlRv8= -github.com/whyrusleeping/mafmt v1.2.8/go.mod h1:faQJFPbLSxzD9xpA02ttW/tS9vZykNvXwGvqIpk20FA= -github.com/whyrusleeping/mdns v0.0.0-20180901202407-ef14215e6b30/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4= -github.com/whyrusleeping/mdns v0.0.0-20190826153040-b9b60ed33aa9/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4= -github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7/go.mod h1:X2c0RVCI1eSUFI8eLcY3c0423ykwiUdxLJtkDvruhjI= -github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee/go.mod h1:m2aV4LZI4Aez7dP5PMyVKEHhUyEJ/RjmPEDOpDvudHg= -github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= +github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU= +github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= +github.com/xen0n/gosmopolitan v1.2.2 h1:/p2KTnMzwRexIW8GlKawsTWOxn7UHA+jCMF/V8HHtvU= +github.com/xen0n/gosmopolitan v1.2.2/go.mod h1:7XX7Mj61uLYrj0qmeN0zi7XDon9JRAEhYQqAPLVNTeg= github.com/yagipy/maintidx v1.0.0 h1:h5NvIsCz+nRDapQ0exNv4aJ0yXSI0420omVANTv3GJM= github.com/yagipy/maintidx v1.0.0/go.mod h1:0qNf/I/CCZXSMhsRsrEPDZ+DkekpKLXAJfsTACwgXLk= -github.com/yeya24/promlinter v0.2.0 h1:xFKDQ82orCU5jQujdaD8stOHiv8UN68BSdn2a8u8Y3o= -github.com/yeya24/promlinter v0.2.0/go.mod h1:u54lkmBOZrpEbQQ6gox2zWKKLKu2SGe+2KOiextY+IA= +github.com/yeya24/promlinter v0.3.0 h1:JVDbMp08lVCP7Y6NP3qHroGAO6z2yGKQtS5JsjqtoFs= +github.com/yeya24/promlinter v0.3.0/go.mod h1:cDfJQQYv9uYciW60QT0eeHlFodotkYZlL+YcPQN+mW4= +github.com/ykadowak/zerologlint v0.1.5 h1:Gy/fMz1dFQN9JZTPjv1hxEk+sRWm05row04Yoolgdiw= +github.com/ykadowak/zerologlint v0.1.5/go.mod h1:KaUskqF3e/v59oPmdq1U1DnKcuHokl2/K1U4pmIELKg= 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= @@ -1680,231 +815,138 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -gitlab.com/bosi/decorder v0.2.3 h1:gX4/RgK16ijY8V+BRQHAySfQAb354T7/xQpDB2n10P0= -gitlab.com/bosi/decorder v0.2.3/go.mod h1:9K1RB5+VPNQYtXtTDAzd2OEftsZb1oV0IrJrzChSdGE= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= +github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI= +github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE= +github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= +github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= +gitlab.com/bosi/decorder v0.4.2 h1:qbQaV3zgwnBZ4zPMhGLW4KZe7A7NwxEhJx39R3shffo= +gitlab.com/bosi/decorder v0.4.2/go.mod h1:muuhHoaJkA9QLcYHq4Mj8FJUwDZ+EirSHRiaTcTf6T8= +go-simpler.org/assert v0.9.0 h1:PfpmcSvL7yAnWyChSjOz6Sp6m9j5lyK8Ok9pEL31YkQ= +go-simpler.org/assert v0.9.0/go.mod h1:74Eqh5eI6vCK6Y5l3PI8ZYFXG4Sa+tkr70OIPJAUr28= +go-simpler.org/musttag v0.12.2 h1:J7lRc2ysXOq7eM8rwaTYnNrHd5JwjppzB6mScysB2Cs= +go-simpler.org/musttag v0.12.2/go.mod h1:uN1DVIasMTQKk6XSik7yrJoEysGtR2GRqvWnI9S7TYM= +go-simpler.org/sloglint v0.7.2 h1:Wc9Em/Zeuu7JYpl+oKoYOsQSy2X560aVueCW/m6IijY= +go-simpler.org/sloglint v0.7.2/go.mod h1:US+9C80ppl7VsThQclkM7BkCHQAzuz8kHLsW3ppuluo= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= -go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -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.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= -go.opentelemetry.io/otel v1.2.0 h1:YOQDvxO1FayUcT9MIhJhgMyNO1WqoduiyvQHzGN0kUQ= -go.opentelemetry.io/otel v1.2.0/go.mod h1:aT17Fk0Z1Nor9e0uisf98LrntPGMnk4frBO9+dkf69I= -go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= -go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= -go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= -go.opentelemetry.io/otel/sdk v1.2.0 h1:wKN260u4DesJYhyjxDa7LRFkuhH7ncEVKU37LWcyNIo= -go.opentelemetry.io/otel/sdk v1.2.0/go.mod h1:jNN8QtpvbsKhgaC6V5lHiejMoKD+V8uadoSafgHPx1U= -go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= -go.opentelemetry.io/otel/trace v1.2.0 h1:Ys3iqbqZhcf28hHzrm5WAquMkDHNZTUkw7KHbuNjej0= -go.opentelemetry.io/otel/trace v1.2.0/go.mod h1:N5FLswTubnxKxOJHM7XZC074qpeEdLy3CgAVsdMucK0= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= +go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= +go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE= +go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY= +go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys= +go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= -go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= +go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= +go.uber.org/dig v1.18.0 h1:imUL1UiY0Mg4bqbFfsRQO5G4CGRBec/ZujWTvSVp3pw= +go.uber.org/dig v1.18.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= +go.uber.org/fx v1.23.0 h1:lIr/gYWQGfTwGcSXWXu4vP5Ws6iqnNEIY+F/aFzCKTg= +go.uber.org/fx v1.23.0/go.mod h1:o/D9n+2mLP6v1EG+qsdT1O8wKopYAsqZasju97SDFCU= 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.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= -go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +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.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= +go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= 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.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= -go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= -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.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= -go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= -go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= +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/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= -go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= -go.uber.org/zap v1.22.0 h1:Zcye5DUgBloQ9BaT4qc9BnjOFog5TvBSAGkJ3Nf70c0= -go.uber.org/zap v1.22.0/go.mod h1:H4siCOZOrAolnUPJEkfaSjDqyP+BDS0DdDWzwcgt3+U= +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= 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-20181029021203-45a5f77698d3/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-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 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-20190530122614-20be4c3c3ed5/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-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= 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-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/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-20200820211705-5c72a883971a/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-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +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.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +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.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= 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-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= -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-20210615023648-acb5c1269671/go.mod h1:DVyR6MI7P4kEQgvZJSj1fQGrWIi2RzIrfYWycwheUAc= -golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA= -golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= +golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY= +golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= -golang.org/x/exp/typeparams v0.0.0-20220613132600-b0d781184e0d h1:+W8Qf4iJtMGKkyAygcKohjxTk4JPsL9DpzApJ22m5Ic= -golang.org/x/exp/typeparams v0.0.0-20220613132600-b0d781184e0d/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= -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/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= +golang.org/x/exp/typeparams v0.0.0-20240613232115-7f521ea00fb8 h1:+ZJmEdDFzH5H0CnzOrwgbH3elHctfTecW9X0k2tkn5M= +golang.org/x/exp/typeparams v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= 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/mobile v0.0.0-20201217150744-e6ae53a27f4f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= -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.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= 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-20181023162649-9b4f9f5ad519/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-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/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-20190125091013-d26f9f9a57f3/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-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -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-20190813141303-74dc4d7220e7/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-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-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-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200904194848-62affa334b73/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-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= 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-20210428140749-89ef3d95e781/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-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/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-20220418201149-a630d4f3e7a2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E= -golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/net v0.6.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.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +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.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= 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/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= @@ -1912,334 +954,160 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ 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-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 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-20181026203630-95b1ffbd15a5/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-20181107165924-66b7b1311ac8/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-20181122145206-62eef0e2fa9b/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-20190302025703-b6889370fb10/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-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-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-20190524122548-abf6ff778158/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190524152521-dbbf3f1254d4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190526052359-791d8a0f4d09/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/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-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/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-20191206220618-eeba5f6aabab/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/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-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-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/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-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210317225723-c4fcb01b228e/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-20210511113859-b0526f3d8744/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/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-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211105183446-c75c47738b0c/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-20220412211240-33da011f77ad/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-20220702020025-31831981b65f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/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-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0/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.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +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.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= +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.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +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/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= 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 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +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.13.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.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= 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/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= +golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 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-20190307163923-6a08e3108db3/go.mod h1:25r3+/G6/xytQM8iWZKq3Hn0kr0rgFKPUNVEL/dr3z4= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/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-20190321232350-e250d351ecad/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/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-20190624222133-a101b041ded4/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-20190910044552-dd2b5c81c578/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-20190916130336-e45ffcd953cc/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-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -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-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/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-20200117220505-0cba7a3a9ee9/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-20200324003944-a576cf524670/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200329025819-fd4102a86c65/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200414032229-332987a829c3/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -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-20200622203043-20e05c1c8ffa/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200624225443-88f3c62a19ff/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200625211823-6506e20df31f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200724022722-7017fd6b1305/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -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-20200812195022-5ae4c3c160a0/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200820010801-b793a1359eac/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200831203904-5a2aa26beb65/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201001104356-43ebab892c4c/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= -golang.org/x/tools v0.0.0-20201002184944-ecd9fd270d5d/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20201023174141-c8cfbd0f21e6/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -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-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201230224404-63754364767c/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.1-0.20210205202024-ef80cdb6ec6d/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= golang.org/x/tools v0.1.1-0.20210302220138-2ac05c832e1a/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.9-0.20211228192929-ee1ca4ffc4da/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= -golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4= -golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= +golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= +golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= +golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= +golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= 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-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df h1:5Pf6pFKu98ODmgnpvkJ3kFUOQGGLIzLIkbzUHp47618= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +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.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= -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-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= -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-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-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/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.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/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.28.1/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.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= 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= @@ -2248,81 +1116,42 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi 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.27.1/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 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= 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-20200227125254-8fa46927fb4f/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/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= -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/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= -gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.66.6 h1:LATuAqN/shcYAOkv3wl2L4rkaKqkcgTBQjOyYDvcPKI= -gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -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/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 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-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= -gotest.tools/gotestsum v0.4.2 h1:QOdtb6bnnPUuHKkR9+/QQa8e6qjpTTP7cDi7G9/10C4= -gotest.tools/gotestsum v0.4.2/go.mod h1:a32lmn/7xfm0+QHj8K5NyQY1NNNNhZoAp+/OHkLs77Y= -gotest.tools/v3 v3.0.2 h1:kG1BFyqVHuQoVQiR1bWGnfz/fmHvvuiSPIV7rvl360E= -gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= +gotest.tools/gotestsum v1.12.0 h1:CmwtaGDkHxrZm4Ib0Vob89MTfpc3GrEFMJKovliPwGk= +gotest.tools/gotestsum v1.12.0/go.mod h1:fAvqkSptospfSbQw26CTYzNwnsE/ztqLeyhP0h67ARY= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= 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= -honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= -honnef.co/go/tools v0.3.3 h1:oDx7VAwstgpYpb3wv0oxiZlxY+foCpRAwY7Vk6XpAgA= -honnef.co/go/tools v0.3.3/go.mod h1:jzwdWgg7Jdq75wlfblQxO4neNaFFSvgc1tD5Wv8U0Yw= -lukechampine.com/blake3 v1.1.6/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= -lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0= -lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= -mvdan.cc/gofumpt v0.3.1 h1:avhhrOmv0IuvQVK7fvwV91oFSGAk5/6Po8GXTzICeu8= -mvdan.cc/gofumpt v0.3.1/go.mod h1:w3ymliuxvzVx8DAutBnVyDqYb1Niy/yCJt/lk821YCE= -mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I= -mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= -mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo= -mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= -mvdan.cc/unparam v0.0.0-20220706161116-678bad134442 h1:seuXWbRB1qPrS3NQnHmFKLJLtskWyueeIzmLXghMGgk= -mvdan.cc/unparam v0.0.0-20220706161116-678bad134442/go.mod h1:F/Cxw/6mVrNKqrR2YjFf5CaW0Bw4RL8RfbEf4GRggJk= -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= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= +honnef.co/go/tools v0.5.1 h1:4bH5o3b5ZULQ4UrBmP+63W9r7qIkqJClEA9ko5YKx+I= +honnef.co/go/tools v0.5.1/go.mod h1:e9irvo83WDG9/irijV44wr3tbhcFeRnfpVlRqVwpzMs= +lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= +lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= +mvdan.cc/gofumpt v0.7.0 h1:bg91ttqXmi9y2xawvkuMXyvAA/1ZGJqYAEGjXuP0JXU= +mvdan.cc/gofumpt v0.7.0/go.mod h1:txVFJy/Sc/mvaycET54pV8SW8gWxTlUuGHVEcncmNUo= +mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f h1:lMpcwN6GxNbWtbpI1+xzFLSW8XzX0u72NttUGVFjO3U= +mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f/go.mod h1:RSLa7mKKCNeTTMHBw5Hsy2rfJmd6O2ivt9Dw9ZqCQpQ= 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= diff --git a/test/dependencies/graphsync-get/graphsync-get.go b/test/dependencies/graphsync-get/graphsync-get.go deleted file mode 100644 index 005d1e53c..000000000 --- a/test/dependencies/graphsync-get/graphsync-get.go +++ /dev/null @@ -1,128 +0,0 @@ -package main - -import ( - "context" - "fmt" - "io" - "log" - "os" - - "github.com/ipfs/go-blockservice" - "github.com/ipfs/go-cid" - "github.com/ipfs/go-datastore" - dssync "github.com/ipfs/go-datastore/sync" - graphsync "github.com/ipfs/go-graphsync" - gsimpl "github.com/ipfs/go-graphsync/impl" - "github.com/ipfs/go-graphsync/network" - "github.com/ipfs/go-graphsync/storeutil" - blockstore "github.com/ipfs/go-ipfs-blockstore" - offline "github.com/ipfs/go-ipfs-exchange-offline" - "github.com/ipfs/go-merkledag" - uio "github.com/ipfs/go-unixfs/io" - "github.com/ipld/go-ipld-prime" - cidlink "github.com/ipld/go-ipld-prime/linking/cid" - basicnode "github.com/ipld/go-ipld-prime/node/basicnode" - ipldselector "github.com/ipld/go-ipld-prime/traversal/selector" - "github.com/ipld/go-ipld-prime/traversal/selector/builder" - "github.com/libp2p/go-libp2p" - "github.com/libp2p/go-libp2p/core/host" - "github.com/libp2p/go-libp2p/core/peer" - "github.com/multiformats/go-multiaddr" -) - -func newGraphsync(ctx context.Context, p2p host.Host, bs blockstore.Blockstore) (graphsync.GraphExchange, error) { - network := network.NewFromLibp2pHost(p2p) - return gsimpl.New(ctx, - network, - storeutil.LinkSystemForBlockstore(bs), - ), nil -} - -var selectAll ipld.Node = func() ipld.Node { - ssb := builder.NewSelectorSpecBuilder(basicnode.Prototype.Any) - return ssb.ExploreRecursive( - ipldselector.RecursionLimitDepth(100), // default max - ssb.ExploreAll(ssb.ExploreRecursiveEdge()), - ).Node() -}() - -func fetch(ctx context.Context, gs graphsync.GraphExchange, p peer.ID, c cid.Cid) error { - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - resps, errs := gs.Request(ctx, p, cidlink.Link{Cid: c}, selectAll) - for { - select { - case <-ctx.Done(): - return ctx.Err() - case _, ok := <-resps: - if !ok { - resps = nil - } - case err, ok := <-errs: - if !ok { - // done. - return nil - } - if err != nil { - return fmt.Errorf("got an unexpected error: %s", err) - } - } - } -} - -func main() { - if len(os.Args) != 3 { - log.Fatalf("expected a multiaddr and a CID, got %d args", len(os.Args)-1) - } - addr, err := multiaddr.NewMultiaddr(os.Args[1]) - if err != nil { - log.Fatalf("failed to multiaddr '%q': %s", os.Args[1], err) - } - ai, err := peer.AddrInfoFromP2pAddr(addr) - if err != nil { - log.Fatal(err) - } - - target, err := cid.Decode(os.Args[2]) - if err != nil { - log.Fatalf("failed to decode CID '%q': %s", os.Args[2], err) - } - - p2p, err := libp2p.New(libp2p.NoListenAddrs) - if err != nil { - log.Fatal(err) - } - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - err = p2p.Connect(ctx, *ai) - if err != nil { - log.Fatal(err) - } - - bs := blockstore.NewBlockstore(dssync.MutexWrap(datastore.NewMapDatastore())) - gs, err := newGraphsync(ctx, p2p, bs) - if err != nil { - log.Fatal("failed to start", err) - } - err = fetch(ctx, gs, ai.ID, target) - if err != nil { - log.Fatal(err) - } - - dag := merkledag.NewDAGService(blockservice.New(bs, offline.Exchange(bs))) - root, err := dag.Get(ctx, target) - if err != nil { - log.Fatal(err) - } - reader, err := uio.NewDagReader(ctx, root, dag) - if err != nil { - log.Fatal(err) - } - _, err = io.Copy(os.Stdout, reader) - if err != nil { - log.Fatal(err) - } -} diff --git a/test/dependencies/iptb/iptb.go b/test/dependencies/iptb/iptb.go index e3668c0a1..3344c656b 100644 --- a/test/dependencies/iptb/iptb.go +++ b/test/dependencies/iptb/iptb.go @@ -19,7 +19,6 @@ func init() { PluginName: plugin.PluginName, BuiltIn: true, }, false) - if err != nil { panic(err) } diff --git a/test/dependencies/ma-pipe-unidir/main.go b/test/dependencies/ma-pipe-unidir/main.go index 5fdd3e09e..f8a2e32dc 100644 --- a/test/dependencies/ma-pipe-unidir/main.go +++ b/test/dependencies/ma-pipe-unidir/main.go @@ -57,7 +57,7 @@ func app() int { if len(opts.PidFile) > 0 { data := []byte(strconv.Itoa(os.Getpid())) - err := os.WriteFile(opts.PidFile, data, 0644) + err := os.WriteFile(opts.PidFile, data, 0o644) if err != nil { return 1 } @@ -78,7 +78,7 @@ func app() int { if len(opts.PidFile) > 0 { data := []byte(strconv.Itoa(os.Getpid())) - err := os.WriteFile(opts.PidFile, data, 0644) + err := os.WriteFile(opts.PidFile, data, 0o644) if err != nil { return 1 } diff --git a/test/dependencies/pollEndpoint/main.go b/test/dependencies/pollEndpoint/main.go index 0c548d8c9..fbea6fd77 100644 --- a/test/dependencies/pollEndpoint/main.go +++ b/test/dependencies/pollEndpoint/main.go @@ -10,7 +10,7 @@ import ( "os" "time" - logging "github.com/ipfs/go-log" + logging "github.com/ipfs/go-log/v2" ma "github.com/multiformats/go-multiaddr" manet "github.com/multiformats/go-multiaddr/net" ) diff --git a/test/integration/addcat_test.go b/test/integration/addcat_test.go index 45e8729ac..936b28c6d 100644 --- a/test/integration/addcat_test.go +++ b/test/integration/addcat_test.go @@ -11,14 +11,14 @@ import ( "testing" "time" - files "github.com/ipfs/go-ipfs-files" + "github.com/ipfs/boxo/bootstrap" + "github.com/ipfs/boxo/files" logging "github.com/ipfs/go-log" + "github.com/ipfs/go-test/random" "github.com/ipfs/kubo/core" - "github.com/ipfs/kubo/core/bootstrap" "github.com/ipfs/kubo/core/coreapi" mock "github.com/ipfs/kubo/core/mock" "github.com/ipfs/kubo/thirdparty/unit" - "github.com/jbenet/go-random" testutil "github.com/libp2p/go-libp2p-testing/net" "github.com/libp2p/go-libp2p/core/peer" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" @@ -84,12 +84,8 @@ func AddCatPowers(conf testutil.LatencyConfig, megabytesMax int64) error { } func RandomBytes(n int64) []byte { - var data bytes.Buffer - err := random.WritePseudoRandomBytes(n, &data, kSeed) - if err != nil { - panic(err) - } - return data.Bytes() + random.SetSeed(kSeed) + return random.Bytes(int(n)) } func DirectAddCat(data []byte, conf testutil.LatencyConfig) error { diff --git a/test/integration/bench_cat_test.go b/test/integration/bench_cat_test.go index d7e37dbb9..d0c0a4b89 100644 --- a/test/integration/bench_cat_test.go +++ b/test/integration/bench_cat_test.go @@ -8,9 +8,9 @@ import ( "math" "testing" - files "github.com/ipfs/go-ipfs-files" + "github.com/ipfs/boxo/bootstrap" + "github.com/ipfs/boxo/files" "github.com/ipfs/kubo/core" - "github.com/ipfs/kubo/core/bootstrap" "github.com/ipfs/kubo/core/coreapi" mock "github.com/ipfs/kubo/core/mock" "github.com/ipfs/kubo/thirdparty/unit" diff --git a/test/integration/bench_test.go b/test/integration/bench_test.go index b702bae03..c269eddad 100644 --- a/test/integration/bench_test.go +++ b/test/integration/bench_test.go @@ -8,7 +8,6 @@ import ( ) func benchmarkAddCat(numBytes int64, conf testutil.LatencyConfig, b *testing.B) { - b.StopTimer() b.SetBytes(numBytes) data := RandomBytes(numBytes) // we don't want to measure the time it takes to generate this data diff --git a/test/integration/pubsub_msg_seen_cache_test.go b/test/integration/pubsub_msg_seen_cache_test.go index 394cda5b1..85cc8ae9f 100644 --- a/test/integration/pubsub_msg_seen_cache_test.go +++ b/test/integration/pubsub_msg_seen_cache_test.go @@ -10,9 +10,9 @@ import ( "go.uber.org/fx" + "github.com/ipfs/boxo/bootstrap" "github.com/ipfs/kubo/config" "github.com/ipfs/kubo/core" - "github.com/ipfs/kubo/core/bootstrap" "github.com/ipfs/kubo/core/coreapi" libp2p2 "github.com/ipfs/kubo/core/node/libp2p" "github.com/ipfs/kubo/repo" @@ -20,15 +20,17 @@ import ( "github.com/ipfs/go-datastore" syncds "github.com/ipfs/go-datastore/sync" - "github.com/libp2p/go-libp2p-pubsub" - "github.com/libp2p/go-libp2p-pubsub/pb" + pubsub "github.com/libp2p/go-libp2p-pubsub" + pubsub_pb "github.com/libp2p/go-libp2p-pubsub/pb" + "github.com/libp2p/go-libp2p-pubsub/timecache" "github.com/libp2p/go-libp2p/core/peer" mock "github.com/ipfs/kubo/core/mock" - "github.com/libp2p/go-libp2p/p2p/net/mock" + mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" ) func TestMessageSeenCacheTTL(t *testing.T) { + t.Skip("skipping PubSub seen cache TTL test due to flakiness") if err := RunMessageSeenCacheTTLTest(t, "10s"); err != nil { t.Fatal(err) } @@ -76,7 +78,6 @@ func RunMessageSeenCacheTTLTest(t *testing.T, seenMessagesCacheTTL string) error var bootstrapNode, consumerNode, producerNode *core.IpfsNode var bootstrapPeerID, consumerPeerID, producerPeerID peer.ID - sendDupMsg := false mn := mocknet.New() bootstrapNode, err := mockNode(ctx, mn, false, "") // no need for PubSub configuration @@ -98,6 +99,12 @@ func RunMessageSeenCacheTTLTest(t *testing.T, seenMessagesCacheTTL string) error t.Fatal(err) } + // Used for logging the timeline + startTime := time.Time{} + + // Used for overriding the message ID + sendMsgID := "" + // Set up the pubsub message ID generation override for the producer core.RegisterFXOptionFunc(func(info core.FXNodeInfo) ([]fx.Option, error) { var pubsubOptions []pubsub.Option @@ -105,19 +112,23 @@ func RunMessageSeenCacheTTLTest(t *testing.T, seenMessagesCacheTTL string) error pubsubOptions, pubsub.WithSeenMessagesTTL(ttl), pubsub.WithMessageIdFn(func(pmsg *pubsub_pb.Message) string { - now := time.Now().Format(time.StampMilli) + now := time.Now() + if startTime.Second() == 0 { + startTime = now + } + timeElapsed := now.Sub(startTime).Seconds() msg := string(pmsg.Data) - var msgID string from, _ := peer.IDFromBytes(pmsg.From) - if (from == producerPeerID) && sendDupMsg { - msgID = "DupMsg" - t.Logf("sending [%s] with duplicate message ID at [%s]", msg, now) + var msgID string + if from == producerPeerID { + msgID = sendMsgID + t.Logf("sending [%s] with message ID [%s] at T%fs", msg, msgID, timeElapsed) } else { msgID = pubsub.DefaultMsgIdFn(pmsg) - t.Logf("sending [%s] with unique message ID at [%s]", msg, now) } return msgID }), + pubsub.WithSeenMessagesStrategy(timecache.Strategy_LastSeen), ) return append( info.FXOptions, @@ -165,8 +176,8 @@ func RunMessageSeenCacheTTLTest(t *testing.T, seenMessagesCacheTTL string) error t.Fatal(err) } // Utility functions defined inline to include context in closure - now := func() string { - return time.Now().Format(time.StampMilli) + now := func() float64 { + return time.Since(startTime).Seconds() } ctr := 0 msgGen := func() string { @@ -188,57 +199,87 @@ func RunMessageSeenCacheTTLTest(t *testing.T, seenMessagesCacheTTL string) error msg, err := consumerSubscription.Next(rxCtx) if shouldFind { if err != nil { - t.Logf("did not receive [%s] by [%s]", msgTxt, now()) + t.Logf("expected but did not receive [%s] at T%fs", msgTxt, now()) t.Fatal(err) } - t.Logf("received [%s] at [%s]", string(msg.Data()), now()) + t.Logf("received [%s] at T%fs", string(msg.Data()), now()) if !bytes.Equal(msg.Data(), []byte(msgTxt)) { t.Fatalf("consumed data [%s] does not match published data [%s]", string(msg.Data()), msgTxt) } } else { if err == nil { - t.Logf("received [%s] at [%s]", string(msg.Data()), now()) + t.Logf("not expected but received [%s] at T%fs", string(msg.Data()), now()) t.Fail() } - t.Logf("did not receive [%s] by [%s]", msgTxt, now()) + t.Logf("did not receive [%s] at T%fs", msgTxt, now()) } } - // Send message 1 with the message ID we're going to duplicate later - sendDupMsg = true + const MsgID1 = "MsgID1" + const MsgID2 = "MsgID2" + const MsgID3 = "MsgID3" + + // Send message 1 with the message ID we're going to duplicate + sentMsg1 := time.Now() + sendMsgID = MsgID1 msgTxt := produceMessage() - consumeMessage(msgTxt, true) // should find message + // Should find the message because it's new + consumeMessage(msgTxt, true) - // Send message 2 with the same message ID as before - sendDupMsg = true + // Send message 2 with a duplicate message ID + sendMsgID = MsgID1 msgTxt = produceMessage() - consumeMessage(msgTxt, false) // should NOT find message, because it got deduplicated (sent twice within the SeenMessagesTTL window) - - // Wait for seen cache TTL time to let seen cache entries time out - time.Sleep(ttl) + // Should NOT find message because it got deduplicated (sent 2 times within the SeenMessagesTTL window). + consumeMessage(msgTxt, false) // Send message 3 with a new message ID - // - // This extra step is necessary for testing the cache TTL because the PubSub code only garbage collects when a - // message ID was not already present in the cache. This means that message 2's cache entry, even though it has - // technically timed out, will still cause the message to be considered duplicate. When a message with a different - // ID passes through, it will be added to the cache and garbage collection will clean up message 2's entry. This is - // another bug in the pubsub/cache implementation that will be fixed once the code is refactored for this issue: - // https://github.com/libp2p/go-libp2p-pubsub/issues/502 - sendDupMsg = false + sendMsgID = MsgID2 msgTxt = produceMessage() - consumeMessage(msgTxt, true) // should find message + // Should find the message because it's new + consumeMessage(msgTxt, true) - // Send message 4 with the same message ID as before - sendDupMsg = true + // Wait till just before the SeenMessagesTTL window has passed since message 1 was sent + time.Sleep(time.Until(sentMsg1.Add(ttl - 100*time.Millisecond))) + + // Send message 4 with a duplicate message ID + sendMsgID = MsgID1 msgTxt = produceMessage() - consumeMessage(msgTxt, true) // should find message again (time since the last read > SeenMessagesTTL, so it looks like a new message). + // Should NOT find the message because it got deduplicated (sent 3 times within the SeenMessagesTTL window). This + // time, however, the expiration for the message should also get pushed out for a whole SeenMessagesTTL window since + // the default time cache now implements a sliding window algorithm. + consumeMessage(msgTxt, false) - // Send message 5 with a new message ID + // Send message 5 with a duplicate message ID. This will be a second after the last attempt above since NOT finding + // a message takes a second to determine. That would put this attempt at ~1 second after the SeenMessagesTTL window + // starting at message 1 has expired. + sentMsg5 := time.Now() + sendMsgID = MsgID1 + msgTxt = produceMessage() + // Should NOT find the message, because it got deduplicated (sent 2 times since the updated SeenMessagesTTL window + // started). This time again, the expiration should get pushed out for another SeenMessagesTTL window. + consumeMessage(msgTxt, false) + + // Send message 6 with a message ID that hasn't been seen within a SeenMessagesTTL window + sendMsgID = MsgID2 + msgTxt = produceMessage() + // Should find the message since last read > SeenMessagesTTL, so it looks like a new message. + consumeMessage(msgTxt, true) + + // Sleep for a full SeenMessagesTTL window to let cache entries time out + time.Sleep(time.Until(sentMsg5.Add(ttl + 100*time.Millisecond))) + + // Send message 7 with a duplicate message ID + sendMsgID = MsgID1 + msgTxt = produceMessage() + // Should find the message this time since last read > SeenMessagesTTL, so it looks like a new message. + consumeMessage(msgTxt, true) + + // Send message 8 with a brand new message ID // // This step is not strictly necessary, but has been added for good measure. - sendDupMsg = false + sendMsgID = MsgID3 msgTxt = produceMessage() - consumeMessage(msgTxt, true) // should find message + // Should find the message because it's new + consumeMessage(msgTxt, true) return nil } diff --git a/test/integration/three_legged_cat_test.go b/test/integration/three_legged_cat_test.go index f0358272b..fa594f1e5 100644 --- a/test/integration/three_legged_cat_test.go +++ b/test/integration/three_legged_cat_test.go @@ -9,12 +9,12 @@ import ( "testing" "time" - bootstrap2 "github.com/ipfs/kubo/core/bootstrap" + bootstrap2 "github.com/ipfs/boxo/bootstrap" "github.com/ipfs/kubo/core/coreapi" mock "github.com/ipfs/kubo/core/mock" "github.com/ipfs/kubo/thirdparty/unit" - files "github.com/ipfs/go-ipfs-files" + "github.com/ipfs/boxo/files" testutil "github.com/libp2p/go-libp2p-testing/net" "github.com/libp2p/go-libp2p/core/peer" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" diff --git a/test/integration/wan_lan_dht_test.go b/test/integration/wan_lan_dht_test.go index 58558de33..7c70aa98f 100644 --- a/test/integration/wan_lan_dht_test.go +++ b/test/integration/wan_lan_dht_test.go @@ -51,8 +51,10 @@ func TestDHTConnectivitySlowRouting(t *testing.T) { } // wan prefix must have a real corresponding ASN for the peer diversity filter to work. -var wanPrefix = net.ParseIP("2001:218:3004::") -var lanPrefix = net.ParseIP("fe80::") +var ( + wanPrefix = net.ParseIP("2001:218:3004::") + lanPrefix = net.ParseIP("fe80::") +) func makeAddr(n uint32, wan bool) ma.Multiaddr { var ip net.IP diff --git a/test/ipfs-test-lib.sh b/test/ipfs-test-lib.sh index eabf17020..a3bc32bbb 100644 --- a/test/ipfs-test-lib.sh +++ b/test/ipfs-test-lib.sh @@ -50,19 +50,19 @@ test_path_cmp() { # Docker -# This takes a Dockerfile, and a build context directory +# This takes a Dockerfile, a tag name, and a build context directory docker_build() { - docker build --rm -f "$1" "$2" | ansi_strip + docker build --rm --tag "$1" --file "$2" "$3" | ansi_strip } # This takes an image as argument and writes a docker ID on stdout docker_run() { - docker run -d "$1" + docker run --detach "$1" } # This takes a docker ID and a command as arguments docker_exec() { - docker exec -t "$1" /bin/sh -c "$2" + docker exec --tty "$1" /bin/sh -c "$2" } # This takes a docker ID as argument @@ -72,12 +72,12 @@ docker_stop() { # This takes a docker ID as argument docker_rm() { - docker rm -f -v "$1" > /dev/null + docker rm --force --volumes "$1" > /dev/null } # This takes a docker image name as argument docker_rmi() { - docker rmi -f "$1" > /dev/null + docker rmi --force "$1" > /dev/null } # Test whether all the expected lines are included in a file. The file diff --git a/test/sharness/.gitignore b/test/sharness/.gitignore index c7ab08146..b9a5a21ae 100644 --- a/test/sharness/.gitignore +++ b/test/sharness/.gitignore @@ -1,5 +1,14 @@ +# symlinks to lib/sharness +/sharness.sh +/lib-sharness +# clone of sharness lib/sharness/ +# deps downloaded by lib/*.sh scripts +lib/dependencies/ +# sharness files test-results/ trash directory.*.sh/ +# makefile files plugins +# macos files *.DS_Store diff --git a/test/sharness/README.md b/test/sharness/README.md index 16cb508c1..6ab8539da 100644 --- a/test/sharness/README.md +++ b/test/sharness/README.md @@ -1,4 +1,4 @@ -# ipfs whole tests using the [sharness framework](https://github.com/mlafeldt/sharness/) +# ipfs whole tests using the [sharness framework](https://github.com/pl-strflt/sharness/tree/feat/junit) ## Running all the tests @@ -46,7 +46,7 @@ $ ./t0010-basic-commands.sh -v -i When running sharness tests from main Makefile or when `test_sharness_deps` target is run dependencies for sharness -will be downloaded from its github repo and installed in a "lib/sharness" +will be downloaded from its GitHub repo and installed in a "lib/sharness" directory. Please do not change anything in the "lib/sharness" directory. diff --git a/test/sharness/Rules.mk b/test/sharness/Rules.mk index 49e41824c..c1e70eb09 100644 --- a/test/sharness/Rules.mk +++ b/test/sharness/Rules.mk @@ -7,7 +7,7 @@ T_$(d) = $(sort $(wildcard $(d)/t[0-9][0-9][0-9][0-9]-*.sh)) DEPS_$(d) := test/bin/random test/bin/multihash test/bin/pollEndpoint \ test/bin/iptb test/bin/go-sleep test/bin/random-files \ test/bin/go-timeout test/bin/hang-fds test/bin/ma-pipe-unidir \ - test/bin/cid-fmt test/bin/graphsync-get + test/bin/cid-fmt DEPS_$(d) += cmd/ipfs/ipfs DEPS_$(d) += $(d)/clean-test-results DEPS_$(d) += $(SHARNESS_$(d)) @@ -21,7 +21,7 @@ $(PLUGINS_$(d)): $(ORGIN_PLUGINS_$(d)) @mkdir -p $(@D) cp -f plugin/plugins/$(@F) $@ -ifneq ($(TEST_NO_PLUGIN),1) +ifneq ($(TEST_PLUGIN),0) DEPS_$(d) += $(PLUGINS_$(d)) endif endif @@ -42,10 +42,10 @@ $(d)/aggregate: $(T_$(d)) @(cd $(@D) && ./lib/test-aggregate-results.sh) .PHONY: $(d)/aggregate -$(d)/test-results/sharness.xml: export TEST_GENERATE_JUNIT=1 -$(d)/test-results/sharness.xml: test_sharness_expensive +$(d)/test-results/sharness.xml: $(T_$(d)) @echo "*** $@ ***" - @(cd $(@D)/.. && ./lib/gen-junit-report.sh) + @(cd $(@D)/.. && ./lib/test-aggregate-junit-reports.sh) +.PHONY: $(d)/test-results/sharness.xml $(d)/clean-test-results: rm -rf $(@D)/test-results @@ -62,16 +62,10 @@ $(d)/deps: $(SHARNESS_$(d)) $$(DEPS_$(d)) # use second expansion so coverage can test_sharness_deps: $(d)/deps .PHONY: test_sharness_deps -test_sharness_short: $(d)/aggregate -.PHONY: test_sharness_short +test_sharness: $(d)/aggregate +.PHONY: test_sharness - -test_sharness_expensive: export TEST_EXPENSIVE=1 -test_sharness_expensive: test_sharness_short -.PHONY: test_sharness_expensive - -TEST += test_sharness_expensive -TEST_SHORT += test_sharness_short +TEST += test_sharness include mk/footer.mk diff --git a/test/sharness/lib/0001-Generate-partial-JUnit-reports.patch b/test/sharness/lib/0001-Generate-partial-JUnit-reports.patch deleted file mode 100644 index 1a8b5f21b..000000000 --- a/test/sharness/lib/0001-Generate-partial-JUnit-reports.patch +++ /dev/null @@ -1,250 +0,0 @@ -From bc6bf844ef4e4cd468bc1ec96f2d6af738eb8d2f Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?=C5=81ukasz=20Magiera?= -Date: Sat, 21 Apr 2018 22:01:45 +0200 -Subject: [PATCH] Generate partial JUnit reports - ---- - sharness.sh | 114 +++++++++++++++++++++++++++++++++++++++++++++++++--- - 1 file changed, 108 insertions(+), 6 deletions(-) - -diff --git a/sharness.sh b/sharness.sh -index 6750ff7..336e426 100644 ---- a/sharness.sh -+++ b/sharness.sh -@@ -1,4 +1,4 @@ --#!/bin/sh -+#!/usr/bin/env bash - # - # Copyright (c) 2011-2012 Mathias Lafeldt - # Copyright (c) 2005-2012 Git project -@@ -106,6 +106,10 @@ if test -n "$color"; then - test -n "$quiet" && return;; - esac - shift -+ -+ if test -n "$TEST_GENERATE_JUNIT"; then -+ echo "$*" >> .junit/tout -+ fi - printf "%s" "$*" - tput sgr0 - echo -@@ -115,6 +119,10 @@ else - say_color() { - test -z "$1" && test -n "$quiet" && return - shift -+ -+ if test -n "$TEST_GENERATE_JUNIT"; then -+ echo "$*" >> .junit/tout -+ fi - printf "%s\n" "$*" - } - fi -@@ -129,6 +137,12 @@ say() { - say_color info "$*" - } - -+esc=$(printf '\033') -+ -+esc_xml() { -+ sed 's/&/\&/g; s//\>/g; s/"/\"/g; s/'"$esc"'//g; s///g;' -+} -+ - test -n "$test_description" || error "Test script did not set test_description." - - if test "$help" = "t"; then -@@ -251,30 +265,78 @@ test_have_prereq() { - test $total_prereq = $ok_prereq - } - -+# junit_testcase generates a testcase xml file after each test -+ -+junit_testcase() { -+ if test -z "$TEST_GENERATE_JUNIT"; then -+ return -+ fi -+ -+ test_name=$1 -+ tc_file=".junit/case-$(printf "%04d" $test_count)" -+ time_sec="$(cat .junit/time | xargs printf '%04d' | sed -e 's/\(...\)$/.\1/g')" -+ -+ echo "$(expr $(cat .junit/time_total) + $(cat .junit/time) )" > .junit/time_total -+ -+ shift -+ cat > "$tc_file" <<-EOF -+ -+ $@ -+ EOF -+ -+ if test -f .junit/tout; then -+ cat >> "$tc_file" <<-EOF -+ -+ $(cat .junit/tout | esc_xml) -+ -+ EOF -+ fi -+ -+ if test -f .junit/terr; then -+ cat >> "$tc_file" <<-EOF -+ -+ $(cat .junit/terr | esc_xml) -+ -+ EOF -+ fi -+ -+ echo "" >> "$tc_file" -+ rm -f .junit/tout .junit/terr .junit/time -+} -+ - # You are not expected to call test_ok_ and test_failure_ directly, use - # the text_expect_* functions instead. - - test_ok_() { - test_success=$(($test_success + 1)) - say_color "" "ok $test_count - $@" -+ -+ junit_testcase "$@" - } - - test_failure_() { - test_failure=$(($test_failure + 1)) - say_color error "not ok $test_count - $1" -+ test_name=$1 - shift - echo "$@" | sed -e 's/^/# /' -+ junit_testcase "$test_name" ''$(echo $@ | esc_xml)'' -+ - test "$immediate" = "" || { EXIT_OK=t; exit 1; } - } - - test_known_broken_ok_() { - test_fixed=$(($test_fixed + 1)) - say_color error "ok $test_count - $@ # TODO known breakage vanished" -+ -+ junit_testcase "$@" '' - } - - test_known_broken_failure_() { - test_broken=$(($test_broken + 1)) - say_color warn "not ok $test_count - $@ # TODO known breakage" -+ -+ junit_testcase "$@" - } - - # Public: Execute commands in debug mode. -@@ -310,15 +372,25 @@ test_pause() { - test_eval_() { - # This is a separate function because some tests use - # "return" to end a test_expect_success block early. -- eval &3 2>&4 "$*" -+ if test -n "$TEST_GENERATE_JUNIT"; then -+ eval >(tee -a .junit/tout >&3) 2> >(tee -a .junit/terr >&4) "$*" -+ else -+ eval &3 2>&4 "$*" -+ fi - } - - test_run_() { - test_cleanup=: - expecting_failure=$2 -+ -+ start_time_ms=$(date "+%s%3N"); - test_eval_ "$1" - eval_ret=$? - -+ if test -n "$TEST_GENERATE_JUNIT"; then -+ echo $(expr $(date "+%s%3N") - ${start_time_ms} ) > .junit/time; -+ fi -+ - if test "$chain_lint" = "t"; then - test_eval_ "(exit 117) && $1" - if test "$?" != 117; then -@@ -355,8 +427,18 @@ test_skip_() { - of_prereq=" of $test_prereq" - fi - -- say_color skip >&3 "skipping test: $@" -- say_color skip "ok $test_count # skip $1 (missing $missing_prereq${of_prereq})" -+ say_color skip >&3 "skipping test: $1" -+ say_color skip "ok $test_count # skip $1 (missing $missing_prereqm${of_prereq})" -+ -+ if test -n "$TEST_GENERATE_JUNIT"; then -+ cat > ".junit/case-$(printf "%04d" $test_count)" <<-EOF -+ -+ -+ skip $(echo $1 | esc_xml) (missing $missing_prereq${of_prereq}) -+ -+ -+ EOF -+ fi - : true - ;; - *) -@@ -403,7 +485,7 @@ test_expect_success() { - test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq= - test "$#" = 2 || error "bug in the test script: not 2 or 3 parameters to test_expect_success" - export test_prereq -- if ! test_skip_ "$@"; then -+ if ! test_skip_ "$@" "$1"; then - say >&3 "expecting success: $2" - if test_run_ "$2"; then - test_ok_ "$1" -@@ -442,7 +524,7 @@ test_expect_failure() { - test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq= - test "$#" = 2 || error "bug in the test script: not 2 or 3 parameters to test_expect_failure" - export test_prereq -- if ! test_skip_ "$@"; then -+ if ! test_skip_ "$@" "$1"; then - say >&3 "checking known breakage: $2" - if test_run_ "$2" expecting_failure; then - test_known_broken_ok_ "$1" -@@ -675,6 +757,7 @@ test_done() { - test_results_dir="$SHARNESS_TEST_DIRECTORY/test-results" - mkdir -p "$test_results_dir" - test_results_path="$test_results_dir/${SHARNESS_TEST_FILE%.$SHARNESS_TEST_EXTENSION}.$$.counts" -+ junit_results_path="$test_results_dir/${SHARNESS_TEST_FILE%.$SHARNESS_TEST_EXTENSION}.$$.xml.part" - - cat >>"$test_results_path" <<-EOF - total $test_count -@@ -684,6 +767,16 @@ test_done() { - failed $test_failure - - EOF -+ -+ if test -n "$TEST_GENERATE_JUNIT"; then -+ time_sec="$(cat .junit/time_total | xargs printf "%04d" | sed -e 's/\(...\)$/.\1/g')" -+ -+ cat >>"$junit_results_path" <<-EOF -+ -+ $(find .junit -name 'case-*' | sort | xargs cat) -+ -+ EOF -+ fi - fi - - if test "$test_fixed" != 0; then -@@ -745,6 +838,9 @@ export PATH SHARNESS_BUILD_DIRECTORY - SHARNESS_TEST_FILE="$0" - export SHARNESS_TEST_FILE - -+SHARNESS_TEST_NAME=$(basename ${SHARNESS_TEST_FILE} ".sh") -+export SHARNESS_TEST_NAME -+ - # Prepare test area. - test_dir="trash directory.$(basename "$SHARNESS_TEST_FILE" ".$SHARNESS_TEST_EXTENSION")" - test -n "$root" && test_dir="$root/$test_dir" -@@ -771,6 +867,12 @@ mkdir -p "$test_dir" || exit 1 - # in subprocesses like git equals our $PWD (for pathname comparisons). - cd -P "$test_dir" || exit 1 - -+# Prepare JUnit report dir -+if test -n "$TEST_GENERATE_JUNIT"; then -+ mkdir -p .junit -+ echo 0 > .junit/time_total -+fi -+ - this_test=${SHARNESS_TEST_FILE##*/} - this_test=${this_test%.$SHARNESS_TEST_EXTENSION} - for skp in $SKIP_TESTS; do --- -2.17.0 - diff --git a/test/sharness/lib/gen-junit-report.sh b/test/sharness/lib/gen-junit-report.sh deleted file mode 100755 index c69f39adb..000000000 --- a/test/sharness/lib/gen-junit-report.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -cat > test-results/sharness.xml <<-EOF - - - $(find test-results -name '*.xml.part' | sort | xargs cat) - -EOF diff --git a/test/sharness/lib/install-sharness.sh b/test/sharness/lib/install-sharness.sh index ac6cb2f74..c695a3419 100755 --- a/test/sharness/lib/install-sharness.sh +++ b/test/sharness/lib/install-sharness.sh @@ -1,60 +1,50 @@ #!/bin/sh # install sharness.sh # -# Copyright (c) 2014 Juan Batiz-Benet +# Copyright (c) 2014, 2022 Juan Batiz-Benet, Piotr Galar # MIT Licensed; see the LICENSE file in this repository. # -# settings -version=5eee9b51b5621cec95a64018f0cc779963b230d2 -patch_version=17 +gitrepo=ipfs/sharness +githash=803df39d3cba16bb7d493dd6cd8bc5e29826da61 -urlprefix=https://github.com/mlafeldt/sharness.git if test ! -n "$clonedir" ; then clonedir=lib fi sharnessdir=sharness - -if test -f "$clonedir/$sharnessdir/SHARNESS_VERSION_${version}_p${patch_version}" -then - # There is the right version file. Great, we are done! - exit 0 -fi +gitdir="$clonedir/$sharnessdir/.git" die() { echo >&2 "$@" exit 1 } -apply_patches() { - git config --local user.email "noone@nowhere" - git config --local user.name "No One" - git am ../0001-Generate-partial-JUnit-reports.patch - - touch "SHARNESS_VERSION_${version}_p${patch_version}" || die "Could not create 'SHARNESS_VERSION_${version}_p${patch_version}'" -} - -checkout_version() { - git checkout "$version" || die "Could not checkout '$version'" - rm -f SHARNESS_VERSION_* || die "Could not remove 'SHARNESS_VERSION_*'" - echo "Sharness version $version is checked out!" - - apply_patches -} - -if test -d "$clonedir/$sharnessdir/.git" -then - # We need to update sharness! - cd "$clonedir/$sharnessdir" || die "Could not cd into '$clonedir/$sharnessdir' directory" - git fetch || die "Could not fetch to update sharness" - checkout_version -else - # We need to clone sharness! - mkdir -p "$clonedir" || die "Could not create '$clonedir' directory" - cd "$clonedir" || die "Could not cd into '$clonedir' directory" - - git clone "$urlprefix" || die "Could not clone '$urlprefix'" - cd "$sharnessdir" || die "Could not cd into '$sharnessdir' directory" - checkout_version +if test -d "$clonedir/$sharnessdir"; then + giturl="git@github.com:${gitrepo}.git" + echo "Checking if $giturl is already cloned (and if its origin is correct)" + if ! test -d "$gitdir" || test "$(git --git-dir "$gitdir" remote get-url origin)" != "$giturl"; then + echo "Removing $clonedir/$sharnessdir" + rm -rf "$clonedir/$sharnessdir" || die "Could not remove $clonedir/$sharnessdir" + fi fi + +if ! test -d "$clonedir/$sharnessdir"; then + giturl="https://github.com/${gitrepo}.git" + echo "Cloning $giturl into $clonedir/$sharnessdir" + git clone "$giturl" "$clonedir/$sharnessdir" || die "Could not clone $giturl into $clonedir/$sharnessdir" +fi + + +echo "Changing directory to $clonedir/$sharnessdir" +cd "$clonedir/$sharnessdir" || die "Could not cd into '$clonedir/$sharnessdir' directory" + +echo "Checking if $githash is already fetched" +if ! git show "$githash" >/dev/null 2>&1; then + echo "Fetching $githash" + git fetch origin "$githash" || die "Could not fetch $githash" +fi + +echo "Resetting to $githash" +git reset --hard "$githash" || die "Could not reset to $githash" + exit 0 diff --git a/test/sharness/lib/iptb-lib.sh b/test/sharness/lib/iptb-lib.sh index 3d2e95a49..8b2d956c2 100644 --- a/test/sharness/lib/iptb-lib.sh +++ b/test/sharness/lib/iptb-lib.sh @@ -34,6 +34,10 @@ startup_cluster() { other_args="$@" bound=$(expr "$num_nodes" - 1) + test_expect_success "set Routing.LoopbackAddressesOnLanDHT to true" ' + iptb run [0-$bound] -- ipfs config --json "Routing.LoopbackAddressesOnLanDHT" true + ' + if test -n "$other_args"; then test_expect_success "start up nodes with additional args" " iptb start -wait [0-$bound] -- ${other_args[@]} diff --git a/test/sharness/lib/test-aggregate-junit-reports.sh b/test/sharness/lib/test-aggregate-junit-reports.sh new file mode 100755 index 000000000..ad39e5668 --- /dev/null +++ b/test/sharness/lib/test-aggregate-junit-reports.sh @@ -0,0 +1,17 @@ +#!/bin/sh +# +# Script to aggregate results using Sharness +# +# Copyright (c) 2014, 2022 Christian Couder, Piotr Galar +# MIT Licensed; see the LICENSE file in this repository. +# + +SHARNESS_AGGREGATE_JUNIT="lib/sharness/aggregate-junit-reports.sh" + +test -f "$SHARNESS_AGGREGATE_JUNIT" || { + echo >&2 "Cannot find: $SHARNESS_AGGREGATE_JUNIT" + echo >&2 "Please check Sharness installation." + exit 1 +} + +ls test-results/t*-*.sh.*.xml.part | "$SHARNESS_AGGREGATE_JUNIT" > test-results/sharness.xml diff --git a/test/sharness/lib/test-lib.sh b/test/sharness/lib/test-lib.sh index 0757c323c..69fd2e66c 100644 --- a/test/sharness/lib/test-lib.sh +++ b/test/sharness/lib/test-lib.sh @@ -3,7 +3,7 @@ # Copyright (c) 2014 Christian Couder # MIT Licensed; see the LICENSE file in this repository. # -# We are using sharness (https://github.com/mlafeldt/sharness) +# We are using sharness (https://github.com/pl-strflt/sharness/tree/feat/junit) # which was extracted from the Git test framework. # use the ipfs tool to test against @@ -27,14 +27,17 @@ fi # to pass through in some cases. test "$TEST_VERBOSE" = 1 && verbose=t test "$TEST_IMMEDIATE" = 1 && immediate=t +test "$TEST_JUNIT" = 1 && junit=t +test "$TEST_NO_COLOR" = 1 && no_color=t # source the common hashes first. . lib/test-lib-hashes.sh -SHARNESS_LIB="lib/sharness/sharness.sh" +ln -sf lib/sharness/sharness.sh . +ln -sf lib/sharness/lib-sharness . -. "$SHARNESS_LIB" || { - echo >&2 "Cannot source: $SHARNESS_LIB" +. "sharness.sh" || { + echo >&2 "Cannot source: sharness.sh" echo >&2 "Please check Sharness installation." exit 1 } @@ -91,10 +94,10 @@ export TERM=dumb TEST_OS="$(uname -s | tr '[a-z]' '[A-Z]')" # grab + output options -test "$TEST_NO_FUSE" != 1 && test_set_prereq FUSE +test "$TEST_FUSE" = 1 && test_set_prereq FUSE test "$TEST_EXPENSIVE" = 1 && test_set_prereq EXPENSIVE -test "$TEST_NO_DOCKER" != 1 && type docker >/dev/null 2>&1 && groups | egrep "\bdocker\b" && test_set_prereq DOCKER -test "$TEST_NO_PLUGIN" != 1 && test "$TEST_OS" = "LINUX" && test_set_prereq PLUGIN +test "$TEST_DOCKER" = 1 && type docker >/dev/null 2>&1 && groups | egrep "\bdocker\b" && test_set_prereq DOCKER +test "$TEST_PLUGIN" = 1 && test "$TEST_OS" = "LINUX" && test_set_prereq PLUGIN # this may not be available, skip a few dependent tests type socat >/dev/null 2>&1 && test_set_prereq SOCAT @@ -106,10 +109,15 @@ expr "$TEST_OS" : "CYGWIN_NT" >/dev/null || test_set_prereq STD_ERR_MSG if test "$TEST_VERBOSE" = 1; then echo '# TEST_VERBOSE='"$TEST_VERBOSE" - echo '# TEST_NO_FUSE='"$TEST_NO_FUSE" - echo '# TEST_NO_PLUGIN='"$TEST_NO_PLUGIN" + echo '# TEST_IMMEDIATE='"$TEST_IMMEDIATE" + echo '# TEST_FUSE='"$TEST_FUSE" + echo '# TEST_DOCKER='"$TEST_DOCKER" + echo '# TEST_PLUGIN='"$TEST_PLUGIN" echo '# TEST_EXPENSIVE='"$TEST_EXPENSIVE" echo '# TEST_OS='"$TEST_OS" + echo '# TEST_JUNIT='"$TEST_JUNIT" + echo '# TEST_NO_COLOR='"$TEST_NO_COLOR" + echo '# TEST_ULIMIT_PRESET='"$TEST_ULIMIT_PRESET" fi # source our generic test lib @@ -136,8 +144,8 @@ test_run_repeat_60_sec() { return 1 # failed } -test_wait_output_n_lines_60_sec() { - for i in $(test_seq 1 600) +test_wait_output_n_lines() { + for i in $(test_seq 1 3600) do test $(cat "$1" | wc -l | tr -d " ") -ge $2 && return go-sleep 100ms @@ -186,7 +194,7 @@ test_config_set() { } test_init_ipfs() { - + args=("$@") # we set the Addresses.API config variable. # the cli client knows to use it, so only need to set. @@ -194,7 +202,7 @@ test_init_ipfs() { test_expect_success "ipfs init succeeds" ' export IPFS_PATH="$(pwd)/.ipfs" && - ipfs init --profile=test > /dev/null + ipfs init "${args[@]}" --profile=test > /dev/null ' test_expect_success "prepare config -- mounting" ' @@ -206,13 +214,6 @@ test_init_ipfs() { } -test_config_ipfs_gateway_writable() { - test_expect_success "prepare config -- gateway writable" ' - test_config_set --bool Gateway.Writable true || - test_fsh cat "\"$IPFS_PATH/config\"" - ' -} - test_wait_for_file() { loops=$1 delay=$2 @@ -239,7 +240,7 @@ test_set_address_vars() { API_ADDR=$(convert_tcp_maddr $API_MADDR) && API_PORT=$(port_from_maddr $API_MADDR) && - GWAY_MADDR=$(sed -n "s/^Gateway (.*) server listening on //p" "$daemon_output") && + GWAY_MADDR=$(sed -n "s/^Gateway server listening on //p" "$daemon_output") && GWAY_ADDR=$(convert_tcp_maddr $GWAY_MADDR) && GWAY_PORT=$(port_from_maddr $GWAY_MADDR) ' @@ -511,7 +512,7 @@ port_from_maddr() { findprovs_empty() { test_expect_success 'findprovs '$1' succeeds' ' - ipfsi 1 dht findprovs -n 1 '$1' > findprovsOut + ipfsi 1 routing findprovs -n 1 '$1' > findprovsOut ' test_expect_success "findprovs $1 output is empty" ' @@ -521,7 +522,7 @@ findprovs_empty() { findprovs_expect() { test_expect_success 'findprovs '$1' succeeds' ' - ipfsi 1 dht findprovs -n 1 '$1' > findprovsOut && + ipfsi 1 routing findprovs -n 1 '$1' > findprovsOut && echo '$2' > expected ' @@ -541,4 +542,3 @@ purge_blockstore() { [[ -z "$( ipfs repo gc )" ]] ' } - diff --git a/test/sharness/t0300-docker-image.sh b/test/sharness/t0002-docker-image.sh similarity index 82% rename from test/sharness/t0300-docker-image.sh rename to test/sharness/t0002-docker-image.sh index a3a802b28..2ff827806 100755 --- a/test/sharness/t0300-docker-image.sh +++ b/test/sharness/t0002-docker-image.sh @@ -27,18 +27,12 @@ TEST_TRASH_DIR=$(pwd) TEST_SCRIPTS_DIR=$(dirname "$TEST_TRASH_DIR") TEST_TESTS_DIR=$(dirname "$TEST_SCRIPTS_DIR") APP_ROOT_DIR=$(dirname "$TEST_TESTS_DIR") +IMAGE_TAG=kubo_test test_expect_success "docker image build succeeds" ' - docker_build "$TEST_TESTS_DIR/../Dockerfile" "$APP_ROOT_DIR" | tee build-actual || + docker_build "$IMAGE_TAG" "$TEST_TESTS_DIR/../Dockerfile" "$APP_ROOT_DIR" || test_fsh echo "TEST_TESTS_DIR: $TEST_TESTS_DIR" || - test_fsh echo "APP_ROOT_DIR : $APP_ROOT_DIR" || - test_fsh cat build-actual -' - -test_expect_success "docker image build output looks good" ' - SUCCESS_LINE=$(egrep "^Successfully built" build-actual) && - IMAGE_ID=$(expr "$SUCCESS_LINE" : "^Successfully built \(.*\)") || - test_fsh cat build-actual + test_fsh echo "APP_ROOT_DIR : $APP_ROOT_DIR" ' test_expect_success "write init scripts" ' @@ -52,11 +46,11 @@ test_expect_success "docker image runs" ' -p 127.0.0.1:5001:5001 -p 127.0.0.1:8080:8080 \ -v "$PWD/001.sh":/container-init.d/001.sh \ -v "$PWD/002.sh":/container-init.d/002.sh \ - "$IMAGE_ID") + "$IMAGE_TAG") ' test_expect_success "docker container gateway is up" ' - pollEndpoint -host=/ip4/127.0.0.1/tcp/8080 -http-url http://localhost:8080/api/v0/version -v -tries 30 -tout 1s + pollEndpoint -host=/ip4/127.0.0.1/tcp/8080 -http-url http://localhost:8080/ipfs/bafkqaddimvwgy3zao5xxe3debi -v -tries 30 -tout 1s ' test_expect_success "docker container API is up" ' @@ -79,9 +73,9 @@ test_expect_success "check that init script configs were applied" ' ' test_expect_success "simple ipfs add/cat can be run in docker container" ' - expected="Hello Worlds" && - HASH=$(docker_exec "$DOC_ID" "echo $(cat expected) | ipfs add | cut -d' ' -f2") && - docker_exec "$DOC_ID" "ipfs cat $HASH" >actual && + echo "Hello Worlds" | tr -d "[:cntrl:]" > expected && + HASH=$(docker_exec "$DOC_ID" "echo $(cat expected) | ipfs add -q" | tr -d "[:cntrl:]") && + docker_exec "$DOC_ID" "ipfs cat $HASH" | tr -d "[:cntrl:]" > actual && test_cmp expected actual ' @@ -100,6 +94,5 @@ test_expect_success "stop docker container" ' ' docker_rm "$DOC_ID" -docker_rmi "$IMAGE_ID" +docker_rmi "$IMAGE_TAG" test_done - diff --git a/test/sharness/t0301-docker-migrate.sh b/test/sharness/t0003-docker-migrate.sh similarity index 77% rename from test/sharness/t0301-docker-migrate.sh rename to test/sharness/t0003-docker-migrate.sh index 3f7d32f21..c2c7ce969 100755 --- a/test/sharness/t0301-docker-migrate.sh +++ b/test/sharness/t0003-docker-migrate.sh @@ -24,10 +24,10 @@ TEST_TRASH_DIR=$(pwd) TEST_SCRIPTS_DIR=$(dirname "$TEST_TRASH_DIR") TEST_TESTS_DIR=$(dirname "$TEST_SCRIPTS_DIR") APP_ROOT_DIR=$(dirname "$TEST_TESTS_DIR") +IMAGE_TAG=kubo_migrate test_expect_success "docker image build succeeds" ' - docker_build "$TEST_TESTS_DIR/../Dockerfile" "$APP_ROOT_DIR" >actual && - IMAGE_ID=$(tail -n1 actual | cut -d " " -f 3) + docker_build "$IMAGE_TAG" "$TEST_TESTS_DIR/../Dockerfile" "$APP_ROOT_DIR" ' test_init_ipfs @@ -36,15 +36,20 @@ test_expect_success "configure migration sources" ' ipfs config --json Migration.DownloadSources "[\"http://127.0.0.1:17233\"]" ' -test_expect_success "make repo be version 4" ' - echo 4 > "$IPFS_PATH/version" +test_expect_success "setup http response" ' + mkdir migration && + echo "v1.1.1" > migration/versions && + mkdir -p migration/fs-repo-6-to-7 && + echo "v1.1.1" > migration/fs-repo-6-to-7/versions && + CID=$(ipfs add -r -Q migration) && + echo "HTTP/1.1 200 OK" > vers_resp && + echo "Content-Type: application/vnd.ipld.car" >> vers_resp && + echo "" >> vers_resp && + ipfs dag export $CID >> vers_resp ' -test_expect_success "setup http response" ' - echo "HTTP/1.1 200 OK" > vers_resp && - echo "Content-Length: 7" >> vers_resp && - echo "" >> vers_resp && - echo "v1.1.1" >> vers_resp +test_expect_success "make repo be version 4" ' + echo 4 > "$IPFS_PATH/version" ' test_expect_success "startup fake dists server" ' @@ -53,7 +58,7 @@ test_expect_success "startup fake dists server" ' ' test_expect_success "docker image runs" ' - DOC_ID=$(docker run -d -v "$IPFS_PATH":/data/ipfs --net=host "$IMAGE_ID") + DOC_ID=$(docker run -d -v "$IPFS_PATH":/data/ipfs -e IPFS_DIST_PATH=/ipfs/$CID --net=host "$IMAGE_TAG") ' test_expect_success "docker container tries to pull migrations from netcat" ' @@ -78,6 +83,5 @@ test_expect_success "correct version was requested" ' ' docker_rm "$DOC_ID" -docker_rmi "$IMAGE_ID" +docker_rmi "$IMAGE_TAG" test_done - diff --git a/test/sharness/t0021-config.sh b/test/sharness/t0021-config.sh index 5264908c7..95a8a7d87 100755 --- a/test/sharness/t0021-config.sh +++ b/test/sharness/t0021-config.sh @@ -281,7 +281,7 @@ test_config_cmd() { # won't work as it changes datastore definition, which makes ipfs not launch # without converting first - # test_profile_apply_revert badgerds + # test_profile_apply_revert pebbleds test_expect_success "cleanup config backups" ' find "$IPFS_PATH" -name "config-*" -exec rm {} \; diff --git a/test/sharness/t0025-datastores.sh b/test/sharness/t0025-datastores.sh index ec99accb5..6be9eb3ed 100755 --- a/test/sharness/t0025-datastores.sh +++ b/test/sharness/t0025-datastores.sh @@ -4,13 +4,20 @@ test_description="Test non-standard datastores" . lib/test-lib.sh -test_expect_success "'ipfs init --profile=badgerds' succeeds" ' - BITS="2048" && - ipfs init --profile=badgerds -' +profiles=("flatfs" "pebbleds" "badgerds") +proot="$(mktemp -d "${TMPDIR:-/tmp}/t0025.XXXXXX")" -test_expect_success "'ipfs pin ls' works" ' - ipfs pin ls | wc -l | grep 9 -' +for profile in "${profiles[@]}"; do + test_expect_success "'ipfs init --empty-repo=false --profile=$profile' succeeds" ' + BITS="2048" && + IPFS_PATH="$proot/$profile" && + ipfs init --empty-repo=false --profile=$profile + ' + test_expect_success "'ipfs pin add' and 'pin ls' works with $profile" ' + export IPFS_PATH="$proot/$profile" && + echo -n "hello_$profile" | ipfs block put --pin=true > hello_cid && + ipfs pin ls -t recursive "$(cat hello_cid)" + ' +done test_done diff --git a/test/sharness/t0026-id.sh b/test/sharness/t0026-id.sh index 5d6d3db09..992892a39 100755 --- a/test/sharness/t0026-id.sh +++ b/test/sharness/t0026-id.sh @@ -32,12 +32,6 @@ test_expect_success "checking AgentVersion" ' test_cmp expected-agent-version actual-agent-version ' -test_expect_success "checking ProtocolVersion" ' - echo "ipfs/0.1.0" > expected-protocol-version && - ipfs id -f "\n" > actual-protocol-version && - test_cmp expected-protocol-version actual-protocol-version -' - test_expect_success "checking ID of self" ' ipfs config Identity.PeerID > expected-id && ipfs id -f "\n" > actual-id && @@ -67,7 +61,20 @@ test_expect_success "checking AgentVersion with suffix (fetched via libp2p ident ipfsi 1 id "$(ipfsi 0 config Identity.PeerID)" -f "\n" > actual-libp2p-identify-agent-version && test_cmp expected-identify-agent-version actual-libp2p-identify-agent-version ' +iptb stop + test_kill_ipfs_daemon +# Version.AgentSuffix overrides --agent-version-suffix (local, offline) +test_expect_success "setting Version.AgentSuffix in config" ' + ipfs config Version.AgentSuffix json-config-suffix +' +test_launch_ipfs_daemon --agent-version-suffix=ignored-cli-suffix +test_expect_success "checking AgentVersion with suffix set via JSON config" ' + test_id_compute_agent json-config-suffix > expected-agent-version && + ipfs id -f "\n" > actual-agent-version && + test_cmp expected-agent-version actual-agent-version +' +test_kill_ipfs_daemon test_done diff --git a/test/sharness/t0041-ping.sh b/test/sharness/t0041-ping.sh deleted file mode 100755 index 14268989d..000000000 --- a/test/sharness/t0041-ping.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env bash - -test_description="Test ping command" - -. lib/test-lib.sh - -test_init_ipfs - -BAD_PEER="QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJx" - -# start iptb + wait for peering -test_expect_success 'init iptb' ' - iptb testbed create -type localipfs -count 2 -init -' - -startup_cluster 2 - -test_expect_success 'peer ids' ' - PEERID_0=$(iptb attr get 0 id) && - PEERID_1=$(iptb attr get 1 id) -' - -test_expect_success "test ping other" ' - ipfsi 0 ping -n2 -- "$PEERID_1" && - ipfsi 1 ping -n2 -- "$PEERID_0" -' - -test_expect_success "test ping unreachable peer" ' - printf "Looking up peer %s\n" "$BAD_PEER" > bad_ping_exp && - printf "PING QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJx.\nPing error: no addresses\nError: ping failed\n" >> bad_ping_exp && - ! ipfsi 0 ping -n2 -- "$BAD_PEER" > bad_ping_actual 2>&1 && - test_cmp bad_ping_exp bad_ping_actual -' - -test_expect_success "test ping self" ' - ! ipfsi 0 ping -n2 -- "$PEERID_0" && - ! ipfsi 1 ping -n2 -- "$PEERID_1" -' - -test_expect_success "test ping 0" ' - ! ipfsi 0 ping -n0 -- "$PEERID_1" && - ! ipfsi 1 ping -n0 -- "$PEERID_0" -' - -test_expect_success "test ping offline" ' - iptb stop 1 && sleep 2 && - ! ipfsi 0 ping -n2 -- "$PEERID_1" -' - -test_expect_success 'stop iptb' ' - iptb stop 0 -' - -test_done diff --git a/test/sharness/t0047-add-mode-mtime.sh b/test/sharness/t0047-add-mode-mtime.sh new file mode 100755 index 000000000..520c692f3 --- /dev/null +++ b/test/sharness/t0047-add-mode-mtime.sh @@ -0,0 +1,513 @@ +#!/usr/bin/env bash + +test_description="Test storing and retrieving mode and mtime" + +. lib/test-lib.sh + +test_init_ipfs + +test_expect_success "set Import defaults to ensure deterministic cids for mod and mtime tests" ' + ipfs config --json Import.CidVersion 0 && + ipfs config Import.HashFunction sha2-256 && + ipfs config Import.UnixFSChunker size-262144 +' + +HASH_NO_PRESERVE=QmbFMke1KXqnYyBBWxB74N4c5SBnJMVAiMNRcGu6x1AwQH + +PRESERVE_MTIME=1604320482 +PRESERVE_MODE="0640" +HASH_PRESERVE_MODE=QmQLgxypSNGNFTuUPGCecq6dDEjb6hNB5xSyVmP3cEuNtq +HASH_PRESERVE_MTIME=QmQ6kErEW8kztQFV8vbwNU8E4dmtGsYpRiboiLxUEwibvj +HASH_PRESERVE_LINK_MTIME=QmbJwotgtr84JxcnjpwJ86uZiyMoxbZuNH4YrdJMypkYaB +HASH_PRESERVE_MODE_AND_MTIME=QmYkvboLsvLFcSYmqVJRxvBdYRQLroLv9kELf3LRiCqBri + +CUSTOM_MTIME=1603539720 +CUSTOM_MTIME_NSECS=54321 +CUSTOM_MODE="0764" +HASH_CUSTOM_MODE=QmchD3BN8TQ3RW6jPLxSaNkqvfuj7syKhzTRmL4EpyY1Nz +HASH_CUSTOM_MTIME=QmT3aY4avDcYXCWpU8CJzqUkW7YEuEsx36S8cTNoLcuK1B +HASH_CUSTOM_MTIME_NSECS=QmaKH8H5rXBUBCX4vdxi7ktGQEL7wejV7L9rX2qpZjwncz +HASH_CUSTOM_MODE_AND_MTIME=QmUkxrtBA8tPjwCYz1HrsoRfDz6NgKut3asVeHVQNH4C8L +HASH_CUSTOM_LINK_MTIME=QmV1Uot2gy4bhY9yvYiZxhhchhyYC6MKKoGV1XtWNmpCLe +HASH_CUSTOM_LINK_MTIME_NSECS=QmPHYCxYvvHj6VxiPNJ3kXxcPsnJLDYUJqsDJWjvytmrmY + +mk_name() { + tr -dc '[:alnum:]' pb_block_out && - test_cmp pb_block_out ../t0051-object-data/testPut.pb + test_cmp pb_block_out ../t0050-block-data/testPut.pb ' # @@ -210,33 +210,33 @@ test_expect_success "multi-block 'ipfs block rm -q' produces no output" ' # --format used 'protobuf' for 'dag-pb' which was invalid, but we keep # for backward-compatibility test_expect_success "can set deprecated --format=protobuf on block put" ' - HASH=$(ipfs block put --format=protobuf ../t0051-object-data/testPut.pb) + HASH=$(ipfs block put --format=protobuf ../t0050-block-data/testPut.pb) ' test_expect_success "created an object correctly!" ' - ipfs object get $HASH > obj_out && - echo "{\"Links\":[],\"Data\":\"test json for sharness test\"}" > obj_exp && + ipfs dag get $HASH > obj_out && + echo -n "{\"Data\":{\"/\":{\"bytes\":\"dGVzdCBqc29uIGZvciBzaGFybmVzcyB0ZXN0\"}},\"Links\":[]}" > obj_exp && test_cmp obj_out obj_exp ' test_expect_success "block get output looks right" ' ipfs block get $HASH > pb_block_out && - test_cmp pb_block_out ../t0051-object-data/testPut.pb + test_cmp pb_block_out ../t0050-block-data/testPut.pb ' test_expect_success "can set --cid-codec=dag-pb on block put" ' - HASH=$(ipfs block put --cid-codec=dag-pb ../t0051-object-data/testPut.pb) + HASH=$(ipfs block put --cid-codec=dag-pb ../t0050-block-data/testPut.pb) ' test_expect_success "created an object correctly!" ' - ipfs object get $HASH > obj_out && - echo "{\"Links\":[],\"Data\":\"test json for sharness test\"}" > obj_exp && + ipfs dag get $HASH > obj_out && + echo -n "{\"Data\":{\"/\":{\"bytes\":\"dGVzdCBqc29uIGZvciBzaGFybmVzcyB0ZXN0\"}},\"Links\":[]}" > obj_exp && test_cmp obj_out obj_exp ' test_expect_success "block get output looks right" ' ipfs block get $HASH > pb_block_out && - test_cmp pb_block_out ../t0051-object-data/testPut.pb + test_cmp pb_block_out ../t0050-block-data/testPut.pb ' test_expect_success "can set multihash type and length on block put with --format=raw (deprecated)" ' @@ -248,7 +248,7 @@ test_expect_success "output looks good" ' ' test_expect_success "can't use both legacy format and custom cid-codec at the same time" ' - test_expect_code 1 ipfs block put --format=dag-cbor --cid-codec=dag-json < ../t0051-object-data/testPut.pb 2> output && + test_expect_code 1 ipfs block put --format=dag-cbor --cid-codec=dag-json < ../t0050-block-data/testPut.pb 2> output && test_should_contain "unable to use \"format\" (deprecated) and a custom \"cid-codec\" at the same time" output ' diff --git a/test/sharness/t0051-object-data/UTF-8-test.txt b/test/sharness/t0051-object-data/UTF-8-test.txt deleted file mode 100644 index 56213a84a..000000000 Binary files a/test/sharness/t0051-object-data/UTF-8-test.txt and /dev/null differ diff --git a/test/sharness/t0051-object-data/brokenPut.json b/test/sharness/t0051-object-data/brokenPut.json deleted file mode 100644 index 6ba2d6f3b..000000000 --- a/test/sharness/t0051-object-data/brokenPut.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "this": "should", - "return": "an", - "error":"not valid dag object" -} \ No newline at end of file diff --git a/test/sharness/t0051-object-data/brokenPut.xml b/test/sharness/t0051-object-data/brokenPut.xml deleted file mode 100644 index 331bbac99..000000000 --- a/test/sharness/t0051-object-data/brokenPut.xml +++ /dev/null @@ -1 +0,0 @@ -This is not a valid dag object fail diff --git a/test/sharness/t0051-object-data/expected_getOut b/test/sharness/t0051-object-data/expected_getOut deleted file mode 100644 index dc12f63ba..000000000 --- a/test/sharness/t0051-object-data/expected_getOut +++ /dev/null @@ -1 +0,0 @@ -{"Links":[],"Data":"\u0008\u0002\u0012\nHello Mars\u0018\n"} diff --git a/test/sharness/t0051-object-data/mixed.json b/test/sharness/t0051-object-data/mixed.json deleted file mode 100644 index b8de2b8d8..000000000 --- a/test/sharness/t0051-object-data/mixed.json +++ /dev/null @@ -1,5 +0,0 @@ -{"Data": "another", - "Links": [ - {"Name": "some link", "Hash": "QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V", "Size": 8}, - {"Name": "inlined", "Hash": "z4CrgyEyhm4tAw1pgzQtNNuP7", "Size": 14} -]} diff --git a/test/sharness/t0051-object-data/testPut.json b/test/sharness/t0051-object-data/testPut.json deleted file mode 100644 index c97f4ec0b..000000000 --- a/test/sharness/t0051-object-data/testPut.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "Data": "test json for sharness test" -} diff --git a/test/sharness/t0051-object-data/testPut.xml b/test/sharness/t0051-object-data/testPut.xml deleted file mode 100644 index 5cc290b27..000000000 --- a/test/sharness/t0051-object-data/testPut.xml +++ /dev/null @@ -1 +0,0 @@ -Test xml for sharness test diff --git a/test/sharness/t0051-object.sh b/test/sharness/t0051-object.sh index 316c220ab..4bac61374 100755 --- a/test/sharness/t0051-object.sh +++ b/test/sharness/t0051-object.sh @@ -27,204 +27,21 @@ test_patch_create_path() { } test_object_cmd() { - - test_expect_success "'ipfs add testData' succeeds" ' - printf "Hello Mars" >expected_in && - ipfs add expected_in >actual_Addout - ' - - test_expect_success "'ipfs add testData' output looks good" ' - HASH="QmWkHFpYBZ9mpPRreRbMhhYWXfUhBAue3JkbbpFqwowSRb" && - echo "added $HASH expected_in" >expected_Addout && - test_cmp expected_Addout actual_Addout - ' - - test_expect_success "'ipfs object get' succeeds" ' - ipfs object get $HASH >actual_getOut - ' - - test_expect_success "'ipfs object get' output looks good" ' - test_cmp ../t0051-object-data/expected_getOut actual_getOut - ' - - test_expect_success "'ipfs object get' can specify data encoding as base64" ' - ipfs object get --data-encoding base64 $HASH > obj_out && - echo "{\"Links\":[],\"Data\":\"CAISCkhlbGxvIE1hcnMYCg==\"}" > obj_exp && - test_cmp obj_out obj_exp - ' - - test_expect_success "'ipfs object get' can specify data encoding as text" ' - echo "{\"Links\":[],\"Data\":\"Hello Mars\"}" | ipfs object put && - ipfs object get --data-encoding text QmS3hVY6eYrMQ6L22agwrx3YHBEsc3LJxVXCtyQHqRBukH > obj_out && - echo "{\"Links\":[],\"Data\":\"Hello Mars\"}" > obj_exp && - test_cmp obj_out obj_exp - ' - - test_expect_failure "'ipfs object get' requires known data encoding" ' - ipfs object get --data-encoding nonsensical-encoding $HASH - ' - - test_expect_success "'ipfs object stat' succeeds" ' - ipfs object stat $HASH >actual_stat - ' - - test_expect_success "'ipfs object get' output looks good" ' - echo "NumLinks: 0" > expected_stat && - echo "BlockSize: 18" >> expected_stat && - echo "LinksSize: 2" >> expected_stat && - echo "DataSize: 16" >> expected_stat && - echo "CumulativeSize: 18" >> expected_stat && - test_cmp expected_stat actual_stat - ' - - test_expect_success "'ipfs object put file.json' succeeds" ' - ipfs object put ../t0051-object-data/testPut.json > actual_putOut - ' - - test_expect_success "'ipfs object put file.json' output looks good" ' - HASH="QmUTSAdDi2xsNkDtLqjFgQDMEn5di3Ab9eqbrt4gaiNbUD" && - printf "added $HASH\n" > expected_putOut && - test_cmp expected_putOut actual_putOut - ' - - test_expect_success "'ipfs object put --quiet file.json' succeeds" ' - ipfs object put --quiet ../t0051-object-data/testPut.json > actual_putOut - ' - - test_expect_success "'ipfs object put --quiet file.json' output looks good" ' - HASH="QmUTSAdDi2xsNkDtLqjFgQDMEn5di3Ab9eqbrt4gaiNbUD" && - printf "$HASH\n" > expected_putOut && - test_cmp expected_putOut actual_putOut - ' - - test_expect_success "'ipfs object put file.xml' succeeds" ' - ipfs object put ../t0051-object-data/testPut.xml --inputenc=xml > actual_putOut - ' - - test_expect_success "'ipfs object put file.xml' output looks good" ' - HASH="QmQzNKUHy4HyEUGkqKe3q3t796ffPLQXYCkHCcXUNT5JNK" && - printf "added $HASH\n" > expected_putOut && - test_cmp expected_putOut actual_putOut - ' - - test_expect_success "'ipfs object put' from stdin succeeds" ' - cat ../t0051-object-data/testPut.xml | ipfs object put --inputenc=xml > actual_putStdinOut - ' - - test_expect_failure "'ipfs object put broken.xml' should fail" ' - test_expect_code 1 ipfs object put ../t0051-object-data/brokenPut.xml --inputenc=xml 2>actual_putBrokenErr >actual_putBroken - ' - - test_expect_failure "'ipfs object put broken.hxml' output looks good" ' - touch expected_putBroken && - printf "Error: no data or links in this node\n" > expected_putBrokenErr && - test_cmp expected_putBroken actual_putBroken && - test_cmp expected_putBrokenErr actual_putBrokenErr - ' - test_expect_success "'ipfs object get --enc=xml' succeeds" ' - ipfs object get --enc=xml $HASH >utf8_xml - ' - - test_expect_success "'ipfs object put --inputenc=xml' succeeds" ' - ipfs object put --inputenc=xml actual - ' - - test_expect_failure "'ipfs object put --inputenc=xml' output looks good" ' - echo "added $HASH\n" >expected && - test_cmp expected actual - ' - - test_expect_success "'ipfs object put file.pb' succeeds" ' - ipfs object put --inputenc=protobuf ../t0051-object-data/testPut.pb > actual_putOut - ' - - test_expect_success "'ipfs object put file.pb' output looks good" ' - HASH="QmUTSAdDi2xsNkDtLqjFgQDMEn5di3Ab9eqbrt4gaiNbUD" && - printf "added $HASH\n" > expected_putOut && - test_cmp expected_putOut actual_putOut - ' - - test_expect_success "'ipfs object put' from stdin succeeds" ' - cat ../t0051-object-data/testPut.json | ipfs object put > actual_putStdinOut - ' - - test_expect_success "'ipfs object put' from stdin output looks good" ' - HASH="QmUTSAdDi2xsNkDtLqjFgQDMEn5di3Ab9eqbrt4gaiNbUD" && - printf "added $HASH\n" > expected_putStdinOut && - test_cmp expected_putStdinOut actual_putStdinOut - ' - - test_expect_success "'ipfs object put' from stdin (pb) succeeds" ' - cat ../t0051-object-data/testPut.pb | ipfs object put --inputenc=protobuf > actual_putPbStdinOut - ' - - test_expect_success "'ipfs object put' from stdin (pb) output looks good" ' - HASH="QmUTSAdDi2xsNkDtLqjFgQDMEn5di3Ab9eqbrt4gaiNbUD" && - printf "added $HASH\n" > expected_putStdinOut && - test_cmp expected_putStdinOut actual_putPbStdinOut - ' - - test_expect_success "'ipfs object put broken.json' should fail" ' - test_expect_code 1 ipfs object put ../t0051-object-data/brokenPut.json 2>actual_putBrokenErr >actual_putBroken - ' - - test_expect_success "'ipfs object put broken.hjson' output looks good" ' - touch expected_putBroken && - printf "Error: json: unknown field \"this\"\n" > expected_putBrokenErr && - test_cmp expected_putBroken actual_putBroken && - test_cmp expected_putBrokenErr actual_putBrokenErr - ' - - test_expect_success "setup: add UTF-8 test file" ' - HASH="QmNY5sQeH9ttVCg24sizH71dNbcZTpGd7Yb3YwsKZ4jiFP" && - ipfs add ../t0051-object-data/UTF-8-test.txt >actual && - echo "added $HASH UTF-8-test.txt" >expected && - test_cmp expected actual - ' - - test_expect_success "'ipfs object get --enc=json' succeeds" ' - ipfs object get --enc=json $HASH >utf8_json - ' - - test_expect_success "'ipfs object put --inputenc=json' succeeds" ' - ipfs object put --inputenc=json actual - ' - - test_expect_failure "'ipfs object put --inputenc=json' output looks good" ' - echo "added $HASH" >expected && - test_cmp expected actual - ' - - test_expect_success "'ipfs object put --pin' succeeds" ' - HASH="QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V" && - echo "added $HASH" >expected && - echo "{ \"Data\": \"abc\" }" | ipfs object put --pin >actual - ' - - test_expect_success "'ipfs object put --pin' output looks good" ' - echo "added $HASH" >expected && - test_cmp expected actual - ' - - test_expect_success "after gc, objects still accessible" ' - ipfs repo gc > /dev/null && - ipfs refs -r --timeout=2s $HASH > /dev/null - ' + EMPTY_DIR=$(echo '{"Links":[]}' | ipfs dag put --store-codec dag-pb) + EMPTY_UNIXFS_DIR=$(echo '{"Data":{"/":{"bytes":"CAE"}},"Links":[]}' | ipfs dag put --store-codec dag-pb) test_expect_success "'ipfs object patch' should work (no unixfs-dir)" ' - EMPTY_DIR=$(ipfs object new) && OUTPUT=$(ipfs object patch $EMPTY_DIR add-link foo $EMPTY_DIR) && - ipfs object stat $OUTPUT + ipfs dag stat $OUTPUT ' test_expect_success "'ipfs object patch' should work" ' - EMPTY_DIR=$(ipfs object new unixfs-dir) && - OUTPUT=$(ipfs object patch $EMPTY_DIR add-link foo $EMPTY_DIR) && - ipfs object stat $OUTPUT + OUTPUT=$(ipfs object patch $EMPTY_UNIXFS_DIR add-link foo $EMPTY_UNIXFS_DIR) && + ipfs dag stat $OUTPUT ' test_expect_success "'ipfs object patch' check output block size" ' - DIR=$(ipfs object new unixfs-dir) + DIR=$EMPTY_UNIXFS_DIR for i in {1..13} do DIR=$(ipfs object patch "$DIR" add-link "$DIR.jpg" "$DIR") @@ -241,32 +58,20 @@ test_object_cmd() { test_expect_code 0 ipfs object patch --allow-big-block=true "$DIR" add-link "$DIR.jpg" "$DIR" ' - test_expect_success "'ipfs object new foo' shouldn't crash" ' - test_expect_code 1 ipfs object new foo - ' - - test_expect_success "'ipfs object links' gives the correct results" ' - echo "$EMPTY_DIR" 4 foo > expected && - ipfs object links "$OUTPUT" > actual && - test_cmp expected actual - ' - test_expect_success "'ipfs object patch add-link' should work with paths" ' - EMPTY_DIR=$(ipfs object new unixfs-dir) && - N1=$(ipfs object patch $EMPTY_DIR add-link baz $EMPTY_DIR) && - N2=$(ipfs object patch $EMPTY_DIR add-link bar $N1) && - N3=$(ipfs object patch $EMPTY_DIR add-link foo /ipfs/$N2/bar) && - ipfs object stat /ipfs/$N3 > /dev/null && - ipfs object stat $N3/foo > /dev/null && - ipfs object stat /ipfs/$N3/foo/baz > /dev/null + N1=$(ipfs object patch $EMPTY_UNIXFS_DIR add-link baz $EMPTY_UNIXFS_DIR) && + N2=$(ipfs object patch $EMPTY_UNIXFS_DIR add-link bar $N1) && + N3=$(ipfs object patch $EMPTY_UNIXFS_DIR add-link foo /ipfs/$N2/bar) && + ipfs dag stat /ipfs/$N3 > /dev/null && + ipfs dag stat $N3/foo > /dev/null && + ipfs dag stat /ipfs/$N3/foo/baz > /dev/null ' test_expect_success "'ipfs object patch add-link' allow linking IPLD objects" ' - EMPTY_DIR=$(ipfs object new unixfs-dir) && OBJ=$(echo "123" | ipfs dag put) && - N1=$(ipfs object patch $EMPTY_DIR add-link foo $OBJ) && + N1=$(ipfs object patch $EMPTY_UNIXFS_DIR add-link foo $OBJ) && - ipfs object stat /ipfs/$N1 > /dev/null && + ipfs dag stat /ipfs/$N1 > /dev/null && ipfs resolve /ipfs/$N1/foo > actual && echo /ipfs/$OBJ > expected && @@ -274,7 +79,7 @@ test_object_cmd() { ' test_expect_success "object patch creation looks right" ' - echo "QmPc73aWK9dgFBXe86P4PvQizHo9e5Qt7n7DAMXWuigFuG" > hash_exp && + echo "bafybeiakusqwohnt7bs75kx6jhmt4oi47l634bmudxfv4qxhpco6xuvgna" > hash_exp && echo $N3 > hash_actual && test_cmp hash_exp hash_actual ' @@ -282,7 +87,7 @@ test_object_cmd() { test_expect_success "multilayer ipfs patch works" ' echo "hello world" > hwfile && FILE=$(ipfs add -q hwfile) && - EMPTY=$(ipfs object new unixfs-dir) && + EMPTY=$EMPTY_UNIXFS_DIR && ONE=$(ipfs object patch $EMPTY add-link b $EMPTY) && TWO=$(ipfs object patch $EMPTY add-link a $ONE) && ipfs object patch $TWO add-link a/b/c $FILE > multi_patch @@ -293,49 +98,12 @@ test_object_cmd() { test_cmp hwfile hwfile_out ' - test_expect_success "ipfs object stat path succeeds" ' - ipfs object stat $(cat multi_patch)/a > obj_stat_out - ' - - test_expect_success "ipfs object stat output looks good" ' - echo "NumLinks: 1" > obj_stat_exp && - echo "BlockSize: 47" >> obj_stat_exp && - echo "LinksSize: 45" >> obj_stat_exp && - echo "DataSize: 2" >> obj_stat_exp && - echo "CumulativeSize: 114" >> obj_stat_exp && - - test_cmp obj_stat_exp obj_stat_out - ' - - test_expect_success "'ipfs object stat --human' succeeds" ' - ipfs object stat $(cat multi_patch)/a --human > obj_stat_human_out - ' - - test_expect_success "ipfs object stat --human output looks good" ' - echo "NumLinks: 1" > obj_stat_human_exp && - echo "BlockSize: 47" >> obj_stat_human_exp && - echo "LinksSize: 45" >> obj_stat_human_exp && - echo "DataSize: 2" >> obj_stat_human_exp && - echo "CumulativeSize: 114 B" >> obj_stat_human_exp && - - test_cmp obj_stat_human_exp obj_stat_human_out - ' - - test_expect_success "should have created dir within a dir" ' - ipfs ls $OUTPUT > patched_output - ' - - test_expect_success "output looks good" ' - echo "QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn - foo/" > patched_exp && - test_cmp patched_exp patched_output - ' - test_expect_success "can remove the directory" ' ipfs object patch $OUTPUT rm-link foo > rmlink_output ' test_expect_success "output should be empty" ' - echo QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn > rmlink_exp && + echo bafybeiczsscdsbs7ffqz55asqdf3smv6klcw3gofszvwlyarci47bgf354 > rmlink_exp && test_cmp rmlink_exp rmlink_output ' @@ -344,7 +112,7 @@ test_object_cmd() { ' test_expect_success "output looks good" ' - echo "QmZD3r9cZjzU8huNY2JS9TC6n8daDfT8TmE8zBSqG31Wvq" > multi_link_rm_exp && + echo "bafybeicourxysmtbe5hacxqico4d5hyvh7gqkrwlmqa4ew7zufn3pj3juu" > multi_link_rm_exp && test_cmp multi_link_rm_exp multi_link_rm_out ' @@ -355,7 +123,7 @@ test_object_cmd() { test_patch_create_path $EMPTY a/b/b/b/b $FILE test_expect_success "can create blank object" ' - BLANK=$(ipfs object new) + BLANK=$EMPTY_DIR ' test_patch_create_path $BLANK a $FILE @@ -363,98 +131,6 @@ test_object_cmd() { test_expect_success "create bad path fails" ' test_must_fail ipfs object patch $EMPTY add-link --create / $FILE ' - - test_expect_success "patch set-data works" ' - EMPTY=$(ipfs object new) && - HASH=$(printf "foo" | ipfs object patch $EMPTY set-data) - ' - - test_expect_success "output looks good" ' - echo "{\"Links\":[],\"Data\":\"foo\"}" > exp_data_set && - ipfs object get $HASH > actual_data_set && - test_cmp exp_data_set actual_data_set - ' - - test_expect_success "patch append-data works" ' - HASH=$(printf "bar" | ipfs object patch $HASH append-data) - ' - - test_expect_success "output looks good" ' - echo "{\"Links\":[],\"Data\":\"foobar\"}" > exp_data_append && - ipfs object get $HASH > actual_data_append && - test_cmp exp_data_append actual_data_append - ' - - # - # CidBase Tests - # - - test_expect_success "'ipfs object put file.json --cid-base=base32' succeeds" ' - ipfs object put --cid-base=base32 ../t0051-object-data/testPut.json > actual_putOut - ' - - test_expect_success "'ipfs object put file.json --cid-base=base32' output looks good" ' - HASH="QmUTSAdDi2xsNkDtLqjFgQDMEn5di3Ab9eqbrt4gaiNbUD" && - printf "added $HASH\n" > expected_putOut && - test_cmp expected_putOut actual_putOut - ' - - test_expect_success "'ipfs object put file.json --cid-base=base32 --upgrade-cidv0-in-output=true' succeeds" ' - ipfs object put --cid-base=base32 --upgrade-cidv0-in-output=true ../t0051-object-data/testPut.json > actual_putOut - ' - - test_expect_success "'ipfs object put file.json --cid-base=base32 --upgrade-cidv0-in-output=true' output looks good" ' - HASH=$(ipfs cid base32 "QmUTSAdDi2xsNkDtLqjFgQDMEn5di3Ab9eqbrt4gaiNbUD") && - printf "added $HASH\n" > expected_putOut && - test_cmp expected_putOut actual_putOut - ' - - test_expect_success "'insert json dag with both CidV0 and CidV1 links'" ' - MIXED=$(ipfs object put ../t0051-object-data/mixed.json -q) && - echo $MIXED - ' - - test_expect_success "ipfs object get then put creates identical object with --cid-base=base32" ' - ipfs object get --cid-base=base32 $MIXED > mixedv2.json && - MIXED2=$(ipfs object put -q mixedv2.json) && - echo "$MIXED =? $MIXED2" && - test "$MIXED" = "$MIXED2" - ' - - HASHv0=QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V - HASHv1=bafkqadsimvwgy3zajb2w2yloeefau - - test_expect_success "ipfs object get with --cid-base=base32 uses base32 for CidV1 link only" ' - ipfs object get --cid-base=base32 $MIXED > mixed.actual && - grep -q $HASHv0 mixed.actual && - grep -q $(ipfs cid base32 $HASHv1) mixed.actual - ' - - test_expect_success "ipfs object links --cid-base=base32 --upgrade-cidv0-in-output=true converts both links" ' - ipfs object links --cid-base=base32 --upgrade-cidv0-in-output=true $MIXED | awk "{print \$1}" | sort > links.actual && - echo $(ipfs cid base32 $HASHv1) > links.expected - echo $(ipfs cid base32 $HASHv0) >> links.expected - test_cmp links.actual links.expected - ' -} - -test_object_content_type() { - - test_expect_success "'ipfs object get --encoding=protobuf' returns the correct content type" ' - curl -X POST -sI "http://$API_ADDR/api/v0/object/get?arg=$HASH&encoding=protobuf" | grep -q "^Content-Type: application/protobuf" - ' - - test_expect_success "'ipfs object get --encoding=json' returns the correct content type" ' - curl -X POST -sI "http://$API_ADDR/api/v0/object/get?arg=$HASH&encoding=json" | grep -q "^Content-Type: application/json" - ' - - test_expect_success "'ipfs object get --encoding=text' returns the correct content type" ' - curl -X POST -sI "http://$API_ADDR/api/v0/object/get?arg=$HASH&encoding=text" | grep -q "^Content-Type: text/plain" - ' - - test_expect_success "'ipfs object get --encoding=xml' returns the correct content type" ' - curl -X POST -sI "http://$API_ADDR/api/v0/object/get?arg=$HASH&encoding=xml" | grep -q "^Content-Type: application/xml" - ' } # should work offline @@ -463,7 +139,6 @@ test_object_cmd # should work online test_launch_ipfs_daemon test_object_cmd -test_object_content_type test_kill_ipfs_daemon test_done diff --git a/test/sharness/t0053-dag.sh b/test/sharness/t0053-dag.sh index 7514476be..21fd2c04f 100755 --- a/test/sharness/t0053-dag.sh +++ b/test/sharness/t0053-dag.sh @@ -428,40 +428,7 @@ test_expect_success "'ipfs dag put' check block size" ' test_cmp resolve_data_exp resolve_data ' - test_expect_success "dag stat of simple IPLD object" ' - ipfs dag stat $NESTED_HASH > actual_stat_inner_ipld_obj && - echo "Size: 8, NumBlocks: 1" > exp_stat_inner_ipld_obj && - test_cmp exp_stat_inner_ipld_obj actual_stat_inner_ipld_obj && - ipfs dag stat $HASH > actual_stat_ipld_obj && - echo "Size: 54, NumBlocks: 2" > exp_stat_ipld_obj && - test_cmp exp_stat_ipld_obj actual_stat_ipld_obj - ' - test_expect_success "dag stat of simple UnixFS object" ' - BASIC_UNIXFS=$(echo "1234" | ipfs add --pin=false -q) && - ipfs dag stat $BASIC_UNIXFS > actual_stat_basic_unixfs && - echo "Size: 13, NumBlocks: 1" > exp_stat_basic_unixfs && - test_cmp exp_stat_basic_unixfs actual_stat_basic_unixfs - ' - - # The multiblock file is just 10000000 copies of the number 1 - # As most of its data is replicated it should have a small number of blocks - test_expect_success "dag stat of multiblock UnixFS object" ' - MULTIBLOCK_UNIXFS=$(printf "1%.0s" {1..10000000} | ipfs add --pin=false -q) && - ipfs dag stat $MULTIBLOCK_UNIXFS > actual_stat_multiblock_unixfs && - echo "Size: 302582, NumBlocks: 3" > exp_stat_multiblock_unixfs && - test_cmp exp_stat_multiblock_unixfs actual_stat_multiblock_unixfs - ' - - test_expect_success "dag stat of directory of UnixFS objects" ' - mkdir -p unixfsdir && - echo "1234" > unixfsdir/small.txt - printf "1%.0s" {1..10000000} > unixfsdir/many1s.txt && - DIRECTORY_UNIXFS=$(ipfs add -r --pin=false -Q unixfsdir) && - ipfs dag stat $DIRECTORY_UNIXFS > actual_stat_directory_unixfs && - echo "Size: 302705, NumBlocks: 5" > exp_stat_directory_unixfs && - test_cmp exp_stat_directory_unixfs actual_stat_directory_unixfs - ' } # should work offline diff --git a/test/sharness/t0054-dag-car-import-export-data/README.md b/test/sharness/t0054-dag-car-import-export-data/README.md index 2b6b5c00f..786f9ade0 100644 --- a/test/sharness/t0054-dag-car-import-export-data/README.md +++ b/test/sharness/t0054-dag-car-import-export-data/README.md @@ -20,9 +20,13 @@ - versions identical to the above, but with a single "empty-block" root each ( in order to work around go-car not following the current "roots can be empty" spec ) - combined_naked_roots_genesis_and_128.car - - only the roots of `lotus_devnet_genesis.car` and `lotus_testnet_export_128.car`, to to be used in combination with the root-less parts to validate "transactional" pinning + - only the roots of `lotus_devnet_genesis.car` and `lotus_testnet_export_128.car`,to be used in combination with the root-less parts to validate "transactional" pinning - lotus_testnet_export_128_v2.car - lotus_devnet_genesis_v2.car - generated with `car index lotus_testnet_export_128.car > lotus_testnet_export_128_v2.car` - install `go-car` CLI from https://github.com/ipld/go-car + +- partial-dag-scope-entity.car + - unixfs directory entity exported from gateway via `?format=car&dag-scope=entity` ([IPIP-402](https://github.com/ipfs/specs/pull/402)) + - CAR roots includes directory CID, but only the root block is included in the CAR, making the DAG incomplete diff --git a/test/sharness/t0054-dag-car-import-export-data/partial-dag-scope-entity.car b/test/sharness/t0054-dag-car-import-export-data/partial-dag-scope-entity.car new file mode 100644 index 000000000..b149a18df Binary files /dev/null and b/test/sharness/t0054-dag-car-import-export-data/partial-dag-scope-entity.car differ diff --git a/test/sharness/t0054-dag-car-import-export.sh b/test/sharness/t0054-dag-car-import-export.sh index f378e9128..e277cc466 100755 --- a/test/sharness/t0054-dag-car-import-export.sh +++ b/test/sharness/t0054-dag-car-import-export.sh @@ -171,20 +171,18 @@ test_expect_success "shut down nodes" ' # We want to just init the repo, without using a daemon for stuff below -test_init_ipfs +test_init_ipfs --empty-repo=false test_expect_success "basic offline export of 'getting started' dag works" ' ipfs dag export "$HASH_WELCOME_DOCS" >/dev/null ' - -echo "Error: block was not found locally (offline): ipld: could not find QmYwAPJXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX (currently offline, perhaps retry after attaching to the network)" > offline_fetch_error_expected test_expect_success "basic offline export of nonexistent cid" ' ! ipfs dag export QmYwAPJXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 2> offline_fetch_error_actual >/dev/null ' test_expect_success "correct error" ' - test_cmp_sorted offline_fetch_error_expected offline_fetch_error_actual + test_should_contain "Error: block was not found locally (offline): ipld: could not find QmYwAPJXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" offline_fetch_error_actual ' cat >multiroot_import_json_stats_expected < partial_nopin_import_expected << EOE +{"Stats":{"BlockCount":1,"BlockBytesCount":1618}} +EOE +test_expect_success "'ipfs dag import' without pinning works fine with incomplete DAG (unixfs dir exported as dag-scope=entity from IPIP-402)" ' + ipfs dag import --stats --enc=json --pin-roots=false ../t0054-dag-car-import-export-data/partial-dag-scope-entity.car >partial_nopin_import_out 2>&1 && + test_cmp partial_nopin_import_expected partial_nopin_import_out +' + +test_expect_success "'ipfs dag import' with pinning errors due to incomplete DAG (unixfs dir exported as dag-scope=entity from IPIP-402)" ' + ipfs dag import --stats --enc=json --pin-roots=true ../t0054-dag-car-import-export-data/partial-dag-scope-entity.car >partial_pin_import_out 2>&1 && + test_should_contain "\"PinErrorMsg\":\"block was not found locally" partial_pin_import_out +' + +test_expect_success "'ipfs dag import' pin error in default CLI mode produces exit code 1 (unixfs dir exported as dag-scope=entity from IPIP-402)" ' + test_expect_code 1 ipfs dag import ../t0054-dag-car-import-export-data/partial-dag-scope-entity.car >partial_pin_import_out 2>&1 && + test_should_contain "Error: pinning root \"QmPDC11yLAbVw3dX5jMeEuSdk4BiVjSd9X87zaYRdVjzW3\" FAILED: block was not found locally" partial_pin_import_out +' + test_done diff --git a/test/sharness/t0060-daemon.sh b/test/sharness/t0060-daemon.sh index ca40fd0d1..431ff245c 100755 --- a/test/sharness/t0060-daemon.sh +++ b/test/sharness/t0060-daemon.sh @@ -76,25 +76,16 @@ test_expect_success "ipfs gateway works with the correct allowed origin port" ' curl -s -X POST -H "Origin:http://localhost:$GWAY_PORT" -I "http://$GWAY_ADDR/api/v0/version" ' -test_expect_success "ipfs daemon output looks good" ' - STARTFILE="ipfs cat /ipfs/$HASH_WELCOME_DOCS/readme" && - echo "Initializing daemon..." >expected_daemon && - ipfs version --all >> expected_daemon && - echo "" >>expected_daemon && - echo "Computing default go-libp2p Resource Manager limits based on:" >>expected_daemon && - echo " - '"'"'Swarm.ResourceMgr.MaxMemory'"'"': \"4GB\"" >>expected_daemon && - echo " - '"'"'Swarm.ResourceMgr.MaxFileDescriptors'"'"': 1024" >>expected_daemon && - echo "" >>expected_daemon && - echo "Applying any user-supplied overrides on top." >>expected_daemon && - echo "Run '"'"'ipfs swarm limit all'"'"' to see the resulting limits." >>expected_daemon && - echo "" >>expected_daemon && - sed "s/^/Swarm listening on /" listen_addrs >>expected_daemon && - sed "s/^/Swarm announcing /" local_addrs >>expected_daemon && - echo "API server listening on '$API_MADDR'" >>expected_daemon && - echo "WebUI: http://'$API_ADDR'/webui" >>expected_daemon && - echo "Gateway (readonly) server listening on '$GWAY_MADDR'" >>expected_daemon && - echo "Daemon is ready" >>expected_daemon && - test_cmp expected_daemon actual_daemon +test_expect_success "ipfs daemon output includes looks good" ' + test_should_contain "Initializing daemon..." actual_daemon && + test_should_contain "$(ipfs version --all)" actual_daemon && + test_should_contain "PeerID: $(ipfs config Identity.PeerID)" actual_daemon && + test_should_contain "Swarm listening on 127.0.0.1:" actual_daemon && + test_should_contain "RPC API server listening on '$API_MADDR'" actual_daemon && + test_should_contain "WebUI: http://'$API_ADDR'/webui" actual_daemon && + test_should_contain "Gateway server listening on '$GWAY_MADDR'" actual_daemon && + test_should_contain "Daemon is ready" actual_daemon && + cat actual_daemon ' test_expect_success ".ipfs/ has been created" ' diff --git a/test/sharness/t0080-repo.sh b/test/sharness/t0080-repo.sh index 7bd84b276..3f33a5f44 100755 --- a/test/sharness/t0080-repo.sh +++ b/test/sharness/t0080-repo.sh @@ -8,7 +8,7 @@ test_description="Test ipfs repo operations" . lib/test-lib.sh -test_init_ipfs +test_init_ipfs --empty-repo=false test_launch_ipfs_daemon_without_network test_expect_success "'ipfs repo gc' succeeds" ' diff --git a/test/sharness/t0081-repo-pinning.sh b/test/sharness/t0081-repo-pinning.sh index 030f3fa3d..92cb71c38 100755 --- a/test/sharness/t0081-repo-pinning.sh +++ b/test/sharness/t0081-repo-pinning.sh @@ -114,8 +114,8 @@ test_expect_success "objects are there" ' ' # saving this output for later -test_expect_success "ipfs object links $HASH_DIR1 works" ' - ipfs object links $HASH_DIR1 > DIR1_objlink +test_expect_success "ipfs dag get $HASH_DIR1 works" ' + ipfs dag get $HASH_DIR1 | jq -r ".Links[] | .Hash | .[\"/\"]" > DIR1_objlink ' @@ -224,7 +224,7 @@ test_expect_success "some objects are still there" ' ipfs cat "$HASH_FILE1" >>actual8 && ipfs ls "$HASH_DIR4" >>actual8 && ipfs ls "$HASH_DIR2" >>actual8 && - ipfs object links "$HASH_DIR1" >>actual8 && + ipfs dag get "$HASH_DIR1" | jq -r ".Links[] | .Hash | .[\"/\"]" >>actual8 && test_cmp expected8 actual8 ' diff --git a/test/sharness/t0090-get.sh b/test/sharness/t0090-get.sh index 5e16c7d01..6a803080e 100755 --- a/test/sharness/t0090-get.sh +++ b/test/sharness/t0090-get.sh @@ -129,9 +129,8 @@ test_get_cmd() { ' test_expect_success "ipfs get ../.. should fail" ' - echo "Error: invalid path \"../..\": selected encoding not supported" >expected && test_must_fail ipfs get ../.. 2>actual && - test_cmp expected actual + test_should_contain "Error: invalid path \"../..\"" actual ' test_expect_success "create small file" ' @@ -158,13 +157,13 @@ test_get_cmd() { test_get_fail() { test_expect_success "create an object that has unresolvable links" ' cat <<-\EOF >bad_object && -{ "Links": [ { "Name": "foo", "Hash": "QmZzaC6ydNXiR65W8VjGA73ET9MZ6VFAqUT1ngYMXcpihn", "Size": 1897 }, { "Name": "bar", "Hash": "Qmd4mG6pDFDmDTn6p3hX1srP8qTbkyXKj5yjpEsiHDX3u8", "Size": 56 }, { "Name": "baz", "Hash": "QmUTjwRnG28dSrFFVTYgbr6LiDLsBmRr2SaUSTGheK2YqG", "Size": 24266 } ], "Data": "\b\u0001" } +{"Data":{"/":{"bytes":"CAE"}},"Links":[{"Hash":{"/":"Qmd4mG6pDFDmDTn6p3hX1srP8qTbkyXKj5yjpEsiHDX3u8"},"Name":"bar","Tsize":56},{"Hash":{"/":"QmUTjwRnG28dSrFFVTYgbr6LiDLsBmRr2SaUSTGheK2YqG"},"Name":"baz","Tsize":24266},{"Hash":{"/":"QmZzaC6ydNXiR65W8VjGA73ET9MZ6VFAqUT1ngYMXcpihn"},"Name":"foo","Tsize":1897}]} EOF - cat bad_object | ipfs object put > put_out + cat bad_object | ipfs dag put --store-codec dag-pb > put_out ' test_expect_success "output looks good" ' - echo "added QmaGidyrnX8FMbWJoxp8HVwZ1uRKwCyxBJzABnR1S2FVUr" > put_exp && + echo "bafybeifrjjol3gixedca6etdwccnvwfvhurc4wb3i5mnk2rvwvyfcgwxd4" > put_exp && test_cmp put_exp put_out ' diff --git a/test/sharness/t0100-name.sh b/test/sharness/t0100-name.sh deleted file mode 100755 index 934cc0127..000000000 --- a/test/sharness/t0100-name.sh +++ /dev/null @@ -1,293 +0,0 @@ -#!/usr/bin/env bash -# -# Copyright (c) 2014 Jeromy Johnson -# MIT Licensed; see the LICENSE file in this repository. -# - -test_description="Test ipfs repo operations" - -. lib/test-lib.sh - -test_name_with_self() { - SELF_ALG=$1 - - test_expect_success "ipfs init (variant self $SELF_ALG)" ' - export IPFS_PATH="$(pwd)/.ipfs" && - case $SELF_ALG in - default) - ipfs init --profile=test > /dev/null - ;; - rsa) - ipfs init --profile=test -a=rsa > /dev/null - ;; - ed25519) - ipfs init --profile=test -a=ed25519 > /dev/null - ;; - esac && - export PEERID=`ipfs key list --ipns-base=base36 -l | grep self | cut -d " " -f1` && - test_check_peerid "${PEERID}" - ' - - # test publishing a hash - - test_expect_success "'ipfs name publish --allow-offline' succeeds" ' - ipfs name publish --allow-offline "/ipfs/$HASH_WELCOME_DOCS" >publish_out - ' - - test_expect_success "publish output looks good" ' - echo "Published to ${PEERID}: /ipfs/$HASH_WELCOME_DOCS" >expected1 && - test_cmp expected1 publish_out - ' - - test_expect_success "'ipfs name resolve' succeeds" ' - ipfs name resolve "$PEERID" >output - ' - - test_expect_success "resolve output looks good" ' - printf "/ipfs/%s\n" "$HASH_WELCOME_DOCS" >expected2 && - test_cmp expected2 output - ' - - # test publishing with -Q option - - test_expect_success "'ipfs name publish --quieter' succeeds" ' - ipfs name publish --allow-offline -Q "/ipfs/$HASH_WELCOME_DOCS" >publish_out - ' - - test_expect_success "publish --quieter output looks good" ' - echo "${PEERID}" >expected1 && - test_cmp expected1 publish_out - ' - - test_expect_success "'ipfs name resolve' succeeds" ' - ipfs name resolve "$PEERID" >output - ' - - test_expect_success "resolve output looks good" ' - printf "/ipfs/%s\n" "$HASH_WELCOME_DOCS" >expected2 && - test_cmp expected2 output - ' - - # now test with a path - - test_expect_success "'ipfs name publish --allow-offline' succeeds" ' - ipfs name publish --allow-offline "/ipfs/$HASH_WELCOME_DOCS/help" >publish_out - ' - - test_expect_success "publish a path looks good" ' - echo "Published to ${PEERID}: /ipfs/$HASH_WELCOME_DOCS/help" >expected3 && - test_cmp expected3 publish_out - ' - - test_expect_success "'ipfs name resolve' succeeds" ' - ipfs name resolve "$PEERID" >output - ' - - test_expect_success "resolve output looks good" ' - printf "/ipfs/%s/help\n" "$HASH_WELCOME_DOCS" >expected4 && - test_cmp expected4 output - ' - - test_expect_success "ipfs cat on published content succeeds" ' - ipfs cat "/ipfs/$HASH_WELCOME_DOCS/help" >expected && - ipfs cat "/ipns/$PEERID" >actual && - test_cmp expected actual - ' - - # publish with an explicit node ID - - test_expect_failure "'ipfs name publish --allow-offline ' succeeds" ' - echo ipfs name publish --allow-offline "${PEERID}" "/ipfs/$HASH_WELCOME_DOCS" && - ipfs name publish --allow-offline "${PEERID}" "/ipfs/$HASH_WELCOME_DOCS" >actual_node_id_publish - ' - - test_expect_failure "publish with our explicit node ID looks good" ' - echo "Published to ${PEERID}: /ipfs/$HASH_WELCOME_DOCS" >expected_node_id_publish && - test_cmp expected_node_id_publish actual_node_id_publish - ' - - # test publishing with B36CID and B58MH resolve to the same B36CID - - test_expect_success "verify self key output" ' - B58MH_ID=`ipfs key list --ipns-base=b58mh -l | grep self | cut -d " " -f1` && - B36CID_ID=`ipfs key list --ipns-base=base36 -l | grep self | cut -d " " -f1` && - test_check_peerid "${B58MH_ID}" && - test_check_peerid "${B36CID_ID}" - ' - - test_expect_success "'ipfs name publish --allow-offline --key= ' succeeds" ' - ipfs name publish --allow-offline --key=${B58MH_ID} "/ipfs/$HASH_WELCOME_DOCS" >b58mh_published_id_base36 && - ipfs name publish --allow-offline --key=${B36CID_ID} "/ipfs/$HASH_WELCOME_DOCS" >base36_published_id_base36 && - ipfs name publish --allow-offline --key=${B58MH_ID} --ipns-base=b58mh "/ipfs/$HASH_WELCOME_DOCS" >b58mh_published_id_b58mh && - ipfs name publish --allow-offline --key=${B36CID_ID} --ipns-base=b58mh "/ipfs/$HASH_WELCOME_DOCS" >base36_published_id_b58mh - ' - - test_expect_success "publish an explicit node ID as two key in B58MH and B36CID, name looks good" ' - echo "Published to ${B36CID_ID}: /ipfs/$HASH_WELCOME_DOCS" >expected_published_id_base36 && - echo "Published to ${B58MH_ID}: /ipfs/$HASH_WELCOME_DOCS" >expected_published_id_b58mh && - test_cmp expected_published_id_base36 b58mh_published_id_base36 && - test_cmp expected_published_id_base36 base36_published_id_base36 && - test_cmp expected_published_id_b58mh b58mh_published_id_b58mh && - test_cmp expected_published_id_b58mh base36_published_id_b58mh - ' - - test_expect_success "'ipfs name resolve' succeeds" ' - ipfs name resolve "$B36CID_ID" >output - ' - - test_expect_success "resolve output looks good" ' - printf "/ipfs/%s\n" "$HASH_WELCOME_DOCS" >expected2 && - test_cmp expected2 output - ' - - # test IPNS + IPLD - - test_expect_success "'ipfs dag put' succeeds" ' - HELLO_HASH="$(echo "\"hello world\"" | ipfs dag put)" && - OBJECT_HASH="$(echo "{\"thing\": {\"/\": \"${HELLO_HASH}\" }}" | ipfs dag put)" - ' - test_expect_success "'ipfs name publish --allow-offline /ipld/...' succeeds" ' - test_check_peerid "${PEERID}" && - ipfs name publish --allow-offline "/ipld/$OBJECT_HASH/thing" >publish_out - ' - test_expect_success "publish a path looks good" ' - echo "Published to ${PEERID}: /ipld/$OBJECT_HASH/thing" >expected3 && - test_cmp expected3 publish_out - ' - test_expect_success "'ipfs name resolve' succeeds" ' - ipfs name resolve "$PEERID" >output - ' - test_expect_success "resolve output looks good (IPNS + IPLD)" ' - printf "/ipld/%s/thing\n" "$OBJECT_HASH" >expected4 && - test_cmp expected4 output - ' - - # test publishing nothing - - test_expect_success "'ipfs name publish' fails" ' - printf '' | test_expect_code 1 ipfs name publish --allow-offline >publish_out 2>&1 - ' - - test_expect_success "publish output has the correct error" ' - grep "argument \"ipfs-path\" is required" publish_out - ' - - test_expect_success "'ipfs name publish' fails" ' - printf '' | test_expect_code 1 ipfs name publish -Q --allow-offline >publish_out 2>&1 - ' - - test_expect_success "publish output has the correct error" ' - grep "argument \"ipfs-path\" is required" publish_out - ' - - test_expect_success "'ipfs name publish --help' succeeds" ' - ipfs name publish --help - ' - - # test offline resolve - - test_expect_success "'ipfs name resolve --offline' succeeds" ' - ipfs name resolve --offline "$PEERID" >output - ' - test_expect_success "resolve output looks good (offline resolve)" ' - printf "/ipld/%s/thing\n" "$OBJECT_HASH" >expected4 && - test_cmp expected4 output - ' - - test_expect_success "'ipfs name resolve --offline -n' succeeds" ' - ipfs name resolve --offline -n "$PEERID" >output - ' - test_expect_success "resolve output looks good (offline resolve, -n)" ' - printf "/ipld/%s/thing\n" "$OBJECT_HASH" >expected4 && - test_cmp expected4 output - ' - - test_launch_ipfs_daemon - - test_expect_success "'ipfs name resolve --offline' succeeds" ' - ipfs name resolve --offline "$PEERID" >output - ' - test_expect_success "resolve output looks good (with daemon)" ' - printf "/ipld/%s/thing\n" "$OBJECT_HASH" >expected4 && - test_cmp expected4 output - ' - - test_expect_success "'ipfs name resolve --offline -n' succeeds" ' - ipfs name resolve --offline -n "$PEERID" >output - ' - test_expect_success "resolve output looks good (with daemon, -n)" ' - printf "/ipld/%s/thing\n" "$OBJECT_HASH" >expected4 && - test_cmp expected4 output - ' - - test_expect_success "empty request to name publish doesn't panic and returns error" ' - curl -X POST "http://$API_ADDR/api/v0/name/publish" > curl_out || true && - grep "argument \"ipfs-path\" is required" curl_out - ' - - test_kill_ipfs_daemon - - # Test daemon in offline mode - test_launch_ipfs_daemon_without_network - - test_expect_success "'ipfs name publish' fails offline mode" ' - test_expect_code 1 ipfs name publish "/ipfs/$HASH_WELCOME_DOCS" - ' - - test_kill_ipfs_daemon - - test_expect_success "clean up ipfs dir" ' - rm -rf "$IPFS_PATH" - ' -} -test_name_with_self 'default' -test_name_with_self 'rsa' -test_name_with_self 'ed25519' - -test_name_with_key() { - GEN_ALG=$1 - - test_expect_success "ipfs init (key variant $GEN_ALG)" ' - export IPFS_PATH="$(pwd)/.ipfs" && - ipfs init --profile=test > /dev/null - ' - - test_expect_success "'prepare keys" ' - case $GEN_ALG in - rsa) - export KEY=`ipfs key gen --ipns-base=b58mh --type=rsa --size=2048 key` && - export KEY_B36CID=`ipfs key list --ipns-base=base36 -l | grep key | cut -d " " -f1` - ;; - ed25519_b58) - export KEY=`ipfs key gen --ipns-base=b58mh --type=ed25519 key` - export KEY_B36CID=`ipfs key list --ipns-base=base36 -l | grep key | cut -d " " -f1` - ;; - ed25519_b36) - export KEY=`ipfs key gen --ipns-base=base36 --type=ed25519 key` - export KEY_B36CID=$KEY - ;; - esac && - test_check_peerid "${KEY}" - ' - - # publish with an explicit node ID as key name - - test_expect_success "'ipfs name publish --allow-offline --key= ' succeeds" ' - ipfs name publish --allow-offline --key=${KEY} "/ipfs/$HASH_WELCOME_DOCS" >actual_node_id_publish - ' - - test_expect_success "publish an explicit node ID as key name looks good" ' - echo "Published to ${KEY_B36CID}: /ipfs/$HASH_WELCOME_DOCS" >expected_node_id_publish && - test_cmp expected_node_id_publish actual_node_id_publish - ' - - # cleanup - test_expect_success "clean up ipfs dir" ' - rm -rf "$IPFS_PATH" - ' -} -test_name_with_key 'rsa' -test_name_with_key 'ed25519_b58' -test_name_with_key 'ed25519_b36' - -test_done diff --git a/test/sharness/t0109-gateway-web-_redirects.sh b/test/sharness/t0109-gateway-web-_redirects.sh index 9ebfb911e..0bc2a23b6 100755 --- a/test/sharness/t0109-gateway-web-_redirects.sh +++ b/test/sharness/t0109-gateway-web-_redirects.sh @@ -14,7 +14,7 @@ test_launch_ipfs_daemon # Import test case # Run `ipfs cat /ipfs/$REDIRECTS_DIR_CID/_redirects` to see sample _redirects file test_expect_success "Add the _redirects file test directory" ' - ipfs dag import ../t0109-gateway-web-_redirects-data/redirects.car + ipfs dag import --pin-roots ../t0109-gateway-web-_redirects-data/redirects.car ' CAR_ROOT_CID=QmQyqMY5vUBSbSxyitJqthgwZunCQjDVtNd8ggVCxzuPQ4 diff --git a/test/sharness/t0110-gateway-data/foo.block b/test/sharness/t0110-gateway-data/foo.block deleted file mode 100644 index 39c7ef60b..000000000 --- a/test/sharness/t0110-gateway-data/foo.block +++ /dev/null @@ -1,2 +0,0 @@ - - foo \ No newline at end of file diff --git a/test/sharness/t0110-gateway-data/foofoo.block b/test/sharness/t0110-gateway-data/foofoo.block deleted file mode 100644 index 9e5177b18..000000000 Binary files a/test/sharness/t0110-gateway-data/foofoo.block and /dev/null differ diff --git a/test/sharness/t0110-gateway.sh b/test/sharness/t0110-gateway.sh deleted file mode 100755 index 87aa61c70..000000000 --- a/test/sharness/t0110-gateway.sh +++ /dev/null @@ -1,357 +0,0 @@ -#!/usr/bin/env bash -# -# Copyright (c) 2015 Matt Bell -# MIT Licensed; see the LICENSE file in this repository. -# - -test_description="Test HTTP Gateway" - -. lib/test-lib.sh - -test_init_ipfs -test_launch_ipfs_daemon - -port=$GWAY_PORT -apiport=$API_PORT - -# TODO check both 5001 and 5002. -# 5001 should have a readable gateway (part of the API) -# 5002 should have a readable gateway (using ipfs config Addresses.Gateway) -# but ideally we should only write the tests once. so maybe we need to -# define a function to test a gateway, and do so for each port. -# for now we check 5001 here as 5002 will be checked in gateway-writable. - -test_expect_success "Make a file to test with" ' - echo "Hello Worlds!" >expected && - HASH=$(ipfs add -q expected) || - test_fsh cat daemon_err -' - -test_expect_success "GET IPFS path succeeds" ' - curl -sfo actual "http://127.0.0.1:$port/ipfs/$HASH" -' - -test_expect_success "GET IPFS path with explicit ?filename succeeds with proper header" " - curl -fo actual -D actual_headers 'http://127.0.0.1:$port/ipfs/$HASH?filename=testтест.pdf' && - grep -F 'Content-Disposition: inline; filename=\"test____.pdf\"; filename*=UTF-8'\'\''test%D1%82%D0%B5%D1%81%D1%82.pdf' actual_headers -" - -test_expect_success "GET IPFS path with explicit ?filename and &download=true succeeds with proper header" " - curl -fo actual -D actual_headers 'http://127.0.0.1:$port/ipfs/$HASH?filename=testтест.mp4&download=true' && - grep -F 'Content-Disposition: attachment; filename=\"test____.mp4\"; filename*=UTF-8'\'\''test%D1%82%D0%B5%D1%81%D1%82.mp4' actual_headers -" - -# https://github.com/ipfs/go-ipfs/issues/4025#issuecomment-342250616 -test_expect_success "GET for Service Worker registration outside of an IPFS content root errors" " - curl -H 'Service-Worker: script' -svX GET 'http://127.0.0.1:$port/ipfs/$HASH?filename=sw.js' > curl_sw_out 2>&1 && - grep 'HTTP/1.1 400 Bad Request' curl_sw_out && - grep 'navigator.serviceWorker: registration is not allowed for this scope' curl_sw_out -" - -test_expect_success "GET IPFS path output looks good" ' - test_cmp expected actual && - rm actual -' - -test_expect_success "GET IPFS directory path succeeds" ' - mkdir -p dir/dirwithindex && - echo "12345" >dir/test && - echo "hello i am a webpage" >dir/dirwithindex/index.html && - ipfs add -r -q dir >actual && - HASH2=$(tail -n 1 actual) && - curl -sf "http://127.0.0.1:$port/ipfs/$HASH2" -' - -test_expect_success "GET IPFS directory file succeeds" ' - curl -sfo actual "http://127.0.0.1:$port/ipfs/$HASH2/test" -' - -test_expect_success "GET IPFS directory file output looks good" ' - test_cmp dir/test actual -' - -test_expect_success "GET IPFS directory with index.html returns redirect to add trailing slash" " - curl -sI -o response_without_slash \"http://127.0.0.1:$port/ipfs/$HASH2/dirwithindex?query=to-remember\" && - test_should_contain \"HTTP/1.1 301 Moved Permanently\" response_without_slash && - test_should_contain \"Location: /ipfs/$HASH2/dirwithindex/?query=to-remember\" response_without_slash -" - -# This enables go get to parse go-import meta tags from index.html files stored in IPFS -# https://github.com/ipfs/kubo/pull/3963 -test_expect_success "GET IPFS directory with index.html and no trailing slash returns expected output when go-get is passed" " - curl -s -o response_with_slash \"http://127.0.0.1:$port/ipfs/$HASH2/dirwithindex?go-get=1\" && - test_should_contain \"hello i am a webpage\" response_with_slash -" - -test_expect_success "GET IPFS directory with index.html and trailing slash returns expected output" " - curl -s -o response_with_slash \"http://127.0.0.1:$port/ipfs/$HASH2/dirwithindex/?query=to-remember\" && - test_should_contain \"hello i am a webpage\" response_with_slash -" - -test_expect_success "GET IPFS nonexistent file returns 404 (Not Found)" ' - test_curl_resp_http_code "http://127.0.0.1:$port/ipfs/$HASH2/pleaseDontAddMe" "HTTP/1.1 404 Not Found" -' - -test_expect_success "GET IPFS invalid CID returns 400 (Bad Request)" ' - test_curl_resp_http_code "http://127.0.0.1:$port/ipfs/QmInvalid/pleaseDontAddMe" "HTTP/1.1 400 Bad Request" -' - -# https://github.com/ipfs/go-ipfs/issues/8230 -test_expect_success "GET IPFS inlined zero-length data object returns ok code (200)" ' - curl -sD - "http://127.0.0.1:$port/ipfs/bafkqaaa" > empty_ok_response && - test_should_contain "HTTP/1.1 200 OK" empty_ok_response && - test_should_contain "Content-Length: 0" empty_ok_response -' - -# https://github.com/ipfs/kubo/issues/9238 -test_expect_success "GET IPFS inlined zero-length data object with byte range returns ok code (200)" ' - curl -sD - "http://127.0.0.1:$port/ipfs/bafkqaaa" -H "Range: bytes=0-1048575" > empty_ok_response && - test_should_contain "HTTP/1.1 200 OK" empty_ok_response && - test_should_contain "Content-Length: 0" empty_ok_response && - test_should_contain "Content-Type: text/plain" empty_ok_response -' - -test_expect_success "GET /ipfs/ipfs/{cid} returns redirect to the valid path" ' - curl -sD - "http://127.0.0.1:$port/ipfs/ipfs/bafkqaaa?query=to-remember" > response_with_double_ipfs_ns && - test_should_contain "" response_with_double_ipfs_ns && - test_should_contain "" response_with_double_ipfs_ns -' - -test_expect_success "GET invalid IPNS root returns 400 (Bad Request)" ' - test_curl_resp_http_code "http://127.0.0.1:$port/ipns/QmInvalid/pleaseDontAddMe" "HTTP/1.1 400 Bad Request" -' - -test_expect_failure "GET IPNS path succeeds" ' - ipfs name publish --allow-offline "$HASH" && - PEERID=$(ipfs config Identity.PeerID) && - test_check_peerid "$PEERID" && - curl -sfo actual "http://127.0.0.1:$port/ipns/$PEERID" -' - -test_expect_failure "GET IPNS path output looks good" ' - test_cmp expected actual -' - -test_expect_success "GET /ipfs/ipns/{peerid} returns redirect to the valid path" ' - PEERID=$(ipfs config Identity.PeerID) && - curl -sD - "http://127.0.0.1:$port/ipfs/ipns/${PEERID}?query=to-remember" > response_with_ipfs_ipns_ns && - test_should_contain "" response_with_ipfs_ipns_ns && - test_should_contain "" response_with_ipfs_ipns_ns -' - -test_expect_success "GET invalid IPFS path errors" ' - test_must_fail curl -sf "http://127.0.0.1:$port/ipfs/12345" -' - -test_expect_success "GET invalid path errors" ' - test_must_fail curl -sf "http://127.0.0.1:$port/12345" -' - -test_expect_success "GET /webui returns code expected" ' - test_curl_resp_http_code "http://127.0.0.1:$apiport/webui" "HTTP/1.1 302 Found" "HTTP/1.1 301 Moved Permanently" -' - -test_expect_success "GET /webui/ returns code expected" ' - test_curl_resp_http_code "http://127.0.0.1:$apiport/webui/" "HTTP/1.1 302 Found" "HTTP/1.1 301 Moved Permanently" -' - -test_expect_success "GET /logs returns logs" ' - test_expect_code 28 curl http://127.0.0.1:$apiport/logs -m1 > log_out -' - -test_expect_success "log output looks good" ' - grep "log API client connected" log_out -' - -test_expect_success "GET /api/v0/version succeeds" ' - curl -X POST -v "http://127.0.0.1:$apiport/api/v0/version" 2> version_out -' - -test_expect_success "output only has one transfer encoding header" ' - grep "Transfer-Encoding: chunked" version_out | wc -l | xargs echo > tecount_out && - echo "1" > tecount_exp && - test_cmp tecount_out tecount_exp -' - -curl_pprofmutex() { - curl -f -X POST "http://127.0.0.1:$apiport/debug/pprof-mutex/?fraction=$1" -} - -test_expect_success "set mutex fraction for pprof (negative so it doesn't enable)" ' - curl_pprofmutex -1 -' - -test_expect_success "test failure conditions of mutex pprof endpoint" ' - test_must_fail curl_pprofmutex && - test_must_fail curl_pprofmutex that_is_string && - test_must_fail curl -f -X GET "http://127.0.0.1:$apiport/debug/pprof-mutex/?fraction=-1" -' - -curl_pprofblock() { - curl -f -X POST "http://127.0.0.1:$apiport/debug/pprof-block/?rate=$1" -} - -test_expect_success "set blocking profiler rate for pprof (0 so it doesn't enable)" ' - curl_pprofblock 0 -' - -test_expect_success "test failure conditions of mutex block endpoint" ' - test_must_fail curl_pprofblock && - test_must_fail curl_pprofblock that_is_string && - test_must_fail curl -f -X GET "http://127.0.0.1:$apiport/debug/pprof-block/?rate=0" -' - -test_expect_success "setup index hash" ' - mkdir index && - echo "

" > index/index.html && - INDEXHASH=$(ipfs add -Q -r index) - echo index: $INDEXHASH -' - -test_expect_success "GET 'index.html' has correct content type" ' - curl -I "http://127.0.0.1:$port/ipfs/$INDEXHASH/" > indexout -' - -test_expect_success "output looks good" ' - grep "Content-Type: text/html" indexout -' - -test_expect_success "HEAD 'index.html' has no content" ' - curl -X HEAD --max-time 1 http://127.0.0.1:$port/ipfs/$INDEXHASH/ > output; - [ ! -s output ] -' - -# test ipfs readonly api - -test_curl_gateway_api() { - curl -sfo actual "http://127.0.0.1:$port/api/v0/$1" -} - -test_expect_success "get IPFS directory file through readonly API succeeds" ' - test_curl_gateway_api "cat?arg=$HASH2/test" -' - -test_expect_success "get IPFS directory file through readonly API output looks good" ' - test_cmp dir/test actual -' - -test_expect_success "refs IPFS directory file through readonly API succeeds" ' - test_curl_gateway_api "refs?arg=$HASH2/test" -' - -for cmd in add \ - block/put \ - bootstrap \ - config \ - dag/put \ - dag/import \ - dht \ - diag \ - id \ - mount \ - name/publish \ - object/put \ - object/new \ - object/patch \ - pin \ - ping \ - repo \ - stats \ - swarm \ - file \ - update \ - bitswap -do - test_expect_success "test gateway api is sanitized: $cmd" ' - test_curl_resp_http_code "http://127.0.0.1:$port/api/v0/$cmd" "HTTP/1.1 404 Not Found" - ' -done - -# This one is different. `local` will be interpreted as a path if the command isn't defined. -test_expect_success "test gateway api is sanitized: refs/local" ' - echo "Error: invalid path \"local\": selected encoding not supported" > refs_local_expected && - ! ipfs --api /ip4/127.0.0.1/tcp/$port refs local > refs_local_actual 2>&1 && - test_cmp refs_local_expected refs_local_actual - ' - -test_expect_success "create raw-leaves node" ' - echo "This is RAW!" > rfile && - echo "This is RAW!" | ipfs add --raw-leaves -q > rhash -' - -test_expect_success "try fetching it from gateway" ' - curl http://127.0.0.1:$port/ipfs/$(cat rhash) > ffile && - test_cmp rfile ffile -' - -test_expect_success "Add compact blocks" ' - ipfs block put ../t0110-gateway-data/foo.block && - FOO2_HASH=$(ipfs block put --cid-codec=dag-pb ../t0110-gateway-data/foofoo.block) && - printf "foofoo" > expected -' - -test_expect_success "GET compact blocks succeeds" ' - curl -o actual "http://127.0.0.1:$port/ipfs/$FOO2_HASH" && - test_cmp expected actual -' - -test_expect_success "Verify gateway file" ' - cat "$IPFS_PATH/gateway" > gateway_file_actual && - echo -n "http://$GWAY_ADDR" > gateway_daemon_actual && - test_cmp gateway_daemon_actual gateway_file_actual -' - -test_kill_ipfs_daemon - -GWPORT=32563 - -test_expect_success "Verify gateway file diallable while on unspecified" ' - ipfs config Addresses.Gateway /ip4/0.0.0.0/tcp/$GWPORT && - test_launch_ipfs_daemon && - cat "$IPFS_PATH/gateway" > gateway_file_actual && - echo -n "http://127.0.0.1:$GWPORT" > gateway_file_expected && - test_cmp gateway_file_expected gateway_file_actual -' - -test_kill_ipfs_daemon - -test_expect_success "set up iptb testbed" ' - iptb testbed create -type localipfs -count 5 -force -init && - ipfsi 0 config Addresses.Gateway /ip4/127.0.0.1/tcp/$GWPORT && - PEERID_1=$(iptb attr get 1 id) -' - -test_expect_success "set NoFetch to true in config of node 0" ' - ipfsi 0 config --bool=true Gateway.NoFetch true -' - -test_expect_success "start ipfs nodes" ' - iptb start -wait && - iptb connect 0 1 -' - -test_expect_success "try fetching not present key from node 0" ' - FOO=$(echo "foo" | ipfsi 1 add -Q) && - test_expect_code 22 curl -f "http://127.0.0.1:$GWPORT/ipfs/$FOO" -' - -test_expect_success "try fetching not present ipns key from node 0" ' - ipfsi 1 name publish /ipfs/$FOO && - test_expect_code 22 curl -f "http://127.0.0.1:$GWPORT/ipns/$PEERID_1" -' - -test_expect_success "try fetching present key from node 0" ' - BAR=$(echo "bar" | ipfsi 0 add -Q) && - curl -f "http://127.0.0.1:$GWPORT/ipfs/$BAR" -' - -test_expect_success "try fetching present ipns key from node 0" ' - ipfsi 1 name publish /ipfs/$BAR && - curl "http://127.0.0.1:$GWPORT/ipns/$PEERID_1" -' - -test_expect_success "stop testbed" ' - iptb stop -' - -test_done diff --git a/test/sharness/t0111-gateway-writeable.sh b/test/sharness/t0111-gateway-writeable.sh deleted file mode 100755 index 53d4fc1aa..000000000 --- a/test/sharness/t0111-gateway-writeable.sh +++ /dev/null @@ -1,132 +0,0 @@ -#!/usr/bin/env bash -# -# Copyright (c) 2014 Christian Couder -# MIT Licensed; see the LICENSE file in this repository. -# - -test_description="Test HTTP Gateway (Writable)" - -. lib/test-lib.sh - -test_init_ipfs - -test_launch_ipfs_daemon --writable -test_expect_success "ipfs daemon --writable overrides config" ' - curl -v -X POST http://$GWAY_ADDR/ipfs/ 2> outfile && - grep "HTTP/1.1 201 Created" outfile && - grep "Location: /ipfs/QmbFMke1KXqnYyBBWxB74N4c5SBnJMVAiMNRcGu6x1AwQH" outfile -' -test_kill_ipfs_daemon - -test_config_ipfs_gateway_writable -test_launch_ipfs_daemon --writable=false -test_expect_success "ipfs daemon --writable=false overrides Writable=true config" ' - curl -v -X POST http://$GWAY_ADDR/ipfs/ 2> outfile && - grep "HTTP/1.1 405 Method Not Allowed" outfile -' -test_kill_ipfs_daemon -test_launch_ipfs_daemon - -port=$GWAY_PORT - -test_expect_success "ipfs daemon up" ' - pollEndpoint -host $GWAY_MADDR -v -tout=1s -tries=60 2>poll_apierr > poll_apiout || - test_fsh cat poll_apierr || test_fsh cat poll_apiout -' - -test_expect_success "HTTP gateway gives access to sample file" ' - curl -s -o welcome "http://$GWAY_ADDR/ipfs/$HASH_WELCOME_DOCS/readme" && - grep "Hello and Welcome to IPFS!" welcome -' - -test_expect_success "HTTP POST file gives Hash" ' - echo "$RANDOM" >infile && - URL="http://127.0.0.1:$port/ipfs/" && - curl -svX POST --data-binary @infile "$URL" 2>curl_post.out && - grep "HTTP/1.1 201 Created" curl_post.out && - LOCATION=$(grep Location curl_post.out) && - HASH=$(echo $LOCATION | cut -d":" -f2- |tr -d " \n\r") -' - -test_expect_success "We can HTTP GET file just created" ' - URL="http://127.0.0.1:${port}${HASH}" && - curl -so outfile "$URL" && - test_cmp infile outfile -' - -test_expect_success "We got the correct hash" ' - ADD_HASH="/ipfs/$(ipfs add -q infile)" && - test "x$ADD_HASH" = "x$HASH" || test_fsh echo "$ADD_HASH != $HASH" -' - -test_expect_success "HTTP GET empty directory" ' - URL="http://127.0.0.1:$port/ipfs/$HASH_EMPTY_DIR/" && - echo "GET $URL" && - curl -so outfile "$URL" 2>curl_getEmpty.out && - cat outfile | tr -s "\n" " " | grep "Index of /ipfs/$HASH_EMPTY_DIR" -' - -test_expect_success "HTTP PUT file to construct a hierarchy" ' - echo "$RANDOM" >infile && - URL="http://127.0.0.1:$port/ipfs/$HASH_EMPTY_DIR/test.txt" && - echo "PUT $URL" && - curl -svX PUT --data-binary @infile "$URL" 2>curl_put.out && - grep "HTTP/1.1 201 Created" curl_put.out && - LOCATION=$(grep Location curl_put.out) && - HASH=$(expr "$LOCATION" : "< Location: /ipfs/\(.*\)/test.txt") -' - -test_expect_success "We can HTTP GET file just created" ' - URL="http://127.0.0.1:$port/ipfs/$HASH/test.txt" && - echo "GET $URL" && - curl -so outfile "$URL" && - test_cmp infile outfile -' - -test_expect_success "HTTP PUT file to append to existing hierarchy" ' - echo "$RANDOM" >infile2 && - URL="http://127.0.0.1:$port/ipfs/$HASH/test/test.txt" && - echo "PUT $URL" && - curl -svX PUT --data-binary @infile2 "$URL" 2>curl_putAgain.out && - grep "HTTP/1.1 201 Created" curl_putAgain.out && - LOCATION=$(grep Location curl_putAgain.out) && - HASH=$(expr "$LOCATION" : "< Location: /ipfs/\(.*\)/test/test.txt") -' - - -test_expect_success "We can HTTP GET file just updated" ' - URL="http://127.0.0.1:$port/ipfs/$HASH/test/test.txt" && - echo "GET $URL" && - curl -svo outfile2 "$URL" 2>curl_getAgain.out && - test_cmp infile2 outfile2 -' - -test_expect_success "HTTP PUT to replace a directory" ' - echo "$RANDOM" >infile3 && - URL="http://127.0.0.1:$port/ipfs/$HASH/test" && - echo "PUT $URL" && - curl -svX PUT --data-binary @infile3 "$URL" 2>curl_putOverDirectory.out && - grep "HTTP/1.1 201 Created" curl_putOverDirectory.out && - LOCATION=$(grep Location curl_putOverDirectory.out) && - HASH=$(expr "$LOCATION" : "< Location: /ipfs/\(.*\)/test") -' - -test_expect_success "We can HTTP GET file just put over a directory" ' - URL="http://127.0.0.1:$port/ipfs/$HASH/test" && - echo "GET $URL" && - curl -svo outfile3 "$URL" 2>curl_getOverDirectory.out && - test_cmp infile3 outfile3 -' - -test_expect_success "HTTP PUT to /ipns fails" ' - PEERID=`ipfs id --format=""` && - URL="http://127.0.0.1:$port/ipns/$PEERID/test.txt" && - echo "PUT $URL" && - curl -svX PUT --data-binary @infile1 "$URL" 2>curl_putIpns.out && - grep "HTTP/1.1 400 Bad Request" curl_putIpns.out -' - - -test_kill_ipfs_daemon - -test_done diff --git a/test/sharness/t0112-gateway-cors.sh b/test/sharness/t0112-gateway-cors.sh index 98b92f7de..90813ad6a 100755 --- a/test/sharness/t0112-gateway-cors.sh +++ b/test/sharness/t0112-gateway-cors.sh @@ -7,21 +7,9 @@ test_description="Test CORS behavior on Gateway port" test_init_ipfs # Default config -test_expect_success "Default Gateway.HTTPHeaders config match expected values" ' +test_expect_success "Default Gateway.HTTPHeaders is empty (implicit CORS values from boxo/gateway)" ' cat < expected -{ - "Access-Control-Allow-Headers": [ - "X-Requested-With", - "Range", - "User-Agent" - ], - "Access-Control-Allow-Methods": [ - "GET" - ], - "Access-Control-Allow-Origin": [ - "*" - ] -} +{} EOF ipfs config --json Gateway.HTTPHeaders > actual && test_cmp expected actual @@ -43,13 +31,19 @@ test_expect_success "GET to Gateway succeeds" ' test_expect_success "GET response for Gateway resource looks good" ' test_should_contain "< Access-Control-Allow-Origin: \*" curl_output && test_should_contain "< Access-Control-Allow-Methods: GET" curl_output && + test_should_contain "< Access-Control-Allow-Methods: HEAD" curl_output && + test_should_contain "< Access-Control-Allow-Methods: OPTIONS" curl_output && + test_should_contain "< Access-Control-Allow-Headers: Content-Type" curl_output && test_should_contain "< Access-Control-Allow-Headers: Range" curl_output && + test_should_contain "< Access-Control-Allow-Headers: User-Agent" curl_output && + test_should_contain "< Access-Control-Allow-Headers: X-Requested-With" curl_output && test_should_contain "< Access-Control-Expose-Headers: Content-Range" curl_output && test_should_contain "< Access-Control-Expose-Headers: Content-Length" curl_output && + test_should_contain "< Access-Control-Expose-Headers: X-Chunked-Output" curl_output && + test_should_contain "< Access-Control-Expose-Headers: X-Stream-Output" curl_output && test_should_contain "< Access-Control-Expose-Headers: X-Ipfs-Path" curl_output && test_should_contain "< Access-Control-Expose-Headers: X-Ipfs-Roots" curl_output ' - # HTTP OPTIONS Request test_expect_success "OPTIONS to Gateway succeeds" ' curl -svX OPTIONS -H "Origin: https://example.com" "http://127.0.0.1:$GWAY_PORT/ipfs/$thash" 2>curl_output && @@ -60,13 +54,34 @@ test_expect_success "OPTIONS to Gateway succeeds" ' test_expect_success "OPTIONS response for Gateway resource looks good" ' test_should_contain "< Access-Control-Allow-Origin: \*" curl_output && test_should_contain "< Access-Control-Allow-Methods: GET" curl_output && + test_should_contain "< Access-Control-Allow-Methods: HEAD" curl_output && + test_should_contain "< Access-Control-Allow-Methods: OPTIONS" curl_output && + test_should_contain "< Access-Control-Allow-Headers: Content-Type" curl_output && test_should_contain "< Access-Control-Allow-Headers: Range" curl_output && + test_should_contain "< Access-Control-Allow-Headers: User-Agent" curl_output && + test_should_contain "< Access-Control-Allow-Headers: X-Requested-With" curl_output && test_should_contain "< Access-Control-Expose-Headers: Content-Range" curl_output && test_should_contain "< Access-Control-Expose-Headers: Content-Length" curl_output && + test_should_contain "< Access-Control-Expose-Headers: X-Chunked-Output" curl_output && + test_should_contain "< Access-Control-Expose-Headers: X-Stream-Output" curl_output && test_should_contain "< Access-Control-Expose-Headers: X-Ipfs-Path" curl_output && test_should_contain "< Access-Control-Expose-Headers: X-Ipfs-Roots" curl_output ' +# HTTP OPTIONS Request on path → subdomain HTTP 301 redirect +# (regression test for https://github.com/ipfs/kubo/issues/9983#issuecomment-1599673976) +test_expect_success "OPTIONS to Gateway succeeds" ' + curl -svX OPTIONS -H "Origin: https://example.com" "http://localhost:$GWAY_PORT/ipfs/$thash" 2>curl_output && + cat curl_output +' +# OPTION Response from Gateway should contain CORS headers +test_expect_success "OPTIONS response for subdomain redirect looks good" ' + test_should_contain "HTTP/1.1 301 Moved Permanently" curl_output && + test_should_contain "Location" curl_output && + test_should_contain "< Access-Control-Allow-Origin: \*" curl_output && + test_should_contain "< Access-Control-Allow-Methods: GET" curl_output +' + test_kill_ipfs_daemon # Test CORS safelisting of custom headers @@ -112,68 +127,6 @@ test_expect_success "Access-Control-Allow-Origin replaces the implicit list" ' test_should_contain "< Access-Control-Allow-Origin: localhost" curl_output ' -# Read-Only /api/v0 RPC API (legacy subset, exposed on the Gateway Port) -# TODO: we want to remove it, but for now this guards the legacy behavior to not go any further - -# also check this, as due to legacy reasons Kubo exposes small subset of /api/v0 on GW port -test_expect_success "Assert the default API.HTTPHeaders config is empty" ' - echo "{}" > expected && - ipfs config --json API.HTTPHeaders > actual && - test_cmp expected actual -' - -# HTTP GET Request -test_expect_success "Default CORS GET to {gw}/api/v0" ' - curl -svX GET -H "Origin: https://example.com" "http://127.0.0.1:$GWAY_PORT/api/v0/cat?arg=$thash" >/dev/null 2>curl_output -' -test_expect_success "Default CORS GET response from {gw}/api/v0 is 403 Forbidden and has no CORS headers" ' - test_should_contain "HTTP/1.1 403 Forbidden" curl_output && - test_should_not_contain "< Access-Control-" curl_output -' - -# HTTP OPTIONS Request -test_expect_success "Default OPTIONS to {gw}/api/v0" ' - curl -svX OPTIONS -H "Origin: https://example.com" "http://127.0.0.1:$GWAY_PORT/api/v0/cat?arg=$thash" 2>curl_output -' -# OPTIONS Response from the API should NOT contain CORS headers -test_expect_success "OPTIONS response from {gw}/api/v0 has no CORS header" ' - test_should_not_contain "< Access-Control-" curl_output -' - -test_kill_ipfs_daemon - -# TODO: /api/v0 with CORS headers set in API.HTTPHeaders does not really work, -# as not all headers are correctly set. Below is only a basic regression test that documents -# current state. Fixing CORS on /api/v0 (RPC and Gateway port) is tracked in https://github.com/ipfs/kubo/issues/7667 - -test_expect_success "Manually set API.HTTPHeaders config to be as relaxed as Gateway.HTTPHeaders" " - ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin '[\"https://example.com\"]' -" -# TODO: ipfs config --json API.HTTPHeaders.Access-Control-Allow-Methods '[\"GET\",\"POST\"]' && -# TODO: ipfs config --json API.HTTPHeaders.Access-Control-Allow-Headers '[\"X-Requested-With\", \"Range\", \"User-Agent\"]' - -test_launch_ipfs_daemon - -# HTTP GET Request -test_expect_success "Manually relaxed CORS GET to {gw}/api/v0" ' - curl -svX GET -H "Origin: https://example.com" "http://127.0.0.1:$GWAY_PORT/api/v0/cat?arg=$thash" >/dev/null 2>curl_output -' -test_expect_success "Manually relaxed CORS GET response from {gw}/api/v0 is the same as Gateway" ' - test_should_contain "HTTP/1.1 200 OK" curl_output && - test_should_contain "< Access-Control-Allow-Origin: https://example.com" curl_output -' -# TODO: test_should_contain "< Access-Control-Allow-Methods: GET" curl_output - -# HTTP OPTIONS Request -test_expect_success "Manually relaxed OPTIONS to {gw}/api/v0" ' - curl -svX OPTIONS -H "Origin: https://example.com" "http://127.0.0.1:$GWAY_PORT/api/v0/cat?arg=$thash" 2>curl_output -' -# OPTIONS Response from the API should NOT contain CORS headers -test_expect_success "Manually relaxed OPTIONS response from {gw}/api/v0 is the same as Gateway" ' - test_should_contain "< Access-Control-Allow-Origin: https://example.com" curl_output -' -# TODO: test_should_contain "< Access-Control-Allow-Methods: GET" curl_output - test_kill_ipfs_daemon test_done diff --git a/test/sharness/t0113-gateway-symlink.sh b/test/sharness/t0113-gateway-symlink.sh deleted file mode 100755 index 9fa7ffa6e..000000000 --- a/test/sharness/t0113-gateway-symlink.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env bash -# -# Copyright (c) Protocol Labs - -test_description="Test symlink support on the HTTP gateway" - -. lib/test-lib.sh - -test_init_ipfs -test_launch_ipfs_daemon - - -test_expect_success "Create a test directory with symlinks" ' - mkdir testfiles && - echo "content" > testfiles/foo && - ln -s foo testfiles/bar && - test_cmp testfiles/foo testfiles/bar -' - -test_expect_success "Add the test directory" ' - HASH=$(ipfs add -Qr testfiles) -' - -test_expect_success "Test the directory listing" ' - curl "$GWAY_ADDR/ipfs/$HASH/" > list_response && - test_should_contain ">foo<" list_response && - test_should_contain ">bar<" list_response -' - -test_expect_success "Test the symlink" ' - curl "$GWAY_ADDR/ipfs/$HASH/bar" > bar_actual && - echo -n "foo" > bar_expected && - test_cmp bar_expected bar_actual -' - -test_kill_ipfs_daemon - -test_done diff --git a/test/sharness/t0114-gateway-subdomains.sh b/test/sharness/t0114-gateway-subdomains.sh index a7e5a59c9..5d9927d8e 100755 --- a/test/sharness/t0114-gateway-subdomains.sh +++ b/test/sharness/t0114-gateway-subdomains.sh @@ -91,50 +91,31 @@ test_expect_success "ipfs init" ' test_launch_ipfs_daemon_without_network -# CIDv0to1 is necessary because raw-leaves are enabled by default during -# "ipfs add" with CIDv1 and disabled with CIDv0 -test_expect_success "Add test text file" ' - CID_VAL="hello" - CIDv1=$(echo $CID_VAL | ipfs add --cid-version 1 -Q) - CIDv0=$(echo $CID_VAL | ipfs add --cid-version 0 -Q) - CIDv0to1=$(echo "$CIDv0" | ipfs cid base32) - echo CIDv0to1=${CIDv0to1} -' +# Import test case +# See the static fixtures in ./t0114-gateway-subdomains/ +CID_VAL=hello +CIDv1=bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am +CIDv0=QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN +CIDv0to1=bafybeiffndsajwhk3lwjewwdxqntmjm4b5wxaaanokonsggenkbw6slwk4 +CIDv1_TOO_LONG=bafkrgqhhyivzstcz3hhswshfjgy6ertgmnqeleynhwt4dlfsthi4hn7zgh4uvlsb5xncykzapi3ocd4lzogukir6ksdy6wzrnz6ohnv4aglcs +DIR_CID=bafybeiht6dtwk3les7vqm6ibpvz6qpohidvlshsfyr7l5mpysdw2vmbbhe -# Directory tree crafted to test for edge cases like "/ipfs/ipfs/ipns/bar" -test_expect_success "Add the test directory" ' - mkdir -p testdirlisting/ipfs/ipns && - echo "hello" > testdirlisting/hello && - echo "text-file-content" > testdirlisting/ipfs/ipns/bar && - mkdir -p testdirlisting/api && - mkdir -p testdirlisting/ipfs && - echo "I am a txt file" > testdirlisting/api/file.txt && - echo "I am a txt file" > testdirlisting/ipfs/file.txt && - DIR_CID=$(ipfs add -Qr --cid-version 1 testdirlisting) -' +RSA_KEY=QmVujd5Vb7moysJj8itnGufN7MEtPRCNHkKpNuA4onsRa3 +RSA_IPNS_IDv0=QmVujd5Vb7moysJj8itnGufN7MEtPRCNHkKpNuA4onsRa3 +RSA_IPNS_IDv1=k2k4r8m7xvggw5pxxk3abrkwyer625hg01hfyggrai7lk1m63fuihi7w +RSA_IPNS_IDv1_DAGPB=k2jmtxu61bnhrtj301lw7zizknztocdbeqhxgv76l2q9t36fn9jbzipo -test_expect_success "Publish test text file to IPNS using RSA keys" ' - RSA_KEY=$(ipfs key gen --ipns-base=b58mh --type=rsa --size=2048 test_key_rsa | head -n1 | tr -d "\n") - RSA_IPNS_IDv0=$(echo "$RSA_KEY" | ipfs cid format -v 0) - RSA_IPNS_IDv1=$(echo "$RSA_KEY" | ipfs cid format -v 1 --mc libp2p-key -b base36) - RSA_IPNS_IDv1_DAGPB=$(echo "$RSA_IPNS_IDv0" | ipfs cid format -v 1 -b base36) - test_check_peerid "${RSA_KEY}" && - ipfs name publish --key test_key_rsa --allow-offline -Q "/ipfs/$CIDv1" > name_publish_out && - ipfs name resolve "$RSA_KEY" > output && - printf "/ipfs/%s\n" "$CIDv1" > expected2 && - test_cmp expected2 output -' +ED25519_KEY=12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d +ED25519_IPNS_IDv0=12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d +ED25519_IPNS_IDv1=k51qzi5uqu5dk3v4rmjber23h16xnr23bsggmqqil9z2gduiis5se8dht36dam +ED25519_IPNS_IDv1_DAGPB=k50rm9yjlt0jey4fqg6wafvqprktgbkpgkqdg27tpqje6iimzxewnhvtin9hhq +IPNS_ED25519_B58MH=12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d +IPNS_ED25519_B36CID=k51qzi5uqu5dk3v4rmjber23h16xnr23bsggmqqil9z2gduiis5se8dht36dam -test_expect_success "Publish test text file to IPNS using ED25519 keys" ' - ED25519_KEY=$(ipfs key gen --ipns-base=b58mh --type=ed25519 test_key_ed25519 | head -n1 | tr -d "\n") - ED25519_IPNS_IDv0=$ED25519_KEY - ED25519_IPNS_IDv1=$(ipfs key list -l --ipns-base=base36 | grep test_key_ed25519 | cut -d " " -f1 | tr -d "\n") - ED25519_IPNS_IDv1_DAGPB=$(echo "$ED25519_IPNS_IDv1" | ipfs cid format -v 1 -b base36 --mc dag-pb) - test_check_peerid "${ED25519_KEY}" && - ipfs name publish --key test_key_ed25519 --allow-offline -Q "/ipfs/$CIDv1" > name_publish_out && - ipfs name resolve "$ED25519_KEY" > output && - printf "/ipfs/%s\n" "$CIDv1" > expected2 && - test_cmp expected2 output +test_expect_success "Add the test fixtures" ' + ipfs dag import --pin-roots ../t0114-gateway-subdomains/fixtures.car && + ipfs routing put --allow-offline /ipns/${RSA_KEY} ../t0114-gateway-subdomains/${RSA_KEY}.ipns-record && + ipfs routing put --allow-offline /ipns/${ED25519_KEY} ../t0114-gateway-subdomains/${ED25519_KEY}.ipns-record ' # ensure we start with empty Gateway.PublicGateways @@ -181,14 +162,17 @@ test_localhost_gateway_response_should_contain \ "http://localhost:$GWAY_PORT/ipfs/$DIR_CID/" \ "Location: http://$DIR_CID.ipfs.localhost:$GWAY_PORT/" -# We return body with HTTP 301 so existing cli scripts that use path-based -# gateway do not break (curl doesn't auto-redirect without passing -L; wget -# does not span across hostnames by default) -# Context: https://github.com/ipfs/go-ipfs/issues/6975 +# Kubo specific end-to-end test +# (independend of gateway-conformance) + +# We return human-readable body with HTTP 301 so existing cli scripts that use path-based +# gateway are informed to enable following HTTP redirects test_localhost_gateway_response_should_contain \ - "request for localhost/ipfs/{CIDv1} includes valid payload in body for CLI tools like curl" \ + "request for localhost/ipfs/{CIDv1} includes human-readable link and redirect info in HTTP 301 body" \ "http://localhost:$GWAY_PORT/ipfs/$CIDv1" \ - "$CID_VAL" + ">Moved Permanently" + +# end Kubo specific end-to-end test test_localhost_gateway_response_should_contain \ "request for localhost/ipfs/{CIDv0} redirects to CIDv1 representation in subdomain" \ @@ -209,29 +193,15 @@ test_localhost_gateway_response_should_contain \ # /ipns/ +# Kubo specific end-to-end test +# (independend of gateway-conformance) + test_localhost_gateway_response_should_contain \ "request for localhost/ipns/{fqdn} redirects to DNSLink in subdomain" \ "http://localhost:$GWAY_PORT/ipns/en.wikipedia-on-ipfs.org/wiki" \ "Location: http://en.wikipedia-on-ipfs.org.ipns.localhost:$GWAY_PORT/wiki" -# API on localhost subdomain gateway - -# /api/v0 present on the root hostname -test_localhost_gateway_response_should_contain \ - "request for localhost/api" \ - "http://localhost:$GWAY_PORT/api/v0/refs?arg=${DIR_CID}&r=true" \ - "Ref" - -# /api/v0 not mounted on content root subdomains -test_localhost_gateway_response_should_contain \ - "request for {cid}.ipfs.localhost/api returns data if present on the content root" \ - "http://${DIR_CID}.ipfs.localhost:$GWAY_PORT/api/file.txt" \ - "I am a txt file" - -test_localhost_gateway_response_should_contain \ - "request for {cid}.ipfs.localhost/api/v0/refs returns 404" \ - "http://${DIR_CID}.ipfs.localhost:$GWAY_PORT/api/v0/refs?arg=${DIR_CID}&r=true" \ - "404 Not Found" +# end Kubo specific end-to-end test ## ============================================================================ ## Test subdomain-based requests to a local gateway with default config @@ -257,6 +227,10 @@ test_localhost_gateway_response_should_contain \ "http://${DIR_CID}.ipfs.localhost:$GWAY_PORT/ipfs/file.txt" \ "I am a txt file" +# Kubo specific end-to-end test +# (independend of gateway-conformance) +# This tests link to parent specific to boxo + relative pathing end-to-end tests specific to Kubo. + # {CID}.ipfs.localhost/sub/dir (Directory Listing) DIR_HOSTNAME="${DIR_CID}.ipfs.localhost:$GWAY_PORT" @@ -276,7 +250,7 @@ test_expect_success "request for deep path resource at {cid}.ipfs.localhost/sub/ curl -s --resolve $DIR_HOSTNAME:127.0.0.1 "http://$DIR_HOSTNAME/ipfs/ipns/bar" > list_response && test_should_contain "text-file-content" list_response ' - +# end Kubo specific end-to-end test # *.ipns.localhost @@ -315,14 +289,6 @@ test_localhost_gateway_response_should_contain \ "http://$DNSLINK_FQDN.ipns.localhost:$GWAY_PORT" \ "$CID_VAL" -# api.localhost/api - -# Note: we use DIR_CID so refs -r returns some CIDs for child nodes -test_localhost_gateway_response_should_contain \ - "request for api.localhost returns API response" \ - "http://api.localhost:$GWAY_PORT/api/v0/refs?arg=$DIR_CID&r=true" \ - "Ref" - ## ============================================================================ ## Test DNSLink inlining on HTTP gateways ## ============================================================================ @@ -462,6 +428,10 @@ test_hostname_gateway_response_should_contain \ "http://127.0.0.1:$GWAY_PORT/ipfs/$CIDv1" \ "404 Not Found" +# Kubo specific end-to-end test +# (independend of gateway-conformance) +# HTML specific to Boxo/Kubo, and relative pathing specific to code in Kubo + # {CID}.ipfs.example.com/sub/dir (Directory Listing) DIR_FQDN="${DIR_CID}.ipfs.example.com" @@ -485,6 +455,8 @@ test_expect_success "valid breadcrumb links in the header of directory listing a test_should_contain "/ipfs/${DIR_CID}/ipfs/ipns" list_response ' +# end Kubo specific end-to-end test + test_expect_success "request for deep path resource {cid}.ipfs.example.com/sub/dir/file" ' curl -s -H "Host: $DIR_FQDN" http://127.0.0.1:$GWAY_PORT/ipfs/ipns/bar > list_response && test_should_contain "text-file-content" list_response @@ -519,54 +491,6 @@ test_hostname_gateway_response_should_contain \ "http://127.0.0.1:$GWAY_PORT" \ "Location: http://${ED25519_IPNS_IDv1}.ipns.example.com/" -# API on subdomain gateway example.com -# ============================================================================ - -# present at the root domain -test_hostname_gateway_response_should_contain \ - "request for example.com/api/v0/refs returns expected payload when /api is on Paths whitelist" \ - "example.com" \ - "http://127.0.0.1:$GWAY_PORT/api/v0/refs?arg=${DIR_CID}&r=true" \ - "Ref" - -# not mounted on content root subdomains -test_hostname_gateway_response_should_contain \ - "request for {cid}.ipfs.example.com/api returns data if present on the content root" \ - "$DIR_CID.ipfs.example.com" \ - "http://127.0.0.1:$GWAY_PORT/api/file.txt" \ - "I am a txt file" - -test_hostname_gateway_response_should_contain \ - "request for {cid}.ipfs.example.com/api/v0/refs returns 404" \ - "$CIDv1.ipfs.example.com" \ - "http://127.0.0.1:$GWAY_PORT/api/v0/refs?arg=${DIR_CID}&r=true" \ - "404 Not Found" - -# disable /api on example.com -ipfs config --json Gateway.PublicGateways '{ - "example.com": { - "UseSubdomains": true, - "Paths": ["/ipfs", "/ipns"] - } -}' || exit 1 -# restart daemon to apply config changes -test_kill_ipfs_daemon -test_launch_ipfs_daemon_without_network - -# not mounted at the root domain -test_hostname_gateway_response_should_contain \ - "request for example.com/api/v0/refs returns 404 if /api not on Paths whitelist" \ - "example.com" \ - "http://127.0.0.1:$GWAY_PORT/api/v0/refs?arg=${DIR_CID}&r=true" \ - "404 Not Found" - -# not mounted on content root subdomains -test_hostname_gateway_response_should_contain \ - "request for {cid}.ipfs.example.com/api returns data if present on the content root" \ - "$DIR_CID.ipfs.example.com" \ - "http://127.0.0.1:$GWAY_PORT/api/file.txt" \ - "I am a txt file" - # DNSLink: .ipns.example.com # (not really useful outside of localhost, as setting TLS for more than one # level of wildcard is a pain, but we support it if someone really wants it) @@ -596,13 +520,6 @@ test_expect_success \ ## https://github.com/ipfs/go-ipfs/issues/7318 ## ============================================================================ -# ed25519 fits under 63 char limit when represented in base36 -IPNS_KEY="test_key_ed25519" -IPNS_ED25519_B58MH=$(ipfs key list -l --ipns-base b58mh | grep $IPNS_KEY | cut -d" " -f1 | tr -d "\n") -IPNS_ED25519_B36CID=$(ipfs key list -l --ipns-base base36 | grep $IPNS_KEY | cut -d" " -f1 | tr -d "\n") -# sha512 will be over 63char limit, even when represented in Base36 -CIDv1_TOO_LONG=$(echo $CID_VAL | ipfs add --cid-version 1 --hash sha2-512 -Q) - # local: *.localhost test_localhost_gateway_response_should_contain \ "request for a ED25519 libp2p-key at localhost/ipns/{b58mh} returns Location HTTP header for DNS-safe subdomain redirect in browsers" \ @@ -883,6 +800,10 @@ test_expect_success "request for http://fake.domain.com/ipfs/{CID} with X-Forwar test_should_contain \"Location: https://$CIDv1.ipfs.example.com/\" response " +# Kubo specific end-to-end test +# (independend of gateway-conformance) +# test cofiguration beign wired up correctly end-to-end + ## ============================================================================ ## Test support for wildcards in gateway config ## ============================================================================ @@ -994,3 +915,5 @@ test_expect_success "clean up ipfs dir" ' ' test_done + +# end Kubo specific end-to-end test \ No newline at end of file diff --git a/test/sharness/t0114-gateway-subdomains/12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d.ipns-record b/test/sharness/t0114-gateway-subdomains/12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d.ipns-record new file mode 100644 index 000000000..39b2f41a4 Binary files /dev/null and b/test/sharness/t0114-gateway-subdomains/12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d.ipns-record differ diff --git a/test/sharness/t0114-gateway-subdomains/QmVujd5Vb7moysJj8itnGufN7MEtPRCNHkKpNuA4onsRa3.ipns-record b/test/sharness/t0114-gateway-subdomains/QmVujd5Vb7moysJj8itnGufN7MEtPRCNHkKpNuA4onsRa3.ipns-record new file mode 100644 index 000000000..b37d9b75b Binary files /dev/null and b/test/sharness/t0114-gateway-subdomains/QmVujd5Vb7moysJj8itnGufN7MEtPRCNHkKpNuA4onsRa3.ipns-record differ diff --git a/test/sharness/t0114-gateway-subdomains/README.md b/test/sharness/t0114-gateway-subdomains/README.md new file mode 100644 index 000000000..005a7f0c5 --- /dev/null +++ b/test/sharness/t0114-gateway-subdomains/README.md @@ -0,0 +1,111 @@ +# Dataset description/sources + +- fixtures.car + - raw CARv1 + +- QmUKd....ipns-record + - ipns record, encoded with protocol buffer + +- 12D3K....ipns-record + - ipns record, encoded with protocol buffer + +Generated with: + +```sh +# using ipfs version 0.21.0-dev (03a98280e3e642774776cd3d0435ab53e5dfa867) + +# CIDv0to1 is necessary because raw-leaves are enabled by default during +# "ipfs add" with CIDv1 and disabled with CIDv0 +CID_VAL="hello" +CIDv1=$(echo $CID_VAL | ipfs add --cid-version 1 -Q) +CIDv0=$(echo $CID_VAL | ipfs add --cid-version 0 -Q) +CIDv0to1=$(echo "$CIDv0" | ipfs cid base32) +# sha512 will be over 63char limit, even when represented in Base36 +CIDv1_TOO_LONG=$(echo $CID_VAL | ipfs add --cid-version 1 --hash sha2-512 -Q) + +echo CID_VAL=${CID_VAL} +echo CIDv1=${CIDv1} +echo CIDv0=${CIDv0} +echo CIDv0to1=${CIDv0to1} +echo CIDv1_TOO_LONG=${CIDv1_TOO_LONG} + +# Directory tree crafted to test for edge cases like "/ipfs/ipfs/ipns/bar" +mkdir -p testdirlisting/ipfs/ipns && +echo "hello" > testdirlisting/hello && +echo "text-file-content" > testdirlisting/ipfs/ipns/bar && +mkdir -p testdirlisting/api && +mkdir -p testdirlisting/ipfs && +echo "I am a txt file" > testdirlisting/api/file.txt && +echo "I am a txt file" > testdirlisting/ipfs/file.txt && +DIR_CID=$(ipfs add -Qr --cid-version 1 testdirlisting) + +echo DIR_CID=${DIR_CID} # ./testdirlisting + +ipfs files mkdir /t0114/ +ipfs files cp /ipfs/${CIDv1} /t0114/ +ipfs files cp /ipfs/${CIDv0} /t0114/ +ipfs files cp /ipfs/${CIDv0to1} /t0114/ +ipfs files cp /ipfs/${DIR_CID} /t0114/ +ipfs files cp /ipfs/${CIDv1_TOO_LONG} /t0114/ + +ROOT=`ipfs files stat /t0114/ --hash` + +ipfs dag export ${ROOT} > ./fixtures.car + +# Then the keys + +KEY_NAME=test_key_rsa_$RANDOM +RSA_KEY=$(ipfs key gen --ipns-base=b58mh --type=rsa --size=2048 ${KEY_NAME} | head -n1 | tr -d "\n") +RSA_IPNS_IDv0=$(echo "$RSA_KEY" | ipfs cid format -v 0) +RSA_IPNS_IDv1=$(echo "$RSA_KEY" | ipfs cid format -v 1 --mc libp2p-key -b base36) +RSA_IPNS_IDv1_DAGPB=$(echo "$RSA_IPNS_IDv0" | ipfs cid format -v 1 -b base36) + +# publish a record valid for a 100 years +ipfs name publish --key ${KEY_NAME} --allow-offline -Q --ttl=876600h --lifetime=876600h "/ipfs/$CIDv1" +ipfs routing get /ipns/${RSA_KEY} > ${RSA_KEY}.ipns-record + +echo RSA_KEY=${RSA_KEY} +echo RSA_IPNS_IDv0=${RSA_IPNS_IDv0} +echo RSA_IPNS_IDv1=${RSA_IPNS_IDv1} +echo RSA_IPNS_IDv1_DAGPB=${RSA_IPNS_IDv1_DAGPB} + +KEY_NAME=test_key_ed25519_$RANDOM +ED25519_KEY=$(ipfs key gen --ipns-base=b58mh --type=ed25519 ${KEY_NAME} | head -n1 | tr -d "\n") +ED25519_IPNS_IDv0=$ED25519_KEY +ED25519_IPNS_IDv1=$(ipfs key list -l --ipns-base=base36 | grep ${KEY_NAME} | cut -d " " -f1 | tr -d "\n") +ED25519_IPNS_IDv1_DAGPB=$(echo "$ED25519_IPNS_IDv1" | ipfs cid format -v 1 -b base36 --mc dag-pb) + +# ed25519 fits under 63 char limit when represented in base36 +IPNS_ED25519_B58MH=$(ipfs key list -l --ipns-base b58mh | grep $KEY_NAME | cut -d" " -f1 | tr -d "\n") +IPNS_ED25519_B36CID=$(ipfs key list -l --ipns-base base36 | grep $KEY_NAME | cut -d" " -f1 | tr -d "\n") + +# publish a record valid for a 100 years +ipfs name publish --key ${KEY_NAME} --allow-offline -Q --ttl=876600h --lifetime=876600h "/ipfs/$CIDv1" +ipfs routing get /ipns/${ED25519_KEY} > ${ED25519_KEY}.ipns-record + +echo ED25519_KEY=${ED25519_KEY} +echo ED25519_IPNS_IDv0=${ED25519_IPNS_IDv0} +echo ED25519_IPNS_IDv1=${ED25519_IPNS_IDv1} +echo ED25519_IPNS_IDv1_DAGPB=${ED25519_IPNS_IDv1_DAGPB} +echo IPNS_ED25519_B58MH=${IPNS_ED25519_B58MH} +echo IPNS_ED25519_B36CID=${IPNS_ED25519_B36CID} + +# CID_VAL=hello +# CIDv1=bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am +# CIDv0=QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN +# CIDv0to1=bafybeiffndsajwhk3lwjewwdxqntmjm4b5wxaaanokonsggenkbw6slwk4 +# CIDv1_TOO_LONG=bafkrgqhhyivzstcz3hhswshfjgy6ertgmnqeleynhwt4dlfsthi4hn7zgh4uvlsb5xncykzapi3ocd4lzogukir6ksdy6wzrnz6ohnv4aglcs +# DIR_CID=bafybeiht6dtwk3les7vqm6ibpvz6qpohidvlshsfyr7l5mpysdw2vmbbhe # ./testdirlisting + +# RSA_KEY=QmVujd5Vb7moysJj8itnGufN7MEtPRCNHkKpNuA4onsRa3 +# RSA_IPNS_IDv0=QmVujd5Vb7moysJj8itnGufN7MEtPRCNHkKpNuA4onsRa3 +# RSA_IPNS_IDv1=k2k4r8m7xvggw5pxxk3abrkwyer625hg01hfyggrai7lk1m63fuihi7w +# RSA_IPNS_IDv1_DAGPB=k2jmtxu61bnhrtj301lw7zizknztocdbeqhxgv76l2q9t36fn9jbzipo + +# ED25519_KEY=12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d +# ED25519_IPNS_IDv0=12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d +# ED25519_IPNS_IDv1=k51qzi5uqu5dk3v4rmjber23h16xnr23bsggmqqil9z2gduiis5se8dht36dam +# ED25519_IPNS_IDv1_DAGPB=k50rm9yjlt0jey4fqg6wafvqprktgbkpgkqdg27tpqje6iimzxewnhvtin9hhq +# IPNS_ED25519_B58MH=12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d +# IPNS_ED25519_B36CID=k51qzi5uqu5dk3v4rmjber23h16xnr23bsggmqqil9z2gduiis5se8dht36dam +``` diff --git a/test/sharness/t0114-gateway-subdomains/fixtures.car b/test/sharness/t0114-gateway-subdomains/fixtures.car new file mode 100644 index 000000000..bc7b913df Binary files /dev/null and b/test/sharness/t0114-gateway-subdomains/fixtures.car differ diff --git a/test/sharness/t0115-gateway-dir-listing.sh b/test/sharness/t0115-gateway-dir-listing.sh index 708e0c4cf..1ce0861b2 100755 --- a/test/sharness/t0115-gateway-dir-listing.sh +++ b/test/sharness/t0115-gateway-dir-listing.sh @@ -18,20 +18,14 @@ test_expect_success "ipfs init" ' test_launch_ipfs_daemon_without_network +# Import test case +# See the static fixtures in ./t0115-gateway-dir-listing/ test_expect_success "Add the test directory" ' - mkdir -p rootDir/ipfs && - mkdir -p rootDir/ipns && - mkdir -p rootDir/api && - mkdir -p rootDir/ą/ę && - echo "I am a txt file on path with utf8" > rootDir/ą/ę/file-źł.txt && - echo "I am a txt file in confusing /api dir" > rootDir/api/file.txt && - echo "I am a txt file in confusing /ipfs dir" > rootDir/ipfs/file.txt && - echo "I am a txt file in confusing /ipns dir" > rootDir/ipns/file.txt && - DIR_CID=$(ipfs add -Qr --cid-version 1 rootDir) && - FILE_CID=$(ipfs files stat --enc=json /ipfs/$DIR_CID/ą/ę/file-źł.txt | jq -r .Hash) && - FILE_SIZE=$(ipfs files stat --enc=json /ipfs/$DIR_CID/ą/ę/file-źł.txt | jq -r .Size) - echo "$FILE_CID / $FILE_SIZE" + ipfs dag import --pin-roots ../t0115-gateway-dir-listing/fixtures.car ' +DIR_CID=bafybeig6ka5mlwkl4subqhaiatalkcleo4jgnr3hqwvpmsqfca27cijp3i # ./rootDir/ +FILE_CID=bafkreialihlqnf5uwo4byh4n3cmwlntwqzxxs2fg5vanqdi3d7tb2l5xkm # ./rootDir/ą/ę/file-źł.txt +FILE_SIZE=34 ## ============================================================================ ## Test dir listing on path gateway (eg. 127.0.0.1:8080/ipfs/) diff --git a/test/sharness/t0115-gateway-dir-listing/README.md b/test/sharness/t0115-gateway-dir-listing/README.md new file mode 100644 index 000000000..937438bcd --- /dev/null +++ b/test/sharness/t0115-gateway-dir-listing/README.md @@ -0,0 +1,32 @@ +# Dataset description/sources + +- fixtures.car + - raw CARv1 + +generated with: + +```sh +# using ipfs version 0.18.1 +mkdir -p rootDir/ipfs && +mkdir -p rootDir/ipns && +mkdir -p rootDir/api && +mkdir -p rootDir/ą/ę && +echo "I am a txt file on path with utf8" > rootDir/ą/ę/file-źł.txt && +echo "I am a txt file in confusing /api dir" > rootDir/api/file.txt && +echo "I am a txt file in confusing /ipfs dir" > rootDir/ipfs/file.txt && +echo "I am a txt file in confusing /ipns dir" > rootDir/ipns/file.txt && +DIR_CID=$(ipfs add -Qr --cid-version 1 rootDir) && +FILE_CID=$(ipfs files stat --enc=json /ipfs/$DIR_CID/ą/ę/file-źł.txt | jq -r .Hash) && +FILE_SIZE=$(ipfs files stat --enc=json /ipfs/$DIR_CID/ą/ę/file-źł.txt | jq -r .Size) +echo "$FILE_CID / $FILE_SIZE" + +echo DIR_CID=${DIR_CID} +echo FILE_CID=${FILE_CID} +echo FILE_SIZE=${FILE_SIZE} + +ipfs dag export ${DIR_CID} > ./fixtures.car + +# DIR_CID=bafybeig6ka5mlwkl4subqhaiatalkcleo4jgnr3hqwvpmsqfca27cijp3i # ./rootDir/ +# FILE_CID=bafkreialihlqnf5uwo4byh4n3cmwlntwqzxxs2fg5vanqdi3d7tb2l5xkm # ./rootDir/ą/ę/file-źł.txt +# FILE_SIZE=34 +``` diff --git a/test/sharness/t0115-gateway-dir-listing/fixtures.car b/test/sharness/t0115-gateway-dir-listing/fixtures.car new file mode 100644 index 000000000..71a560382 Binary files /dev/null and b/test/sharness/t0115-gateway-dir-listing/fixtures.car differ diff --git a/test/sharness/t0116-gateway-cache.sh b/test/sharness/t0116-gateway-cache.sh index 0af4ec0eb..6dd81657c 100755 --- a/test/sharness/t0116-gateway-cache.sh +++ b/test/sharness/t0116-gateway-cache.sh @@ -25,177 +25,28 @@ test_launch_ipfs_daemon_without_network # Caching of things like raw blocks, CARs, dag-json and dag-cbor # is tested in their respective suites. +ROOT1_CID=bafybeib3ffl2teiqdncv3mkz4r23b5ctrwkzrrhctdbne6iboayxuxk5ui # ./ +ROOT2_CID=bafybeih2w7hjocxjg6g2ku25hvmd53zj7og4txpby3vsusfefw5rrg5sii # ./root2 +ROOT3_CID=bafybeiawdvhmjcz65x5egzx4iukxc72hg4woks6v6fvgyupiyt3oczk5ja # ./root2/root3 +ROOT4_CID=bafybeifq2rzpqnqrsdupncmkmhs3ckxxjhuvdcbvydkgvch3ms24k5lo7q # ./root2/root3/root4 +FILE_CID=bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am # ./root2/root3/root4/index.html +TEST_IPNS_ID=k51qzi5uqu5dlxdsdu5fpuu7h69wu4ohp32iwm9pdt9nq3y5rpn3ln9j12zfhe + +# Import test case +# See the static fixtures in ./t0116-gateway-cache/ test_expect_success "Add the test directory" ' - mkdir -p root2/root3/root4 && - echo "hello" > root2/root3/root4/index.html && - ROOT1_CID=$(ipfs add -Qrw --cid-version 1 root2) - ROOT2_CID=$(ipfs resolve -r /ipfs/$ROOT1_CID/root2 | cut -d "/" -f3) - ROOT3_CID=$(ipfs resolve -r /ipfs/$ROOT1_CID/root2/root3 | cut -d "/" -f3) - ROOT4_CID=$(ipfs resolve -r /ipfs/$ROOT1_CID/root2/root3/root4 | cut -d "/" -f3) - FILE_CID=$(ipfs resolve -r /ipfs/$ROOT1_CID/root2/root3/root4/index.html | cut -d "/" -f3) + ipfs dag import --pin-roots ../t0116-gateway-cache/fixtures.car + ipfs routing put --allow-offline /ipns/${TEST_IPNS_ID} ../t0116-gateway-cache/${TEST_IPNS_ID}.ipns-record ' -test_expect_success "Prepare IPNS unixfs content path for testing" ' - TEST_IPNS_ID=$(ipfs key gen --ipns-base=base36 --type=ed25519 cache_test_key | head -n1 | tr -d "\n") - ipfs name publish --key cache_test_key --allow-offline -Q "/ipfs/$ROOT1_CID" > name_publish_out && - test_check_peerid "${TEST_IPNS_ID}" && - ipfs name resolve "${TEST_IPNS_ID}" > output && - printf "/ipfs/%s\n" "$ROOT1_CID" > expected && - test_cmp expected output -' - -# GET /ipfs/ - # unixfs - test_expect_success "GET for /ipfs/ unixfs dir listing succeeds" ' - curl -svX GET "http://127.0.0.1:$GWAY_PORT/ipfs/$ROOT1_CID/root2/root3/" >/dev/null 2>curl_ipfs_dir_listing_output - ' - test_expect_success "GET for /ipfs/ unixfs dir with index.html succeeds" ' - curl -svX GET "http://127.0.0.1:$GWAY_PORT/ipfs/$ROOT1_CID/root2/root3/root4/" >/dev/null 2>curl_ipfs_dir_index.html_output - ' - test_expect_success "GET for /ipfs/ unixfs file succeeds" ' - curl -svX GET "http://127.0.0.1:$GWAY_PORT/ipfs/$ROOT1_CID/root2/root3/root4/index.html" >/dev/null 2>curl_ipfs_file_output - ' - # unixfs dir as dag-json - test_expect_success "GET for /ipfs/ unixfs dir as DAG-JSON succeeds" ' - curl -svX GET "http://127.0.0.1:$GWAY_PORT/ipfs/$ROOT1_CID/root2/root3/root4/?format=dag-json" >/dev/null 2>curl_ipfs_dir_dag-json_output && - curl -svX GET "http://127.0.0.1:$GWAY_PORT/ipfs/$ROOT1_CID/root2/root3/root4/?format=json" >/dev/null 2>curl_ipfs_dir_json_output - ' -# GET /ipns/ - # unixfs - test_expect_success "GET for /ipns/ unixfs dir listing succeeds" ' - curl -svX GET "http://127.0.0.1:$GWAY_PORT/ipns/$TEST_IPNS_ID/root2/root3/" >/dev/null 2>curl_ipns_dir_listing_output - ' - test_expect_success "GET for /ipns/ unixfs dir with index.html succeeds" ' - curl -svX GET "http://127.0.0.1:$GWAY_PORT/ipns/$TEST_IPNS_ID/root2/root3/root4/" >/dev/null 2>curl_ipns_dir_index.html_output - ' - test_expect_success "GET for /ipns/ unixfs file succeeds" ' - curl -svX GET "http://127.0.0.1:$GWAY_PORT/ipns/$TEST_IPNS_ID/root2/root3/root4/index.html" >/dev/null 2>curl_ipns_file_output - ' - # unixfs dir as dag-json - test_expect_success "GET for /ipns/ unixfs dir as DAG-JSON succeeds" ' - curl -svX GET "http://127.0.0.1:$GWAY_PORT/ipns/$TEST_IPNS_ID/root2/root3/root4/?format=dag-json" >/dev/null 2>curl_ipns_dir_dag-json_output && - curl -svX GET "http://127.0.0.1:$GWAY_PORT/ipns/$TEST_IPNS_ID/root2/root3/root4/?format=json" >/dev/null 2>curl_ipns_dir_json_output - ' - -# Cache-Control - -# Cache-Control: immutable /ipfs/ file - test_expect_success "GET /ipfs/ unixfs file has expected Cache-Control" ' - test_should_contain "< Cache-Control: public, max-age=29030400, immutable" curl_ipfs_file_output - ' -# Cache-Control: generated /ipfs/dir/ (listing) - # TODO: test_should_contain "< Cache-Control: public, max-age=TBD" curl_ipfs_dir_listing_output - test_expect_success "GET /ipfs/ unixfs dir listing has no Cache-Control" ' - test_should_not_contain "< Cache-Control" curl_ipns_dir_listing_output - ' -# Cache-Control: immutable /ipfs/dir/ (index.html) - test_expect_success "GET /ipfs/ unixfs dir with index.html has expected Cache-Control" ' - test_should_contain "< Cache-Control: public, max-age=29030400, immutable" curl_ipfs_dir_index.html_output - ' -# Cache-Control: immutable /ipfs/ unixfs dir as dag-json - test_expect_success "GET /ipfs/ dag-json has expected Cache-Control" ' - test_should_contain "< Cache-Control: public, max-age=29030400, immutable" curl_ipfs_dir_dag-json_output - ' -# Cache-Control: immutable /ipfs/ unixfs dir as json - test_expect_success "GET /ipfs/ unixfs dir as json has expected Cache-Control" ' - test_should_contain "< Cache-Control: public, max-age=29030400, immutable" curl_ipfs_dir_json_output - ' -# Cache-Control: mutable /ipns/ file - test_expect_success "GET /ipns/ unixfs file has no Cache-Control" ' - test_should_not_contain "< Cache-Control" curl_ipns_file_output - ' -# Cache-Control: mutable /ipns/dir/ (generated listing) - test_expect_success "GET /ipns/ unixfs dir listing has no Cache-Control" ' - test_should_not_contain "< Cache-Control" curl_ipns_dir_listing_output - ' -# Cache-Control: mutable /ipns/dir/ (index.html) - test_expect_success "GET /ipns/ unixfs dir with index.html has no Cache-Control" ' - test_should_not_contain "< Cache-Control" curl_ipns_dir_index.html_output - ' -# Cache-Control: mutable /ipns/dir/ as dag-json - test_expect_success "GET /ipns/ unixfs dir as dag-json has no Cache-Control" ' - test_should_not_contain "< Cache-Control" curl_ipns_dir_dag-json_output - ' -# Cache-Control: mutable /ipns/dir/ as json - test_expect_success "GET /ipns/ unixfs dir as json has no Cache-Control" ' - test_should_not_contain "< Cache-Control" curl_ipns_dir_json_output - ' - -# Cache-Control: only-if-cached - test_expect_success "HEAD for /ipfs/ with only-if-cached succeeds when in local datastore" ' - curl -sv -I -H "Cache-Control: only-if-cached" "http://127.0.0.1:$GWAY_PORT/ipfs/$ROOT1_CID/root2/root3/root4/index.html" > curl_onlyifcached_postitive_head 2>&1 && - cat curl_onlyifcached_postitive_head && - grep "< HTTP/1.1 200 OK" curl_onlyifcached_postitive_head - ' - test_expect_success "HEAD for /ipfs/ with only-if-cached fails when not in local datastore" ' - curl -sv -I -H "Cache-Control: only-if-cached" "http://127.0.0.1:$GWAY_PORT/ipfs/$(date | ipfs add --only-hash -Q)" > curl_onlyifcached_negative_head 2>&1 && - cat curl_onlyifcached_negative_head && - grep "< HTTP/1.1 412 Precondition Failed" curl_onlyifcached_negative_head - ' - test_expect_success "GET for /ipfs/ with only-if-cached succeeds when in local datastore" ' - curl -svX GET -H "Cache-Control: only-if-cached" "http://127.0.0.1:$GWAY_PORT/ipfs/$ROOT1_CID/root2/root3/root4/index.html" >/dev/null 2>curl_onlyifcached_postitive_out && - cat curl_onlyifcached_postitive_out && - grep "< HTTP/1.1 200 OK" curl_onlyifcached_postitive_out - ' - test_expect_success "GET for /ipfs/ with only-if-cached fails when not in local datastore" ' - curl -svX GET -H "Cache-Control: only-if-cached" "http://127.0.0.1:$GWAY_PORT/ipfs/$(date | ipfs add --only-hash -Q)" >/dev/null 2>curl_onlyifcached_negative_out && - cat curl_onlyifcached_negative_out && - grep "< HTTP/1.1 412 Precondition Failed" curl_onlyifcached_negative_out - ' - -# X-Ipfs-Path - - ## dir generated listing - test_expect_success "GET /ipfs/ dir listing response has original content path in X-Ipfs-Path" ' - test_should_contain "< X-Ipfs-Path: /ipfs/$ROOT1_CID/root2/root3" curl_ipfs_dir_listing_output - ' - test_expect_success "GET /ipns/ dir listing response has original content path in X-Ipfs-Path" ' - test_should_contain "< X-Ipfs-Path: /ipns/$TEST_IPNS_ID/root2/root3" curl_ipns_dir_listing_output - ' - - ## dir static index.html - test_expect_success "GET /ipfs/ dir index.html response has original content path in X-Ipfs-Path" ' - test_should_contain "< X-Ipfs-Path: /ipfs/$ROOT1_CID/root2/root3/root4/" curl_ipfs_dir_index.html_output - ' - test_expect_success "GET /ipns/ dir index.html response has original content path in X-Ipfs-Path" ' - test_should_contain "< X-Ipfs-Path: /ipns/$TEST_IPNS_ID/root2/root3/root4/" curl_ipns_dir_index.html_output - ' - - # file - test_expect_success "GET /ipfs/ file response has original content path in X-Ipfs-Path" ' - test_should_contain "< X-Ipfs-Path: /ipfs/$ROOT1_CID/root2/root3/root4/index.html" curl_ipfs_file_output - ' - test_expect_success "GET /ipns/ file response has original content path in X-Ipfs-Path" ' - test_should_contain "< X-Ipfs-Path: /ipns/$TEST_IPNS_ID/root2/root3/root4/index.html" curl_ipns_file_output - ' - -# X-Ipfs-Roots - - ## dir generated listing - test_expect_success "GET /ipfs/ dir listing response has logical CID roots in X-Ipfs-Roots" ' - test_should_contain "< X-Ipfs-Roots: ${ROOT1_CID},${ROOT2_CID},${ROOT3_CID}" curl_ipfs_dir_listing_output - ' - test_expect_success "GET /ipns/ dir listing response has logical CID roots in X-Ipfs-Roots" ' - test_should_contain "< X-Ipfs-Roots: ${ROOT1_CID},${ROOT2_CID},${ROOT3_CID}" curl_ipns_dir_listing_output - ' - - ## dir static index.html - test_expect_success "GET /ipfs/ dir index.html response has logical CID roots in X-Ipfs-Roots" ' - test_should_contain "< X-Ipfs-Roots: ${ROOT1_CID},${ROOT2_CID},${ROOT3_CID},${ROOT4_CID}" curl_ipfs_dir_index.html_output - ' - test_expect_success "GET /ipns/ dir index.html response has logical CID roots in X-Ipfs-Roots" ' - test_should_contain "< X-Ipfs-Roots: ${ROOT1_CID},${ROOT2_CID},${ROOT3_CID},${ROOT4_CID}" curl_ipns_dir_index.html_output - ' - - ## file - test_expect_success "GET /ipfs/ file response has logical CID roots in X-Ipfs-Roots" ' - test_should_contain "< X-Ipfs-Roots: ${ROOT1_CID},${ROOT2_CID},${ROOT3_CID},${ROOT4_CID},${FILE_CID}" curl_ipfs_file_output - ' - test_expect_success "GET /ipns/ file response has logical CID roots in X-Ipfs-Roots" ' - test_should_contain "< X-Ipfs-Roots: ${ROOT1_CID},${ROOT2_CID},${ROOT3_CID},${ROOT4_CID},${FILE_CID}" curl_ipns_file_output - ' - # Etag + test_expect_success "GET for /ipfs/ unixfs dir listing succeeds" ' + curl -svX GET "http://127.0.0.1:$GWAY_PORT/ipfs/$ROOT1_CID/root2/root3/" >/dev/null 2>curl_ipfs_dir_listing_output + ' + + test_expect_success "GET for /ipns/ unixfs dir listing succeeds" ' + curl -svX GET "http://127.0.0.1:$GWAY_PORT/ipns/$TEST_IPNS_ID/root2/root3/" >/dev/null 2>curl_ipns_dir_listing_output + ' ## dir generated listing test_expect_success "GET /ipfs/ dir response has special Etag for generated dir listing" ' @@ -203,74 +54,10 @@ test_expect_success "Prepare IPNS unixfs content path for testing" ' grep -E "< Etag: \"DirIndex-.+_CID-${ROOT3_CID}\"" curl_ipfs_dir_listing_output ' test_expect_success "GET /ipns/ dir response has special Etag for generated dir listing" ' - test_should_contain "< Etag: \"DirIndex" curl_ipfs_dir_listing_output && + test_should_contain "< Etag: \"DirIndex" curl_ipns_dir_listing_output && grep -E "< Etag: \"DirIndex-.+_CID-${ROOT3_CID}\"" curl_ipns_dir_listing_output ' - ## dir static index.html should use CID of the index.html file for improved HTTP caching - test_expect_success "GET /ipfs/ dir index.html response has dir CID as Etag" ' - test_should_contain "< Etag: \"${ROOT4_CID}\"" curl_ipfs_dir_index.html_output - ' - test_expect_success "GET /ipns/ dir index.html response has dir CID as Etag" ' - test_should_contain "< Etag: \"${ROOT4_CID}\"" curl_ipns_dir_index.html_output - ' - - ## file - test_expect_success "GET /ipfs/ response has CID as Etag for a file" ' - test_should_contain "< Etag: \"${FILE_CID}\"" curl_ipfs_file_output - ' - test_expect_success "GET /ipns/ response has CID as Etag for a file" ' - test_should_contain "< Etag: \"${FILE_CID}\"" curl_ipns_file_output - ' - -# If-None-Match (return 304 Not Modified when client sends matching Etag they already have) - - test_expect_success "GET for /ipfs/ file with matching Etag in If-None-Match returns 304 Not Modified" ' - curl -svX GET -H "If-None-Match: \"$FILE_CID\"" "http://127.0.0.1:$GWAY_PORT/ipfs/$ROOT1_CID/root2/root3/root4/index.html" >/dev/null 2>curl_output && - test_should_contain "304 Not Modified" curl_output - ' - - test_expect_success "GET for /ipfs/ dir with index.html file with matching Etag in If-None-Match returns 304 Not Modified" ' - curl -svX GET -H "If-None-Match: \"$ROOT4_CID\"" "http://127.0.0.1:$GWAY_PORT/ipfs/$ROOT1_CID/root2/root3/root4/" >/dev/null 2>curl_output && - test_should_contain "304 Not Modified" curl_output - ' - - test_expect_success "GET for /ipfs/ file with matching third Etag in If-None-Match returns 304 Not Modified" ' - curl -svX GET -H "If-None-Match: \"fakeEtag1\", \"fakeEtag2\", \"$FILE_CID\"" "http://127.0.0.1:$GWAY_PORT/ipfs/$ROOT1_CID/root2/root3/root4/index.html" >/dev/null 2>curl_output && - test_should_contain "304 Not Modified" curl_output - ' - - test_expect_success "GET for /ipfs/ file with matching weak Etag in If-None-Match returns 304 Not Modified" ' - curl -svX GET -H "If-None-Match: W/\"$FILE_CID\"" "http://127.0.0.1:$GWAY_PORT/ipfs/$ROOT1_CID/root2/root3/root4/index.html" >/dev/null 2>curl_output && - test_should_contain "304 Not Modified" curl_output - ' - - test_expect_success "GET for /ipfs/ file with wildcard Etag in If-None-Match returns 304 Not Modified" ' - curl -svX GET -H "If-None-Match: *" "http://127.0.0.1:$GWAY_PORT/ipfs/$ROOT1_CID/root2/root3/root4/index.html" >/dev/null 2>curl_output && - test_should_contain "304 Not Modified" curl_output - ' - - test_expect_success "GET for /ipns/ file with matching Etag in If-None-Match returns 304 Not Modified" ' - curl -svX GET -H "If-None-Match: \"$FILE_CID\"" "http://127.0.0.1:$GWAY_PORT/ipns/$TEST_IPNS_ID/root2/root3/root4/index.html" >/dev/null 2>curl_output && - test_should_contain "304 Not Modified" curl_output - ' - - test_expect_success "GET for /ipfs/ dir listing with matching weak Etag in If-None-Match returns 304 Not Modified" ' - curl -svX GET -H "If-None-Match: W/\"$ROOT3_CID\"" "http://127.0.0.1:$GWAY_PORT/ipfs/$ROOT1_CID/root2/root3/" >/dev/null 2>curl_output && - test_should_contain "304 Not Modified" curl_output - ' - - # DirIndex etag is based on xxhash(./assets/dir-index-html), so we need to fetch it dynamically - test_expect_success "GET for /ipfs/ dir listing with matching strong Etag in If-None-Match returns 304 Not Modified" ' - curl -Is "http://127.0.0.1:$GWAY_PORT/ipfs/$ROOT1_CID/root2/root3/"| grep -i Etag | cut -f2- -d: | tr -d "[:space:]\"" > dir_index_etag && - curl -svX GET -H "If-None-Match: \"$(/dev/null 2>curl_output && - test_should_contain "304 Not Modified" curl_output - ' - test_expect_success "GET for /ipfs/ dir listing with matching weak Etag in If-None-Match returns 304 Not Modified" ' - curl -Is "http://127.0.0.1:$GWAY_PORT/ipfs/$ROOT1_CID/root2/root3/"| grep -i Etag | cut -f2- -d: | tr -d "[:space:]\"" > dir_index_etag && - curl -svX GET -H "If-None-Match: W/\"$(/dev/null 2>curl_output && - test_should_contain "304 Not Modified" curl_output - ' test_kill_ipfs_daemon diff --git a/test/sharness/t0116-gateway-cache/README.md b/test/sharness/t0116-gateway-cache/README.md new file mode 100644 index 000000000..1be92f454 --- /dev/null +++ b/test/sharness/t0116-gateway-cache/README.md @@ -0,0 +1,39 @@ +# Dataset description/sources + +- fixtures.car + - raw CARv1 + +generated with: + +```sh +# using ipfs version 0.21.0-dev (03a98280e3e642774776cd3d0435ab53e5dfa867) + +mkdir -p root2/root3/root4 && +echo "hello" > root2/root3/root4/index.html && +ROOT1_CID=$(ipfs add -Qrw --cid-version 1 root2) +ROOT2_CID=$(ipfs resolve -r /ipfs/$ROOT1_CID/root2 | cut -d "/" -f3) +ROOT3_CID=$(ipfs resolve -r /ipfs/$ROOT1_CID/root2/root3 | cut -d "/" -f3) +ROOT4_CID=$(ipfs resolve -r /ipfs/$ROOT1_CID/root2/root3/root4 | cut -d "/" -f3) +FILE_CID=$(ipfs resolve -r /ipfs/$ROOT1_CID/root2/root3/root4/index.html | cut -d "/" -f3) + +TEST_IPNS_ID=$(ipfs key gen --ipns-base=base36 --type=ed25519 cache_test_key | head -n1 | tr -d "\n") +# publish a record valid for a 100 years +ipfs name publish --key cache_test_key --allow-offline -Q --ttl=876600h --lifetime=876600h "/ipfs/$ROOT1_CID" +ipfs routing get /ipns/${TEST_IPNS_ID} > ${TEST_IPNS_ID}.ipns-record + +echo ROOT1_CID=${ROOT1_CID} # ./ +echo ROOT2_CID=${ROOT2_CID} # ./root2 +echo ROOT3_CID=${ROOT3_CID} # ./root2/root3 +echo ROOT4_CID=${ROOT4_CID} # ./root2/root3/root4 +echo FILE_CID=${FILE_CID} # ./root2/root3/root4/index.html +echo TEST_IPNS_ID=${TEST_IPNS_ID} + +ipfs dag export ${ROOT1_CID} > ./fixtures.car + +# ROOT1_CID=bafybeib3ffl2teiqdncv3mkz4r23b5ctrwkzrrhctdbne6iboayxuxk5ui # ./ +# ROOT2_CID=bafybeih2w7hjocxjg6g2ku25hvmd53zj7og4txpby3vsusfefw5rrg5sii # ./root2 +# ROOT3_CID=bafybeiawdvhmjcz65x5egzx4iukxc72hg4woks6v6fvgyupiyt3oczk5ja # ./root2/root3 +# ROOT4_CID=bafybeifq2rzpqnqrsdupncmkmhs3ckxxjhuvdcbvydkgvch3ms24k5lo7q # ./root2/root3/root4 +# FILE_CID=bafkreicysg23kiwv34eg2d7qweipxwosdo2py4ldv42nbauguluen5v6am # ./root2/root3/root4/index.html +# TEST_IPNS_ID=k51qzi5uqu5dlxdsdu5fpuu7h69wu4ohp32iwm9pdt9nq3y5rpn3ln9j12zfhe +``` diff --git a/test/sharness/t0116-gateway-cache/fixtures.car b/test/sharness/t0116-gateway-cache/fixtures.car new file mode 100644 index 000000000..43e570e1d Binary files /dev/null and b/test/sharness/t0116-gateway-cache/fixtures.car differ diff --git a/test/sharness/t0116-gateway-cache/k51qzi5uqu5dlxdsdu5fpuu7h69wu4ohp32iwm9pdt9nq3y5rpn3ln9j12zfhe.ipns-record b/test/sharness/t0116-gateway-cache/k51qzi5uqu5dlxdsdu5fpuu7h69wu4ohp32iwm9pdt9nq3y5rpn3ln9j12zfhe.ipns-record new file mode 100644 index 000000000..9cd7491bf Binary files /dev/null and b/test/sharness/t0116-gateway-cache/k51qzi5uqu5dlxdsdu5fpuu7h69wu4ohp32iwm9pdt9nq3y5rpn3ln9j12zfhe.ipns-record differ diff --git a/test/sharness/t0116-prometheus-data/prometheus_metrics b/test/sharness/t0116-prometheus-data/prometheus_metrics deleted file mode 100644 index 32c0d8e43..000000000 --- a/test/sharness/t0116-prometheus-data/prometheus_metrics +++ /dev/null @@ -1,759 +0,0 @@ -flatfs_datastore_batchcommit_errors_total -flatfs_datastore_batchcommit_latency_seconds_bucket -flatfs_datastore_batchcommit_latency_seconds_bucket -flatfs_datastore_batchcommit_latency_seconds_bucket -flatfs_datastore_batchcommit_latency_seconds_bucket -flatfs_datastore_batchcommit_latency_seconds_bucket -flatfs_datastore_batchcommit_latency_seconds_bucket -flatfs_datastore_batchcommit_latency_seconds_count -flatfs_datastore_batchcommit_latency_seconds_sum -flatfs_datastore_batchcommit_total -flatfs_datastore_batchdelete_errors_total -flatfs_datastore_batchdelete_latency_seconds_bucket -flatfs_datastore_batchdelete_latency_seconds_bucket -flatfs_datastore_batchdelete_latency_seconds_bucket -flatfs_datastore_batchdelete_latency_seconds_bucket -flatfs_datastore_batchdelete_latency_seconds_bucket -flatfs_datastore_batchdelete_latency_seconds_bucket -flatfs_datastore_batchdelete_latency_seconds_count -flatfs_datastore_batchdelete_latency_seconds_sum -flatfs_datastore_batchdelete_total -flatfs_datastore_batchput_errors_total -flatfs_datastore_batchput_latency_seconds_bucket -flatfs_datastore_batchput_latency_seconds_bucket -flatfs_datastore_batchput_latency_seconds_bucket -flatfs_datastore_batchput_latency_seconds_bucket -flatfs_datastore_batchput_latency_seconds_bucket -flatfs_datastore_batchput_latency_seconds_bucket -flatfs_datastore_batchput_latency_seconds_count -flatfs_datastore_batchput_latency_seconds_sum -flatfs_datastore_batchput_size_bytes_bucket -flatfs_datastore_batchput_size_bytes_bucket -flatfs_datastore_batchput_size_bytes_bucket -flatfs_datastore_batchput_size_bytes_bucket -flatfs_datastore_batchput_size_bytes_bucket -flatfs_datastore_batchput_size_bytes_count -flatfs_datastore_batchput_size_bytes_sum -flatfs_datastore_batchput_total -flatfs_datastore_check_errors_total -flatfs_datastore_check_latency_seconds_bucket -flatfs_datastore_check_latency_seconds_bucket -flatfs_datastore_check_latency_seconds_bucket -flatfs_datastore_check_latency_seconds_bucket -flatfs_datastore_check_latency_seconds_bucket -flatfs_datastore_check_latency_seconds_bucket -flatfs_datastore_check_latency_seconds_count -flatfs_datastore_check_latency_seconds_sum -flatfs_datastore_check_total -flatfs_datastore_delete_errors_total -flatfs_datastore_delete_latency_seconds_bucket -flatfs_datastore_delete_latency_seconds_bucket -flatfs_datastore_delete_latency_seconds_bucket -flatfs_datastore_delete_latency_seconds_bucket -flatfs_datastore_delete_latency_seconds_bucket -flatfs_datastore_delete_latency_seconds_bucket -flatfs_datastore_delete_latency_seconds_count -flatfs_datastore_delete_latency_seconds_sum -flatfs_datastore_delete_total -flatfs_datastore_du_errors_total -flatfs_datastore_du_latency_seconds_bucket -flatfs_datastore_du_latency_seconds_bucket -flatfs_datastore_du_latency_seconds_bucket -flatfs_datastore_du_latency_seconds_bucket -flatfs_datastore_du_latency_seconds_bucket -flatfs_datastore_du_latency_seconds_bucket -flatfs_datastore_du_latency_seconds_count -flatfs_datastore_du_latency_seconds_sum -flatfs_datastore_du_total -flatfs_datastore_gc_errors_total -flatfs_datastore_gc_latency_seconds_bucket -flatfs_datastore_gc_latency_seconds_bucket -flatfs_datastore_gc_latency_seconds_bucket -flatfs_datastore_gc_latency_seconds_bucket -flatfs_datastore_gc_latency_seconds_bucket -flatfs_datastore_gc_latency_seconds_bucket -flatfs_datastore_gc_latency_seconds_count -flatfs_datastore_gc_latency_seconds_sum -flatfs_datastore_gc_total -flatfs_datastore_get_errors_total -flatfs_datastore_get_latency_seconds_bucket -flatfs_datastore_get_latency_seconds_bucket -flatfs_datastore_get_latency_seconds_bucket -flatfs_datastore_get_latency_seconds_bucket -flatfs_datastore_get_latency_seconds_bucket -flatfs_datastore_get_latency_seconds_bucket -flatfs_datastore_get_latency_seconds_count -flatfs_datastore_get_latency_seconds_sum -flatfs_datastore_get_size_bytes_bucket -flatfs_datastore_get_size_bytes_bucket -flatfs_datastore_get_size_bytes_bucket -flatfs_datastore_get_size_bytes_bucket -flatfs_datastore_get_size_bytes_bucket -flatfs_datastore_get_size_bytes_count -flatfs_datastore_get_size_bytes_sum -flatfs_datastore_get_total -flatfs_datastore_getsize_errors_total -flatfs_datastore_getsize_latency_seconds_bucket -flatfs_datastore_getsize_latency_seconds_bucket -flatfs_datastore_getsize_latency_seconds_bucket -flatfs_datastore_getsize_latency_seconds_bucket -flatfs_datastore_getsize_latency_seconds_bucket -flatfs_datastore_getsize_latency_seconds_bucket -flatfs_datastore_getsize_latency_seconds_count -flatfs_datastore_getsize_latency_seconds_sum -flatfs_datastore_getsize_total -flatfs_datastore_has_errors_total -flatfs_datastore_has_latency_seconds_bucket -flatfs_datastore_has_latency_seconds_bucket -flatfs_datastore_has_latency_seconds_bucket -flatfs_datastore_has_latency_seconds_bucket -flatfs_datastore_has_latency_seconds_bucket -flatfs_datastore_has_latency_seconds_bucket -flatfs_datastore_has_latency_seconds_count -flatfs_datastore_has_latency_seconds_sum -flatfs_datastore_has_total -flatfs_datastore_put_errors_total -flatfs_datastore_put_latency_seconds_bucket -flatfs_datastore_put_latency_seconds_bucket -flatfs_datastore_put_latency_seconds_bucket -flatfs_datastore_put_latency_seconds_bucket -flatfs_datastore_put_latency_seconds_bucket -flatfs_datastore_put_latency_seconds_bucket -flatfs_datastore_put_latency_seconds_count -flatfs_datastore_put_latency_seconds_sum -flatfs_datastore_put_size_bytes_bucket -flatfs_datastore_put_size_bytes_bucket -flatfs_datastore_put_size_bytes_bucket -flatfs_datastore_put_size_bytes_bucket -flatfs_datastore_put_size_bytes_bucket -flatfs_datastore_put_size_bytes_count -flatfs_datastore_put_size_bytes_sum -flatfs_datastore_put_total -flatfs_datastore_query_errors_total -flatfs_datastore_query_latency_seconds_bucket -flatfs_datastore_query_latency_seconds_bucket -flatfs_datastore_query_latency_seconds_bucket -flatfs_datastore_query_latency_seconds_bucket -flatfs_datastore_query_latency_seconds_bucket -flatfs_datastore_query_latency_seconds_bucket -flatfs_datastore_query_latency_seconds_count -flatfs_datastore_query_latency_seconds_sum -flatfs_datastore_query_total -flatfs_datastore_scrub_errors_total -flatfs_datastore_scrub_latency_seconds_bucket -flatfs_datastore_scrub_latency_seconds_bucket -flatfs_datastore_scrub_latency_seconds_bucket -flatfs_datastore_scrub_latency_seconds_bucket -flatfs_datastore_scrub_latency_seconds_bucket -flatfs_datastore_scrub_latency_seconds_bucket -flatfs_datastore_scrub_latency_seconds_count -flatfs_datastore_scrub_latency_seconds_sum -flatfs_datastore_scrub_total -flatfs_datastore_sync_errors_total -flatfs_datastore_sync_latency_seconds_bucket -flatfs_datastore_sync_latency_seconds_bucket -flatfs_datastore_sync_latency_seconds_bucket -flatfs_datastore_sync_latency_seconds_bucket -flatfs_datastore_sync_latency_seconds_bucket -flatfs_datastore_sync_latency_seconds_bucket -flatfs_datastore_sync_latency_seconds_count -flatfs_datastore_sync_latency_seconds_sum -flatfs_datastore_sync_total -go_gc_duration_seconds -go_gc_duration_seconds -go_gc_duration_seconds -go_gc_duration_seconds -go_gc_duration_seconds -go_gc_duration_seconds_count -go_gc_duration_seconds_sum -go_goroutines -go_info -go_memstats_alloc_bytes -go_memstats_alloc_bytes_total -go_memstats_buck_hash_sys_bytes -go_memstats_frees_total -go_memstats_gc_sys_bytes -go_memstats_heap_alloc_bytes -go_memstats_heap_idle_bytes -go_memstats_heap_inuse_bytes -go_memstats_heap_objects -go_memstats_heap_released_bytes -go_memstats_heap_sys_bytes -go_memstats_last_gc_time_seconds -go_memstats_lookups_total -go_memstats_mallocs_total -go_memstats_mcache_inuse_bytes -go_memstats_mcache_sys_bytes -go_memstats_mspan_inuse_bytes -go_memstats_mspan_sys_bytes -go_memstats_next_gc_bytes -go_memstats_other_sys_bytes -go_memstats_stack_inuse_bytes -go_memstats_stack_sys_bytes -go_memstats_sys_bytes -go_threads -ipfs_bitswap_active_block_tasks -ipfs_bitswap_active_tasks -ipfs_bitswap_pending_block_tasks -ipfs_bitswap_pending_tasks -ipfs_bitswap_recv_all_blocks_bytes_bucket -ipfs_bitswap_recv_all_blocks_bytes_bucket -ipfs_bitswap_recv_all_blocks_bytes_bucket -ipfs_bitswap_recv_all_blocks_bytes_bucket -ipfs_bitswap_recv_all_blocks_bytes_bucket -ipfs_bitswap_recv_all_blocks_bytes_bucket -ipfs_bitswap_recv_all_blocks_bytes_bucket -ipfs_bitswap_recv_all_blocks_bytes_count -ipfs_bitswap_recv_all_blocks_bytes_sum -ipfs_bitswap_recv_dup_blocks_bytes_bucket -ipfs_bitswap_recv_dup_blocks_bytes_bucket -ipfs_bitswap_recv_dup_blocks_bytes_bucket -ipfs_bitswap_recv_dup_blocks_bytes_bucket -ipfs_bitswap_recv_dup_blocks_bytes_bucket -ipfs_bitswap_recv_dup_blocks_bytes_bucket -ipfs_bitswap_recv_dup_blocks_bytes_bucket -ipfs_bitswap_recv_dup_blocks_bytes_count -ipfs_bitswap_recv_dup_blocks_bytes_sum -ipfs_bitswap_send_times_bucket -ipfs_bitswap_send_times_bucket -ipfs_bitswap_send_times_bucket -ipfs_bitswap_send_times_bucket -ipfs_bitswap_send_times_bucket -ipfs_bitswap_send_times_bucket -ipfs_bitswap_send_times_bucket -ipfs_bitswap_send_times_bucket -ipfs_bitswap_send_times_count -ipfs_bitswap_send_times_sum -ipfs_bitswap_sent_all_blocks_bytes_bucket -ipfs_bitswap_sent_all_blocks_bytes_bucket -ipfs_bitswap_sent_all_blocks_bytes_bucket -ipfs_bitswap_sent_all_blocks_bytes_bucket -ipfs_bitswap_sent_all_blocks_bytes_bucket -ipfs_bitswap_sent_all_blocks_bytes_bucket -ipfs_bitswap_sent_all_blocks_bytes_bucket -ipfs_bitswap_sent_all_blocks_bytes_count -ipfs_bitswap_sent_all_blocks_bytes_sum -ipfs_bitswap_want_blocks_total -ipfs_bitswap_wantlist_total -ipfs_bs_cache_arc_hits_total -ipfs_bs_cache_arc_total -ipfs_fsrepo_datastore_batchcommit_errors_total -ipfs_fsrepo_datastore_batchcommit_latency_seconds_bucket -ipfs_fsrepo_datastore_batchcommit_latency_seconds_bucket -ipfs_fsrepo_datastore_batchcommit_latency_seconds_bucket -ipfs_fsrepo_datastore_batchcommit_latency_seconds_bucket -ipfs_fsrepo_datastore_batchcommit_latency_seconds_bucket -ipfs_fsrepo_datastore_batchcommit_latency_seconds_bucket -ipfs_fsrepo_datastore_batchcommit_latency_seconds_count -ipfs_fsrepo_datastore_batchcommit_latency_seconds_sum -ipfs_fsrepo_datastore_batchcommit_total -ipfs_fsrepo_datastore_batchdelete_errors_total -ipfs_fsrepo_datastore_batchdelete_latency_seconds_bucket -ipfs_fsrepo_datastore_batchdelete_latency_seconds_bucket -ipfs_fsrepo_datastore_batchdelete_latency_seconds_bucket -ipfs_fsrepo_datastore_batchdelete_latency_seconds_bucket -ipfs_fsrepo_datastore_batchdelete_latency_seconds_bucket -ipfs_fsrepo_datastore_batchdelete_latency_seconds_bucket -ipfs_fsrepo_datastore_batchdelete_latency_seconds_count -ipfs_fsrepo_datastore_batchdelete_latency_seconds_sum -ipfs_fsrepo_datastore_batchdelete_total -ipfs_fsrepo_datastore_batchput_errors_total -ipfs_fsrepo_datastore_batchput_latency_seconds_bucket -ipfs_fsrepo_datastore_batchput_latency_seconds_bucket -ipfs_fsrepo_datastore_batchput_latency_seconds_bucket -ipfs_fsrepo_datastore_batchput_latency_seconds_bucket -ipfs_fsrepo_datastore_batchput_latency_seconds_bucket -ipfs_fsrepo_datastore_batchput_latency_seconds_bucket -ipfs_fsrepo_datastore_batchput_latency_seconds_count -ipfs_fsrepo_datastore_batchput_latency_seconds_sum -ipfs_fsrepo_datastore_batchput_size_bytes_bucket -ipfs_fsrepo_datastore_batchput_size_bytes_bucket -ipfs_fsrepo_datastore_batchput_size_bytes_bucket -ipfs_fsrepo_datastore_batchput_size_bytes_bucket -ipfs_fsrepo_datastore_batchput_size_bytes_bucket -ipfs_fsrepo_datastore_batchput_size_bytes_count -ipfs_fsrepo_datastore_batchput_size_bytes_sum -ipfs_fsrepo_datastore_batchput_total -ipfs_fsrepo_datastore_check_errors_total -ipfs_fsrepo_datastore_check_latency_seconds_bucket -ipfs_fsrepo_datastore_check_latency_seconds_bucket -ipfs_fsrepo_datastore_check_latency_seconds_bucket -ipfs_fsrepo_datastore_check_latency_seconds_bucket -ipfs_fsrepo_datastore_check_latency_seconds_bucket -ipfs_fsrepo_datastore_check_latency_seconds_bucket -ipfs_fsrepo_datastore_check_latency_seconds_count -ipfs_fsrepo_datastore_check_latency_seconds_sum -ipfs_fsrepo_datastore_check_total -ipfs_fsrepo_datastore_delete_errors_total -ipfs_fsrepo_datastore_delete_latency_seconds_bucket -ipfs_fsrepo_datastore_delete_latency_seconds_bucket -ipfs_fsrepo_datastore_delete_latency_seconds_bucket -ipfs_fsrepo_datastore_delete_latency_seconds_bucket -ipfs_fsrepo_datastore_delete_latency_seconds_bucket -ipfs_fsrepo_datastore_delete_latency_seconds_bucket -ipfs_fsrepo_datastore_delete_latency_seconds_count -ipfs_fsrepo_datastore_delete_latency_seconds_sum -ipfs_fsrepo_datastore_delete_total -ipfs_fsrepo_datastore_du_errors_total -ipfs_fsrepo_datastore_du_latency_seconds_bucket -ipfs_fsrepo_datastore_du_latency_seconds_bucket -ipfs_fsrepo_datastore_du_latency_seconds_bucket -ipfs_fsrepo_datastore_du_latency_seconds_bucket -ipfs_fsrepo_datastore_du_latency_seconds_bucket -ipfs_fsrepo_datastore_du_latency_seconds_bucket -ipfs_fsrepo_datastore_du_latency_seconds_count -ipfs_fsrepo_datastore_du_latency_seconds_sum -ipfs_fsrepo_datastore_du_total -ipfs_fsrepo_datastore_gc_errors_total -ipfs_fsrepo_datastore_gc_latency_seconds_bucket -ipfs_fsrepo_datastore_gc_latency_seconds_bucket -ipfs_fsrepo_datastore_gc_latency_seconds_bucket -ipfs_fsrepo_datastore_gc_latency_seconds_bucket -ipfs_fsrepo_datastore_gc_latency_seconds_bucket -ipfs_fsrepo_datastore_gc_latency_seconds_bucket -ipfs_fsrepo_datastore_gc_latency_seconds_count -ipfs_fsrepo_datastore_gc_latency_seconds_sum -ipfs_fsrepo_datastore_gc_total -ipfs_fsrepo_datastore_get_errors_total -ipfs_fsrepo_datastore_get_latency_seconds_bucket -ipfs_fsrepo_datastore_get_latency_seconds_bucket -ipfs_fsrepo_datastore_get_latency_seconds_bucket -ipfs_fsrepo_datastore_get_latency_seconds_bucket -ipfs_fsrepo_datastore_get_latency_seconds_bucket -ipfs_fsrepo_datastore_get_latency_seconds_bucket -ipfs_fsrepo_datastore_get_latency_seconds_count -ipfs_fsrepo_datastore_get_latency_seconds_sum -ipfs_fsrepo_datastore_get_size_bytes_bucket -ipfs_fsrepo_datastore_get_size_bytes_bucket -ipfs_fsrepo_datastore_get_size_bytes_bucket -ipfs_fsrepo_datastore_get_size_bytes_bucket -ipfs_fsrepo_datastore_get_size_bytes_bucket -ipfs_fsrepo_datastore_get_size_bytes_count -ipfs_fsrepo_datastore_get_size_bytes_sum -ipfs_fsrepo_datastore_get_total -ipfs_fsrepo_datastore_getsize_errors_total -ipfs_fsrepo_datastore_getsize_latency_seconds_bucket -ipfs_fsrepo_datastore_getsize_latency_seconds_bucket -ipfs_fsrepo_datastore_getsize_latency_seconds_bucket -ipfs_fsrepo_datastore_getsize_latency_seconds_bucket -ipfs_fsrepo_datastore_getsize_latency_seconds_bucket -ipfs_fsrepo_datastore_getsize_latency_seconds_bucket -ipfs_fsrepo_datastore_getsize_latency_seconds_count -ipfs_fsrepo_datastore_getsize_latency_seconds_sum -ipfs_fsrepo_datastore_getsize_total -ipfs_fsrepo_datastore_has_errors_total -ipfs_fsrepo_datastore_has_latency_seconds_bucket -ipfs_fsrepo_datastore_has_latency_seconds_bucket -ipfs_fsrepo_datastore_has_latency_seconds_bucket -ipfs_fsrepo_datastore_has_latency_seconds_bucket -ipfs_fsrepo_datastore_has_latency_seconds_bucket -ipfs_fsrepo_datastore_has_latency_seconds_bucket -ipfs_fsrepo_datastore_has_latency_seconds_count -ipfs_fsrepo_datastore_has_latency_seconds_sum -ipfs_fsrepo_datastore_has_total -ipfs_fsrepo_datastore_put_errors_total -ipfs_fsrepo_datastore_put_latency_seconds_bucket -ipfs_fsrepo_datastore_put_latency_seconds_bucket -ipfs_fsrepo_datastore_put_latency_seconds_bucket -ipfs_fsrepo_datastore_put_latency_seconds_bucket -ipfs_fsrepo_datastore_put_latency_seconds_bucket -ipfs_fsrepo_datastore_put_latency_seconds_bucket -ipfs_fsrepo_datastore_put_latency_seconds_count -ipfs_fsrepo_datastore_put_latency_seconds_sum -ipfs_fsrepo_datastore_put_size_bytes_bucket -ipfs_fsrepo_datastore_put_size_bytes_bucket -ipfs_fsrepo_datastore_put_size_bytes_bucket -ipfs_fsrepo_datastore_put_size_bytes_bucket -ipfs_fsrepo_datastore_put_size_bytes_bucket -ipfs_fsrepo_datastore_put_size_bytes_count -ipfs_fsrepo_datastore_put_size_bytes_sum -ipfs_fsrepo_datastore_put_total -ipfs_fsrepo_datastore_query_errors_total -ipfs_fsrepo_datastore_query_latency_seconds_bucket -ipfs_fsrepo_datastore_query_latency_seconds_bucket -ipfs_fsrepo_datastore_query_latency_seconds_bucket -ipfs_fsrepo_datastore_query_latency_seconds_bucket -ipfs_fsrepo_datastore_query_latency_seconds_bucket -ipfs_fsrepo_datastore_query_latency_seconds_bucket -ipfs_fsrepo_datastore_query_latency_seconds_count -ipfs_fsrepo_datastore_query_latency_seconds_sum -ipfs_fsrepo_datastore_query_total -ipfs_fsrepo_datastore_scrub_errors_total -ipfs_fsrepo_datastore_scrub_latency_seconds_bucket -ipfs_fsrepo_datastore_scrub_latency_seconds_bucket -ipfs_fsrepo_datastore_scrub_latency_seconds_bucket -ipfs_fsrepo_datastore_scrub_latency_seconds_bucket -ipfs_fsrepo_datastore_scrub_latency_seconds_bucket -ipfs_fsrepo_datastore_scrub_latency_seconds_bucket -ipfs_fsrepo_datastore_scrub_latency_seconds_count -ipfs_fsrepo_datastore_scrub_latency_seconds_sum -ipfs_fsrepo_datastore_scrub_total -ipfs_fsrepo_datastore_sync_errors_total -ipfs_fsrepo_datastore_sync_latency_seconds_bucket -ipfs_fsrepo_datastore_sync_latency_seconds_bucket -ipfs_fsrepo_datastore_sync_latency_seconds_bucket -ipfs_fsrepo_datastore_sync_latency_seconds_bucket -ipfs_fsrepo_datastore_sync_latency_seconds_bucket -ipfs_fsrepo_datastore_sync_latency_seconds_bucket -ipfs_fsrepo_datastore_sync_latency_seconds_count -ipfs_fsrepo_datastore_sync_latency_seconds_sum -ipfs_fsrepo_datastore_sync_total -ipfs_http_request_duration_seconds -ipfs_http_request_duration_seconds -ipfs_http_request_duration_seconds -ipfs_http_request_duration_seconds_count -ipfs_http_request_duration_seconds_sum -ipfs_http_request_size_bytes -ipfs_http_request_size_bytes -ipfs_http_request_size_bytes -ipfs_http_request_size_bytes_count -ipfs_http_request_size_bytes_sum -ipfs_http_requests_total -ipfs_http_response_size_bytes -ipfs_http_response_size_bytes -ipfs_http_response_size_bytes -ipfs_http_response_size_bytes_count -ipfs_http_response_size_bytes_sum -ipfs_info -leveldb_datastore_batchcommit_errors_total -leveldb_datastore_batchcommit_latency_seconds_bucket -leveldb_datastore_batchcommit_latency_seconds_bucket -leveldb_datastore_batchcommit_latency_seconds_bucket -leveldb_datastore_batchcommit_latency_seconds_bucket -leveldb_datastore_batchcommit_latency_seconds_bucket -leveldb_datastore_batchcommit_latency_seconds_bucket -leveldb_datastore_batchcommit_latency_seconds_count -leveldb_datastore_batchcommit_latency_seconds_sum -leveldb_datastore_batchcommit_total -leveldb_datastore_batchdelete_errors_total -leveldb_datastore_batchdelete_latency_seconds_bucket -leveldb_datastore_batchdelete_latency_seconds_bucket -leveldb_datastore_batchdelete_latency_seconds_bucket -leveldb_datastore_batchdelete_latency_seconds_bucket -leveldb_datastore_batchdelete_latency_seconds_bucket -leveldb_datastore_batchdelete_latency_seconds_bucket -leveldb_datastore_batchdelete_latency_seconds_count -leveldb_datastore_batchdelete_latency_seconds_sum -leveldb_datastore_batchdelete_total -leveldb_datastore_batchput_errors_total -leveldb_datastore_batchput_latency_seconds_bucket -leveldb_datastore_batchput_latency_seconds_bucket -leveldb_datastore_batchput_latency_seconds_bucket -leveldb_datastore_batchput_latency_seconds_bucket -leveldb_datastore_batchput_latency_seconds_bucket -leveldb_datastore_batchput_latency_seconds_bucket -leveldb_datastore_batchput_latency_seconds_count -leveldb_datastore_batchput_latency_seconds_sum -leveldb_datastore_batchput_size_bytes_bucket -leveldb_datastore_batchput_size_bytes_bucket -leveldb_datastore_batchput_size_bytes_bucket -leveldb_datastore_batchput_size_bytes_bucket -leveldb_datastore_batchput_size_bytes_bucket -leveldb_datastore_batchput_size_bytes_count -leveldb_datastore_batchput_size_bytes_sum -leveldb_datastore_batchput_total -leveldb_datastore_check_errors_total -leveldb_datastore_check_latency_seconds_bucket -leveldb_datastore_check_latency_seconds_bucket -leveldb_datastore_check_latency_seconds_bucket -leveldb_datastore_check_latency_seconds_bucket -leveldb_datastore_check_latency_seconds_bucket -leveldb_datastore_check_latency_seconds_bucket -leveldb_datastore_check_latency_seconds_count -leveldb_datastore_check_latency_seconds_sum -leveldb_datastore_check_total -leveldb_datastore_delete_errors_total -leveldb_datastore_delete_latency_seconds_bucket -leveldb_datastore_delete_latency_seconds_bucket -leveldb_datastore_delete_latency_seconds_bucket -leveldb_datastore_delete_latency_seconds_bucket -leveldb_datastore_delete_latency_seconds_bucket -leveldb_datastore_delete_latency_seconds_bucket -leveldb_datastore_delete_latency_seconds_count -leveldb_datastore_delete_latency_seconds_sum -leveldb_datastore_delete_total -leveldb_datastore_du_errors_total -leveldb_datastore_du_latency_seconds_bucket -leveldb_datastore_du_latency_seconds_bucket -leveldb_datastore_du_latency_seconds_bucket -leveldb_datastore_du_latency_seconds_bucket -leveldb_datastore_du_latency_seconds_bucket -leveldb_datastore_du_latency_seconds_bucket -leveldb_datastore_du_latency_seconds_count -leveldb_datastore_du_latency_seconds_sum -leveldb_datastore_du_total -leveldb_datastore_gc_errors_total -leveldb_datastore_gc_latency_seconds_bucket -leveldb_datastore_gc_latency_seconds_bucket -leveldb_datastore_gc_latency_seconds_bucket -leveldb_datastore_gc_latency_seconds_bucket -leveldb_datastore_gc_latency_seconds_bucket -leveldb_datastore_gc_latency_seconds_bucket -leveldb_datastore_gc_latency_seconds_count -leveldb_datastore_gc_latency_seconds_sum -leveldb_datastore_gc_total -leveldb_datastore_get_errors_total -leveldb_datastore_get_latency_seconds_bucket -leveldb_datastore_get_latency_seconds_bucket -leveldb_datastore_get_latency_seconds_bucket -leveldb_datastore_get_latency_seconds_bucket -leveldb_datastore_get_latency_seconds_bucket -leveldb_datastore_get_latency_seconds_bucket -leveldb_datastore_get_latency_seconds_count -leveldb_datastore_get_latency_seconds_sum -leveldb_datastore_get_size_bytes_bucket -leveldb_datastore_get_size_bytes_bucket -leveldb_datastore_get_size_bytes_bucket -leveldb_datastore_get_size_bytes_bucket -leveldb_datastore_get_size_bytes_bucket -leveldb_datastore_get_size_bytes_count -leveldb_datastore_get_size_bytes_sum -leveldb_datastore_get_total -leveldb_datastore_getsize_errors_total -leveldb_datastore_getsize_latency_seconds_bucket -leveldb_datastore_getsize_latency_seconds_bucket -leveldb_datastore_getsize_latency_seconds_bucket -leveldb_datastore_getsize_latency_seconds_bucket -leveldb_datastore_getsize_latency_seconds_bucket -leveldb_datastore_getsize_latency_seconds_bucket -leveldb_datastore_getsize_latency_seconds_count -leveldb_datastore_getsize_latency_seconds_sum -leveldb_datastore_getsize_total -leveldb_datastore_has_errors_total -leveldb_datastore_has_latency_seconds_bucket -leveldb_datastore_has_latency_seconds_bucket -leveldb_datastore_has_latency_seconds_bucket -leveldb_datastore_has_latency_seconds_bucket -leveldb_datastore_has_latency_seconds_bucket -leveldb_datastore_has_latency_seconds_bucket -leveldb_datastore_has_latency_seconds_count -leveldb_datastore_has_latency_seconds_sum -leveldb_datastore_has_total -leveldb_datastore_put_errors_total -leveldb_datastore_put_latency_seconds_bucket -leveldb_datastore_put_latency_seconds_bucket -leveldb_datastore_put_latency_seconds_bucket -leveldb_datastore_put_latency_seconds_bucket -leveldb_datastore_put_latency_seconds_bucket -leveldb_datastore_put_latency_seconds_bucket -leveldb_datastore_put_latency_seconds_count -leveldb_datastore_put_latency_seconds_sum -leveldb_datastore_put_size_bytes_bucket -leveldb_datastore_put_size_bytes_bucket -leveldb_datastore_put_size_bytes_bucket -leveldb_datastore_put_size_bytes_bucket -leveldb_datastore_put_size_bytes_bucket -leveldb_datastore_put_size_bytes_count -leveldb_datastore_put_size_bytes_sum -leveldb_datastore_put_total -leveldb_datastore_query_errors_total -leveldb_datastore_query_latency_seconds_bucket -leveldb_datastore_query_latency_seconds_bucket -leveldb_datastore_query_latency_seconds_bucket -leveldb_datastore_query_latency_seconds_bucket -leveldb_datastore_query_latency_seconds_bucket -leveldb_datastore_query_latency_seconds_bucket -leveldb_datastore_query_latency_seconds_count -leveldb_datastore_query_latency_seconds_sum -leveldb_datastore_query_total -leveldb_datastore_scrub_errors_total -leveldb_datastore_scrub_latency_seconds_bucket -leveldb_datastore_scrub_latency_seconds_bucket -leveldb_datastore_scrub_latency_seconds_bucket -leveldb_datastore_scrub_latency_seconds_bucket -leveldb_datastore_scrub_latency_seconds_bucket -leveldb_datastore_scrub_latency_seconds_bucket -leveldb_datastore_scrub_latency_seconds_count -leveldb_datastore_scrub_latency_seconds_sum -leveldb_datastore_scrub_total -leveldb_datastore_sync_errors_total -leveldb_datastore_sync_latency_seconds_bucket -leveldb_datastore_sync_latency_seconds_bucket -leveldb_datastore_sync_latency_seconds_bucket -leveldb_datastore_sync_latency_seconds_bucket -leveldb_datastore_sync_latency_seconds_bucket -leveldb_datastore_sync_latency_seconds_bucket -leveldb_datastore_sync_latency_seconds_count -leveldb_datastore_sync_latency_seconds_sum -leveldb_datastore_sync_total -process_cpu_seconds_total -process_max_fds -process_open_fds -process_resident_memory_bytes -process_start_time_seconds -process_virtual_memory_bytes -process_virtual_memory_max_bytes -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_bucket -quic_connection_duration_count -quic_connection_duration_sum -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_bucket -quic_smoothed_rtt_count -quic_smoothed_rtt_sum -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_bucket -tcp_connection_duration_count -tcp_connection_duration_sum -tcp_rcvd_segments_total -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_bucket -tcp_rtt_count -tcp_rtt_sum -tcp_sent_segments_total diff --git a/test/sharness/t0117-gateway-block.sh b/test/sharness/t0117-gateway-block.sh deleted file mode 100755 index 5f1d080a3..000000000 --- a/test/sharness/t0117-gateway-block.sh +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env bash - -test_description="Test HTTP Gateway Raw Block (application/vnd.ipld.raw) Support" - -. lib/test-lib.sh - -test_init_ipfs -test_launch_ipfs_daemon_without_network - -test_expect_success "Create text fixtures" ' - mkdir -p dir && - echo "hello application/vnd.ipld.raw" > dir/ascii.txt && - ROOT_DIR_CID=$(ipfs add -Qrw --cid-version 1 dir) && - FILE_CID=$(ipfs resolve -r /ipfs/$ROOT_DIR_CID/dir/ascii.txt | cut -d "/" -f3) -' - -# GET unixfs dir root block and compare it with `ipfs block get` output - - test_expect_success "GET with format=raw param returns a raw block" ' - ipfs block get "/ipfs/$ROOT_DIR_CID/dir" > expected && - curl -sX GET "http://127.0.0.1:$GWAY_PORT/ipfs/$ROOT_DIR_CID/dir?format=raw" -o curl_ipfs_dir_block_param_output && - test_cmp expected curl_ipfs_dir_block_param_output - ' - - test_expect_success "GET for application/vnd.ipld.raw returns a raw block" ' - ipfs block get "/ipfs/$ROOT_DIR_CID/dir" > expected_block && - curl -sX GET -H "Accept: application/vnd.ipld.raw" "http://127.0.0.1:$GWAY_PORT/ipfs/$ROOT_DIR_CID/dir" -o curl_ipfs_dir_block_accept_output && - test_cmp expected_block curl_ipfs_dir_block_accept_output - ' - -# Make sure expected HTTP headers are returned with the block bytes - - test_expect_success "GET response for application/vnd.ipld.raw has expected Content-Type" ' - curl -svX GET -H "Accept: application/vnd.ipld.raw" "http://127.0.0.1:$GWAY_PORT/ipfs/$ROOT_DIR_CID/dir/ascii.txt" >/dev/null 2>curl_output && - test_should_contain "< Content-Type: application/vnd.ipld.raw" curl_output - ' - - test_expect_success "GET response for application/vnd.ipld.raw includes Content-Length" ' - BYTES=$(ipfs block get $FILE_CID | wc --bytes) - test_should_contain "< Content-Length: $BYTES" curl_output - ' - - test_expect_success "GET response for application/vnd.ipld.raw includes Content-Disposition" ' - test_should_contain "< Content-Disposition: attachment\; filename=\"${FILE_CID}.bin\"" curl_output - ' - - test_expect_success "GET response for application/vnd.ipld.raw includes nosniff hint" ' - test_should_contain "< X-Content-Type-Options: nosniff" curl_output - ' - - test_expect_success "GET for application/vnd.ipld.raw with query filename includes Content-Disposition with custom filename" ' - curl -svX GET -H "Accept: application/vnd.ipld.raw" "http://127.0.0.1:$GWAY_PORT/ipfs/$ROOT_DIR_CID/dir/ascii.txt?filename=foobar.bin" >/dev/null 2>curl_output_filename && - test_should_contain "< Content-Disposition: attachment\; filename=\"foobar.bin\"" curl_output_filename - ' - -# Cache control HTTP headers -# (basic checks, detailed behavior is tested in t0116-gateway-cache.sh) - - test_expect_success "GET response for application/vnd.ipld.raw includes Etag" ' - test_should_contain "< Etag: \"${FILE_CID}.raw\"" curl_output - ' - - test_expect_success "GET response for application/vnd.ipld.raw includes X-Ipfs-Path and X-Ipfs-Roots" ' - test_should_contain "< X-Ipfs-Path" curl_output && - test_should_contain "< X-Ipfs-Roots" curl_output - ' - - test_expect_success "GET response for application/vnd.ipld.raw includes Cache-Control" ' - test_should_contain "< Cache-Control: public, max-age=29030400, immutable" curl_output - ' - -test_kill_ipfs_daemon - -test_done diff --git a/test/sharness/t0118-gateway-car.sh b/test/sharness/t0118-gateway-car.sh deleted file mode 100755 index 3d8d7b08f..000000000 --- a/test/sharness/t0118-gateway-car.sh +++ /dev/null @@ -1,137 +0,0 @@ -#!/usr/bin/env bash - -test_description="Test HTTP Gateway CAR (application/vnd.ipld.car) Support" - -. lib/test-lib.sh - -test_init_ipfs -test_launch_ipfs_daemon_without_network - -# CAR stream is not deterministic, as blocks can arrive in random order, -# but if we have a small file that fits into a single block, and export its CID -# we will get a CAR that is a deterministic array of bytes. - - test_expect_success "Create a deterministic CAR for testing" ' - mkdir -p subdir && - echo "hello application/vnd.ipld.car" > subdir/ascii.txt && - ROOT_DIR_CID=$(ipfs add -Qrw --cid-version 1 subdir) && - FILE_CID=$(ipfs resolve -r /ipfs/$ROOT_DIR_CID/subdir/ascii.txt | cut -d "/" -f3) && - ipfs dag export $ROOT_DIR_CID > test-dag.car && - ipfs dag export $FILE_CID > deterministic.car && - purge_blockstore - ' - -# GET a reference DAG with dag-cbor+dag-pb+raw blocks as CAR - - # This test uses official CARv1 fixture from https://ipld.io/specs/transport/car/fixture/carv1-basic/ - test_expect_success "GET for application/vnd.ipld.car with dag-cbor root returns a CARv1 stream with full DAG" ' - ipfs dag import ../t0118-gateway-car/carv1-basic.car && - DAG_CBOR_CID=bafyreihyrpefhacm6kkp4ql6j6udakdit7g3dmkzfriqfykhjw6cad5lrm && - curl -sX GET -H "Accept: application/vnd.ipld.car" "http://127.0.0.1:$GWAY_PORT/ipfs/$DAG_CBOR_CID" -o gateway-dag-cbor.car && - purge_blockstore && - ipfs dag import gateway-dag-cbor.car && - ipfs dag stat --offline $DAG_CBOR_CID - ' - -# GET unixfs file as CAR -# (by using a single file we ensure deterministic result that can be compared byte-for-byte) - - test_expect_success "GET with format=car param returns a CARv1 stream" ' - ipfs dag import test-dag.car && - curl -sX GET "http://127.0.0.1:$GWAY_PORT/ipfs/$ROOT_DIR_CID/subdir/ascii.txt?format=car" -o gateway-param.car && - test_cmp deterministic.car gateway-param.car - ' - - test_expect_success "GET for application/vnd.ipld.car returns a CARv1 stream" ' - ipfs dag import test-dag.car && - curl -sX GET -H "Accept: application/vnd.ipld.car" "http://127.0.0.1:$GWAY_PORT/ipfs/$ROOT_DIR_CID/subdir/ascii.txt" -o gateway-header.car && - test_cmp deterministic.car gateway-header.car - ' - - # explicit version=1 - test_expect_success "GET for application/vnd.ipld.raw version=1 returns a CARv1 stream" ' - ipfs dag import test-dag.car && - curl -sX GET -H "Accept: application/vnd.ipld.car;version=1" "http://127.0.0.1:$GWAY_PORT/ipfs/$ROOT_DIR_CID/subdir/ascii.txt" -o gateway-header-v1.car && - test_cmp deterministic.car gateway-header-v1.car - ' - - # explicit version=1 with whitepace - test_expect_success "GET for application/vnd.ipld.raw version=1 returns a CARv1 stream (with whitespace)" ' - ipfs dag import test-dag.car && - curl -sX GET -H "Accept: application/vnd.ipld.car; version=1" "http://127.0.0.1:$GWAY_PORT/ipfs/$ROOT_DIR_CID/subdir/ascii.txt" -o gateway-header-v1.car && - test_cmp deterministic.car gateway-header-v1.car - ' - - # explicit version=2 - test_expect_success "GET for application/vnd.ipld.raw version=2 returns HTTP 400 Bad Request error" ' - curl -svX GET -H "Accept: application/vnd.ipld.car;version=2" "http://127.0.0.1:$GWAY_PORT/ipfs/$ROOT_DIR_CID/subdir/ascii.txt" > curl_output 2>&1 && - cat curl_output && - grep "400 Bad Request" curl_output && - grep "unsupported CAR version" curl_output - ' - -# GET unixfs directory as a CAR with DAG and some selector - - # TODO: this is basic test for "full" selector, we will add support for custom ones in https://github.com/ipfs/go-ipfs/issues/8769 - test_expect_success "GET for application/vnd.ipld.car with unixfs dir returns a CARv1 stream with full DAG" ' - ipfs dag import test-dag.car && - curl -sX GET -H "Accept: application/vnd.ipld.car" "http://127.0.0.1:$GWAY_PORT/ipfs/$ROOT_DIR_CID" -o gateway-dir.car && - purge_blockstore && - ipfs dag import gateway-dir.car && - ipfs dag stat --offline $ROOT_DIR_CID - ' - -# Make sure expected HTTP headers are returned with the CAR bytes - - test_expect_success "GET response for application/vnd.ipld.car has expected Content-Type" ' - ipfs dag import test-dag.car && - curl -svX GET -H "Accept: application/vnd.ipld.car" "http://127.0.0.1:$GWAY_PORT/ipfs/$ROOT_DIR_CID/subdir/ascii.txt" >/dev/null 2>curl_output && - cat curl_output && - grep "< Content-Type: application/vnd.ipld.car; version=1" curl_output - ' - - # CAR is streamed, gateway may not have the entire thing, unable to calculate total size - test_expect_success "GET response for application/vnd.ipld.car includes no Content-Length" ' - grep -qv "< Content-Length:" curl_output - ' - - test_expect_success "GET response for application/vnd.ipld.car includes Content-Disposition" ' - grep "< Content-Disposition: attachment\; filename=\"${FILE_CID}.car\"" curl_output - ' - - test_expect_success "GET response for application/vnd.ipld.car includes nosniff hint" ' - grep "< X-Content-Type-Options: nosniff" curl_output - ' - - # CAR is streamed, gateway may not have the entire thing, unable to support range-requests - # Partial downloads and resumes should be handled using - # IPLD selectors: https://github.com/ipfs/go-ipfs/issues/8769 - test_expect_success "GET response for application/vnd.ipld.car includes Accept-Ranges header" ' - grep "< Accept-Ranges: none" curl_output - ' - - test_expect_success "GET for application/vnd.ipld.car with query filename includes Content-Disposition with custom filename" ' - curl -svX GET -H "Accept: application/vnd.ipld.car" "http://127.0.0.1:$GWAY_PORT/ipfs/$ROOT_DIR_CID/subdir/ascii.txt?filename=foobar.car" > curl_output_filename 2>&1 && - cat curl_output_filename && - grep "< Content-Disposition: attachment\; filename=\"foobar.car\"" curl_output_filename - ' - -# Cache control HTTP headers - - test_expect_success "GET response for application/vnd.ipld.car includes a weak Etag" ' - grep "< Etag: W/\"${FILE_CID}.car\"" curl_output - ' - - # (basic checks, detailed behavior for some fields is tested in t0116-gateway-cache.sh) - test_expect_success "GET response for application/vnd.ipld.car includes X-Ipfs-Path and X-Ipfs-Roots" ' - grep "< X-Ipfs-Path" curl_output && - grep "< X-Ipfs-Roots" curl_output - ' - - test_expect_success "GET response for application/vnd.ipld.car includes same Cache-Control as a block or a file" ' - grep "< Cache-Control: public, max-age=29030400, immutable" curl_output - ' - -test_kill_ipfs_daemon - -test_done diff --git a/test/sharness/t0118-gateway-car/README.md b/test/sharness/t0118-gateway-car/README.md deleted file mode 100644 index 2efccc185..000000000 --- a/test/sharness/t0118-gateway-car/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Dataset description/sources - -- carv1-basic.car - - raw CARv1 - - Source: https://ipld.io/specs/transport/car/fixture/carv1-basic/carv1-basic.car - -- carv1-basic.json - - description of the contents and layout of the raw CAR, encoded in DAG-JSON - - Source: https://ipld.io/specs/transport/car/fixture/carv1-basic/carv1-basic.json - diff --git a/test/sharness/t0118-gateway-car/carv1-basic.car b/test/sharness/t0118-gateway-car/carv1-basic.car deleted file mode 100644 index 48c67a3d8..000000000 Binary files a/test/sharness/t0118-gateway-car/carv1-basic.car and /dev/null differ diff --git a/test/sharness/t0118-gateway-car/carv1-basic.json b/test/sharness/t0118-gateway-car/carv1-basic.json deleted file mode 100644 index 99a7eadab..000000000 --- a/test/sharness/t0118-gateway-car/carv1-basic.json +++ /dev/null @@ -1,159 +0,0 @@ -{ - "blocks": [ - { - "blockLength": 55, - "blockOffset": 137, - "cid": { - "/": "bafyreihyrpefhacm6kkp4ql6j6udakdit7g3dmkzfriqfykhjw6cad5lrm" - }, - "content": { - "link": { - "/": "QmNX6Tffavsya4xgBi2VJQnSuqy9GsxongxZZ9uZBqp16d" - }, - "name": "blip" - }, - "length": 92, - "offset": 100 - }, - { - "blockLength": 97, - "blockOffset": 228, - "cid": { - "/": "QmNX6Tffavsya4xgBi2VJQnSuqy9GsxongxZZ9uZBqp16d" - }, - "content": { - "Links": [ - { - "Hash": { - "/": "bafkreifw7plhl6mofk6sfvhnfh64qmkq73oeqwl6sloru6rehaoujituke" - }, - "Name": "bear", - "Tsize": 4 - }, - { - "Hash": { - "/": "QmWXZxVQ9yZfhQxLD35eDR8LiMRsYtHxYqTFCBbJoiJVys" - }, - "Name": "second", - "Tsize": 149 - } - ] - }, - "length": 133, - "offset": 192 - }, - { - "blockLength": 4, - "blockOffset": 362, - "cid": { - "/": "bafkreifw7plhl6mofk6sfvhnfh64qmkq73oeqwl6sloru6rehaoujituke" - }, - "content": { - "/": { - "bytes": "Y2NjYw" - } - }, - "length": 41, - "offset": 325 - }, - { - "blockLength": 94, - "blockOffset": 402, - "cid": { - "/": "QmWXZxVQ9yZfhQxLD35eDR8LiMRsYtHxYqTFCBbJoiJVys" - }, - "content": { - "Links": [ - { - "Hash": { - "/": "bafkreiebzrnroamgos2adnbpgw5apo3z4iishhbdx77gldnbk57d4zdio4" - }, - "Name": "dog", - "Tsize": 4 - }, - { - "Hash": { - "/": "QmdwjhxpxzcMsR3qUuj7vUL8pbA7MgR3GAxWi2GLHjsKCT" - }, - "Name": "first", - "Tsize": 51 - } - ] - }, - "length": 130, - "offset": 366 - }, - { - "blockLength": 4, - "blockOffset": 533, - "cid": { - "/": "bafkreiebzrnroamgos2adnbpgw5apo3z4iishhbdx77gldnbk57d4zdio4" - }, - "content": { - "/": { - "bytes": "YmJiYg" - } - }, - "length": 41, - "offset": 496 - }, - { - "blockLength": 47, - "blockOffset": 572, - "cid": { - "/": "QmdwjhxpxzcMsR3qUuj7vUL8pbA7MgR3GAxWi2GLHjsKCT" - }, - "content": { - "Links": [ - { - "Hash": { - "/": "bafkreidbxzk2ryxwwtqxem4l3xyyjvw35yu4tcct4cqeqxwo47zhxgxqwq" - }, - "Name": "cat", - "Tsize": 4 - } - ] - }, - "length": 82, - "offset": 537 - }, - { - "blockLength": 4, - "blockOffset": 656, - "cid": { - "/": "bafkreidbxzk2ryxwwtqxem4l3xyyjvw35yu4tcct4cqeqxwo47zhxgxqwq" - }, - "content": { - "/": { - "bytes": "YWFhYQ" - } - }, - "length": 41, - "offset": 619 - }, - { - "blockLength": 18, - "blockOffset": 697, - "cid": { - "/": "bafyreidj5idub6mapiupjwjsyyxhyhedxycv4vihfsicm2vt46o7morwlm" - }, - "content": { - "link": null, - "name": "limbo" - }, - "length": 55, - "offset": 660 - } - ], - "header": { - "roots": [ - { - "/": "bafyreihyrpefhacm6kkp4ql6j6udakdit7g3dmkzfriqfykhjw6cad5lrm" - }, - { - "/": "bafyreidj5idub6mapiupjwjsyyxhyhedxycv4vihfsicm2vt46o7morwlm" - } - ], - "version": 1 - } -} diff --git a/test/sharness/t0119-prometheus-data/prometheus_metrics b/test/sharness/t0119-prometheus-data/prometheus_metrics new file mode 100644 index 000000000..f575bf778 --- /dev/null +++ b/test/sharness/t0119-prometheus-data/prometheus_metrics @@ -0,0 +1,370 @@ +flatfs_datastore_batchcommit_errors_total +flatfs_datastore_batchcommit_latency_seconds_bucket +flatfs_datastore_batchcommit_latency_seconds_count +flatfs_datastore_batchcommit_latency_seconds_sum +flatfs_datastore_batchcommit_total +flatfs_datastore_batchdelete_errors_total +flatfs_datastore_batchdelete_latency_seconds_bucket +flatfs_datastore_batchdelete_latency_seconds_count +flatfs_datastore_batchdelete_latency_seconds_sum +flatfs_datastore_batchdelete_total +flatfs_datastore_batchput_errors_total +flatfs_datastore_batchput_latency_seconds_bucket +flatfs_datastore_batchput_latency_seconds_count +flatfs_datastore_batchput_latency_seconds_sum +flatfs_datastore_batchput_size_bytes_bucket +flatfs_datastore_batchput_size_bytes_count +flatfs_datastore_batchput_size_bytes_sum +flatfs_datastore_batchput_total +flatfs_datastore_check_errors_total +flatfs_datastore_check_latency_seconds_bucket +flatfs_datastore_check_latency_seconds_count +flatfs_datastore_check_latency_seconds_sum +flatfs_datastore_check_total +flatfs_datastore_delete_errors_total +flatfs_datastore_delete_latency_seconds_bucket +flatfs_datastore_delete_latency_seconds_count +flatfs_datastore_delete_latency_seconds_sum +flatfs_datastore_delete_total +flatfs_datastore_du_errors_total +flatfs_datastore_du_latency_seconds_bucket +flatfs_datastore_du_latency_seconds_count +flatfs_datastore_du_latency_seconds_sum +flatfs_datastore_du_total +flatfs_datastore_gc_errors_total +flatfs_datastore_gc_latency_seconds_bucket +flatfs_datastore_gc_latency_seconds_count +flatfs_datastore_gc_latency_seconds_sum +flatfs_datastore_gc_total +flatfs_datastore_get_errors_total +flatfs_datastore_get_latency_seconds_bucket +flatfs_datastore_get_latency_seconds_count +flatfs_datastore_get_latency_seconds_sum +flatfs_datastore_get_size_bytes_bucket +flatfs_datastore_get_size_bytes_count +flatfs_datastore_get_size_bytes_sum +flatfs_datastore_get_total +flatfs_datastore_getsize_errors_total +flatfs_datastore_getsize_latency_seconds_bucket +flatfs_datastore_getsize_latency_seconds_count +flatfs_datastore_getsize_latency_seconds_sum +flatfs_datastore_getsize_total +flatfs_datastore_has_errors_total +flatfs_datastore_has_latency_seconds_bucket +flatfs_datastore_has_latency_seconds_count +flatfs_datastore_has_latency_seconds_sum +flatfs_datastore_has_total +flatfs_datastore_put_errors_total +flatfs_datastore_put_latency_seconds_bucket +flatfs_datastore_put_latency_seconds_count +flatfs_datastore_put_latency_seconds_sum +flatfs_datastore_put_size_bytes_bucket +flatfs_datastore_put_size_bytes_count +flatfs_datastore_put_size_bytes_sum +flatfs_datastore_put_total +flatfs_datastore_query_errors_total +flatfs_datastore_query_latency_seconds_bucket +flatfs_datastore_query_latency_seconds_count +flatfs_datastore_query_latency_seconds_sum +flatfs_datastore_query_total +flatfs_datastore_scrub_errors_total +flatfs_datastore_scrub_latency_seconds_bucket +flatfs_datastore_scrub_latency_seconds_count +flatfs_datastore_scrub_latency_seconds_sum +flatfs_datastore_scrub_total +flatfs_datastore_sync_errors_total +flatfs_datastore_sync_latency_seconds_bucket +flatfs_datastore_sync_latency_seconds_count +flatfs_datastore_sync_latency_seconds_sum +flatfs_datastore_sync_total +go_gc_duration_seconds +go_gc_duration_seconds_count +go_gc_duration_seconds_sum +go_gc_gogc_percent +go_gc_gomemlimit_bytes +go_goroutines +go_info +go_memstats_alloc_bytes +go_memstats_alloc_bytes_total +go_memstats_buck_hash_sys_bytes +go_memstats_frees_total +go_memstats_gc_sys_bytes +go_memstats_heap_alloc_bytes +go_memstats_heap_idle_bytes +go_memstats_heap_inuse_bytes +go_memstats_heap_objects +go_memstats_heap_released_bytes +go_memstats_heap_sys_bytes +go_memstats_last_gc_time_seconds +go_memstats_mallocs_total +go_memstats_mcache_inuse_bytes +go_memstats_mcache_sys_bytes +go_memstats_mspan_inuse_bytes +go_memstats_mspan_sys_bytes +go_memstats_next_gc_bytes +go_memstats_other_sys_bytes +go_memstats_stack_inuse_bytes +go_memstats_stack_sys_bytes +go_memstats_sys_bytes +go_sched_gomaxprocs_threads +go_threads +ipfs_bitswap_active_block_tasks +ipfs_bitswap_active_tasks +ipfs_bitswap_pending_block_tasks +ipfs_bitswap_pending_tasks +ipfs_bitswap_recv_all_blocks_bytes_bucket +ipfs_bitswap_recv_all_blocks_bytes_count +ipfs_bitswap_recv_all_blocks_bytes_sum +ipfs_bitswap_recv_dup_blocks_bytes_bucket +ipfs_bitswap_recv_dup_blocks_bytes_count +ipfs_bitswap_recv_dup_blocks_bytes_sum +ipfs_bitswap_send_times_bucket +ipfs_bitswap_send_times_count +ipfs_bitswap_send_times_sum +ipfs_bitswap_sent_all_blocks_bytes_bucket +ipfs_bitswap_sent_all_blocks_bytes_count +ipfs_bitswap_sent_all_blocks_bytes_sum +ipfs_bitswap_want_blocks_total +ipfs_bitswap_wantlist_total +ipfs_bs_cache_boxo_blockstore_cache_hits +ipfs_bs_cache_boxo_blockstore_cache_total +ipfs_fsrepo_datastore_batchcommit_errors_total +ipfs_fsrepo_datastore_batchcommit_latency_seconds_bucket +ipfs_fsrepo_datastore_batchcommit_latency_seconds_count +ipfs_fsrepo_datastore_batchcommit_latency_seconds_sum +ipfs_fsrepo_datastore_batchcommit_total +ipfs_fsrepo_datastore_batchdelete_errors_total +ipfs_fsrepo_datastore_batchdelete_latency_seconds_bucket +ipfs_fsrepo_datastore_batchdelete_latency_seconds_count +ipfs_fsrepo_datastore_batchdelete_latency_seconds_sum +ipfs_fsrepo_datastore_batchdelete_total +ipfs_fsrepo_datastore_batchput_errors_total +ipfs_fsrepo_datastore_batchput_latency_seconds_bucket +ipfs_fsrepo_datastore_batchput_latency_seconds_count +ipfs_fsrepo_datastore_batchput_latency_seconds_sum +ipfs_fsrepo_datastore_batchput_size_bytes_bucket +ipfs_fsrepo_datastore_batchput_size_bytes_count +ipfs_fsrepo_datastore_batchput_size_bytes_sum +ipfs_fsrepo_datastore_batchput_total +ipfs_fsrepo_datastore_check_errors_total +ipfs_fsrepo_datastore_check_latency_seconds_bucket +ipfs_fsrepo_datastore_check_latency_seconds_count +ipfs_fsrepo_datastore_check_latency_seconds_sum +ipfs_fsrepo_datastore_check_total +ipfs_fsrepo_datastore_delete_errors_total +ipfs_fsrepo_datastore_delete_latency_seconds_bucket +ipfs_fsrepo_datastore_delete_latency_seconds_count +ipfs_fsrepo_datastore_delete_latency_seconds_sum +ipfs_fsrepo_datastore_delete_total +ipfs_fsrepo_datastore_du_errors_total +ipfs_fsrepo_datastore_du_latency_seconds_bucket +ipfs_fsrepo_datastore_du_latency_seconds_count +ipfs_fsrepo_datastore_du_latency_seconds_sum +ipfs_fsrepo_datastore_du_total +ipfs_fsrepo_datastore_gc_errors_total +ipfs_fsrepo_datastore_gc_latency_seconds_bucket +ipfs_fsrepo_datastore_gc_latency_seconds_count +ipfs_fsrepo_datastore_gc_latency_seconds_sum +ipfs_fsrepo_datastore_gc_total +ipfs_fsrepo_datastore_get_errors_total +ipfs_fsrepo_datastore_get_latency_seconds_bucket +ipfs_fsrepo_datastore_get_latency_seconds_count +ipfs_fsrepo_datastore_get_latency_seconds_sum +ipfs_fsrepo_datastore_get_size_bytes_bucket +ipfs_fsrepo_datastore_get_size_bytes_count +ipfs_fsrepo_datastore_get_size_bytes_sum +ipfs_fsrepo_datastore_get_total +ipfs_fsrepo_datastore_getsize_errors_total +ipfs_fsrepo_datastore_getsize_latency_seconds_bucket +ipfs_fsrepo_datastore_getsize_latency_seconds_count +ipfs_fsrepo_datastore_getsize_latency_seconds_sum +ipfs_fsrepo_datastore_getsize_total +ipfs_fsrepo_datastore_has_errors_total +ipfs_fsrepo_datastore_has_latency_seconds_bucket +ipfs_fsrepo_datastore_has_latency_seconds_count +ipfs_fsrepo_datastore_has_latency_seconds_sum +ipfs_fsrepo_datastore_has_total +ipfs_fsrepo_datastore_put_errors_total +ipfs_fsrepo_datastore_put_latency_seconds_bucket +ipfs_fsrepo_datastore_put_latency_seconds_count +ipfs_fsrepo_datastore_put_latency_seconds_sum +ipfs_fsrepo_datastore_put_size_bytes_bucket +ipfs_fsrepo_datastore_put_size_bytes_count +ipfs_fsrepo_datastore_put_size_bytes_sum +ipfs_fsrepo_datastore_put_total +ipfs_fsrepo_datastore_query_errors_total +ipfs_fsrepo_datastore_query_latency_seconds_bucket +ipfs_fsrepo_datastore_query_latency_seconds_count +ipfs_fsrepo_datastore_query_latency_seconds_sum +ipfs_fsrepo_datastore_query_total +ipfs_fsrepo_datastore_scrub_errors_total +ipfs_fsrepo_datastore_scrub_latency_seconds_bucket +ipfs_fsrepo_datastore_scrub_latency_seconds_count +ipfs_fsrepo_datastore_scrub_latency_seconds_sum +ipfs_fsrepo_datastore_scrub_total +ipfs_fsrepo_datastore_sync_errors_total +ipfs_fsrepo_datastore_sync_latency_seconds_bucket +ipfs_fsrepo_datastore_sync_latency_seconds_count +ipfs_fsrepo_datastore_sync_latency_seconds_sum +ipfs_fsrepo_datastore_sync_total +ipfs_http_request_duration_seconds +ipfs_http_request_duration_seconds_count +ipfs_http_request_duration_seconds_sum +ipfs_http_request_size_bytes +ipfs_http_request_size_bytes_count +ipfs_http_request_size_bytes_sum +ipfs_http_requests_total +ipfs_http_response_size_bytes +ipfs_http_response_size_bytes_count +ipfs_http_response_size_bytes_sum +ipfs_info +leveldb_datastore_batchcommit_errors_total +leveldb_datastore_batchcommit_latency_seconds_bucket +leveldb_datastore_batchcommit_latency_seconds_count +leveldb_datastore_batchcommit_latency_seconds_sum +leveldb_datastore_batchcommit_total +leveldb_datastore_batchdelete_errors_total +leveldb_datastore_batchdelete_latency_seconds_bucket +leveldb_datastore_batchdelete_latency_seconds_count +leveldb_datastore_batchdelete_latency_seconds_sum +leveldb_datastore_batchdelete_total +leveldb_datastore_batchput_errors_total +leveldb_datastore_batchput_latency_seconds_bucket +leveldb_datastore_batchput_latency_seconds_count +leveldb_datastore_batchput_latency_seconds_sum +leveldb_datastore_batchput_size_bytes_bucket +leveldb_datastore_batchput_size_bytes_count +leveldb_datastore_batchput_size_bytes_sum +leveldb_datastore_batchput_total +leveldb_datastore_check_errors_total +leveldb_datastore_check_latency_seconds_bucket +leveldb_datastore_check_latency_seconds_count +leveldb_datastore_check_latency_seconds_sum +leveldb_datastore_check_total +leveldb_datastore_delete_errors_total +leveldb_datastore_delete_latency_seconds_bucket +leveldb_datastore_delete_latency_seconds_count +leveldb_datastore_delete_latency_seconds_sum +leveldb_datastore_delete_total +leveldb_datastore_du_errors_total +leveldb_datastore_du_latency_seconds_bucket +leveldb_datastore_du_latency_seconds_count +leveldb_datastore_du_latency_seconds_sum +leveldb_datastore_du_total +leveldb_datastore_gc_errors_total +leveldb_datastore_gc_latency_seconds_bucket +leveldb_datastore_gc_latency_seconds_count +leveldb_datastore_gc_latency_seconds_sum +leveldb_datastore_gc_total +leveldb_datastore_get_errors_total +leveldb_datastore_get_latency_seconds_bucket +leveldb_datastore_get_latency_seconds_count +leveldb_datastore_get_latency_seconds_sum +leveldb_datastore_get_size_bytes_bucket +leveldb_datastore_get_size_bytes_count +leveldb_datastore_get_size_bytes_sum +leveldb_datastore_get_total +leveldb_datastore_getsize_errors_total +leveldb_datastore_getsize_latency_seconds_bucket +leveldb_datastore_getsize_latency_seconds_count +leveldb_datastore_getsize_latency_seconds_sum +leveldb_datastore_getsize_total +leveldb_datastore_has_errors_total +leveldb_datastore_has_latency_seconds_bucket +leveldb_datastore_has_latency_seconds_count +leveldb_datastore_has_latency_seconds_sum +leveldb_datastore_has_total +leveldb_datastore_put_errors_total +leveldb_datastore_put_latency_seconds_bucket +leveldb_datastore_put_latency_seconds_count +leveldb_datastore_put_latency_seconds_sum +leveldb_datastore_put_size_bytes_bucket +leveldb_datastore_put_size_bytes_count +leveldb_datastore_put_size_bytes_sum +leveldb_datastore_put_total +leveldb_datastore_query_errors_total +leveldb_datastore_query_latency_seconds_bucket +leveldb_datastore_query_latency_seconds_count +leveldb_datastore_query_latency_seconds_sum +leveldb_datastore_query_total +leveldb_datastore_scrub_errors_total +leveldb_datastore_scrub_latency_seconds_bucket +leveldb_datastore_scrub_latency_seconds_count +leveldb_datastore_scrub_latency_seconds_sum +leveldb_datastore_scrub_total +leveldb_datastore_sync_errors_total +leveldb_datastore_sync_latency_seconds_bucket +leveldb_datastore_sync_latency_seconds_count +leveldb_datastore_sync_latency_seconds_sum +leveldb_datastore_sync_total +libp2p_autonat_next_probe_timestamp +libp2p_autonat_reachability_status +libp2p_autonat_reachability_status_confidence +libp2p_autorelay_candidate_loop_state +libp2p_autorelay_candidates_circuit_v2_support_total +libp2p_autorelay_desired_reservations +libp2p_autorelay_relay_addresses_count +libp2p_autorelay_relay_addresses_updated_total +libp2p_autorelay_reservation_requests_outcome_total +libp2p_autorelay_reservations_closed_total +libp2p_autorelay_reservations_opened_total +libp2p_autorelay_status +libp2p_eventbus_events_emitted_total +libp2p_eventbus_subscriber_event_queued +libp2p_eventbus_subscriber_queue_full +libp2p_eventbus_subscriber_queue_length +libp2p_eventbus_subscribers_total +libp2p_holepunch_address_outcomes_total +libp2p_holepunch_outcomes_total +libp2p_identify_addrs_count +libp2p_identify_addrs_received_bucket +libp2p_identify_addrs_received_count +libp2p_identify_addrs_received_sum +libp2p_identify_identify_pushes_triggered_total +libp2p_identify_protocols_count +libp2p_identify_protocols_received_bucket +libp2p_identify_protocols_received_count +libp2p_identify_protocols_received_sum +libp2p_rcmgr_conn_memory_bucket +libp2p_rcmgr_conn_memory_count +libp2p_rcmgr_conn_memory_sum +libp2p_rcmgr_connections +libp2p_rcmgr_fds +libp2p_rcmgr_peer_connections_bucket +libp2p_rcmgr_peer_connections_count +libp2p_rcmgr_peer_connections_sum +libp2p_rcmgr_peer_memory_bucket +libp2p_rcmgr_peer_memory_count +libp2p_rcmgr_peer_memory_sum +libp2p_rcmgr_peer_streams_bucket +libp2p_rcmgr_peer_streams_count +libp2p_rcmgr_peer_streams_sum +libp2p_rcmgr_previous_conn_memory_bucket +libp2p_rcmgr_previous_conn_memory_count +libp2p_rcmgr_previous_conn_memory_sum +libp2p_rcmgr_previous_peer_connections_bucket +libp2p_rcmgr_previous_peer_connections_count +libp2p_rcmgr_previous_peer_connections_sum +libp2p_rcmgr_previous_peer_memory_bucket +libp2p_rcmgr_previous_peer_memory_count +libp2p_rcmgr_previous_peer_memory_sum +libp2p_rcmgr_previous_peer_streams_bucket +libp2p_rcmgr_previous_peer_streams_count +libp2p_rcmgr_previous_peer_streams_sum +libp2p_relaysvc_connection_duration_seconds_bucket +libp2p_relaysvc_connection_duration_seconds_count +libp2p_relaysvc_connection_duration_seconds_sum +libp2p_relaysvc_data_transferred_bytes_total +libp2p_relaysvc_status +libp2p_swarm_dial_ranking_delay_seconds_bucket +libp2p_swarm_dial_ranking_delay_seconds_count +libp2p_swarm_dial_ranking_delay_seconds_sum +process_cpu_seconds_total +process_max_fds +process_network_receive_bytes_total +process_network_transmit_bytes_total +process_open_fds +process_resident_memory_bytes +process_start_time_seconds +process_virtual_memory_bytes +process_virtual_memory_max_bytes diff --git a/test/sharness/t0116-prometheus-data/prometheus_metrics_added_by_enabling_rcmgr b/test/sharness/t0119-prometheus-data/prometheus_metrics_added_by_enabling_rcmgr similarity index 100% rename from test/sharness/t0116-prometheus-data/prometheus_metrics_added_by_enabling_rcmgr rename to test/sharness/t0119-prometheus-data/prometheus_metrics_added_by_enabling_rcmgr diff --git a/test/sharness/t0119-prometheus.sh b/test/sharness/t0119-prometheus.sh index e96b8b96f..fef204e23 100755 --- a/test/sharness/t0119-prometheus.sh +++ b/test/sharness/t0119-prometheus.sh @@ -23,17 +23,17 @@ test_expect_success "collect metrics" ' test_kill_ipfs_daemon test_expect_success "filter metrics" ' - sed -ne "s/^\([a-z0-9_]\+\).*/\1/p" raw_metrics | LC_ALL=C sort > filtered_metrics + sed -ne "s/^\([a-z0-9_]\+\).*/\1/p" raw_metrics | LC_ALL=C sort | uniq > filtered_metrics ' test_expect_success "make sure metrics haven't changed" ' - diff -u ../t0116-prometheus-data/prometheus_metrics filtered_metrics + diff -u ../t0119-prometheus-data/prometheus_metrics filtered_metrics ' # Check what was added by enabling ResourceMgr.Enabled # # NOTE: we won't see all the dynamic ones, but that is ok: the point of the -# test here is to detect regression when rcmgr metrics dissapear due to +# test here is to detect regression when rcmgr metrics disappear due to # refactor/human error. test_expect_success "enable ResourceMgr in the config" ' @@ -50,11 +50,11 @@ test_kill_ipfs_daemon test_expect_success "filter metrics and find ones added by enabling ResourceMgr" ' sed -ne "s/^\([a-z0-9_]\+\).*/\1/p" raw_metrics | LC_ALL=C sort > filtered_metrics && - grep -v -x -f ../t0116-prometheus-data/prometheus_metrics filtered_metrics > rcmgr_metrics + grep -v -x -f ../t0119-prometheus-data/prometheus_metrics filtered_metrics | LC_ALL=C sort | uniq > rcmgr_metrics ' test_expect_success "make sure initial metrics added by setting ResourceMgr.Enabled haven't changed" ' - diff -u ../t0116-prometheus-data/prometheus_metrics_added_by_enabling_rcmgr rcmgr_metrics + diff -u ../t0119-prometheus-data/prometheus_metrics_added_by_enabling_rcmgr rcmgr_metrics ' test_done diff --git a/test/sharness/t0120-bootstrap.sh b/test/sharness/t0120-bootstrap.sh index 892c538f1..2922533c6 100755 --- a/test/sharness/t0120-bootstrap.sh +++ b/test/sharness/t0120-bootstrap.sh @@ -10,7 +10,7 @@ BP2="/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19 BP3="/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb" BP4="/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt" BP5="/ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ" -BP6="/ip4/104.131.131.82/udp/4001/quic/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ" +BP6="/ip4/104.131.131.82/udp/4001/quic-v1/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ" test_description="Test ipfs repo operations" diff --git a/test/sharness/t0122-gateway-tar-data/inside-root.car b/test/sharness/t0122-gateway-tar-data/inside-root.car deleted file mode 100644 index c37b594f8..000000000 Binary files a/test/sharness/t0122-gateway-tar-data/inside-root.car and /dev/null differ diff --git a/test/sharness/t0122-gateway-tar-data/outside-root.car b/test/sharness/t0122-gateway-tar-data/outside-root.car deleted file mode 100644 index 7587dcb7e..000000000 Binary files a/test/sharness/t0122-gateway-tar-data/outside-root.car and /dev/null differ diff --git a/test/sharness/t0122-gateway-tar.sh b/test/sharness/t0122-gateway-tar.sh deleted file mode 100755 index 34dc1ba12..000000000 --- a/test/sharness/t0122-gateway-tar.sh +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/env bash - -test_description="Test HTTP Gateway TAR (application/x-tar) Support" - -. lib/test-lib.sh - -test_init_ipfs -test_launch_ipfs_daemon_without_network - -OUTSIDE_ROOT_CID="bafybeicaj7kvxpcv4neaqzwhrqqmdstu4dhrwfpknrgebq6nzcecfucvyu" -INSIDE_ROOT_CID="bafybeibfevfxlvxp5vxobr5oapczpf7resxnleb7tkqmdorc4gl5cdva3y" - -test_expect_success "Add the test directory" ' - mkdir -p rootDir/ipfs && - mkdir -p rootDir/ipns && - mkdir -p rootDir/api && - mkdir -p rootDir/ą/ę && - echo "I am a txt file on path with utf8" > rootDir/ą/ę/file-źł.txt && - echo "I am a txt file in confusing /api dir" > rootDir/api/file.txt && - echo "I am a txt file in confusing /ipfs dir" > rootDir/ipfs/file.txt && - echo "I am a txt file in confusing /ipns dir" > rootDir/ipns/file.txt && - DIR_CID=$(ipfs add -Qr --cid-version 1 rootDir) && - FILE_CID=$(ipfs files stat --enc=json /ipfs/$DIR_CID/ą/ę/file-źł.txt | jq -r .Hash) && - FILE_SIZE=$(ipfs files stat --enc=json /ipfs/$DIR_CID/ą/ę/file-źł.txt | jq -r .Size) - echo "$FILE_CID / $FILE_SIZE" -' - -test_expect_success "GET TAR with format=tar and extract" ' - curl "http://127.0.0.1:$GWAY_PORT/ipfs/$FILE_CID?format=tar" | tar -x -' - -test_expect_success "GET TAR with 'Accept: application/x-tar' and extract" ' - curl -H "Accept: application/x-tar" "http://127.0.0.1:$GWAY_PORT/ipfs/$FILE_CID" | tar -x -' - -test_expect_success "GET TAR with format=tar has expected Content-Type" ' - curl -sD - "http://127.0.0.1:$GWAY_PORT/ipfs/$FILE_CID?format=tar" > curl_output_filename 2>&1 && - test_should_contain "Content-Disposition: attachment;" curl_output_filename && - test_should_contain "Etag: W/\"$FILE_CID.x-tar" curl_output_filename && - test_should_contain "Content-Type: application/x-tar" curl_output_filename -' - -test_expect_success "GET TAR with 'Accept: application/x-tar' has expected Content-Type" ' - curl -sD - -H "Accept: application/x-tar" "http://127.0.0.1:$GWAY_PORT/ipfs/$FILE_CID" > curl_output_filename 2>&1 && - test_should_contain "Content-Disposition: attachment;" curl_output_filename && - test_should_contain "Etag: W/\"$FILE_CID.x-tar" curl_output_filename && - test_should_contain "Content-Type: application/x-tar" curl_output_filename -' - -test_expect_success "GET TAR has expected root file" ' - rm -rf outputDir && mkdir outputDir && - curl "http://127.0.0.1:$GWAY_PORT/ipfs/$FILE_CID?format=tar" | tar -x -C outputDir && - test -f "outputDir/$FILE_CID" && - echo "I am a txt file on path with utf8" > expected && - test_cmp expected outputDir/$FILE_CID -' - -test_expect_success "GET TAR has expected root directory" ' - rm -rf outputDir && mkdir outputDir && - curl "http://127.0.0.1:$GWAY_PORT/ipfs/$DIR_CID?format=tar" | tar -x -C outputDir && - test -d "outputDir/$DIR_CID" && - echo "I am a txt file on path with utf8" > expected && - test_cmp expected outputDir/$DIR_CID/ą/ę/file-źł.txt -' - -test_expect_success "GET TAR with explicit ?filename= succeeds with modified Content-Disposition header" " - curl -fo actual -D actual_headers 'http://127.0.0.1:$GWAY_PORT/ipfs/$DIR_CID?filename=testтест.tar&format=tar' && - grep -F 'Content-Disposition: attachment; filename=\"test____.tar\"; filename*=UTF-8'\'\''test%D1%82%D0%B5%D1%81%D1%82.tar' actual_headers -" - -test_expect_success "Add CARs with relative paths to test with" ' - ipfs dag import ../t0122-gateway-tar-data/outside-root.car > import_output && - test_should_contain $OUTSIDE_ROOT_CID import_output && - ipfs dag import ../t0122-gateway-tar-data/inside-root.car > import_output && - test_should_contain $INSIDE_ROOT_CID import_output -' - -test_expect_success "GET TAR with relative paths outside root fails" ' - curl -o - "http://127.0.0.1:$GWAY_PORT/ipfs/$OUTSIDE_ROOT_CID?format=tar" > curl_output_filename && - test_should_contain "relative UnixFS paths outside the root are now allowed" curl_output_filename -' - -test_expect_success "GET TAR with relative paths inside root works" ' - rm -rf outputDir && mkdir outputDir && - curl "http://127.0.0.1:$GWAY_PORT/ipfs/$INSIDE_ROOT_CID?format=tar" | tar -x -C outputDir && - test -f outputDir/$INSIDE_ROOT_CID/foobar/file -' - -test_kill_ipfs_daemon - -test_done diff --git a/test/sharness/t0123-gateway-json-cbor.sh b/test/sharness/t0123-gateway-json-cbor.sh deleted file mode 100755 index 812d90f24..000000000 --- a/test/sharness/t0123-gateway-json-cbor.sh +++ /dev/null @@ -1,387 +0,0 @@ -#!/usr/bin/env bash - -test_description="Test HTTP Gateway DAG-JSON (application/vnd.ipld.dag-json) and DAG-CBOR (application/vnd.ipld.dag-cbor) Support" - -. lib/test-lib.sh - -test_init_ipfs -test_launch_ipfs_daemon_without_network - -test_expect_success "Add the test directory" ' - mkdir -p rootDir/ipfs && - mkdir -p rootDir/ipns && - mkdir -p rootDir/api && - mkdir -p rootDir/ą/ę && - echo "I am a txt file on path with utf8" > rootDir/ą/ę/file-źł.txt && - echo "I am a txt file in confusing /api dir" > rootDir/api/file.txt && - echo "I am a txt file in confusing /ipfs dir" > rootDir/ipfs/file.txt && - echo "I am a txt file in confusing /ipns dir" > rootDir/ipns/file.txt && - DIR_CID=$(ipfs add -Qr --cid-version 1 rootDir) && - FILE_CID=$(ipfs files stat --enc=json /ipfs/$DIR_CID/ą/ę/file-źł.txt | jq -r .Hash) && - FILE_SIZE=$(ipfs files stat --enc=json /ipfs/$DIR_CID/ą/ę/file-źł.txt | jq -r .Size) - echo "$FILE_CID / $FILE_SIZE" -' - -## Reading UnixFS (data encoded with dag-pb codec) as DAG-CBOR and DAG-JSON - -test_dag_pb_headers () { - name=$1 - format=$2 - disposition=$3 - - test_expect_success "GET UnixFS as $name with format=dag-$format has expected Content-Type" ' - curl -sD - "http://127.0.0.1:$GWAY_PORT/ipfs/$FILE_CID?format=dag-$format" > curl_output 2>&1 && - test_should_contain "Content-Type: application/vnd.ipld.dag-$format" curl_output && - test_should_contain "Content-Disposition: ${disposition}\; filename=\"${FILE_CID}.${format}\"" curl_output && - test_should_not_contain "Content-Type: application/$format" curl_output - ' - - test_expect_success "GET UnixFS as $name with 'Accept: application/vnd.ipld.dag-$format' has expected Content-Type" ' - curl -sD - -H "Accept: application/vnd.ipld.dag-$format" "http://127.0.0.1:$GWAY_PORT/ipfs/$FILE_CID" > curl_output 2>&1 && - test_should_contain "Content-Disposition: ${disposition}\; filename=\"${FILE_CID}.${format}\"" curl_output && - test_should_contain "Content-Type: application/vnd.ipld.dag-$format" curl_output && - test_should_not_contain "Content-Type: application/$format" curl_output - ' - - test_expect_success "GET UnixFS as $name with format=$format has expected Content-Type" ' - curl -sD - "http://127.0.0.1:$GWAY_PORT/ipfs/$FILE_CID?format=$format" > curl_output 2>&1 && - test_should_contain "Content-Disposition: ${disposition}\; filename=\"${FILE_CID}.${format}\"" curl_output && - test_should_contain "Content-Type: application/$format" curl_output && - test_should_not_contain "Content-Type: application/vnd.ipld.dag-$format" curl_output - ' - - test_expect_success "GET UnixFS as $name with 'Accept: application/$format' has expected Content-Type" ' - curl -sD - -H "Accept: application/$format" "http://127.0.0.1:$GWAY_PORT/ipfs/$FILE_CID" > curl_output 2>&1 && - test_should_contain "Content-Disposition: ${disposition}\; filename=\"${FILE_CID}.${format}\"" curl_output && - test_should_contain "Content-Type: application/$format" curl_output && - test_should_not_contain "Content-Type: application/vnd.ipld.dag-$format" curl_output - ' -} - -test_dag_pb_headers "DAG-JSON" "json" "inline" -test_dag_pb_headers "DAG-CBOR" "cbor" "attachment" - -test_dag_pb () { - name=$1 - format=$2 - - test_expect_success "GET UnixFS as $name has expected output for file" ' - curl -s "http://127.0.0.1:$GWAY_PORT/ipfs/$FILE_CID?format=dag-$format" > curl_output 2>&1 && - ipfs dag get --output-codec dag-$format $FILE_CID > ipfs_dag_get_output 2>&1 && - test_cmp ipfs_dag_get_output curl_output - ' - - test_expect_success "GET UnixFS as $name has expected output for directory" ' - curl -s "http://127.0.0.1:$GWAY_PORT/ipfs/$DIR_CID?format=dag-$format" > curl_output 2>&1 && - ipfs dag get --output-codec dag-$format $DIR_CID > ipfs_dag_get_output 2>&1 && - test_cmp ipfs_dag_get_output curl_output - ' - - test_expect_success "GET UnixFS as $name with format=dag-$format and format=$format produce same output" ' - curl -s "http://127.0.0.1:$GWAY_PORT/ipfs/$DIR_CID?format=dag-$format" > curl_output_1 2>&1 && - curl -s "http://127.0.0.1:$GWAY_PORT/ipfs/$DIR_CID?format=$format" > curl_output_2 2>&1 && - test_cmp curl_output_1 curl_output_2 - ' -} - -test_dag_pb "DAG-JSON" "json" -test_dag_pb "DAG-CBOR" "cbor" - -## Content-Type response based on Accept header and ?format= parameter - -test_cmp_dag_get () { - name=$1 - format=$2 - disposition=$3 - - test_expect_success "GET $name without Accept or format= has expected Content-Type" ' - CID=$(echo "{ \"test\": \"json\" }" | ipfs dag put --input-codec json --store-codec $format) && - curl -sD - "http://127.0.0.1:$GWAY_PORT/ipfs/$CID" > curl_output 2>&1 && - test_should_contain "Content-Disposition: ${disposition}\; filename=\"${CID}.${format}\"" curl_output && - test_should_contain "Content-Type: application/$format" curl_output - ' - - test_expect_success "GET $name without Accept or format= produces correct output" ' - CID=$(echo "{ \"test\": \"json\" }" | ipfs dag put --input-codec json --store-codec $format) && - curl -s "http://127.0.0.1:$GWAY_PORT/ipfs/$CID" > curl_output 2>&1 && - ipfs dag get --output-codec $format $CID > ipfs_dag_get_output 2>&1 && - test_cmp ipfs_dag_get_output curl_output - ' - - test_expect_success "GET $name with format=$format produces expected Content-Type" ' - CID=$(echo "{ \"test\": \"json\" }" | ipfs dag put --input-codec json --store-codec $format) && - curl -sD- "http://127.0.0.1:$GWAY_PORT/ipfs/$CID?format=$format" > curl_output 2>&1 && - test_should_contain "Content-Disposition: ${disposition}\; filename=\"${CID}.${format}\"" curl_output && - test_should_contain "Content-Type: application/$format" curl_output - ' - - test_expect_success "GET $name with format=$format produces correct output" ' - CID=$(echo "{ \"test\": \"json\" }" | ipfs dag put --input-codec json --store-codec $format) && - curl -s "http://127.0.0.1:$GWAY_PORT/ipfs/$CID?format=$format" > curl_output 2>&1 && - ipfs dag get --output-codec $format $CID > ipfs_dag_get_output 2>&1 && - test_cmp ipfs_dag_get_output curl_output - ' - - test_expect_success "GET $name with format=dag-$format produces expected Content-Type" ' - CID=$(echo "{ \"test\": \"json\" }" | ipfs dag put --input-codec json --store-codec $format) && - curl -sD- "http://127.0.0.1:$GWAY_PORT/ipfs/$CID?format=dag-$format" > curl_output 2>&1 && - test_should_contain "Content-Disposition: ${disposition}\; filename=\"${CID}.${format}\"" curl_output && - test_should_contain "Content-Type: application/vnd.ipld.dag-$format" curl_output - ' - - test_expect_success "GET $name with format=dag-$format produces correct output" ' - CID=$(echo "{ \"test\": \"json\" }" | ipfs dag put --input-codec json --store-codec $format) && - curl -s "http://127.0.0.1:$GWAY_PORT/ipfs/$CID?format=dag-$format" > curl_output 2>&1 && - ipfs dag get --output-codec dag-$format $CID > ipfs_dag_get_output 2>&1 && - test_cmp ipfs_dag_get_output curl_output - ' -} - -test_cmp_dag_get "JSON" "json" "inline" -test_cmp_dag_get "CBOR" "cbor" "attachment" - - -## Lossless conversion between JSON and CBOR - -test_expect_success "GET JSON as CBOR produces DAG-CBOR output" ' - CID=$(echo "{ \"test\": \"json\" }" | ipfs dag put --input-codec json --store-codec json) && - curl -s "http://127.0.0.1:$GWAY_PORT/ipfs/$CID?format=cbor" > curl_output 2>&1 && - ipfs dag get --output-codec dag-cbor $CID > ipfs_dag_get_output 2>&1 && - test_cmp ipfs_dag_get_output curl_output -' - -test_expect_success "GET CBOR as JSON produces DAG-JSON output" ' - CID=$(echo "{ \"test\": \"json\" }" | ipfs dag put --input-codec json --store-codec cbor) && - curl -s "http://127.0.0.1:$GWAY_PORT/ipfs/$CID?format=json" > curl_output 2>&1 && - ipfs dag get --output-codec dag-json $CID > ipfs_dag_get_output 2>&1 && - test_cmp ipfs_dag_get_output curl_output -' - - -## Pathing, traversal - -DAG_CBOR_TRAVERSAL_CID="bafyreibs4utpgbn7uqegmd2goqz4bkyflre2ek2iwv743fhvylwi4zeeim" -DAG_JSON_TRAVERSAL_CID="baguqeeram5ujjqrwheyaty3w5gdsmoz6vittchvhk723jjqxk7hakxkd47xq" -DAG_PB_CID="bafybeiegxwlgmoh2cny7qlolykdf7aq7g6dlommarldrbm7c4hbckhfcke" - -test_expect_success "Add CARs for path traversal and DAG-PB representation tests" ' - ipfs dag import ../t0123-gateway-json-cbor/dag-cbor-traversal.car > import_output && - test_should_contain $DAG_CBOR_TRAVERSAL_CID import_output && - ipfs dag import ../t0123-gateway-json-cbor/dag-json-traversal.car > import_output && - test_should_contain $DAG_JSON_TRAVERSAL_CID import_output && - ipfs dag import ../t0123-gateway-json-cbor/dag-pb.car > import_output && - test_should_contain $DAG_PB_CID import_output -' - -test_expect_success "GET DAG-JSON traversal returns 501 if there is path remainder" ' - curl -sD - "http://127.0.0.1:$GWAY_PORT/ipfs/$DAG_JSON_TRAVERSAL_CID/foo?format=dag-json" > curl_output 2>&1 && - test_should_contain "501 Not Implemented" curl_output && - test_should_contain "reading IPLD Kinds other than Links (CBOR Tag 42) is not implemented" curl_output -' - -test_expect_success "GET DAG-JSON traverses multiple links" ' - curl -s "http://127.0.0.1:$GWAY_PORT/ipfs/$DAG_JSON_TRAVERSAL_CID/foo/link/bar?format=dag-json" > curl_output 2>&1 && - jq --sort-keys . curl_output > actual && - echo "{ \"hello\": \"this is not a link\" }" | jq --sort-keys . > expected && - test_cmp expected actual -' - -test_expect_success "GET DAG-CBOR traversal returns 501 if there is path remainder" ' - curl -sD - "http://127.0.0.1:$GWAY_PORT/ipfs/$DAG_CBOR_TRAVERSAL_CID/foo?format=dag-cbor" > curl_output 2>&1 && - test_should_contain "501 Not Implemented" curl_output && - test_should_contain "reading IPLD Kinds other than Links (CBOR Tag 42) is not implemented" curl_output -' - -test_expect_success "GET DAG-CBOR traverses multiple links" ' - curl -s "http://127.0.0.1:$GWAY_PORT/ipfs/$DAG_CBOR_TRAVERSAL_CID/foo/link/bar?format=dag-json" > curl_output 2>&1 && - jq --sort-keys . curl_output > actual && - echo "{ \"hello\": \"this is not a link\" }" | jq --sort-keys . > expected && - test_cmp expected actual -' - -# test_expect_success "GET DAG-PB has expected output" ' -# curl -s "http://127.0.0.1:$GWAY_PORT/ipfs/$DAG_PB_CID?format=dag-json" > curl_output 2>&1 && -# jq --sort-keys . curl_output > actual && -# test_cmp ../t0123-gateway-json-cbor/dag-pb.json actual -# ' - - -## NATIVE TESTS: -## DAG- regression tests for core behaviors when native DAG-(CBOR|JSON) is requested - - -test_native_dag () { - name=$1 - format=$2 - disposition=$3 - CID=$4 - - # GET without explicit format and Accept: text/html returns raw block - - test_expect_success "GET $name from /ipfs without explicit format returns the same payload as the raw block" ' - ipfs block get "/ipfs/$CID" > expected && - curl -sX GET "http://127.0.0.1:$GWAY_PORT/ipfs/$CID" -o curl_output && - test_cmp expected curl_output - ' - - # GET dag-cbor block via Accept and ?format and ensure both are the same as `ipfs block get` output - - test_expect_success "GET $name from /ipfs with format=dag-$format returns the same payload as the raw block" ' - ipfs block get "/ipfs/$CID" > expected && - curl -sX GET "http://127.0.0.1:$GWAY_PORT/ipfs/$CID?format=dag-$format" -o curl_ipfs_dag_param_output && - test_cmp expected curl_ipfs_dag_param_output - ' - - test_expect_success "GET $name from /ipfs with format=$format returns the same payload as format=dag-$format" ' - curl -sX GET "http://127.0.0.1:$GWAY_PORT/ipfs/$CID?format=dag-$format" -o expected && - curl -sX GET "http://127.0.0.1:$GWAY_PORT/ipfs/$CID?format=dag-$format" -o curl_ipfs_dag_param_output && - test_cmp expected curl_ipfs_dag_param_output - ' - - test_expect_success "GET $name from /ipfs with application/vnd.ipld.dag-$format returns the same payload as the raw block" ' - ipfs block get "/ipfs/$CID" > expected_block && - curl -sX GET -H "Accept: application/vnd.ipld.dag-$format" "http://127.0.0.1:$GWAY_PORT/ipfs/$CID" -o curl_ipfs_dag_block_accept_output && - test_cmp expected_block curl_ipfs_dag_block_accept_output - ' - - # Make sure expected HTTP headers are returned with the dag- block - - test_expect_success "GET response for application/vnd.ipld.dag-$format has expected Content-Type" ' - curl -svX GET -H "Accept: application/vnd.ipld.dag-$format" "http://127.0.0.1:$GWAY_PORT/ipfs/$CID" >/dev/null 2>curl_output && - test_should_contain "< Content-Type: application/vnd.ipld.dag-$format" curl_output - ' - - test_expect_success "GET response for application/vnd.ipld.dag-$format includes Content-Length" ' - BYTES=$(ipfs block get $CID | wc --bytes) - test_should_contain "< Content-Length: $BYTES" curl_output - ' - - test_expect_success "GET response for application/vnd.ipld.dag-$format includes Content-Disposition" ' - test_should_contain "< Content-Disposition: ${disposition}\; filename=\"${CID}.${format}\"" curl_output - ' - - test_expect_success "GET response for application/vnd.ipld.dag-$format includes nosniff hint" ' - test_should_contain "< X-Content-Type-Options: nosniff" curl_output - ' - - test_expect_success "GET for application/vnd.ipld.dag-$format with query filename includes Content-Disposition with custom filename" ' - curl -svX GET -H "Accept: application/vnd.ipld.dag-$format" "http://127.0.0.1:$GWAY_PORT/ipfs/$CID?filename=foobar.$format" >/dev/null 2>curl_output_filename && - test_should_contain "< Content-Disposition: ${disposition}\; filename=\"foobar.$format\"" curl_output_filename - ' - - test_expect_success "GET for application/vnd.ipld.dag-$format with ?download=true forces Content-Disposition: attachment" ' - curl -svX GET -H "Accept: application/vnd.ipld.dag-$format" "http://127.0.0.1:$GWAY_PORT/ipfs/$CID?filename=foobar.$format&download=true" >/dev/null 2>curl_output_filename && - test_should_contain "< Content-Disposition: attachment" curl_output_filename - ' - - # Cache control HTTP headers - # (basic checks, detailed behavior is tested in t0116-gateway-cache.sh) - - test_expect_success "GET response for application/vnd.ipld.dag-$format includes Etag" ' - test_should_contain "< Etag: \"${CID}.dag-$format\"" curl_output - ' - - test_expect_success "GET response for application/vnd.ipld.dag-$format includes X-Ipfs-Path and X-Ipfs-Roots" ' - test_should_contain "< X-Ipfs-Path" curl_output && - test_should_contain "< X-Ipfs-Roots" curl_output - ' - - test_expect_success "GET response for application/vnd.ipld.dag-$format includes Cache-Control" ' - test_should_contain "< Cache-Control: public, max-age=29030400, immutable" curl_output - ' - - # HTTP HEAD behavior - test_expect_success "HEAD $name with no explicit format returns HTTP 200" ' - curl -I "http://127.0.0.1:$GWAY_PORT/ipfs/$CID" -o output && - test_should_contain "HTTP/1.1 200 OK" output && - test_should_contain "Content-Type: application/vnd.ipld.dag-$format" output && - test_should_contain "Content-Length: " output - ' - test_expect_success "HEAD $name with an explicit JSON format returns HTTP 200" ' - curl -I "http://127.0.0.1:$GWAY_PORT/ipfs/$CID?format=json" -o output && - test_should_contain "HTTP/1.1 200 OK" output && - test_should_contain "Etag: \"$CID.json\"" output && - test_should_contain "Content-Type: application/json" output && - test_should_contain "Content-Length: " output - ' - test_expect_success "HEAD dag-pb with ?format=$format returns HTTP 200" ' - curl -I "http://127.0.0.1:$GWAY_PORT/ipfs/$FILE_CID?format=$format" -o output && - test_should_contain "HTTP/1.1 200 OK" output && - test_should_contain "Etag: \"$FILE_CID.$format\"" output && - test_should_contain "Content-Type: application/$format" output && - test_should_contain "Content-Length: " output - ' - test_expect_success "HEAD $name with only-if-cached for missing block returns HTTP 412 Precondition Failed" ' - MISSING_CID=$(echo "{\"t\": \"$(date +%s)\"}" | ipfs dag put --store-codec=dag-${format}) && - ipfs block rm -f -q $MISSING_CID && - curl -I -H "Cache-Control: only-if-cached" "http://127.0.0.1:$GWAY_PORT/ipfs/$MISSING_CID" -o output && - test_should_contain "HTTP/1.1 412 Precondition Failed" output - ' - - # IPNS behavior (should be same as immutable /ipfs, but with different caching headers) - # To keep tests small we only confirm payload is the same, and then only test delta around caching headers. - - test_expect_success "Prepare IPNS with dag-$format" ' - IPNS_ID=$(ipfs key gen --ipns-base=base36 --type=ed25519 ${format}_test_key | head -n1 | tr -d "\n") && - ipfs name publish --key ${format}_test_key --allow-offline -Q "/ipfs/$CID" > name_publish_out && - test_check_peerid "${IPNS_ID}" && - ipfs name resolve "${IPNS_ID}" > output && - printf "/ipfs/%s\n" "$CID" > expected && - test_cmp expected output - ' - - test_expect_success "GET $name from /ipns without explicit format returns the same payload as /ipfs" ' - curl -sX GET "http://127.0.0.1:$GWAY_PORT/ipfs/$CID" -o ipfs_output && - curl -sX GET "http://127.0.0.1:$GWAY_PORT/ipns/$IPNS_ID" -o ipns_output && - test_cmp ipfs_output ipns_output - ' - - test_expect_success "GET $name from /ipns without explicit format returns the same payload as /ipfs" ' - curl -sX GET "http://127.0.0.1:$GWAY_PORT/ipfs/$CID?format=dag-$format" -o ipfs_output && - curl -sX GET "http://127.0.0.1:$GWAY_PORT/ipns/$IPNS_ID?format=dag-$format" -o ipns_output && - test_cmp ipfs_output ipns_output - ' - - test_expect_success "GET $name from /ipns with explicit application/vnd.ipld.dag-$format has expected headers" ' - curl -svX GET -H "Accept: application/vnd.ipld.dag-$format" "http://127.0.0.1:$GWAY_PORT/ipns/$IPNS_ID" >/dev/null 2>curl_output && - test_should_not_contain "Cache-Control" curl_output && - test_should_contain "< Content-Type: application/vnd.ipld.dag-$format" curl_output && - test_should_contain "< Etag: \"${CID}.dag-$format\"" curl_output && - test_should_contain "< X-Ipfs-Path" curl_output && - test_should_contain "< X-Ipfs-Roots" curl_output - ' - - - # When Accept header includes text/html and no explicit format is requested for DAG-(CBOR|JSON) - # The gateway returns generated HTML index (see dag-index-html) for web browsers (similar to dir-index-html returned for unixfs dirs) - # As this is generated, we don't return immutable Cache-Control, even on /ipfs (same as for dir-index-html) - - test_expect_success "GET $name on /ipfs with Accept: text/html returns HTML (dag-index-html)" ' - curl -sD - -H "Accept: text/html" "http://127.0.0.1:$GWAY_PORT/ipfs/$CID" > curl_output 2>&1 && - test_should_not_contain "Content-Disposition" curl_output && - test_should_not_contain "Cache-Control" curl_output && - test_should_contain "Etag: \"DagIndex-" curl_output && - test_should_contain "Content-Type: text/html" curl_output && - test_should_contain "" curl_output - ' - - test_expect_success "GET $name on /ipns with Accept: text/html returns HTML (dag-index-html)" ' - curl -sD - -H "Accept: text/html" "http://127.0.0.1:$GWAY_PORT/ipns/$IPNS_ID" > curl_output 2>&1 && - test_should_not_contain "Content-Disposition" curl_output && - test_should_not_contain "Cache-Control" curl_output && - test_should_contain "Etag: \"DagIndex-" curl_output && - test_should_contain "Content-Type: text/html" curl_output && - test_should_contain "" curl_output - ' - - -} - -test_native_dag "DAG-JSON" "json" "inline" "$DAG_JSON_TRAVERSAL_CID" -test_native_dag "DAG-CBOR" "cbor" "attachment" "$DAG_CBOR_TRAVERSAL_CID" - -test_kill_ipfs_daemon - -test_done - -# vim: set ts=2 sw=2 et: diff --git a/test/sharness/t0123-gateway-json-cbor/dag-cbor-traversal.car b/test/sharness/t0123-gateway-json-cbor/dag-cbor-traversal.car deleted file mode 100644 index 92c3d4f3e..000000000 Binary files a/test/sharness/t0123-gateway-json-cbor/dag-cbor-traversal.car and /dev/null differ diff --git a/test/sharness/t0123-gateway-json-cbor/dag-json-traversal.car b/test/sharness/t0123-gateway-json-cbor/dag-json-traversal.car deleted file mode 100644 index 0431ef24a..000000000 Binary files a/test/sharness/t0123-gateway-json-cbor/dag-json-traversal.car and /dev/null differ diff --git a/test/sharness/t0123-gateway-json-cbor/dag-pb.car b/test/sharness/t0123-gateway-json-cbor/dag-pb.car deleted file mode 100644 index a6bb076c7..000000000 Binary files a/test/sharness/t0123-gateway-json-cbor/dag-pb.car and /dev/null differ diff --git a/test/sharness/t0123-gateway-json-cbor/dag-pb.json b/test/sharness/t0123-gateway-json-cbor/dag-pb.json deleted file mode 100644 index ab4f9f011..000000000 --- a/test/sharness/t0123-gateway-json-cbor/dag-pb.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "Data": { - "/": { - "bytes": "CAE" - } - }, - "Links": [ - { - "Hash": { - "/": "bafybeidryarwh34ygbtyypbu7qjkl4euiwxby6cql6uvosonohkq2kwnkm" - }, - "Name": "foo", - "Tsize": 69 - }, - { - "Hash": { - "/": "bafkreic3ondyhizrzeoufvoodehinugpj3ecruwokaygl7elezhn2khqfa" - }, - "Name": "foo.txt", - "Tsize": 13 - } - ] -} diff --git a/test/sharness/t0125-twonode.sh b/test/sharness/t0125-twonode.sh deleted file mode 100755 index 6f819400a..000000000 --- a/test/sharness/t0125-twonode.sh +++ /dev/null @@ -1,136 +0,0 @@ -#!/usr/bin/env bash -# -# Copyright (c) 2017 Jeromy Johnson -# MIT Licensed; see the LICENSE file in this repository. -# - -test_description="Test two ipfs nodes transferring a file" - -. lib/test-lib.sh - -check_file_fetch() { - node=$1 - fhash=$2 - fname=$3 - - test_expect_success "can fetch file" ' - ipfsi $node cat $fhash > fetch_out - ' - - test_expect_success "file looks good" ' - test_cmp $fname fetch_out - ' -} - -check_dir_fetch() { - node=$1 - ref=$2 - - test_expect_success "node can fetch all refs for dir" ' - ipfsi $node refs -r $ref > /dev/null - ' -} - -run_single_file_test() { - test_expect_success "add a file on node1" ' - random 1000000 > filea && - FILEA_HASH=$(ipfsi 1 add -q filea) - ' - - check_file_fetch 0 $FILEA_HASH filea -} - -run_random_dir_test() { - test_expect_success "create a bunch of random files" ' - random-files -depth=3 -dirs=4 -files=5 -seed=5 foobar > /dev/null - ' - - test_expect_success "add those on node 0" ' - DIR_HASH=$(ipfsi 0 add -r -Q foobar) - ' - - check_dir_fetch 1 $DIR_HASH -} - -run_advanced_test() { - startup_cluster 2 "$@" - - test_expect_success "clean repo before test" ' - ipfsi 0 repo gc > /dev/null && - ipfsi 1 repo gc > /dev/null - ' - - run_single_file_test - - run_random_dir_test - - test_expect_success "node0 data transferred looks correct" ' - ipfsi 0 bitswap stat > stat0 && - grep "blocks sent: 126" stat0 > /dev/null && - grep "blocks received: 5" stat0 > /dev/null && - grep "data sent: 228113" stat0 > /dev/null && - grep "data received: 1000256" stat0 > /dev/null - ' - - test_expect_success "node1 data transferred looks correct" ' - ipfsi 1 bitswap stat > stat1 && - grep "blocks received: 126" stat1 > /dev/null && - grep "blocks sent: 5" stat1 > /dev/null && - grep "data received: 228113" stat1 > /dev/null && - grep "data sent: 1000256" stat1 > /dev/null - ' - - test_expect_success "shut down nodes" ' - iptb stop && iptb_wait_stop - ' -} - -test_expect_success "set up tcp testbed" ' - iptb testbed create -type localipfs -count 2 -force -init -' - -addrs='"[\"/ip4/127.0.0.1/tcp/0\", \"/ip4/127.0.0.1/udp/0/quic\"]"' -test_expect_success "configure addresses" ' - ipfsi 0 config --json Addresses.Swarm '"${addrs}"' && - ipfsi 1 config --json Addresses.Swarm '"${addrs}"' -' - -# Test TCP transport -echo "Testing TCP" -test_expect_success "use TCP only" ' - iptb run -- ipfs config --json Swarm.Transports.Network.QUIC false && - iptb run -- ipfs config --json Swarm.Transports.Network.Relay false && - iptb run -- ipfs config --json Swarm.Transports.Network.Websocket false -' -run_advanced_test - -# test multiplex muxer -echo "Running advanced tests with mplex" -test_expect_success "disable yamux" ' - iptb run -- ipfs config --json Swarm.Transports.Multiplexers.Yamux false -' -run_advanced_test - -test_expect_success "re-enable yamux" ' - iptb run -- ipfs config --json Swarm.Transports.Multiplexers.Yamux null -' - -# test Noise - -echo "Running advanced tests with NOISE" -test_expect_success "use noise only" ' - iptb run -- ipfs config --json Swarm.Transports.Security.TLS false -' - -run_advanced_test - -# test QUIC -echo "Running advanced tests over QUIC" -test_expect_success "use QUIC only" ' - iptb run -- ipfs config --json Swarm.Transports.Network.QUIC true && - iptb run -- ipfs config --json Swarm.Transports.Network.TCP false -' - -run_advanced_test - -test_done diff --git a/test/sharness/t0130-multinode.sh b/test/sharness/t0130-multinode.sh deleted file mode 100755 index 04746850b..000000000 --- a/test/sharness/t0130-multinode.sh +++ /dev/null @@ -1,115 +0,0 @@ -#!/usr/bin/env bash -# -# Copyright (c) 2015 Jeromy Johnson -# MIT Licensed; see the LICENSE file in this repository. -# - -test_description="Test multiple ipfs nodes" - -. lib/test-lib.sh - -check_file_fetch() { - node=$1 - fhash=$2 - fname=$3 - - test_expect_success "can fetch file" ' - ipfsi $node cat $fhash > fetch_out - ' - - test_expect_success "file looks good" ' - test_cmp $fname fetch_out - ' -} - -check_dir_fetch() { - node=$1 - ref=$2 - - test_expect_success "node can fetch all refs for dir" ' - ipfsi $node refs -r $ref > /dev/null - ' -} - -run_single_file_test() { - test_expect_success "add a file on node1" ' - random 1000000 > filea && - FILEA_HASH=$(ipfsi 1 add -q filea) - ' - - check_file_fetch 4 $FILEA_HASH filea - check_file_fetch 3 $FILEA_HASH filea - check_file_fetch 2 $FILEA_HASH filea - check_file_fetch 1 $FILEA_HASH filea - check_file_fetch 0 $FILEA_HASH filea -} - -run_random_dir_test() { - test_expect_success "create a bunch of random files" ' - random-files -depth=4 -dirs=3 -files=6 foobar > /dev/null - ' - - test_expect_success "add those on node 2" ' - DIR_HASH=$(ipfsi 2 add -r -Q foobar) - ' - - check_dir_fetch 0 $DIR_HASH - check_dir_fetch 1 $DIR_HASH - check_dir_fetch 2 $DIR_HASH - check_dir_fetch 3 $DIR_HASH - check_dir_fetch 4 $DIR_HASH -} - - -run_basic_test() { - startup_cluster 5 - - run_single_file_test - - test_expect_success "shut down nodes" ' - iptb stop && iptb_wait_stop - ' -} - -run_advanced_test() { - startup_cluster 5 "$@" - - run_single_file_test - - run_random_dir_test - - test_expect_success "shut down nodes" ' - iptb stop && iptb_wait_stop || - test_fsh tail -n +1 .iptb/testbeds/default/*/daemon.std* - ' -} - -test_expect_success "set up /tcp testbed" ' - iptb testbed create -type localipfs -count 5 -force -init -' - -# test default configuration -run_advanced_test - -# test multiplex muxer -test_expect_success "disable yamux" ' - iptb run -- ipfs config --json Swarm.Transports.Multiplexers.Yamux false -' -run_advanced_test - -test_expect_success "set up /ws testbed" ' - iptb testbed create -type localipfs -count 5 -attr listentype,ws -force -init -' - -# test default configuration -run_advanced_test - -# test multiplex muxer -test_expect_success "disable yamux" ' - iptb run -- ipfs config --json Swarm.Transports.Multiplexers.Yamux false -' - -run_advanced_test - - -test_done diff --git a/test/sharness/t0131-multinode-client-routing.sh b/test/sharness/t0131-multinode-client-routing.sh index b62c9790b..8949a1bdf 100755 --- a/test/sharness/t0131-multinode-client-routing.sh +++ b/test/sharness/t0131-multinode-client-routing.sh @@ -43,7 +43,8 @@ run_single_file_test() { NNODES=10 test_expect_success "set up testbed" ' - iptb testbed create -type localipfs -count $NNODES -force -init + iptb testbed create -type localipfs -count $NNODES -force -init && + iptb run -- ipfs config --json "Routing.LoopbackAddressesOnLanDHT" true ' test_expect_success "start up nodes" ' diff --git a/test/sharness/t0139-swarm-rcmgr.sh b/test/sharness/t0139-swarm-rcmgr.sh deleted file mode 100755 index c36ddd3d8..000000000 --- a/test/sharness/t0139-swarm-rcmgr.sh +++ /dev/null @@ -1,230 +0,0 @@ -#!/usr/bin/env bash -# -test_description="Test ipfs swarm ResourceMgr config and commands" - -. lib/test-lib.sh - -test_init_ipfs - -test_expect_success 'Disable resource manager' ' - ipfs config --bool Swarm.ResourceMgr.Enabled false -' - -# test correct behavior when resource manager is disabled -test_launch_ipfs_daemon - -test_expect_success 'Swarm limit should fail since RM is disabled' ' - test_expect_code 1 ipfs swarm limit system 2> actual && - test_should_contain "missing ResourceMgr" actual -' - -test_expect_success 'Swarm stats should fail since RM is disabled' ' - test_expect_code 1 ipfs swarm stats all 2> actual && - test_should_contain "missing ResourceMgr" actual -' - -test_kill_ipfs_daemon - -test_expect_success 'Enable resource manager' ' - ipfs config --bool Swarm.ResourceMgr.Enabled true -' - -# swarm limit|stats should fail in offline mode - -test_expect_success 'disconnected: swarm limit requires running daemon' ' - test_expect_code 1 ipfs swarm limit system 2> actual && - test_should_contain "missing ResourceMgr" actual -' -test_expect_success 'disconnected: swarm stats requires running daemon' ' - test_expect_code 1 ipfs swarm stats all 2> actual && - test_should_contain "missing ResourceMgr" actual -' - -# swarm limit|stats should succeed in online mode by default -# because Resource Manager is opt-out -test_launch_ipfs_daemon - -# every scope has the same fields, so we only inspect System -test_expect_success 'ResourceMgr enabled: swarm limit' ' - ipfs swarm limit system --enc=json | tee json && - jq -e .Conns < json && - jq -e .ConnsInbound < json && - jq -e .ConnsOutbound < json && - jq -e .FD < json && - jq -e .Memory < json && - jq -e .Streams < json && - jq -e .StreamsInbound < json && - jq -e .StreamsOutbound < json -' -test_expect_success 'ResourceMgr enabled: swarm limit reset' ' - ipfs swarm limit system --reset --enc=json 2> reset && - ipfs swarm limit system --enc=json 2> actual && - test_cmp reset actual -' - -test_expect_success 'Swarm stats system with filter should fail' ' - test_expect_code 1 ipfs swarm stats system --min-used-limit-perc=99 2> actual && - test_should_contain "Error: \"min-used-limit-perc\" can only be used when scope is \"all\"" actual -' - -test_expect_success 'ResourceMgr enabled: swarm limit reset on map values' ' - ipfs swarm limit peer:12D3KooWL7i1T9VSPeF8AgQApbyM51GNKZsYPvNvL347aMDmvNzG --reset --enc=json 2> reset && - ipfs swarm limit peer:12D3KooWL7i1T9VSPeF8AgQApbyM51GNKZsYPvNvL347aMDmvNzG --enc=json 2> actual && - test_cmp reset actual -' - -test_expect_success 'ResourceMgr enabled: scope is required using reset flag' ' - test_expect_code 1 ipfs swarm limit --reset 2> actual && - test_should_contain "Error: argument \"scope\" is required" actual -' - -test_expect_success 'connected: swarm stats all working properly' ' - test_expect_code 0 ipfs swarm stats all -' - -# every scope has the same fields, so we only inspect System -test_expect_success 'ResourceMgr enabled: swarm stats' ' - ipfs swarm stats all --enc=json | tee json && - jq -e .System.Memory < json && - jq -e .System.FD < json && - jq -e .System.Conns < json && - jq -e .System.ConnsInbound < json && - jq -e .System.ConnsOutbound < json && - jq -e .System.Streams < json && - jq -e .System.StreamsInbound < json && - jq -e .System.StreamsOutbound < json && - jq -e .Transient.Memory < json -' - -# shut down the daemon, set a limit in the config, and verify that it's applied -test_kill_ipfs_daemon - -test_expect_success "Set system conns limit while daemon is not running" " - ipfs config --json Swarm.ResourceMgr.Limits.System.Conns 99999 -" - -test_expect_success "Set an invalid limit, which should result in a failure" " - test_expect_code 1 ipfs config --json Swarm.ResourceMgr.Limits.System.Conns 'asdf' 2> actual && - test_should_contain 'failed to unmarshal' actual -" - -test_launch_ipfs_daemon - -test_expect_success 'Ensure the new system conns limit is applied' ' - ipfs swarm limit system --enc=json | tee json && - jq -e ".Conns == 99999" < json -' - -test_expect_success 'Set system memory limit while the daemon is running' ' - ipfs swarm limit system | jq ".Memory = 99998" > system.json && - ipfs swarm limit system system.json -' - -test_expect_success 'The new system limits were written to the config' ' - jq -e ".Swarm.ResourceMgr.Limits.System.Memory == 99998" < "$IPFS_PATH/config" -' - -test_expect_success 'The new system limits are in the swarm limit output' ' - ipfs swarm limit system --enc=json | jq -e ".Memory == 99998" -' - -# now test all the other scopes -test_expect_success 'Set limit on transient scope' ' - ipfs swarm limit transient | jq ".Memory = 88888" > transient.json && - ipfs swarm limit transient transient.json && - jq -e ".Swarm.ResourceMgr.Limits.Transient.Memory == 88888" < "$IPFS_PATH/config" && - ipfs swarm limit transient --enc=json | tee limits && - jq -e ".Memory == 88888" < limits -' - -test_expect_success 'Set limit on service scope' ' - ipfs swarm limit svc:foo | jq ".Memory = 77777" > service-foo.json && - ipfs swarm limit svc:foo service-foo.json --enc=json && - jq -e ".Swarm.ResourceMgr.Limits.Service.foo.Memory == 77777" < "$IPFS_PATH/config" && - ipfs swarm limit svc:foo --enc=json | tee limits && - jq -e ".Memory == 77777" < limits -' - -test_expect_success 'Set limit on protocol scope' ' - ipfs swarm limit proto:foo | jq ".Memory = 66666" > proto-foo.json && - ipfs swarm limit proto:foo proto-foo.json --enc=json && - jq -e ".Swarm.ResourceMgr.Limits.Protocol.foo.Memory == 66666" < "$IPFS_PATH/config" && - ipfs swarm limit proto:foo --enc=json | tee limits && - jq -e ".Memory == 66666" < limits -' - -# any valid peer id -PEER_ID=QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN - -test_expect_success 'Set limit on peer scope' ' - ipfs swarm limit peer:$PEER_ID | jq ".Memory = 66666" > peer-$PEER_ID.json && - ipfs swarm limit peer:$PEER_ID peer-$PEER_ID.json --enc=json && - jq -e ".Swarm.ResourceMgr.Limits.Peer.${PEER_ID}.Memory == 66666" < "$IPFS_PATH/config" && - ipfs swarm limit peer:$PEER_ID --enc=json | tee limits && - jq -e ".Memory == 66666" < limits -' - -test_expect_success 'Get limit for peer scope with an invalid peer ID' ' - test_expect_code 1 ipfs swarm limit peer:foo 2> actual && - test_should_contain "invalid peer ID" actual -' - -test_expect_success 'Set limit for peer scope with an invalid peer ID' ' - echo "{\"Memory\": 99}" > invalid-peer-id.json && - test_expect_code 1 ipfs swarm limit peer:foo invalid-peer-id.json 2> actual && - test_should_contain "invalid peer ID" actual -' - -test_kill_ipfs_daemon - -## Test allowlist - -test_expect_success 'init iptb' ' - iptb testbed create -type localipfs -count 3 -init -' - -test_expect_success 'peer ids' ' - PEERID_0=$(iptb attr get 0 id) && - PEERID_1=$(iptb attr get 1 id) && - PEERID_2=$(iptb attr get 2 id) -' - -#enable resource manager -test_expect_success 'enable RCMGR' ' - ipfsi 0 config --bool Swarm.ResourceMgr.Enabled true && - ipfsi 0 config --json Swarm.ResourceMgr.Allowlist "[\"/ip4/0.0.0.0/ipcidr/0/p2p/$PEERID_2\"]" -' - -test_expect_success 'start nodes' ' - iptb start -wait [0-2] -' - -test_expect_success "change system limits on node 0" ' - ipfsi 0 swarm limit system | jq ". + {Conns: 0,ConnsInbound: 0, ConnsOutbound: 0}" > system.json && - ipfsi 0 swarm limit system system.json -' - -test_expect_success "node 0 fails to connect to 1" ' - test_expect_code 1 iptb connect 0 1 -' - -test_expect_success "node 0 connects to 2 because it's allowlisted" ' - iptb connect 0 2 -' - -test_expect_success "node 0 fails to ping 1" ' - test_expect_code 1 ipfsi 0 ping -n2 -- "$PEERID_1" 2> actual && - test_should_contain "Error: ping failed" actual -' - -test_expect_success "node 1 can ping 2" ' - ipfsi 0 ping -n2 -- "$PEERID_2" -' - -test_expect_success 'stop iptb' ' - iptb stop 0 && - iptb stop 1 && - iptb stop 2 -' - -test_done diff --git a/test/sharness/t0142-testfilter.sh b/test/sharness/t0142-testfilter.sh index 971aa6839..bdd7e4f76 100755 --- a/test/sharness/t0142-testfilter.sh +++ b/test/sharness/t0142-testfilter.sh @@ -13,7 +13,8 @@ AF="/ip4/127.0.0.0/ipcidr/24" NUM_NODES=3 test_expect_success "set up testbed" ' - iptb testbed create -type localipfs -count $NUM_NODES -force -init + iptb testbed create -type localipfs -count $NUM_NODES -force -init && + iptb run -- ipfs config --json "Routing.LoopbackAddressesOnLanDHT" true ' test_expect_success 'filter 127.0.0.0/24 on node 1' ' diff --git a/test/sharness/t0150-clisuggest.sh b/test/sharness/t0150-clisuggest.sh index a504b38dd..30ae6acd2 100755 --- a/test/sharness/t0150-clisuggest.sh +++ b/test/sharness/t0150-clisuggest.sh @@ -18,13 +18,13 @@ test_suggest() { ' test_expect_success "test command fails" ' - test_must_fail ipfs lis 2>actual + test_must_fail ipfs li 2>actual ' test_expect_success "test multiple commands are suggested" ' grep "Did you mean any of these?" actual && grep "ls" actual && - grep "id" actual || + grep "log" actual || test_fsh cat actual ' diff --git a/test/sharness/t0160-resolve.sh b/test/sharness/t0160-resolve.sh index ca11947b0..5ec3f99be 100755 --- a/test/sharness/t0160-resolve.sh +++ b/test/sharness/t0160-resolve.sh @@ -30,17 +30,11 @@ test_resolve_setup_name() { local key="$1" local ref="$2" + # we pass here --ttl=0s to ensure that it does not get cached by namesys. + # the alternative would be to wait between tests to ensure that the namesys + # cache gets purged in time, but that adds runtime time for the tests. test_expect_success "resolve: prepare $key" ' - ipfs name publish --key="$key" --allow-offline "$ref" - ' -} - -test_resolve_setup_name_fail() { - local key="$1" - local ref="$2" - - test_expect_failure "resolve: prepare $key" ' - ipfs name publish --key="$key" --allow-offline "$ref" + ipfs name publish --key="$key" --ttl=0s --allow-offline "$ref" ' } @@ -129,23 +123,7 @@ test_resolve_cmd_b32() { ' } - -#todo remove this once the online resolve is fixed -test_resolve_fail() { - src=$1 - dst=$2 - - test_expect_failure "resolve succeeds: $src" ' - ipfs resolve "$src" >actual - ' - - test_expect_failure "resolved correctly: $src -> $dst" ' - printf "$dst" >expected && - test_cmp expected actual - ' -} - -test_resolve_cmd_fail() { +test_resolve_cmd_success() { test_resolve "/ipfs/$a_hash" "/ipfs/$a_hash" test_resolve "/ipfs/$a_hash/b" "/ipfs/$b_hash" test_resolve "/ipfs/$a_hash/b/c" "/ipfs/$c_hash" @@ -155,23 +133,16 @@ test_resolve_cmd_fail() { test_resolve "/ipld/$dag_hash/i/j" "/ipld/$dag_hash/i/j" test_resolve "/ipld/$dag_hash/i" "/ipld/$dag_hash/i" - # At the moment, publishing _fails_ because we fail to put to the DHT. - # However, resolving succeeds because we resolve the record we put to our own - # node. - # - # We should find a nice way to truly support offline publishing. But this - # behavior isn't terrible. - - test_resolve_setup_name_fail "self" "/ipfs/$a_hash" + test_resolve_setup_name "self" "/ipfs/$a_hash" test_resolve "/ipns/$self_hash" "/ipfs/$a_hash" test_resolve "/ipns/$self_hash/b" "/ipfs/$b_hash" test_resolve "/ipns/$self_hash/b/c" "/ipfs/$c_hash" - test_resolve_setup_name_fail "self" "/ipfs/$b_hash" + test_resolve_setup_name "self" "/ipfs/$b_hash" test_resolve "/ipns/$self_hash" "/ipfs/$b_hash" test_resolve "/ipns/$self_hash/c" "/ipfs/$c_hash" - test_resolve_setup_name_fail "self" "/ipfs/$c_hash" + test_resolve_setup_name "self" "/ipfs/$c_hash" test_resolve "/ipns/$self_hash" "/ipfs/$c_hash" } @@ -181,7 +152,7 @@ test_resolve_cmd_b32 # should work online test_launch_ipfs_daemon -test_resolve_cmd_fail +test_resolve_cmd_success test_kill_ipfs_daemon test_done diff --git a/test/sharness/t0170-legacy-dht.sh b/test/sharness/t0170-legacy-dht.sh deleted file mode 100755 index a4dffb34c..000000000 --- a/test/sharness/t0170-legacy-dht.sh +++ /dev/null @@ -1,121 +0,0 @@ -#!/usr/bin/env bash - -# Legacy / deprecated, see: t0170-routing-dht.sh -test_description="Test dht command" - -. lib/test-lib.sh - -test_dht() { - NUM_NODES=5 - - test_expect_success 'init iptb' ' - rm -rf .iptb/ && - iptb testbed create -type localipfs -count $NUM_NODES -init - ' - - test_expect_success 'DHT-only routing' ' - iptb run -- ipfs config Routing.Type dht - ' - - startup_cluster $NUM_NODES $@ - - test_expect_success 'peer ids' ' - PEERID_0=$(iptb attr get 0 id) && - PEERID_2=$(iptb attr get 2 id) - ' - - # ipfs dht findpeer - test_expect_success 'findpeer' ' - ipfsi 1 dht findpeer $PEERID_0 | sort >actual && - ipfsi 0 id -f "" | cut -d / -f 1-5 | sort >expected && - test_cmp actual expected - ' - - # ipfs dht get - test_expect_success 'get with good keys works' ' - HASH="$(echo "hello world" | ipfsi 2 add -q)" && - ipfsi 2 name publish "/ipfs/$HASH" && - ipfsi 1 dht get "/ipns/$PEERID_2" >get_result - ' - - test_expect_success 'get with good keys contains the right value' ' - cat get_result | grep -aq "/ipfs/$HASH" - ' - - test_expect_success 'put round trips (#3124)' ' - ipfsi 0 dht put "/ipns/$PEERID_2" get_result | sort >putted && - [ -s putted ] || - test_fsh cat putted - ' - - test_expect_success 'put with bad keys fails (issue #5113)' ' - ipfsi 0 dht put "foo" <<putted - ipfsi 0 dht put "/pk/foo" <<>putted - ipfsi 0 dht put "/ipns/foo" <<>putted - [ ! -s putted ] || - test_fsh cat putted - ' - - test_expect_success 'put with bad keys returns error (issue #4611)' ' - test_must_fail ipfsi 0 dht put "foo" << afile && - HASH=$(ipfsi 3 add -q afile) - ' - - # ipfs dht findprovs - test_expect_success 'findprovs' ' - ipfsi 4 dht findprovs $HASH > provs && - iptb attr get 3 id > expected && - test_cmp provs expected - ' - - - # ipfs dht query - # - # We test all nodes. 4 nodes should see the same peer ID, one node (the - # closest) should see a different one. - - for i in $(test_seq 0 4); do - test_expect_success "query from $i" ' - ipfsi "$i" dht query "$HASH" | head -1 >closest-$i - ' - done - - test_expect_success "collecting results" ' - cat closest-* | sort | uniq -c | sed -e "s/ *\([0-9]\+\) .*/\1/g" | sort -g > actual && - echo 1 > expected && - echo 4 >> expected - ' - - test_expect_success "checking results" ' - test_cmp actual expected - ' - - test_expect_success 'stop iptb' ' - iptb stop - ' - - test_expect_success "dht commands fail when offline" ' - test_must_fail ipfsi 0 dht findprovs "$HASH" 2>err_findprovs && - test_must_fail ipfsi 0 dht findpeer "$HASH" 2>err_findpeer && - test_must_fail ipfsi 0 dht put "/ipns/$PEERID_2" "get_result" 2>err_put && - test_should_contain "this command must be run in online mode" err_findprovs && - test_should_contain "this command must be run in online mode" err_findpeer && - test_should_contain "this command must be run in online mode" err_put - ' -} - -test_dht -test_dht --enable-pubsub-experiment --enable-namesys-pubsub - -test_done diff --git a/test/sharness/t0170-routing-dht.sh b/test/sharness/t0170-routing-dht.sh deleted file mode 100755 index 2ef0a9cd1..000000000 --- a/test/sharness/t0170-routing-dht.sh +++ /dev/null @@ -1,122 +0,0 @@ -#!/usr/bin/env bash - -# This file does the same tests as t0170-dht.sh but uses 'routing' commands instead -# (only exception is query, which lives only under dht) -test_description="Test routing command for DHT queries" - -. lib/test-lib.sh - -test_dht() { - NUM_NODES=5 - - test_expect_success 'init iptb' ' - rm -rf .iptb/ && - iptb testbed create -type localipfs -count $NUM_NODES -init - ' - - test_expect_success 'DHT-only routing' ' - iptb run -- ipfs config Routing.Type dht - ' - - startup_cluster $NUM_NODES $@ - - test_expect_success 'peer ids' ' - PEERID_0=$(iptb attr get 0 id) && - PEERID_2=$(iptb attr get 2 id) - ' - - # ipfs routing findpeer - test_expect_success 'findpeer' ' - ipfsi 1 routing findpeer $PEERID_0 | sort >actual && - ipfsi 0 id -f "" | cut -d / -f 1-5 | sort >expected && - test_cmp actual expected - ' - - # ipfs routing get - test_expect_success 'get with good keys works' ' - HASH="$(echo "hello world" | ipfsi 2 add -q)" && - ipfsi 2 name publish "/ipfs/$HASH" && - ipfsi 1 routing get "/ipns/$PEERID_2" >get_result - ' - - test_expect_success 'get with good keys contains the right value' ' - cat get_result | grep -aq "/ipfs/$HASH" - ' - - test_expect_success 'put round trips (#3124)' ' - ipfsi 0 routing put "/ipns/$PEERID_2" get_result | sort >putted && - [ -s putted ] || - test_fsh cat putted - ' - - test_expect_success 'put with bad keys fails (issue #5113)' ' - ipfsi 0 routing put "foo" <<putted - ipfsi 0 routing put "/pk/foo" <<>putted - ipfsi 0 routing put "/ipns/foo" <<>putted - [ ! -s putted ] || - test_fsh cat putted - ' - - test_expect_success 'put with bad keys returns error (issue #4611)' ' - test_must_fail ipfsi 0 routing put "foo" << afile && - HASH=$(ipfsi 3 add -q afile) - ' - - # ipfs routing findprovs - test_expect_success 'findprovs' ' - ipfsi 4 routing findprovs $HASH > provs && - iptb attr get 3 id > expected && - test_cmp provs expected - ' - - - # ipfs dht query - # - # We test all nodes. 4 nodes should see the same peer ID, one node (the - # closest) should see a different one. - - for i in $(test_seq 0 4); do - test_expect_success "dht query from $i" ' - ipfsi "$i" dht query "$HASH" | head -1 >closest-$i - ' - done - - test_expect_success "collecting results" ' - cat closest-* | sort | uniq -c | sed -e "s/ *\([0-9]\+\) .*/\1/g" | sort -g > actual && - echo 1 > expected && - echo 4 >> expected - ' - - test_expect_success "checking results" ' - test_cmp actual expected - ' - - test_expect_success 'stop iptb' ' - iptb stop - ' - - test_expect_success "dht commands fail when offline" ' - test_must_fail ipfsi 0 routing findprovs "$HASH" 2>err_findprovs && - test_must_fail ipfsi 0 routing findpeer "$HASH" 2>err_findpeer && - test_must_fail ipfsi 0 routing put "/ipns/$PEERID_2" "get_result" 2>err_put && - test_should_contain "this command must be run in online mode" err_findprovs && - test_should_contain "this command must be run in online mode" err_findpeer && - test_should_contain "this command must be run in online mode" err_put - ' -} - -test_dht -test_dht --enable-pubsub-experiment --enable-namesys-pubsub - -test_done diff --git a/test/sharness/t0171-peering.sh b/test/sharness/t0171-peering.sh deleted file mode 100755 index 207b27980..000000000 --- a/test/sharness/t0171-peering.sh +++ /dev/null @@ -1,127 +0,0 @@ -#!/usr/bin/env bash - -test_description="Test peering service" - -. lib/test-lib.sh - -NUM_NODES=3 - -test_expect_success 'init iptb' ' - rm -rf .iptb/ && - iptb testbed create -type localipfs -count $NUM_NODES -init -' - -test_expect_success 'disabling routing' ' - iptb run -- ipfs config Routing.Type none -' - -for i in $(seq 0 2); do - ADDR="$(printf '["/ip4/127.0.0.1/tcp/%s"]' "$(( 3000 + ( RANDOM % 1000 ) ))")" - test_expect_success "configuring node $i to listen on $ADDR" ' - ipfsi "$i" config --json Addresses.Swarm "$ADDR" - ' -done - -peer_id() { - ipfsi "$1" config Identity.PeerID -} - -peer_addrs() { - ipfsi "$1" config Addresses.Swarm -} - -peer() { - PEER1="$1" && - PEER2="$2" && - PEER_LIST="$(ipfsi "$PEER1" config Peering.Peers || true)" && - { [[ "$PEER_LIST" == "null" ]] || PEER_LIST_INNER="${PEER_LIST:1:-1}"; } && - ADDR_INFO="$(printf '[%s{"ID": "%s", "Addrs": %s}]' \ - "${PEER_LIST_INNER:+${PEER_LIST_INNER},}" \ - "$(peer_id "$PEER2")" \ - "$(peer_addrs "$PEER2")")" && - ipfsi "$PEER1" config --json Peering.Peers "${ADDR_INFO}" -} - -# Peer: -# - 0 <-> 1 -# - 1 -> 2 -test_expect_success 'configure peering' ' - peer 0 1 && - peer 1 0 && - peer 1 2 -' - -list_peers() { - ipfsi "$1" swarm peers | sed 's|.*/p2p/\([^/]*\)$|\1|' | sort -u -} - -check_peers() { - sleep 20 # give it some time to settle. - test_expect_success 'verifying peering for peer 0' ' - list_peers 0 > peers_0_actual && - peer_id 1 > peers_0_expected && - test_cmp peers_0_expected peers_0_actual - ' - - test_expect_success 'verifying peering for peer 1' ' - list_peers 1 > peers_1_actual && - { peer_id 0 && peer_id 2 ; } | sort -u > peers_1_expected && - test_cmp peers_1_expected peers_1_actual - ' - - test_expect_success 'verifying peering for peer 2' ' - list_peers 2 > peers_2_actual && - peer_id 1 > peers_2_expected && - test_cmp peers_2_expected peers_2_actual - ' -} - -test_expect_success 'startup cluster' ' - iptb start -wait && - iptb run -- ipfs log level peering debug -' - -check_peers - -disconnect() { - ipfsi "$1" swarm disconnect "/p2p/$(peer_id "$2")" -} - -# Bidirectional peering shouldn't cause problems (e.g., simultaneous connect -# issues). -test_expect_success 'disconnecting 0->1' ' - disconnect 0 1 -' - -check_peers - -# 1 should reconnect to 2 when 2 disconnects from 1. -test_expect_success 'disconnecting 2->1' ' - disconnect 2 1 -' - -check_peers - -# 2 isn't peering. This test ensures that 1 will re-peer with 2 when it comes -# back online. -test_expect_success 'stopping 2' ' - iptb stop 2 -' - -# Wait to disconnect -sleep 30 - -test_expect_success 'starting 2' ' - iptb start 2 -' - -# Wait for backoff -sleep 30 - -check_peers - -test_expect_success "stop testbed" ' - iptb stop -' - -test_done diff --git a/test/sharness/t0175-provider.sh b/test/sharness/t0175-provider.sh deleted file mode 100755 index cca110fe1..000000000 --- a/test/sharness/t0175-provider.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash - -test_description="Test reprovider" - -. lib/test-lib.sh - -NUM_NODES=2 - -test_expect_success 'init iptb' ' - iptb testbed create -type localipfs -force -count $NUM_NODES -init -' - -test_expect_success 'peer ids' ' - PEERID_0=$(iptb attr get 0 id) && - PEERID_1=$(iptb attr get 1 id) -' - -test_expect_success 'use strategic providing' ' - iptb run -- ipfs config --json Experimental.StrategicProviding false -' - -startup_cluster ${NUM_NODES} - -test_expect_success 'add test object' ' - HASH_0=$(date +"%FT%T.%N%z" | ipfsi 0 add -q) -' - -findprovs_expect '$HASH_0' '$PEERID_0' - -test_expect_success 'stop node 1' ' - iptb stop -' - -test_done diff --git a/test/sharness/t0175-reprovider.sh b/test/sharness/t0175-reprovider.sh deleted file mode 100755 index 09535ecc4..000000000 --- a/test/sharness/t0175-reprovider.sh +++ /dev/null @@ -1,140 +0,0 @@ -#!/usr/bin/env bash - -test_description="Test reprovider" - -. lib/test-lib.sh - -NUM_NODES=6 - -init_strategy() { - test_expect_success 'init iptb' ' - iptb testbed create -type localipfs -force -count $NUM_NODES -init - ' - - test_expect_success 'peer ids' ' - PEERID_0=$(iptb attr get 0 id) && - PEERID_1=$(iptb attr get 1 id) - ' - - test_expect_success 'use pinning strategy for reprovider' ' - ipfsi 0 config Reprovider.Strategy '$1' - ' - - startup_cluster ${NUM_NODES} -} - -reprovide() { - test_expect_success 'reprovide' ' - # TODO: this hangs, though only after reprovision was done - ipfsi 0 bitswap reprovide - ' -} - -# Test 'all' strategy -init_strategy 'all' - -test_expect_success 'add test object' ' - HASH_0=$(date +"%FT%T.%N%z" | ipfsi 0 add -q --local) -' - -findprovs_empty '$HASH_0' -reprovide -findprovs_expect '$HASH_0' '$PEERID_0' - -test_expect_success 'Stop iptb' ' - iptb stop -' - -# Test 'pinned' strategy -init_strategy 'pinned' - -test_expect_success 'prepare test files' ' - date +"%FT%T.%N%z" > f1 && - date +"%FT%T.%N%z" > f2 -' - -test_expect_success 'add test objects' ' - HASH_FOO=$(ipfsi 0 add -q --offline --pin=false f1) && - HASH_BAR=$(ipfsi 0 add -q --offline --pin=false f2) && - HASH_BAR_DIR=$(ipfsi 0 add -q --offline -w f2) -' - -findprovs_empty '$HASH_FOO' -findprovs_empty '$HASH_BAR' -findprovs_empty '$HASH_BAR_DIR' - -reprovide - -findprovs_empty '$HASH_FOO' -findprovs_expect '$HASH_BAR' '$PEERID_0' -findprovs_expect '$HASH_BAR_DIR' '$PEERID_0' - -test_expect_success 'Stop iptb' ' - iptb stop -' - -# Test 'roots' strategy -init_strategy 'roots' - -test_expect_success 'prepare test files' ' - date +"%FT%T.%N%z" > f1 && - date +"%FT%T.%N%z" > f2 && - date +"%FT%T.%N%z" > f3 -' - -test_expect_success 'add test objects' ' - HASH_FOO=$(ipfsi 0 add -q --offline --pin=false f1) && - HASH_BAR=$(ipfsi 0 add -q --offline --pin=false f2) && - HASH_BAZ=$(ipfsi 0 add -q --offline f3) && - HASH_BAR_DIR=$(ipfsi 0 add -Q --offline -w f2) -' - -findprovs_empty '$HASH_FOO' -findprovs_empty '$HASH_BAR' -findprovs_empty '$HASH_BAR_DIR' - -reprovide - -findprovs_empty '$HASH_FOO' -findprovs_empty '$HASH_BAR' -findprovs_expect '$HASH_BAZ' '$PEERID_0' -findprovs_expect '$HASH_BAR_DIR' '$PEERID_0' - -test_expect_success 'Stop iptb' ' - iptb stop -' - -# Test reprovider working with ticking disabled -test_expect_success 'init iptb' ' - iptb testbed create -type localipfs -force -count $NUM_NODES -init -' - -test_expect_success 'peer ids' ' - PEERID_0=$(iptb attr get 0 id) && - PEERID_1=$(iptb attr get 1 id) -' - -test_expect_success 'Disable reprovider ticking' ' - ipfsi 0 config Reprovider.Interval 0 -' - -startup_cluster ${NUM_NODES} - -test_expect_success 'add test object' ' - HASH_0=$(date +"%FT%T.%N%z" | ipfsi 0 add -q --offline) -' - -findprovs_empty '$HASH_0' -reprovide -findprovs_expect '$HASH_0' '$PEERID_0' - -test_expect_success 'resolve object $HASH_0' ' - HASH_WITH_PREFIX=$(ipfsi 1 resolve $HASH_0) -' -findprovs_expect '$HASH_WITH_PREFIX' '$PEERID_0' - -test_expect_success 'Stop iptb' ' - iptb stop -' - -test_done diff --git a/test/sharness/t0175-strategic-provider.sh b/test/sharness/t0175-strategic-provider.sh deleted file mode 100755 index fafd6e538..000000000 --- a/test/sharness/t0175-strategic-provider.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash - -test_description="Test reprovider" - -. lib/test-lib.sh - -NUM_NODES=2 - -test_expect_success 'init iptb' ' - iptb testbed create -type localipfs -force -count $NUM_NODES -init -' - -test_expect_success 'peer ids' ' - PEERID_0=$(iptb attr get 0 id) && - PEERID_1=$(iptb attr get 1 id) -' - -test_expect_success 'use strategic providing' ' - iptb run -- ipfs config --json Experimental.StrategicProviding true -' - -startup_cluster ${NUM_NODES} - -test_expect_success 'add test object' ' - HASH_0=$(date +"%FT%T.%N%z" | ipfsi 0 add -q) -' - -findprovs_empty '$HASH_0' - -test_expect_success 'stop node 1' ' - iptb stop -' - -test_done diff --git a/test/sharness/t0181-private-network.sh b/test/sharness/t0181-private-network.sh index 86c6151d3..46dc45cdf 100755 --- a/test/sharness/t0181-private-network.sh +++ b/test/sharness/t0181-private-network.sh @@ -35,6 +35,7 @@ LIBP2P_FORCE_PNET=1 test_launch_ipfs_daemon test_expect_success "set up iptb testbed" ' iptb testbed create -type localipfs -count 5 -force -init && + iptb run -- ipfs config --json "Routing.LoopbackAddressesOnLanDHT" true && iptb run -- ipfs config --json Addresses.Swarm '"'"'["/ip4/127.0.0.1/tcp/0"]'"'"' ' diff --git a/test/sharness/t0182-circuit-relay.sh b/test/sharness/t0182-circuit-relay.sh index d6e439ae3..c79edfc8e 100755 --- a/test/sharness/t0182-circuit-relay.sh +++ b/test/sharness/t0182-circuit-relay.sh @@ -7,7 +7,8 @@ test_description="Test circuit relay" # start iptb + wait for peering NUM_NODES=3 test_expect_success 'init iptb' ' - iptb testbed create -type localipfs -count $NUM_NODES -init + iptb testbed create -type localipfs -count $NUM_NODES -init && + iptb run -- ipfs config --json "Routing.LoopbackAddressesOnLanDHT" true ' # Network toplogy: A <-> Relay <-> B diff --git a/test/sharness/t0184-http-proxy-over-p2p.sh b/test/sharness/t0184-http-proxy-over-p2p.sh index 9c5308277..98e2f3ab2 100755 --- a/test/sharness/t0184-http-proxy-over-p2p.sh +++ b/test/sharness/t0184-http-proxy-over-p2p.sh @@ -142,6 +142,7 @@ function curl_send_multipart_form_request() { test_expect_success 'configure nodes' ' iptb testbed create -type localipfs -count 2 -force -init && + iptb run -- ipfs config --json "Routing.LoopbackAddressesOnLanDHT" true && ipfsi 0 config --json Experimental.Libp2pStreamMounting true && ipfsi 1 config --json Experimental.Libp2pStreamMounting true && ipfsi 0 config --json Experimental.P2pHttpProxy true && diff --git a/test/sharness/t0190-quic-ping.sh b/test/sharness/t0190-quic-ping.sh index 0c2970cb9..28335870d 100755 --- a/test/sharness/t0190-quic-ping.sh +++ b/test/sharness/t0190-quic-ping.sh @@ -11,8 +11,8 @@ test_expect_success 'init iptb' ' iptb testbed create -type localipfs -count 2 -init ' -addr1='"[\"/ip4/127.0.0.1/udp/0/quic\"]"' -addr2='"[\"/ip4/127.0.0.1/udp/0/quic\"]"' +addr1='"[\"/ip4/127.0.0.1/udp/0/quic-v1\"]"' +addr2='"[\"/ip4/127.0.0.1/udp/0/quic-v1\"]"' test_expect_success "add QUIC swarm addresses" ' ipfsi 0 config --json Addresses.Swarm '$addr1' && ipfsi 1 config --json Addresses.Swarm '$addr2' diff --git a/test/sharness/t0200-unixfs-ls.sh b/test/sharness/t0200-unixfs-ls.sh deleted file mode 100755 index 7499d92ef..000000000 --- a/test/sharness/t0200-unixfs-ls.sh +++ /dev/null @@ -1,140 +0,0 @@ -#!/usr/bin/env bash -# -# Copyright (c) 2014 Christian Couder -# MIT Licensed; see the LICENSE file in this repository. -# - -test_description="Test file ls command" - -. lib/test-lib.sh - -test_init_ipfs - -test_ls_cmd() { - - test_expect_success "'ipfs add -r testData' succeeds" ' - mkdir -p testData testData/d1 testData/d2 && - echo "test" >testData/f1 && - echo "data" >testData/f2 && - echo "hello" >testData/d1/a && - random 128 42 >testData/d1/128 && - echo "world" >testData/d2/a && - random 1024 42 >testData/d2/1024 && - ipfs add -r testData >actual_add - ' - - test_expect_success "'ipfs add' output looks good" ' - cat <<-\EOF >expected_add && -added QmQNd6ubRXaNG6Prov8o6vk3bn6eWsj9FxLGrAVDUAGkGe testData/d1/128 -added QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN testData/d1/a -added QmbQBUSRL9raZtNXfpTDeaxQapibJEG6qEY8WqAN22aUzd testData/d2/1024 -added QmaRGe7bVmVaLmxbrMiVNXqW4pRNNp3xq7hFtyRKA3mtJL testData/d2/a -added QmeomffUNfmQy76CQGy9NdmqEnnHU9soCexBnGU3ezPHVH testData/f1 -added QmNtocSs7MoDkJMc1RkyisCSKvLadujPsfJfSdJ3e1eA1M testData/f2 -added QmSix55yz8CzWXf5ZVM9vgEvijnEeeXiTSarVtsqiiCJss testData/d1 -added QmR3jhV4XpxxPjPT3Y8vNnWvWNvakdcT3H6vqpRBsX1MLy testData/d2 -added QmfNy183bXiRVyrhyWtq3TwHn79yHEkiAGFr18P7YNzESj testData -EOF - test_cmp expected_add actual_add - ' - - test_expect_success "'ipfs file ls ' succeeds" ' - ipfs file ls QmR3jhV4XpxxPjPT3Y8vNnWvWNvakdcT3H6vqpRBsX1MLy >actual_ls_one_directory - ' - - test_expect_success "'ipfs file ls ' output looks good" ' - cat <<-\EOF >expected_ls_one_directory && -1024 -a -EOF - test_cmp expected_ls_one_directory actual_ls_one_directory - ' - - test_expect_success "'ipfs file ls ' succeeds" ' - ipfs file ls QmfNy183bXiRVyrhyWtq3TwHn79yHEkiAGFr18P7YNzESj QmR3jhV4XpxxPjPT3Y8vNnWvWNvakdcT3H6vqpRBsX1MLy QmSix55yz8CzWXf5ZVM9vgEvijnEeeXiTSarVtsqiiCJss >actual_ls_three_directories - ' - - test_expect_success "'ipfs file ls ' output looks good" ' - cat <<-\EOF >expected_ls_three_directories && -QmR3jhV4XpxxPjPT3Y8vNnWvWNvakdcT3H6vqpRBsX1MLy: -1024 -a - -QmSix55yz8CzWXf5ZVM9vgEvijnEeeXiTSarVtsqiiCJss: -128 -a - -QmfNy183bXiRVyrhyWtq3TwHn79yHEkiAGFr18P7YNzESj: -d1 -d2 -f1 -f2 -EOF - test_cmp expected_ls_three_directories actual_ls_three_directories - ' - - test_expect_success "'ipfs file ls ' succeeds" ' - ipfs file ls /ipfs/QmR3jhV4XpxxPjPT3Y8vNnWvWNvakdcT3H6vqpRBsX1MLy/1024 QmQNd6ubRXaNG6Prov8o6vk3bn6eWsj9FxLGrAVDUAGkGe >actual_ls_file - ' - - test_expect_success "'ipfs file ls ' output looks good" ' - cat <<-\EOF >expected_ls_file && -/ipfs/QmR3jhV4XpxxPjPT3Y8vNnWvWNvakdcT3H6vqpRBsX1MLy/1024 -QmQNd6ubRXaNG6Prov8o6vk3bn6eWsj9FxLGrAVDUAGkGe -EOF - test_cmp expected_ls_file actual_ls_file - ' - - test_expect_success "'ipfs file ls ' succeeds" ' - ipfs file ls /ipfs/QmfNy183bXiRVyrhyWtq3TwHn79yHEkiAGFr18P7YNzESj/d1 /ipfs/QmSix55yz8CzWXf5ZVM9vgEvijnEeeXiTSarVtsqiiCJss /ipfs/QmR3jhV4XpxxPjPT3Y8vNnWvWNvakdcT3H6vqpRBsX1MLy/1024 /ipfs/QmbQBUSRL9raZtNXfpTDeaxQapibJEG6qEY8WqAN22aUzd >actual_ls_duplicates_file - ' - - test_expect_success "'ipfs file ls ' output looks good" ' - cat <<-\EOF >expected_ls_duplicates_file && -/ipfs/QmR3jhV4XpxxPjPT3Y8vNnWvWNvakdcT3H6vqpRBsX1MLy/1024 -/ipfs/QmbQBUSRL9raZtNXfpTDeaxQapibJEG6qEY8WqAN22aUzd - -/ipfs/QmSix55yz8CzWXf5ZVM9vgEvijnEeeXiTSarVtsqiiCJss: -/ipfs/QmfNy183bXiRVyrhyWtq3TwHn79yHEkiAGFr18P7YNzESj/d1: -128 -a -EOF - test_cmp expected_ls_duplicates_file actual_ls_duplicates_file - ' - - test_expect_success "'ipfs --encoding=json file ls ' succeeds" ' - ipfs --encoding=json file ls /ipfs/QmR3jhV4XpxxPjPT3Y8vNnWvWNvakdcT3H6vqpRBsX1MLy/1024 >actual_json_ls_file - ' - - test_expect_success "'ipfs --encoding=json file ls ' output looks good" ' - cat <<-\EOF >expected_json_ls_file_trailing_newline && -{"Arguments":{"/ipfs/QmR3jhV4XpxxPjPT3Y8vNnWvWNvakdcT3H6vqpRBsX1MLy/1024":"QmbQBUSRL9raZtNXfpTDeaxQapibJEG6qEY8WqAN22aUzd"},"Objects":{"QmbQBUSRL9raZtNXfpTDeaxQapibJEG6qEY8WqAN22aUzd":{"Hash":"QmbQBUSRL9raZtNXfpTDeaxQapibJEG6qEY8WqAN22aUzd","Size":1024,"Type":"File","Links":null}}} -EOF - printf "%s\n" "$(cat expected_json_ls_file_trailing_newline)" >expected_json_ls_file && - test_cmp expected_json_ls_file actual_json_ls_file - ' - - test_expect_success "'ipfs --encoding=json file ls ' succeeds" ' - ipfs --encoding=json file ls /ipfs/QmfNy183bXiRVyrhyWtq3TwHn79yHEkiAGFr18P7YNzESj/d1 /ipfs/QmSix55yz8CzWXf5ZVM9vgEvijnEeeXiTSarVtsqiiCJss /ipfs/QmR3jhV4XpxxPjPT3Y8vNnWvWNvakdcT3H6vqpRBsX1MLy/1024 /ipfs/QmbQBUSRL9raZtNXfpTDeaxQapibJEG6qEY8WqAN22aUzd >actual_json_ls_duplicates_file - ' - - test_expect_success "'ipfs --encoding=json file ls ' output looks good" ' - cat <<-\EOF >expected_json_ls_duplicates_file_trailing_newline && -{"Arguments":{"/ipfs/QmR3jhV4XpxxPjPT3Y8vNnWvWNvakdcT3H6vqpRBsX1MLy/1024":"QmbQBUSRL9raZtNXfpTDeaxQapibJEG6qEY8WqAN22aUzd","/ipfs/QmSix55yz8CzWXf5ZVM9vgEvijnEeeXiTSarVtsqiiCJss":"QmSix55yz8CzWXf5ZVM9vgEvijnEeeXiTSarVtsqiiCJss","/ipfs/QmbQBUSRL9raZtNXfpTDeaxQapibJEG6qEY8WqAN22aUzd":"QmbQBUSRL9raZtNXfpTDeaxQapibJEG6qEY8WqAN22aUzd","/ipfs/QmfNy183bXiRVyrhyWtq3TwHn79yHEkiAGFr18P7YNzESj/d1":"QmSix55yz8CzWXf5ZVM9vgEvijnEeeXiTSarVtsqiiCJss"},"Objects":{"QmSix55yz8CzWXf5ZVM9vgEvijnEeeXiTSarVtsqiiCJss":{"Hash":"QmSix55yz8CzWXf5ZVM9vgEvijnEeeXiTSarVtsqiiCJss","Size":0,"Type":"Directory","Links":[{"Name":"128","Hash":"QmQNd6ubRXaNG6Prov8o6vk3bn6eWsj9FxLGrAVDUAGkGe","Size":128,"Type":"File"},{"Name":"a","Hash":"QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN","Size":6,"Type":"File"}]},"QmbQBUSRL9raZtNXfpTDeaxQapibJEG6qEY8WqAN22aUzd":{"Hash":"QmbQBUSRL9raZtNXfpTDeaxQapibJEG6qEY8WqAN22aUzd","Size":1024,"Type":"File","Links":null}}} -EOF - printf "%s\n" "$(cat expected_json_ls_duplicates_file_trailing_newline)" >expected_json_ls_duplicates_file && - test_cmp expected_json_ls_duplicates_file actual_json_ls_duplicates_file - ' - -} - - -# should work offline -test_ls_cmd - -# should work online -test_launch_ipfs_daemon -test_ls_cmd -test_kill_ipfs_daemon - -test_done diff --git a/test/sharness/t0210-tar.sh b/test/sharness/t0210-tar.sh deleted file mode 100755 index d2b910550..000000000 --- a/test/sharness/t0210-tar.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env bash -# -# Copyright (c) 2015 Jeromy Johnson -# MIT Licensed; see the LICENSE file in this repository. -# - -test_description="Test tar commands" - -. lib/test-lib.sh - -test_init_ipfs - -test_expect_success "create some random files" ' - mkdir foo && - random 10000 > foo/a && - random 12345 > foo/b && - mkdir foo/bar && - random 5432 > foo/bar/baz && - ln -s ../a foo/bar/link && - echo "exit" > foo/script && - chmod +x foo/script -' - -test_expect_success "tar those random files up" ' - tar cf files.tar foo/ -' - -test_expect_success "'ipfs tar add' succeeds" ' - TAR_HASH=$(ipfs tar add files.tar) -' - -test_expect_success "'ipfs tar cat' succeeds" ' - mkdir output && - ipfs tar cat $TAR_HASH > output/out.tar -' - -test_expect_success "can extract tar" ' - tar xf output/out.tar -C output/ -' - -test_expect_success "files look right" ' - diff foo/a output/foo/a && - diff foo/b output/foo/b && - diff foo/bar/baz output/foo/bar/baz && - [ -L output/foo/bar/link ] && - [ -x foo/script ] -' - -test_expect_success "'ipfs tar add --cid-base=base32' succeeds" ' - ipfs tar add --cid-base=base32 files.tar > actual -' - -test_expect_success "'ipfs tar add --cid-base=base32' has correct hash" ' - ipfs cid base32 $TAR_HASH > expected && - test_cmp expected actual -' - -test_done diff --git a/test/sharness/t0221-graphsync.sh b/test/sharness/t0221-graphsync.sh deleted file mode 100755 index 771ff4f8c..000000000 --- a/test/sharness/t0221-graphsync.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env bash - -test_description="Test fetching from graphsync." - -# imports -. lib/test-lib.sh - -test_init_ipfs - -test_expect_success 'configuring ipfs' ' - ipfs config --json Experimental.GraphsyncEnabled true -' - -test_expect_success 'add content' ' - HASH=$(random 1000000 | ipfs add -q) -' - -test_launch_ipfs_daemon - -test_expect_success 'get addrs' ' - ADDR="$(ipfs id --format="" | head -1)" -' - -test_expect_success 'fetch' ' - graphsync-get "$ADDR" "$HASH" > result -' - -test_expect_success 'check' ' - ipfs add -q < result > hash_actual && - echo "$HASH" > hash_expected && - test_cmp hash_expected hash_actual -' - -test_kill_ipfs_daemon -test_done diff --git a/test/sharness/t0235-cli-request.sh b/test/sharness/t0235-cli-request.sh index 3d50a2e62..02ef514de 100755 --- a/test/sharness/t0235-cli-request.sh +++ b/test/sharness/t0235-cli-request.sh @@ -28,10 +28,25 @@ test_expect_success "start nc" ' ' test_expect_success "can make http request against nc server" ' - ipfs cat /ipfs/Qmabcdef --api /ip4/127.0.0.1/tcp/5005 & + ipfs cat /ipfs/Qmabcdef --api /dns4/localhost/tcp/5005 & IPFSPID=$! - # handle request + # handle request for /api/v0/version + while read line; do + if [[ "$line" == "$(echo -e "\r")" ]]; then + break + fi + echo "$line" + done <&7 >nc_out && + + echo -e "HTTP/1.1 200 OK\r" >&6 && + echo -e "Content-Type: application/json\r" >&6 && + echo -e "Content-Length: 21\r" >&6 && + echo -e "\r" >&6 && + echo -e "{\"Version\":\"0.23.0\"}\r" >&6 && + echo -e "\r" >&6 && + + # handle request for /api/v0/cat while read line; do if [[ "$line" == "$(echo -e "\r")" ]]; then break @@ -65,4 +80,8 @@ test_expect_success "api flag does not appear in request" ' test_expect_code 1 grep "api=/ip4" nc_out ' +test_expect_success "host has dns name not ip address" ' + grep "Host: localhost:5005" nc_out +' + test_done diff --git a/test/sharness/t0236-cli-api-dns-resolve.sh b/test/sharness/t0236-cli-api-dns-resolve.sh index eddbc93da..b42131834 100755 --- a/test/sharness/t0236-cli-api-dns-resolve.sh +++ b/test/sharness/t0236-cli-api-dns-resolve.sh @@ -30,7 +30,22 @@ test_expect_success "can make http request against dns resolved nc server" ' ipfs cat /ipfs/Qmabcdef --api /dns4/localhost/tcp/5006 & IPFSPID=$! - # handle request + # handle request for /api/v0/version + while read line; do + if [[ "$line" == "$(echo -e "\r")" ]]; then + break + fi + echo "$line" + done <&7 >nc_out && + + echo -e "HTTP/1.1 200 OK\r" >&6 && + echo -e "Content-Type: application/json\r" >&6 && + echo -e "Content-Length: 21\r" >&6 && + echo -e "\r" >&6 && + echo -e "{\"Version\":\"0.23.0\"}\r" >&6 && + echo -e "\r" >&6 && + + # handle request for /api/v0/cat while read line; do if [[ "$line" == "$(echo -e "\r")" ]]; then break diff --git a/test/sharness/t0250-files-api.sh b/test/sharness/t0250-files-api.sh index 382758a05..9c01a5bcf 100755 --- a/test/sharness/t0250-files-api.sh +++ b/test/sharness/t0250-files-api.sh @@ -230,6 +230,8 @@ test_files_api() { echo "Size: 4" >> file1stat_expect && echo "ChildBlocks: 0" >> file1stat_expect && echo "Type: file" >> file1stat_expect && + echo "Mode: not set (not set)" >> file1stat_expect && + echo "Mtime: not set" >> file1stat_expect && test_cmp file1stat_expect file1stat_actual ' @@ -243,6 +245,8 @@ test_files_api() { echo "Size: 4" >> file1stat_expect && echo "ChildBlocks: 0" >> file1stat_expect && echo "Type: file" >> file1stat_expect && + echo "Mode: not set (not set)" >> file1stat_expect && + echo "Mtime: not set" >> file1stat_expect && test_cmp file1stat_expect file1stat_actual ' diff --git a/test/sharness/t0252-files-gc.sh b/test/sharness/t0252-files-gc.sh index 7267985d4..f2eb25b4f 100755 --- a/test/sharness/t0252-files-gc.sh +++ b/test/sharness/t0252-files-gc.sh @@ -38,9 +38,9 @@ test_expect_success "gc okay after adding incomplete node -- prep" ' ' test_expect_success "gc okay after adding incomplete node" ' - ipfs object stat $ADIR_HASH && + ipfs dag get $ADIR_HASH && ipfs repo gc && - ipfs object stat $ADIR_HASH + ipfs dag get $ADIR_HASH ' test_expect_success "add directory with direct pin" ' diff --git a/test/sharness/t0272-urlstore.sh b/test/sharness/t0272-urlstore.sh index 40d025454..8fa7ff3b8 100755 --- a/test/sharness/t0272-urlstore.sh +++ b/test/sharness/t0272-urlstore.sh @@ -191,7 +191,6 @@ EOF ' } -test_urlstore urlstore add test_urlstore add -q --nocopy --cid-version=1 test_done diff --git a/test/sharness/t0276-cidv0v1.sh b/test/sharness/t0276-cidv0v1.sh index 2058a9d54..c810f4544 100755 --- a/test/sharness/t0276-cidv0v1.sh +++ b/test/sharness/t0276-cidv0v1.sh @@ -95,7 +95,8 @@ test_expect_success "check that we can access the file when converted to CIDv1" # test_expect_success "set up iptb testbed" ' - iptb testbed create -type localipfs -count 2 -init + iptb testbed create -type localipfs -count 2 -init && + iptb run -- ipfs config --json "Routing.LoopbackAddressesOnLanDHT" true ' test_expect_success "start nodes" ' diff --git a/test/sharness/t0310-tracing.sh b/test/sharness/t0310-tracing.sh deleted file mode 100755 index 96d07ae8d..000000000 --- a/test/sharness/t0310-tracing.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env bash -# -# Copyright (c) 2022 Protocol Labs -# MIT/Apache-2.0 Licensed; see the LICENSE file in this repository. -# - -test_description="Test tracing" - -. lib/test-lib.sh - -test_init_ipfs - -export OTEL_TRACES_EXPORTER=otlp -export OTEL_EXPORTER_OTLP_PROTOCOL=grpc -export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317 - -cat < collector-config.yaml -receivers: - otlp: - protocols: - grpc: - -processors: - batch: - -exporters: - file: - path: /traces/traces.json - -service: - pipelines: - traces: - receivers: [otlp] - processors: [batch] - exporters: [file] -EOF - -# touch traces.json and give it 777 perms, in case docker runs as a different user -rm -rf traces.json && touch traces.json && chmod 777 traces.json - -test_expect_success "run opentelemetry collector" ' - docker run --rm -d -v "$PWD/collector-config.yaml":/config.yaml -v "$PWD":/traces --net=host --name=ipfs-test-otel-collector otel/opentelemetry-collector-contrib:0.52.0 --config /config.yaml -' - -test_launch_ipfs_daemon - -test_expect_success "check that a swarm span eventually appears in exported traces" ' - until cat traces.json | grep CoreAPI.SwarmAPI >/dev/null; do sleep 0.1; done -' - -test_expect_success "kill docker container" ' - docker kill ipfs-test-otel-collector -' - -test_kill_ipfs_daemon - -test_done diff --git a/test/sharness/t0400-api-no-gateway.sh b/test/sharness/t0400-api-no-gateway.sh index 137f99a95..d0daeece3 100755 --- a/test/sharness/t0400-api-no-gateway.sh +++ b/test/sharness/t0400-api-no-gateway.sh @@ -10,6 +10,13 @@ test_description="Test API security" test_init_ipfs +# Import test case +# See the static fixtures in ./t0400-api-no-gateway/ +test_expect_success "Add the test directory" ' + ipfs dag import ../t0400-api-no-gateway/fixtures.car +' +HASH=QmNYERzV2LfD2kkfahtfv44ocHzEFK1sLBaE7zdcYT2GAZ # a file containing the string "testing" + # by default, we don't let you load arbitrary ipfs objects through the api, # because this would open up the api to scripting vulnerabilities. # only the webui objects are allowed. @@ -17,14 +24,12 @@ test_init_ipfs test_launch_ipfs_daemon test_expect_success "Gateway on API unavailable" ' - HASH=$(echo "testing" | ipfs add -q) test_curl_resp_http_code "http://127.0.0.1:$API_PORT/ipfs/$HASH" "HTTP/1.1 404 Not Found" ' test_kill_ipfs_daemon test_launch_ipfs_daemon --unrestricted-api test_expect_success "Gateway on --unrestricted-api API available" ' - HASH=$(echo "testing" | ipfs add -q) test_curl_resp_http_code "http://127.0.0.1:$API_PORT/ipfs/$HASH" "HTTP/1.1 200 OK" ' test_kill_ipfs_daemon diff --git a/test/sharness/t0400-api-no-gateway/README.md b/test/sharness/t0400-api-no-gateway/README.md new file mode 100644 index 000000000..32222d41b --- /dev/null +++ b/test/sharness/t0400-api-no-gateway/README.md @@ -0,0 +1,16 @@ +# Dataset description/sources + +- fixtures.car + - raw CARv1 + +generated with: + +```sh +# using ipfs version 0.18.1 +HASH=$(echo "testing" | ipfs add -q) +ipfs dag export $HASH > fixtures.car + +echo HASH=${HASH} # a file containing the string "testing" + +# HASH=QmNYERzV2LfD2kkfahtfv44ocHzEFK1sLBaE7zdcYT2GAZ # a file containing the string "testing" +``` diff --git a/test/sharness/t0400-api-no-gateway/fixtures.car b/test/sharness/t0400-api-no-gateway/fixtures.car new file mode 100644 index 000000000..1e090db6d Binary files /dev/null and b/test/sharness/t0400-api-no-gateway/fixtures.car differ diff --git a/test/sharness/t0600-issues-and-regressions-online.sh b/test/sharness/t0600-issues-and-regressions-online.sh index 3468f23d6..809121640 100755 --- a/test/sharness/t0600-issues-and-regressions-online.sh +++ b/test/sharness/t0600-issues-and-regressions-online.sh @@ -4,7 +4,7 @@ test_description="Tests for various fixed issues and regressions." . lib/test-lib.sh -test_init_ipfs +test_init_ipfs --empty-repo=false test_launch_ipfs_daemon @@ -29,7 +29,7 @@ test_expect_success "no panic traces on daemon" ' test_expect_success "metrics work" ' curl -X POST "$API_ADDR/debug/metrics/prometheus" > pro_data && - grep "ipfs_bs_cache_arc_hits_total" < pro_data || + grep "ipfs_bs_cache_boxo_blockstore_cache_total" < pro_data || test_fsh cat pro_data ' diff --git a/test/sharness/t0700-remotepin.sh b/test/sharness/t0700-remotepin.sh deleted file mode 100755 index 2566c06d8..000000000 --- a/test/sharness/t0700-remotepin.sh +++ /dev/null @@ -1,332 +0,0 @@ -#!/usr/bin/env bash - -test_description="Test ipfs remote pinning operations" - -. lib/test-lib.sh - -if [ -z ${TEST_DOCKER_HOST+x} ]; then - # TODO: set up instead of skipping? - skip_all='Skipping pinning service integration tests: missing TEST_DOCKER_HOST, remote pinning service not available' - test_done -fi - -# daemon running in online mode to ensure Pin.origins/PinStatus.delegates work -test_init_ipfs -test_launch_ipfs_daemon - -# create user on pinning service -TEST_PIN_SVC="http://${TEST_DOCKER_HOST}:5000/api/v1" -TEST_PIN_SVC_KEY=$(curl -s -X POST "$TEST_PIN_SVC/users" -d email="go-ipfs-sharness@ipfs.example.com" | jq --raw-output .access_token) - -# pin remote service add|ls|rm - -# confirm empty service list response has proper json struct -# https://github.com/ipfs/go-ipfs/pull/7829 -test_expect_success "test 'ipfs pin remote service ls' JSON on empty list" ' - ipfs pin remote service ls --stat --enc=json | tee empty_ls_out && - echo "{\"RemoteServices\":[]}" > exp_ls_out && - test_cmp exp_ls_out empty_ls_out -' - -# add valid and invalid services -test_expect_success "creating test user on remote pinning service" ' - echo CI host IP address ${TEST_PIN_SVC} && - ipfs pin remote service add test_pin_svc ${TEST_PIN_SVC} ${TEST_PIN_SVC_KEY} && - ipfs pin remote service add test_invalid_key_svc ${TEST_PIN_SVC} fake_api_key && - ipfs pin remote service add test_invalid_url_path_svc ${TEST_PIN_SVC}/invalid-path fake_api_key && - ipfs pin remote service add test_invalid_url_dns_svc https://invalid-service.example.com fake_api_key && - ipfs pin remote service add test_pin_mfs_svc ${TEST_PIN_SVC} ${TEST_PIN_SVC_KEY} -' - -# add a service with a invalid endpoint -test_expect_success "adding remote service with invalid endpoint" ' - test_expect_code 1 ipfs pin remote service add test_endpoint_no_protocol invalid-service.example.com fake_api_key && - test_expect_code 1 ipfs pin remote service add test_endpoint_bad_protocol xyz://invalid-service.example.com fake_api_key -' - -test_expect_success "test 'ipfs pin remote service ls'" ' - ipfs pin remote service ls | tee ls_out && - grep -q test_pin_svc ls_out && - grep -q test_invalid_key_svc ls_out && - grep -q test_invalid_url_path_svc ls_out && - grep -q test_invalid_url_dns_svc ls_out -' - -test_expect_success "test enabling mfs pinning" ' - ipfs config --json Pinning.RemoteServices.test_pin_mfs_svc.Policies.MFS.RepinInterval \"10s\" && - ipfs config --json Pinning.RemoteServices.test_pin_mfs_svc.Policies.MFS.PinName \"mfs_test_pin\" && - ipfs config --json Pinning.RemoteServices.test_pin_mfs_svc.Policies.MFS.Enable true && - ipfs config --json Pinning.RemoteServices.test_pin_mfs_svc.Policies.MFS.RepinInterval > repin_interval && - ipfs config --json Pinning.RemoteServices.test_pin_mfs_svc.Policies.MFS.PinName > pin_name && - ipfs config --json Pinning.RemoteServices.test_pin_mfs_svc.Policies.MFS.Enable > enable && - echo 10s > expected_repin_interval && - echo mfs_test_pin > expected_pin_name && - echo true > expected_enable && - test_cmp repin_interval expected_repin_interval && - test_cmp pin_name expected_pin_name && - test_cmp enable expected_enable -' - -# expect PIN to be created -test_expect_success "verify MFS root is being pinned" ' - ipfs files cp /ipfs/bafkqaaa /mfs-pinning-test-$(date +%s.%N) && - ipfs files flush && - sleep 31 && - ipfs files stat / --enc=json | jq -r .Hash > mfs_cid && - ipfs pin remote ls --service=test_pin_mfs_svc --name=mfs_test_pin --status=queued,pinning,pinned,failed --enc=json | tee ls_out | jq -r .Cid > pin_cid && - cat mfs_cid ls_out && - test_cmp mfs_cid pin_cid -' - -# expect existing PIN to be replaced -test_expect_success "verify MFS root is being repinned on CID change" ' - ipfs files cp /ipfs/bafkqaaa /mfs-pinning-repin-test-$(date +%s.%N) && - ipfs files flush && - sleep 31 && - ipfs files stat / --enc=json | jq -r .Hash > mfs_cid && - ipfs pin remote ls --service=test_pin_mfs_svc --name=mfs_test_pin --status=queued,pinning,pinned,failed --enc=json | tee ls_out | jq -r .Cid > pin_cid && - cat mfs_cid ls_out && - test_cmp mfs_cid pin_cid -' - -# SECURITY of access tokens in API.Key fields: -# Pinning.RemoteServices includes API.Key, and we give it the same treatment -# as Identity.PrivKey to prevent exposing it on the network - -test_expect_success "'ipfs config Pinning' fails" ' - test_expect_code 1 ipfs config Pinning 2>&1 > config_out -' -test_expect_success "output does not include API.Key" ' - test_expect_code 1 grep -q Key config_out -' - -test_expect_success "'ipfs config Pinning.RemoteServices.test_pin_svc.API.Key' fails" ' - test_expect_code 1 ipfs config Pinning.RemoteServices.test_pin_svc.API.Key 2> config_out -' - -test_expect_success "output includes meaningful error" ' - echo "Error: cannot show or change pinning services credentials" > config_exp && - test_cmp config_exp config_out -' - -test_expect_success "'ipfs config Pinning.RemoteServices.test_pin_svc' fails" ' - test_expect_code 1 ipfs config Pinning.RemoteServices.test_pin_svc 2> config_out -' -test_expect_success "output includes meaningful error" ' - test_cmp config_exp config_out -' - -test_expect_success "'ipfs config show' does not include Pinning.RemoteServices[*].API.Key" ' - ipfs config show | tee show_config | jq -r .Pinning.RemoteServices > remote_services && - test_expect_code 1 grep \"Key\" remote_services && - test_expect_code 1 grep fake_api_key show_config && - test_expect_code 1 grep "$TEST_PIN_SVC_KEY" show_config -' - -test_expect_success "'ipfs config replace' injects Pinning.RemoteServices[*].API.Key back" ' - test_expect_code 1 grep fake_api_key show_config && - test_expect_code 1 grep "$TEST_PIN_SVC_KEY" show_config && - ipfs config replace show_config && - test_expect_code 0 grep fake_api_key "$IPFS_PATH/config" && - test_expect_code 0 grep "$TEST_PIN_SVC_KEY" "$IPFS_PATH/config" -' - -# note: we remove Identity.PrivKey to ensure error is triggered by Pinning.RemoteServices -test_expect_success "'ipfs config replace' with Pinning.RemoteServices[*].API.Key errors out" ' - jq -M "del(.Identity.PrivKey)" "$IPFS_PATH/config" | jq ".Pinning += { RemoteServices: {\"myservice\": {\"API\": {\"Endpoint\": \"https://example.com/psa\", \"Key\": \"mysecret\"}}}}" > new_config && - test_expect_code 1 ipfs config replace - < new_config 2> replace_out -' -test_expect_success "output includes meaningful error" " - echo \"Error: cannot add or remove remote pinning services with 'config replace'\" > replace_expected && - test_cmp replace_out replace_expected -" - -# /SECURITY - -test_expect_success "pin remote service ls --stat' returns numbers for a valid service" ' - ipfs pin remote service ls --stat | grep -E "^test_pin_svc.+[0-9]+/[0-9]+/[0-9]+/[0-9]+$" -' - -test_expect_success "pin remote service ls --enc=json --stat' returns valid status" " - ipfs pin remote service ls --stat --enc=json | jq --raw-output '.RemoteServices[] | select(.Service == \"test_pin_svc\") | .Stat.Status' | tee stat_out && - echo valid > stat_expected && - test_cmp stat_out stat_expected -" - -test_expect_success "pin remote service ls --stat' returns invalid status for invalid service" ' - ipfs pin remote service ls --stat | grep -E "^test_invalid_url_path_svc.+invalid$" -' - -test_expect_success "pin remote service ls --enc=json --stat' returns invalid status" " - ipfs pin remote service ls --stat --enc=json | jq --raw-output '.RemoteServices[] | select(.Service == \"test_invalid_url_path_svc\") | .Stat.Status' | tee stat_out && - echo invalid > stat_expected && - test_cmp stat_out stat_expected -" - -test_expect_success "pin remote service ls --enc=json' (without --stat) returns no Stat object" " - ipfs pin remote service ls --enc=json | jq --raw-output '.RemoteServices[] | select(.Service == \"test_invalid_url_path_svc\") | .Stat' | tee stat_out && - echo null > stat_expected && - test_cmp stat_out stat_expected -" - -test_expect_success "check connection to the test pinning service" ' - ipfs pin remote ls --service=test_pin_svc --enc=json -' - -test_expect_success "unauthorized pinning service calls fail" ' - test_expect_code 1 ipfs pin remote ls --service=test_invalid_key_svc -' - -test_expect_success "misconfigured pinning service calls fail (wrong path)" ' - test_expect_code 1 ipfs pin remote ls --service=test_invalid_url_path_svc -' - -test_expect_success "misconfigured pinning service calls fail (dns error)" ' - test_expect_code 1 ipfs pin remote ls --service=test_invalid_url_dns_svc -' - -# pin remote service rm - -test_expect_success "remove pinning service" ' - ipfs pin remote service rm test_invalid_key_svc && - ipfs pin remote service rm test_invalid_url_path_svc && - ipfs pin remote service rm test_invalid_url_dns_svc -' - -test_expect_success "verify pinning service removal works" ' - ipfs pin remote service ls | tee ls_out && - test_expect_code 1 grep test_invalid_key_svc ls_out && - test_expect_code 1 grep test_invalid_url_path_svc ls_out && - test_expect_code 1 grep test_invalid_url_dns_svc ls_out -' - -# pin remote add - -# we leverage the fact that inlined CID can be pinned instantly on the remote service -# (https://github.com/ipfs-shipyard/rb-pinning-service-api/issues/8) -# below test ensures that assumption is correct (before we proceed to actual tests) -test_expect_success "verify that default add (implicit --background=false) works with data inlined in CID" ' - ipfs pin remote add --service=test_pin_svc --name=inlined_null bafkqaaa && - ipfs pin remote ls --service=test_pin_svc --enc=json --name=inlined_null --status=pinned | jq --raw-output .Status | tee ls_out && - grep -q "pinned" ls_out -' - -test_remote_pins() { - BASE=$1 - if [ -n "$BASE" ]; then - BASE_ARGS="--cid-base=$BASE" - fi - - # note: HAS_MISSING is not inlined nor imported to IPFS on purpose, to reliably test 'queued' state - test_expect_success "create some hashes using base $BASE" ' - export HASH_A=$(echo -n "A @ $(date +%s.%N)" | ipfs add $BASE_ARGS -q --inline --inline-limit 1000 --pin=false) && - export HASH_B=$(echo -n "B @ $(date +%s.%N)" | ipfs add $BASE_ARGS -q --inline --inline-limit 1000 --pin=false) && - export HASH_C=$(echo -n "C @ $(date +%s.%N)" | ipfs add $BASE_ARGS -q --inline --inline-limit 1000 --pin=false) && - export HASH_MISSING=$(echo "MISSING FROM IPFS @ $(date +%s.%N)" | ipfs add $BASE_ARGS -q --only-hash) && - echo "A: $HASH_A" && - echo "B: $HASH_B" && - echo "C: $HASH_C" && - echo "M: $HASH_MISSING" - ' - - test_expect_success "'ipfs pin remote add --background=true'" ' - ipfs pin remote add --background=true --service=test_pin_svc --enc=json $BASE_ARGS --name=name_a $HASH_A - ' - - test_expect_success "verify background add worked (instantly pinned variant)" ' - ipfs pin remote ls --service=test_pin_svc --enc=json --name=name_a | tee ls_out && - test_expect_code 0 grep -q name_a ls_out && - test_expect_code 0 grep -q $HASH_A ls_out - ' - - test_expect_success "'ipfs pin remote add --background=true' with CID that is not available" ' - test_expect_code 0 ipfs pin remote add --background=true --service=test_pin_svc --enc=json $BASE_ARGS --name=name_m $HASH_MISSING - ' - - test_expect_success "verify background add worked (queued variant)" ' - ipfs pin remote ls --service=test_pin_svc --enc=json --name=name_m --status=queued,pinning | tee ls_out && - test_expect_code 0 grep -q name_m ls_out && - test_expect_code 0 grep -q $HASH_MISSING ls_out - ' - - test_expect_success "'ipfs pin remote add --background=false'" ' - test_expect_code 0 ipfs pin remote add --background=false --service=test_pin_svc --enc=json $BASE_ARGS --name=name_b $HASH_B - ' - - test_expect_success "verify foreground add worked" ' - ipfs pin remote ls --service=test_pin_svc --enc=json $ID_B | tee ls_out && - test_expect_code 0 grep -q name_b ls_out && - test_expect_code 0 grep -q pinned ls_out && - test_expect_code 0 grep -q $HASH_B ls_out - ' - - test_expect_success "'ipfs pin remote ls' for existing pins by multiple statuses" ' - ipfs pin remote ls --service=test_pin_svc --enc=json --status=queued,pinning,pinned,failed | tee ls_out && - test_expect_code 0 grep -q $HASH_A ls_out && - test_expect_code 0 grep -q $HASH_B ls_out && - test_expect_code 0 grep -q $HASH_MISSING ls_out - ' - - test_expect_success "'ipfs pin remote ls' for existing pins by CID" ' - ipfs pin remote ls --service=test_pin_svc --enc=json --cid=$HASH_B | tee ls_out && - test_expect_code 0 grep -q $HASH_B ls_out - ' - - test_expect_success "'ipfs pin remote ls' for existing pins by name" ' - ipfs pin remote ls --service=test_pin_svc --enc=json --name=name_a | tee ls_out && - test_expect_code 0 grep -q $HASH_A ls_out - ' - - test_expect_success "'ipfs pin remote ls' for ongoing pins by status" ' - ipfs pin remote ls --service=test_pin_svc --status=queued,pinning | tee ls_out && - test_expect_code 0 grep -q $HASH_MISSING ls_out - ' - - # --force is required only when more than a single match is found, - # so we add second pin with the same name (but different CID) to simulate that scenario - test_expect_success "'ipfs pin remote rm --name' fails without --force when matching multiple pins" ' - test_expect_code 0 ipfs pin remote add --service=test_pin_svc --enc=json $BASE_ARGS --name=name_b $HASH_C && - test_expect_code 1 ipfs pin remote rm --service=test_pin_svc --name=name_b 2> rm_out && - echo "Error: multiple remote pins are matching this query, add --force to confirm the bulk removal" > rm_expected && - test_cmp rm_out rm_expected - ' - - test_expect_success "'ipfs pin remote rm --name' without --force did not remove matching pins" ' - ipfs pin remote ls --service=test_pin_svc --enc=json --name=name_b | jq --raw-output .Cid | tee ls_out && - test_expect_code 0 grep -q $HASH_B ls_out && - test_expect_code 0 grep -q $HASH_C ls_out - ' - - test_expect_success "'ipfs pin remote rm --name' with --force removes all matching pins" ' - test_expect_code 0 ipfs pin remote rm --service=test_pin_svc --name=name_b --force && - ipfs pin remote ls --service=test_pin_svc --enc=json --name=name_b | jq --raw-output .Cid | tee ls_out && - test_expect_code 1 grep -q $HASH_B ls_out && - test_expect_code 1 grep -q $HASH_C ls_out - ' - - test_expect_success "'ipfs pin remote rm --force' removes all pinned items" ' - ipfs pin remote ls --service=test_pin_svc --enc=json --status=queued,pinning,pinned,failed | jq --raw-output .Cid | tee ls_out && - test_expect_code 0 grep -q $HASH_A ls_out && - test_expect_code 0 grep -q $HASH_MISSING ls_out && - ipfs pin remote rm --service=test_pin_svc --status=queued,pinning,pinned,failed --force && - ipfs pin remote ls --service=test_pin_svc --enc=json --status=queued,pinning,pinned,failed | jq --raw-output .Cid | tee ls_out && - test_expect_code 1 grep -q $HASH_A ls_out && - test_expect_code 1 grep -q $HASH_MISSING ls_out - ' - -} - -test_remote_pins "" - -test_kill_ipfs_daemon - -WARNINGMESSAGE="WARNING: the local node is offline and remote pinning may fail if there is no other provider for this CID" - -test_expect_success "'ipfs pin remote add' shows the warning message while offline" ' - test_expect_code 0 ipfs pin remote add --service=test_pin_svc --background $BASE_ARGS --name=name_a $HASH_A > actual && - test_expect_code 0 grep -q "$WARNINGMESSAGE" actual -' - -test_done - -# vim: ts=2 sw=2 sts=2 et: diff --git a/test/sharness/t0701-delegated-routing-reframe.sh b/test/sharness/t0701-delegated-routing-reframe.sh deleted file mode 100755 index 5070b4fff..000000000 --- a/test/sharness/t0701-delegated-routing-reframe.sh +++ /dev/null @@ -1,171 +0,0 @@ -#!/usr/bin/env bash - -test_description="Test delegated routing via reframe endpoint" - -. lib/test-lib.sh - -if ! test_have_prereq SOCAT; then - skip_all="skipping '$test_description': socat is not available" - test_done -fi - -# simple reframe server mock -# local endpoint responds with deterministic application/vnd.ipfs.rpc+dag-json; version=1 -REFRAME_PORT=5098 -function start_reframe_mock_endpoint() { - REMOTE_SERVER_LOG="reframe-server.log" - rm -f $REMOTE_SERVER_LOG - - touch response - socat tcp-listen:$REFRAME_PORT,fork,bind=127.0.0.1,reuseaddr 'SYSTEM:cat response'!!CREATE:$REMOTE_SERVER_LOG & - REMOTE_SERVER_PID=$! - - socat /dev/null tcp:127.0.0.1:$REFRAME_PORT,retry=10 - return $? -} -function serve_reframe_response() { - local body=$1 - local status_code=${2:-"200 OK"} - local length=$((1 + ${#body})) - echo -e "HTTP/1.1 $status_code\nContent-Type: application/vnd.ipfs.rpc+dag-json; version=1\nContent-Length: $length\n\n$body" > response -} -function stop_reframe_mock_endpoint() { - exec 7<&- - kill $REMOTE_SERVER_PID > /dev/null 2>&1 - wait $REMOTE_SERVER_PID || true -} - -# daemon running in online mode to ensure Pin.origins/PinStatus.delegates work -test_init_ipfs - -# based on static, synthetic reframe messages: -# t0701-delegated-routing-reframe/FindProvidersRequest -# t0701-delegated-routing-reframe/FindProvidersResponse -FINDPROV_CID="bafybeigvgzoolc3drupxhlevdp2ugqcrbcsqfmcek2zxiw5wctk3xjpjwy" -EXPECTED_PROV="QmQzqxhK82kAmKvARFZSkUVS6fo9sySaiogAnx5EnZ6ZmC" - -test_expect_success "default Routing config has no Routers defined" ' - echo null > expected && - ipfs config show | jq .Routing.Routers > actual && - test_cmp expected actual -' - -# turn off all implicit routers -ipfs config Routing.Type none || exit 1 -test_launch_ipfs_daemon -test_expect_success "disabling default router (dht) works" ' - ipfs config Routing.Type > actual && - echo none > expected && - test_cmp expected actual -' -test_expect_success "no routers means findprovs returns no results" ' - ipfs routing findprovs "$FINDPROV_CID" > actual && - echo -n > expected && - test_cmp expected actual -' - -test_kill_ipfs_daemon - -ipfs config Routing.Type --json '"custom"' || exit 1 -ipfs config Routing.Methods --json '{ - "find-peers": { - "RouterName": "TestDelegatedRouter" - }, - "find-providers": { - "RouterName": "TestDelegatedRouter" - }, - "get-ipns": { - "RouterName": "TestDelegatedRouter" - }, - "provide": { - "RouterName": "TestDelegatedRouter" - } - }' || exit 1 - -test_expect_success "missing method params makes daemon fails" ' - echo "Error: constructing the node (see log for full detail): method name \"put-ipns\" is missing from Routing.Methods config param" > expected_error && - GOLOG_LOG_LEVEL=fatal ipfs daemon 2> actual_error || exit 0 && - test_cmp expected_error actual_error -' - -ipfs config Routing.Methods --json '{ - "find-peers": { - "RouterName": "TestDelegatedRouter" - }, - "find-providers": { - "RouterName": "TestDelegatedRouter" - }, - "get-ipns": { - "RouterName": "TestDelegatedRouter" - }, - "provide": { - "RouterName": "TestDelegatedRouter" - }, - "put-ipns": { - "RouterName": "TestDelegatedRouter" - }, - "NOT_SUPPORTED": { - "RouterName": "TestDelegatedRouter" - } - }' || exit 1 - -test_expect_success "having wrong methods makes daemon fails" ' - echo "Error: constructing the node (see log for full detail): method name \"NOT_SUPPORTED\" is not a supported method on Routing.Methods config param" > expected_error && - GOLOG_LOG_LEVEL=fatal ipfs daemon 2> actual_error || exit 0 && - test_cmp expected_error actual_error -' - -# set Routing config to only use delegated routing via mocked reframe endpoint - -ipfs config Routing.Type --json '"custom"' || exit 1 -ipfs config Routing.Routers.TestDelegatedRouter --json '{ - "Type": "reframe", - "Parameters": { - "Endpoint": "http://127.0.0.1:5098/reframe" - } -}' || exit 1 -ipfs config Routing.Methods --json '{ - "find-peers": { - "RouterName": "TestDelegatedRouter" - }, - "find-providers": { - "RouterName": "TestDelegatedRouter" - }, - "get-ipns": { - "RouterName": "TestDelegatedRouter" - }, - "provide": { - "RouterName": "TestDelegatedRouter" - }, - "put-ipns": { - "RouterName": "TestDelegatedRouter" - } - }' || exit 1 - -test_expect_success "adding reframe endpoint to Routing.Routers config works" ' - echo "http://127.0.0.1:5098/reframe" > expected && - ipfs config Routing.Routers.TestDelegatedRouter.Parameters.Endpoint > actual && - test_cmp expected actual -' - -test_launch_ipfs_daemon - -test_expect_success "start_reframe_mock_endpoint" ' - start_reframe_mock_endpoint -' - -test_expect_success "'ipfs routing findprovs' returns result from delegated reframe router" ' - serve_reframe_response "$(<../t0701-delegated-routing-reframe/FindProvidersResponse)" && - echo "$EXPECTED_PROV" > expected && - ipfs routing findprovs "$FINDPROV_CID" > actual && - test_cmp expected actual -' - -test_expect_success "stop_reframe_mock_endpoint" ' - stop_reframe_mock_endpoint -' - - -test_kill_ipfs_daemon -test_done -# vim: ts=2 sw=2 sts=2 et: diff --git a/testplans/bitswap/_compositions/large-k8s.toml b/testplans/bitswap/_compositions/large-k8s.toml deleted file mode 100644 index fea91d000..000000000 --- a/testplans/bitswap/_compositions/large-k8s.toml +++ /dev/null @@ -1,32 +0,0 @@ -[metadata] - -[global] - plan = "bitswap" - case = "speed-test" - total_instances = 20 - builder = "docker:go" - runner = "cluster:k8s" - -[global.build_config] - push_registry=true - go_proxy_mode="remote" - go_proxy_url="http://localhost:8081" - registry_type="aws" - -[global.run.test_params] -size = "1MB" -count = "1000" - -[[groups]] - id = "providers" - instances = { count = 1 } - [groups.resources] - memory = "4096Mi" - cpu = "1000m" - -[[groups]] - id = "requestors" - instances = { count = 19 } - [groups.resources] - memory = "4096Mi" - cpu = "1000m" diff --git a/testplans/bitswap/_compositions/medium-k8s.toml b/testplans/bitswap/_compositions/medium-k8s.toml deleted file mode 100644 index 816d91fe4..000000000 --- a/testplans/bitswap/_compositions/medium-k8s.toml +++ /dev/null @@ -1,33 +0,0 @@ -[metadata] - name = "bitswap-medium-speed-test" - -[global] - plan = "bitswap" - case = "speed-test" - total_instances = 5 - builder = "docker:go" - runner = "cluster:k8s" - -[global.build_config] - push_registry=true - go_proxy_mode="remote" - go_proxy_url="http://localhost:8081" - registry_type="aws" - -[global.run.test_params] -size = "1MB" -count = "1000" - -[[groups]] - id = "providers" - instances = { count = 1 } - [groups.resources] - memory = "4096Mi" - cpu = "1000m" - -[[groups]] - id = "requestors" - instances = { count = 4 } - [groups.resources] - memory = "4096Mi" - cpu = "1000m" diff --git a/testplans/bitswap/_compositions/small-docker.toml b/testplans/bitswap/_compositions/small-docker.toml deleted file mode 100644 index b0e9f96ae..000000000 --- a/testplans/bitswap/_compositions/small-docker.toml +++ /dev/null @@ -1,30 +0,0 @@ -[metadata] - name = "bitswap-local-speed-test" - -[global] - plan = "bitswap" - case = "speed-test" - total_instances = 2 - builder = "docker:go" - runner = "local:docker" - -[global.build_config] - push_registry=false - -[global.run.test_params] -size = "1MB" -count = "1000" - -[[groups]] - id = "providers" - instances = { count = 1 } - [groups.resources] - memory = "4096Mi" - cpu = "1000m" - -[[groups]] - id = "requestors" - instances = { count = 1 } - [groups.resources] - memory = "4096Mi" - cpu = "1000m" diff --git a/testplans/bitswap/_compositions/small-k8s.toml b/testplans/bitswap/_compositions/small-k8s.toml deleted file mode 100644 index 3f454412d..000000000 --- a/testplans/bitswap/_compositions/small-k8s.toml +++ /dev/null @@ -1,33 +0,0 @@ -[metadata] - name = "bitswap-small-speed-test" - -[global] - plan = "bitswap" - case = "speed-test" - total_instances = 2 - builder = "docker:go" - runner = "cluster:k8s" - -[global.build_config] - push_registry=true - go_proxy_mode="remote" - go_proxy_url="http://localhost:8081" - registry_type="aws" - -[global.run.test_params] -size = "1MB" -count = "1000" - -[[groups]] - id = "providers" - instances = { count = 1 } - [groups.resources] - memory = "4096Mi" - cpu = "1000m" - -[[groups]] - id = "requestors" - instances = { count = 1 } - [groups.resources] - memory = "4096Mi" - cpu = "1000m" diff --git a/testplans/bitswap/go.mod b/testplans/bitswap/go.mod deleted file mode 100644 index 3ae17384c..000000000 --- a/testplans/bitswap/go.mod +++ /dev/null @@ -1,23 +0,0 @@ -module github.com/ipfs/go-ipfs/testplans/bitswap - -require ( - github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f // indirect - github.com/ipfs/go-bitswap v0.10.1 - github.com/ipfs/go-block-format v0.0.3 - github.com/ipfs/go-cid v0.2.0 - github.com/ipfs/go-datastore v0.5.1 - github.com/ipfs/go-ipfs-blockstore v1.2.0 - github.com/ipfs/go-ipfs-exchange-interface v0.2.0 - github.com/ipfs/go-ipfs-regression v0.0.1 - github.com/ipld/go-ipld-prime v0.9.1-0.20210324083106-dc342a9917db // indirect - github.com/libp2p/go-libp2p v0.22.0 - github.com/libp2p/go-libp2p-kad-dht v0.18.0 - github.com/multiformats/go-multiaddr v0.6.0 - github.com/multiformats/go-multihash v0.2.1 - github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e // indirect - github.com/smartystreets/assertions v1.0.1 // indirect - github.com/testground/sdk-go v0.2.7 - golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 // indirect -) - -go 1.15 diff --git a/testplans/bitswap/go.sum b/testplans/bitswap/go.sum deleted file mode 100644 index c71296c05..000000000 --- a/testplans/bitswap/go.sum +++ /dev/null @@ -1,1206 +0,0 @@ -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.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.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -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/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= -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= -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/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/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -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/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/avast/retry-go v2.6.0+incompatible h1:FelcMrm7Bxacr1/RM8+/eqkDkmVN7tjlsy51dOzB3LI= -github.com/avast/retry-go v2.6.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= -github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= -github.com/benbjohnson/clock v1.3.0/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/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/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= -github.com/btcsuite/btcd v0.22.1/go.mod h1:wqgTSL29+50LRkmOVknEdmt8ZojIzhuWvgu/iptuN7Y= -github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= -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-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/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/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= -github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/snappy-go v1.0.0/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 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 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= -github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= -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/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= -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/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= -github.com/containerd/cgroups v1.0.4 h1:jN/mbWBEaz+T1pi5OFtnkQ+8qnmEbAr1Oo1FRm5B0dA= -github.com/containerd/cgroups v1.0.4/go.mod h1:nLNQtsF7Sl2HxNebu77i1R0oDlhiTG+kO4JTrUzo6IA= -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 h1:t5Wuyh53qYyg9eqn4BbnlIT+vmhyww0TatL+zT3uWgI= -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.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= -github.com/coreos/go-systemd/v22 v22.3.2/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/cskr/pubsub v1.0.2 h1:vlOzMhl6PFn60gRlTQQsIfVwaPB/B/8MziK8FhEPt/0= -github.com/cskr/pubsub v1.0.2/go.mod h1:/8MzYXk/NJAz782G8RPkFzXTZVu63VotefPnR9TIRis= -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.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= -github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= -github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= -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/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE= -github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= -github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -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/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/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 v1.0.0 h1:DlTHqmzmvcEiKj+4RYo/imoswx/4r6iBlCMfVtrMXpQ= -github.com/flynn/noise v1.0.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/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.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -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-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/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.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -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-redis/redis/v7 v7.4.0 h1:7obg6wUoj05T0EpY0o8B59S9w5yeMWql7sw2kwNW1x4= -github.com/go-redis/redis/v7 v7.4.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -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/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/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 h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/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/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -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 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -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/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.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.8/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.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= -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/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/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -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.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.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/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/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/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/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= -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 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/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= -github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= -github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y= -github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/influxdata/influxdb1-client v0.0.0-20200515024757-02f0bf5dbca3 h1:k3/6a1Shi7GGCp9QpyYuXsMM6ncTOjCzOE9Fd6CDA+Q= -github.com/influxdata/influxdb1-client v0.0.0-20200515024757-02f0bf5dbca3/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -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/go-bitswap v0.10.1 h1:XaMmwMR8NgxpzB2hVtZsVLPJPTn2pfAA66GN8gZJTl8= -github.com/ipfs/go-bitswap v0.10.1/go.mod h1:+fZEvycxviZ7c+5KlKwTzLm0M28g2ukCPqiuLfJk4KA= -github.com/ipfs/go-block-format v0.0.2/go.mod h1:AWR46JfpcObNfg3ok2JHDUfdiHRgWhJgCQF+KIgOPJY= -github.com/ipfs/go-block-format v0.0.3 h1:r8t66QstRp/pd/or4dpnbVfXT5Gt7lOqRvC+/dDTpMc= -github.com/ipfs/go-block-format v0.0.3/go.mod h1:4LmD4ZUw0mhO+JSKdpWwrzATiEfM7WWgQ8H5l6P8MVk= -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.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= -github.com/ipfs/go-cid v0.2.0 h1:01JTiihFq9en9Vz0lc0VDWvZe/uBonGpzo4THP0vcQ0= -github.com/ipfs/go-cid v0.2.0/go.mod h1:P+HXFDF4CVhaVayiEb4wkAy7zBHxBwsJyt0Y5U6MLro= -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.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.5.0/go.mod h1:9zhEApYMTl17C8YDp7JmU7sQZi2/wqiYh73hakZ90Bk= -github.com/ipfs/go-datastore v0.5.1 h1:WkRhLuISI+XPD0uk3OskB0fYFSyqK8Ob5ZYew9Qa1nQ= -github.com/ipfs/go-datastore v0.5.1/go.mod h1:9zhEApYMTl17C8YDp7JmU7sQZi2/wqiYh73hakZ90Bk= -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.2.3/go.mod h1:pEYw0rgg3FIrywKKnL+Snr+w/LjJZVMTBRn4FS6UHUk= -github.com/ipfs/go-ds-badger v0.3.0/go.mod h1:1ke6mXNqeV8K3y5Ak2bAA0osoTfmxUdupVCGm4QUIek= -github.com/ipfs/go-ds-leveldb v0.1.0/go.mod h1:hqAW8y4bwX5LWcCtku2rFNX3vjDZCy5LZCg+cSZvYb8= -github.com/ipfs/go-ds-leveldb v0.4.2/go.mod h1:jpbku/YqBSsBc1qgME8BkWS4AxzF2cEu1Ii2r79Hh9s= -github.com/ipfs/go-ds-leveldb v0.5.0/go.mod h1:d3XG9RUDzQ6V4SHi8+Xgj9j1XuEk1z82lquxrVbml/Q= -github.com/ipfs/go-ipfs-blockstore v1.2.0 h1:n3WTeJ4LdICWs/0VSfjHrlqpPpl6MZ+ySd3j8qz0ykw= -github.com/ipfs/go-ipfs-blockstore v1.2.0/go.mod h1:eh8eTFLiINYNSNawfZOC7HOxNTxpB1PFuA5E1m/7exE= -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-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 v0.1.1/go.mod h1:SbBafGJuGsPI/QL3j9Fc5YPLeAu+SzOkI0gFwAg+mOs= -github.com/ipfs/go-ipfs-ds-help v1.1.0 h1:yLE2w9RAsl31LtfMt91tRZcrx+e61O5mDxFRR994w4Q= -github.com/ipfs/go-ipfs-ds-help v1.1.0/go.mod h1:YR5+6EaebOhfcqVCyqemItCLthrpVNot+rsOU/5IatU= -github.com/ipfs/go-ipfs-exchange-interface v0.2.0 h1:8lMSJmKogZYNo2jjhUs0izT+dck05pqUw4mWNW9Pw6Y= -github.com/ipfs/go-ipfs-exchange-interface v0.2.0/go.mod h1:z6+RhJuDQbqKguVyslSOuVDhqF9JtTrO3eptSAiW2/Y= -github.com/ipfs/go-ipfs-pq v0.0.2 h1:e1vOOW6MuOwG2lqxcLA+wEn93i/9laCY8sXAw76jFOY= -github.com/ipfs/go-ipfs-pq v0.0.2/go.mod h1:LWIqQpqfRG3fNc5XsnIhz/wQ2XXGyugQwls7BgUmUfY= -github.com/ipfs/go-ipfs-regression v0.0.1 h1:LX3lrYYgiCE9QmL/qlc4Mh0cewdOHRl5pArbeQRllsU= -github.com/ipfs/go-ipfs-regression v0.0.1/go.mod h1:I9yKzBjbirC5D0ND0DBkiQ5PPSQ2R2YjhcqHPj8L28A= -github.com/ipfs/go-ipfs-routing v0.2.1 h1:E+whHWhJkdN9YeoHZNj5itzc+OR292AJ2uE9FFiW0BY= -github.com/ipfs/go-ipfs-routing v0.2.1/go.mod h1:xiNNiwgjmLqPS1cimvAw6EyB9rkVDbiocA4yY+wRNLM= -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-ipld-format v0.3.0 h1:Mwm2oRLzIuUwEPewWAWyMuuBQUsn3awfFEYVb8akMOQ= -github.com/ipfs/go-ipld-format v0.3.0/go.mod h1:co/SdBE8h99968X0hViiw1MNlh6fvxxnHpvVLnH7jSM= -github.com/ipfs/go-ipns v0.2.0 h1:BgmNtQhqOw5XEZ8RAfWEpK4DhqaYiuP6h71MhIp7xXU= -github.com/ipfs/go-ipns v0.2.0/go.mod h1:3cLT2rbvgPZGkHJoPO1YMJeh6LtkxopCkKFcio/wE24= -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.4/go.mod h1:oDCg2FkjogeFOhqqb+N39l2RpTNPL6F/StPkB3kPgcs= -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.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-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.7.0 h1:VyO6G4sbzX80K58N60cCaHsSsypbUNs1GjO5seGNsQ0= -github.com/ipfs/go-peertaskqueue v0.7.0/go.mod h1:M/akTIE/z1jGNXMU7kFB4TeSEFvj68ow0Rrb04donIU= -github.com/ipld/go-ipld-prime v0.9.0/go.mod h1:KvBLMr4PX1gWptgkzRjVZCrLmSGcZCb/jioOQwCqZN8= -github.com/ipld/go-ipld-prime v0.9.1-0.20210324083106-dc342a9917db h1:kFwGn8rXa/Z31ev1OFNQsYeNKNCdifnTPl/NvPy5L38= -github.com/ipld/go-ipld-prime v0.9.1-0.20210324083106-dc342a9917db/go.mod h1:KvBLMr4PX1gWptgkzRjVZCrLmSGcZCb/jioOQwCqZN8= -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/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/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/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/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.15.1 h1:y9FcTHGyrebwfP0ZZqFiaxTaiDnUrGkJkI+f583BL1A= -github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -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.1.0 h1:eyi1Ad2aNJMW95zcSbmGg7Cg6cq3ADwLpMAP96d8rF0= -github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= -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.0-20191105050749-2e1c40ed0b5d/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= -github.com/koron/go-ssdp v0.0.3 h1:JivLMY45N76b4p/vsWGOKewBQu6uf39y8l+AQ7sDKx8= -github.com/koron/go-ssdp v0.0.3/go.mod h1:b2MxI6yh02pKrsyNoQUsk4+YNikaGhe4894J+Q5lDvA= -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 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= -github.com/kr/pretty v0.2.1/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-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-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/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.22.0 h1:2Tce0kHOp5zASFKJbNzRElvh0iZwdtG5uZheNW8chIw= -github.com/libp2p/go-libp2p v0.22.0/go.mod h1:UDolmweypBSjQb2f7xutPnwZ/fxioLbMBxSjRksxxU4= -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-asn-util v0.2.0 h1:rg3+Os8jbnO5DxkC7K/Utdi+DkY3q/d1/1q+8WeNAsw= -github.com/libp2p/go-libp2p-asn-util v0.2.0/go.mod h1:WoaWxbHKBymSN41hWSq/lGKJEca7TNm58+gGJi2WsLI= -github.com/libp2p/go-libp2p-core v0.0.1/go.mod h1:g/VxnTZ/1ygHxH3dKok7Vno1VfpvGcGip57wjTU4fco= -github.com/libp2p/go-libp2p-core v0.0.2/go.mod h1:9dAcntw/n46XycV4RnlBq3BpgrmyUi9LuoTNdPrbUco= -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.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.6.1/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-core v0.19.0/go.mod h1:AkA+FUKQfYt1FLNef5fOPlo/naAWjKy/RCjkcPjqzYg= -github.com/libp2p/go-libp2p-core v0.20.0 h1:PGKM74+T+O/FaZNARNW32i90RMBHCcgd/hkum2UQ5eY= -github.com/libp2p/go-libp2p-core v0.20.0/go.mod h1:6zR8H7CvQWgYLsbG4on6oLNSGcyKaYFSEYyDt51+bIY= -github.com/libp2p/go-libp2p-kad-dht v0.18.0 h1:akqO3gPMwixR7qFSFq70ezRun97g5hrA/lBW9jrjUYM= -github.com/libp2p/go-libp2p-kad-dht v0.18.0/go.mod h1:Gb92MYIPm3K2pJLGn8wl0m8wiKDvHrYpg+rOd0GzzPA= -github.com/libp2p/go-libp2p-kbucket v0.3.1/go.mod h1:oyjT5O7tS9CQurok++ERgc46YLwEpuGoFq9ubvoUOio= -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-peerstore v0.1.4/go.mod h1:+4BDbDiiKf4PzpANZDAT+knVdLxvqh7hXOujessqdzs= -github.com/libp2p/go-libp2p-peerstore v0.2.6/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= -github.com/libp2p/go-libp2p-peerstore v0.8.0 h1:bzTG693TA1Ju/zKmUCQzDLSqiJnyRFVwPpuloZ/OZtI= -github.com/libp2p/go-libp2p-peerstore v0.8.0/go.mod h1:9geHWmNA3YDlQBjL/uPEJD6vpDK12aDNlUNHJ6kio/s= -github.com/libp2p/go-libp2p-record v0.1.0/go.mod h1:ujNc8iuE5dlKWVy6wuL6dd58t0n7xI4hAIl8pE6wu5Q= -github.com/libp2p/go-libp2p-record v0.1.2/go.mod h1:pal0eNcT5nqZaTV7UGhqeGqxFgGdsU/9W//C8dqjQDk= -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.2.3/go.mod h1:795bh+9YeoFl99rMASoiVgHdi5bjack0N1+AFAdbvBw= -github.com/libp2p/go-libp2p-testing v0.0.3/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= -github.com/libp2p/go-libp2p-testing v0.11.0/go.mod h1:qG4sF27dfKFoK9KlVzK2y52LQKhp0VEmLjV5aDqr1Hg= -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/go.mod h1:LSTM5yRnjGZbWNTA/hRwq2gGFrvRIbQJscoIL/u6InY= -github.com/libp2p/go-maddr-filter v0.1.0/go.mod h1:VzZhTXkMucEGGEOSKddrwGiOv0tUhgnKqNEmIAz/bPU= -github.com/libp2p/go-mplex v0.7.0/go.mod h1:rW8ThnRcYWft/Jb2jeORBmPd6xuG3dGxWN/W168L9EU= -github.com/libp2p/go-msgio v0.0.4/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= -github.com/libp2p/go-msgio v0.0.6/go.mod h1:4ecVB6d9f4BDSL5fqvPiC4A3KivjWn+Venn/1ALLMWA= -github.com/libp2p/go-msgio v0.2.0 h1:W6shmB+FeynDrUVl2dgFQvzfBZcXiyqY4VmpQLu9FqU= -github.com/libp2p/go-msgio v0.2.0/go.mod h1:dBVM1gW3Jk9XqHkU4eKdGvVHdLa51hoGfll6jMJMSlY= -github.com/libp2p/go-nat v0.1.0 h1:MfVsH6DLcpa04Xr+p8hmVRG4juse0s3J8HyNWYHffXg= -github.com/libp2p/go-nat v0.1.0/go.mod h1:X7teVkwRHNInVNWQiO/tAiAVRwSr5zoRz4YSTC3uRBM= -github.com/libp2p/go-netroute v0.1.2/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk= -github.com/libp2p/go-netroute v0.2.0 h1:0FpsbsvuSnAhXFnCY0VLFbJOzaK0VnP0r1QT/o4nWRE= -github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI= -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.7/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= -github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo= -github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= -github.com/libp2p/go-reuseport v0.2.0 h1:18PRvIMlpY6ZK85nIAicSBuXXvrYoSw3dsBAR7zc560= -github.com/libp2p/go-reuseport v0.2.0/go.mod h1:bvVho6eLMm6Bz5hmU0LYN3ixd3nPPvtIlaURZZgOY4k= -github.com/libp2p/go-sockaddr v0.0.2/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= -github.com/libp2p/go-yamux/v3 v3.1.2 h1:lNEy28MBk1HavUAlzKgShp+F6mn/ea1nDYWftZhFW9Q= -github.com/libp2p/go-yamux/v3 v3.1.2/go.mod h1:jeLEQgLXqE2YqX1ilAClIfCMDY+0uXQUKmmb/qp0gT4= -github.com/libp2p/zeroconf/v2 v2.2.0/go.mod h1:fuJqLnUwZTshS3U/bMRJ3+ow/v9oid1n0DmyYyNO1Xs= -github.com/lucas-clemente/quic-go v0.28.1 h1:Uo0lvVxWg5la9gflIF9lwa39ONq85Xq2D91YNEIslzU= -github.com/lucas-clemente/quic-go v0.28.1/go.mod h1:oGz5DKK41cJt5+773+BSO9BXDsREY4HLf7+0odGAPO0= -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-go1-16 v0.1.5 h1:o9JrYPPco/Nukd/HpOHMHZoBDXQqoNtUCmny98/1uqQ= -github.com/marten-seemann/qtls-go1-16 v0.1.5/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk= -github.com/marten-seemann/qtls-go1-17 v0.1.2 h1:JADBlm0LYiVbuSySCHeY863dNkcpMmDR7s0bLKJeYlQ= -github.com/marten-seemann/qtls-go1-17 v0.1.2/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s= -github.com/marten-seemann/qtls-go1-18 v0.1.2 h1:JH6jmzbduz0ITVQ7ShevK10Av5+jBEKAHMntXmIV7kM= -github.com/marten-seemann/qtls-go1-18 v0.1.2/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4= -github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI= -github.com/marten-seemann/qtls-go1-19 v0.1.0 h1:rLFKD/9mp/uq1SYGYuVZhm83wkmU95pK5df3GufyYYU= -github.com/marten-seemann/qtls-go1-19 v0.1.0/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI= -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/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/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-pointer v0.0.1 h1:n+XhsuGeVO6MEAp7xyEukFINEa+Quek5psIR/ylA6o0= -github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= -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.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.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= -github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= -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.0.0-20190328051042-05b4dd3047e5/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 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= -github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= -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-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.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/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= -github.com/multiformats/go-base32 v0.0.4 h1:+qMh4a2f37b4xTNs6mqitDinryCI+tfO2dRVMN9mjSE= -github.com/multiformats/go-base32 v0.0.4/go.mod h1:jNLFzjPZtp3aIARHbJRZIaPuspdH0J6q39uUM5pnABM= -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.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/go.mod h1:uPbspcUPd5AfaP6ql3ujFY+QWzmBD8uLLL4bXW0XfGc= -github.com/multiformats/go-multiaddr v0.4.1/go.mod h1:3afI9HfVW8csiF8UZqtpYRiDyew8pRX7qLIGHu9FLuM= -github.com/multiformats/go-multiaddr v0.6.0 h1:qMnoOPj2s8xxPU5kZ57Cqdr0hHhARz7mFsPMIiYNqzg= -github.com/multiformats/go-multiaddr v0.6.0/go.mod h1:F4IpaKZuPP360tOMn2Tpyu0At8w23aRyVqeK0DbFeGM= -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-multiaddr-net v0.1.4/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA= -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/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc= -github.com/multiformats/go-multibase v0.1.1 h1:3ASCDsuLX8+j4kx58qnJ4YFq/JWTJpCyDW27ztsVTOI= -github.com/multiformats/go-multibase v0.1.1/go.mod h1:ZEjHE+IsUrgp5mhlEAYjMtZwK1k4haNkcaPg9aoe1a8= -github.com/multiformats/go-multicodec v0.4.1/go.mod h1:1Hj/eHRaVWSXiSNNfcEPcwZleTmdNP81xlxDLnWU9GQ= -github.com/multiformats/go-multicodec v0.5.0 h1:EgU6cBe/D7WRwQb1KmnBvU7lrcFGMggZVTPtOW9dDHs= -github.com/multiformats/go-multicodec v0.5.0/go.mod h1:DiY2HFaEp5EhEXb/iYzVAunmyX/aSFMxq2KMKfWEues= -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/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= -github.com/multiformats/go-multihash v0.0.15/go.mod h1:D6aZrWNLFTV/ynMpKsNtB40mJzmCl4jb1alC0OvHiHg= -github.com/multiformats/go-multihash v0.2.1 h1:aem8ZT0VA2nCHHk7bPJ1BjUbHNciqZC/d16Vve9l108= -github.com/multiformats/go-multihash v0.2.1/go.mod h1:WxoMcYG85AZVQUyRyo9s4wULvW5qrI9vb2Lt6evduFc= -github.com/multiformats/go-multistream v0.3.3 h1:d5PZpjwRgVlbwfdTDjife7XszfZd8KYWfROYFlGcR8o= -github.com/multiformats/go-multistream v0.3.3/go.mod h1:ODRoqamLUsETKS9BNcII4gcRsJBU5VAwRIv7O39cEXg= -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/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/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.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -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.10.1/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.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -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/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.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.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak= -github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= -github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0= -github.com/opencontainers/runtime-spec v1.0.2/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/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/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/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-20190807091052-3d65705ee9f1/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= -github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e h1:ZOcivgkkFRnjfoTcGsDq3UQYiBmekwLA+qg0OjyB/ls= -github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= -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 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -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 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -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.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -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 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -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/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ= -github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -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/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -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/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHeiJApdr3r4w= -github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= -github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -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/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -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 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -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/testground/sdk-go v0.2.7 h1:SDLDukPZIaZOeG2gQjCNsh5EB3QL3mOhOM4p0BMpqlc= -github.com/testground/sdk-go v0.2.7/go.mod h1:Q4dnWsUBH+dZ1u7aEGDBHWGUaLfhitjUq3UJQqxeTmk= -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/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-wish v0.0.0-20200122115046-b9ea61034e4a h1:G++j5e0OC488te356JvdhaM8YS6nMsjLAYF7JxCv07w= -github.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= -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/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= -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.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/otel v1.7.0 h1:Z2lA3Tdch0iDcrhJXDIlC94XE+bxok1F9B+4Lz/lGsM= -go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk= -go.opentelemetry.io/otel/trace v1.7.0 h1:O37Iogk1lEkMRXewVtZ1BBTVn5JEp8GrJvP92bJqC6o= -go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU= -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.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= -go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/goleak v1.0.0/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.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= -go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -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.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= -go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= -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/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= -go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= -go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= -go.uber.org/zap v1.22.0 h1:Zcye5DUgBloQ9BaT4qc9BnjOFog5TvBSAGkJ3Nf70c0= -go.uber.org/zap v1.22.0/go.mod h1:H4siCOZOrAolnUPJEkfaSjDqyP+BDS0DdDWzwcgt3+U= -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-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-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-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/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-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= -golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -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/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 h1:2M3HP5CCK1Si9FQhwnzYhXdG6DXeebvUHFpre8QvbyI= -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.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -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-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-20190923162816-aa69164e4478/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-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-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-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -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-20210428140749-89ef3d95e781/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-20210726213435-c6fcb2dbf985/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-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E= -golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -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-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/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 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -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-20190228124157-a34e9553db1e/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-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-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-20191010194322-b09406accb47/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-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-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-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-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/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-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-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-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/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-20220704084225-05e143d24a9e/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-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -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/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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -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/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-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -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= -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/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/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-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-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-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -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.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -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.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -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/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.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-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.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0= -lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= -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= diff --git a/testplans/bitswap/main.go b/testplans/bitswap/main.go deleted file mode 100644 index 95eb47f03..000000000 --- a/testplans/bitswap/main.go +++ /dev/null @@ -1,201 +0,0 @@ -package main - -import ( - "context" - "errors" - "fmt" - "math/rand" - "time" - - "github.com/testground/sdk-go/network" - "github.com/testground/sdk-go/run" - "github.com/testground/sdk-go/runtime" - "github.com/testground/sdk-go/sync" - - bitswap "github.com/ipfs/go-bitswap" - bsnet "github.com/ipfs/go-bitswap/network" - block "github.com/ipfs/go-block-format" - "github.com/ipfs/go-cid" - datastore "github.com/ipfs/go-datastore" - blockstore "github.com/ipfs/go-ipfs-blockstore" - exchange "github.com/ipfs/go-ipfs-exchange-interface" - bstats "github.com/ipfs/go-ipfs-regression/bitswap" - "github.com/libp2p/go-libp2p" - dht "github.com/libp2p/go-libp2p-kad-dht" - "github.com/libp2p/go-libp2p/core/host" - "github.com/libp2p/go-libp2p/core/peer" - "github.com/multiformats/go-multiaddr" - "github.com/multiformats/go-multihash" -) - -var ( - testcases = map[string]interface{}{ - "speed-test": run.InitializedTestCaseFn(runSpeedTest), - } - networkState = sync.State("network-configured") - readyState = sync.State("ready-to-publish") - readyDLState = sync.State("ready-to-download") - doneState = sync.State("done") - providerTopic = sync.NewTopic("provider", &peer.AddrInfo{}) - blockTopic = sync.NewTopic("blocks", &multihash.Multihash{}) -) - -func main() { - run.InvokeMap(testcases) -} - -func runSpeedTest(runenv *runtime.RunEnv, initCtx *run.InitContext) error { - runenv.RecordMessage("running speed-test") - ctx := context.Background() - linkShape := network.LinkShape{} - // linkShape := network.LinkShape{ - // Latency: 50 * time.Millisecond, - // Jitter: 20 * time.Millisecond, - // Bandwidth: 3e6, - // // Filter: (not implemented) - // Loss: 0.02, - // Corrupt: 0.01, - // CorruptCorr: 0.1, - // Reorder: 0.01, - // ReorderCorr: 0.1, - // Duplicate: 0.02, - // DuplicateCorr: 0.1, - // } - initCtx.NetClient.MustConfigureNetwork(ctx, &network.Config{ - Network: "default", - Enable: true, - Default: linkShape, - CallbackState: networkState, - CallbackTarget: runenv.TestGroupInstanceCount, - RoutingPolicy: network.AllowAll, - }) - listen, err := multiaddr.NewMultiaddr(fmt.Sprintf("/ip4/%s/tcp/3333", initCtx.NetClient.MustGetDataNetworkIP().String())) - if err != nil { - return err - } - h, err := libp2p.New(libp2p.ListenAddrs(listen)) - if err != nil { - return err - } - defer h.Close() - kad, err := dht.New(ctx, h) - if err != nil { - return err - } - for _, a := range h.Addrs() { - runenv.RecordMessage("listening on addr: %s", a.String()) - } - bstore := blockstore.NewBlockstore(datastore.NewMapDatastore()) - ex := bitswap.New(ctx, bsnet.NewFromIpfsHost(h, kad), bstore) - switch runenv.TestGroupID { - case "providers": - runenv.RecordMessage("running provider") - err = runProvide(ctx, runenv, h, bstore, ex) - case "requestors": - runenv.RecordMessage("running requestor") - err = runRequest(ctx, runenv, h, bstore, ex) - default: - runenv.RecordMessage("not part of a group") - err = errors.New("unknown test group id") - } - return err -} - -func runProvide(ctx context.Context, runenv *runtime.RunEnv, h host.Host, bstore blockstore.Blockstore, ex exchange.Interface) error { - tgc := sync.MustBoundClient(ctx, runenv) - ai := peer.AddrInfo{ - ID: h.ID(), - Addrs: h.Addrs(), - } - tgc.MustPublish(ctx, providerTopic, &ai) - tgc.MustSignalAndWait(ctx, readyState, runenv.TestInstanceCount) - - size := runenv.SizeParam("size") - count := runenv.IntParam("count") - for i := 0; i <= count; i++ { - runenv.RecordMessage("generating %d-sized random block", size) - buf := make([]byte, size) - rand.Read(buf) - blk := block.NewBlock(buf) - err := bstore.Put(ctx, blk) - if err != nil { - return err - } - mh := blk.Multihash() - runenv.RecordMessage("publishing block %s", mh.String()) - tgc.MustPublish(ctx, blockTopic, &mh) - } - tgc.MustSignalAndWait(ctx, readyDLState, runenv.TestInstanceCount) - tgc.MustSignalAndWait(ctx, doneState, runenv.TestInstanceCount) - return nil -} - -func runRequest(ctx context.Context, runenv *runtime.RunEnv, h host.Host, bstore blockstore.Blockstore, ex exchange.Interface) error { - tgc := sync.MustBoundClient(ctx, runenv) - providers := make(chan *peer.AddrInfo) - blkmhs := make(chan *multihash.Multihash) - providerSub, err := tgc.Subscribe(ctx, providerTopic, providers) - if err != nil { - return err - } - ai := <-providers - - runenv.RecordMessage("connecting to provider provider: %s", fmt.Sprint(*ai)) - providerSub.Done() - - err = h.Connect(ctx, *ai) - if err != nil { - return fmt.Errorf("could not connect to provider: %w", err) - } - - runenv.RecordMessage("connected to provider") - - blockmhSub, err := tgc.Subscribe(ctx, blockTopic, blkmhs) - if err != nil { - return fmt.Errorf("could not subscribe to block sub: %w", err) - } - defer blockmhSub.Done() - - // tell the provider that we're ready for it to publish blocks - tgc.MustSignalAndWait(ctx, readyState, runenv.TestInstanceCount) - // wait until the provider is ready for us to start downloading - tgc.MustSignalAndWait(ctx, readyDLState, runenv.TestInstanceCount) - - begin := time.Now() - count := runenv.IntParam("count") - for i := 0; i <= count; i++ { - mh := <-blkmhs - runenv.RecordMessage("downloading block %s", mh.String()) - dlBegin := time.Now() - blk, err := ex.GetBlock(ctx, cid.NewCidV0(*mh)) - if err != nil { - return fmt.Errorf("could not download get block %s: %w", mh.String(), err) - } - dlDuration := time.Since(dlBegin) - s := &bstats.BitswapStat{ - SingleDownloadSpeed: &bstats.SingleDownloadSpeed{ - Cid: blk.Cid().String(), - DownloadDuration: dlDuration, - }, - } - runenv.RecordMessage(bstats.Marshal(s)) - - stored, err := bstore.Has(ctx, blk.Cid()) - if err != nil { - return fmt.Errorf("error checking if blck was stored %s: %w", mh.String(), err) - } - if !stored { - return fmt.Errorf("block was not stored %s: %w", mh.String(), err) - } - } - duration := time.Since(begin) - s := &bstats.BitswapStat{ - MultipleDownloadSpeed: &bstats.MultipleDownloadSpeed{ - BlockCount: count, - TotalDuration: duration, - }, - } - runenv.RecordMessage(bstats.Marshal(s)) - tgc.MustSignalEntry(ctx, doneState) - return nil -} diff --git a/testplans/bitswap/manifest.toml b/testplans/bitswap/manifest.toml deleted file mode 100644 index cc6e2720a..000000000 --- a/testplans/bitswap/manifest.toml +++ /dev/null @@ -1,19 +0,0 @@ -name = "bitswap" - -[builders] - "docker:go" = { enabled = true, enable_go_build_cache = true } - -[runners] - "local:docker" = { enabled = true } - "cluster:k8s" = { enabled = true } - -[global.build_config] - enable_go_build_cache = true - -[[testcases]] - name = "speed-test" - instances = { min = 2, max = 100, default = 2 } - - [testcases.params] - size = { type = "int", desc = "size of file to transfer, in human-friendly form", default = "1MiB" } - count = { type = "int", desc = "number of transfers", default = "10" } diff --git a/thirdparty/dir/dir.go b/thirdparty/dir/dir.go index 1549cc80b..5aa93c329 100644 --- a/thirdparty/dir/dir.go +++ b/thirdparty/dir/dir.go @@ -8,7 +8,7 @@ import ( "path/filepath" ) -// Writable ensures the directory exists and is writable +// Writable ensures the directory exists and is writable. func Writable(path string) error { // Construct the path if missing if err := os.MkdirAll(path, os.ModePerm); err != nil { diff --git a/thirdparty/notifier/notifier_test.go b/thirdparty/notifier/notifier_test.go index 9b9692ef1..401b3b02a 100644 --- a/thirdparty/notifier/notifier_test.go +++ b/thirdparty/notifier/notifier_test.go @@ -7,7 +7,7 @@ import ( "time" ) -// test data structures +// test data structures. type Router struct { queue chan Packet notifier Notifier @@ -36,7 +36,6 @@ func (r *Router) notifyAll(notify func(n RouterNotifiee)) { } func (r *Router) Receive(p Packet) { - select { case r.queue <- p: // enqueued r.notifyAll(func(n RouterNotifiee) { @@ -100,7 +99,6 @@ func (m *Metrics) String() string { } func TestNotifies(t *testing.T) { - m := Metrics{received: make(chan struct{})} r := Router{queue: make(chan Packet, 10)} r.Notify(&m) diff --git a/thirdparty/unit/unit.go b/thirdparty/unit/unit.go index feeaa42bb..3b10db44c 100644 --- a/thirdparty/unit/unit.go +++ b/thirdparty/unit/unit.go @@ -15,11 +15,10 @@ const ( ) func (i Information) String() string { - tmp := int64(i) // default - var d = tmp + d := tmp symbol := "B" switch { diff --git a/thirdparty/verifbs/verifbs.go b/thirdparty/verifbs/verifbs.go index 7be5348a8..2fcc52bb9 100644 --- a/thirdparty/verifbs/verifbs.go +++ b/thirdparty/verifbs/verifbs.go @@ -3,10 +3,10 @@ package verifbs import ( "context" + bstore "github.com/ipfs/boxo/blockstore" + "github.com/ipfs/boxo/verifcid" blocks "github.com/ipfs/go-block-format" cid "github.com/ipfs/go-cid" - bstore "github.com/ipfs/go-ipfs-blockstore" - "github.com/ipfs/go-verifcid" ) type VerifBSGC struct { @@ -14,7 +14,7 @@ type VerifBSGC struct { } func (bs *VerifBSGC) Put(ctx context.Context, b blocks.Block) error { - if err := verifcid.ValidateCid(b.Cid()); err != nil { + if err := verifcid.ValidateCid(verifcid.DefaultAllowlist, b.Cid()); err != nil { return err } return bs.GCBlockstore.Put(ctx, b) @@ -22,7 +22,7 @@ func (bs *VerifBSGC) Put(ctx context.Context, b blocks.Block) error { func (bs *VerifBSGC) PutMany(ctx context.Context, blks []blocks.Block) error { for _, b := range blks { - if err := verifcid.ValidateCid(b.Cid()); err != nil { + if err := verifcid.ValidateCid(verifcid.DefaultAllowlist, b.Cid()); err != nil { return err } } @@ -30,7 +30,7 @@ func (bs *VerifBSGC) PutMany(ctx context.Context, blks []blocks.Block) error { } func (bs *VerifBSGC) Get(ctx context.Context, c cid.Cid) (blocks.Block, error) { - if err := verifcid.ValidateCid(c); err != nil { + if err := verifcid.ValidateCid(verifcid.DefaultAllowlist, c); err != nil { return nil, err } return bs.GCBlockstore.Get(ctx, c) @@ -41,7 +41,7 @@ type VerifBS struct { } func (bs *VerifBS) Put(ctx context.Context, b blocks.Block) error { - if err := verifcid.ValidateCid(b.Cid()); err != nil { + if err := verifcid.ValidateCid(verifcid.DefaultAllowlist, b.Cid()); err != nil { return err } return bs.Blockstore.Put(ctx, b) @@ -49,7 +49,7 @@ func (bs *VerifBS) Put(ctx context.Context, b blocks.Block) error { func (bs *VerifBS) PutMany(ctx context.Context, blks []blocks.Block) error { for _, b := range blks { - if err := verifcid.ValidateCid(b.Cid()); err != nil { + if err := verifcid.ValidateCid(verifcid.DefaultAllowlist, b.Cid()); err != nil { return err } } @@ -57,7 +57,7 @@ func (bs *VerifBS) PutMany(ctx context.Context, blks []blocks.Block) error { } func (bs *VerifBS) Get(ctx context.Context, c cid.Cid) (blocks.Block, error) { - if err := verifcid.ValidateCid(c); err != nil { + if err := verifcid.ValidateCid(verifcid.DefaultAllowlist, c); err != nil { return nil, err } return bs.Blockstore.Get(ctx, c) diff --git a/tracing/doc.go b/tracing/doc.go index 84a58febe..d442ea2db 100644 --- a/tracing/doc.go +++ b/tracing/doc.go @@ -10,21 +10,12 @@ // // OTEL_TRACES_EXPORTER: a comma-separated list of exporters: // - otlp -// - jaeger // - zipkin // - file // // Different exporters have their own set of environment variables, depending on the exporter. These are typically // standard environment variables. Some common ones: // -// Jaeger: -// -// - OTEL_EXPORTER_JAEGER_AGENT_HOST -// - OTEL_EXPORTER_JAEGER_AGENT_PORT -// - OTEL_EXPORTER_JAEGER_ENDPOINT -// - OTEL_EXPORTER_JAEGER_USER -// - OTEL_EXPORTER_JAEGER_PASSWORD -// // OTLP HTTP/gRPC: // // - OTEL_EXPORTER_OTLP_PROTOCOL @@ -47,21 +38,24 @@ // default: `$PWD/traces.json` // // For example, if you run a local IPFS daemon, you can use the jaegertracing/all-in-one Docker image to run -// a full Jaeger stack and configure go-ipfs to publish traces to it: +// a full Jaeger stack and configure Kubo to publish traces to it: // -// docker run -d --name jaeger \ -// -e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \ -// -p 5775:5775/udp \ -// -p 6831:6831/udp \ -// -p 6832:6832/udp \ -// -p 5778:5778 \ -// -p 16686:16686 \ -// -p 14268:14268 \ -// -p 14269:14269 \ -// -p 14250:14250 \ -// -p 9411:9411 \ -// jaegertracing/all-in-one -// OTEL_TRACES_EXPORTER=jaeger ipfs daemon +// docker run -d --rm --name jaeger \ +// -e COLLECTOR_OTLP_ENABLED=true \ +// -e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \ +// -p 5775:5775/udp \ +// -p 6831:6831/udp \ +// -p 6832:6832/udp \ +// -p 5778:5778 \ +// -p 16686:16686 \ +// -p 14250:14250 \ +// -p 14268:14268 \ +// -p 14269:14269 \ +// -p 4317:4317 \ +// -p 4318:4318 \ +// -p 9411:9411 \ +// jaegertracing/all-in-one +// OTEL_EXPORTER_OTLP_INSECURE=true OTEL_TRACES_EXPORTER=otlp ipfs daemon --init // // # In this example the Jaeger UI is available at http://localhost:16686. // diff --git a/tracing/file_exporter.go b/tracing/file_exporter.go deleted file mode 100644 index 32ca20ee2..000000000 --- a/tracing/file_exporter.go +++ /dev/null @@ -1,45 +0,0 @@ -package tracing - -import ( - "context" - "fmt" - "os" - - "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" - "go.opentelemetry.io/otel/sdk/trace" -) - -// fileExporter wraps a file-writing exporter and closes the file when the exporter is shutdown. -type fileExporter struct { - file *os.File - writerExporter *stdouttrace.Exporter -} - -func newFileExporter(file string) (*fileExporter, error) { - f, err := os.OpenFile(file, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) - if err != nil { - return nil, fmt.Errorf("opening '%s' for OpenTelemetry file exporter: %w", file, err) - } - stdoutExporter, err := stdouttrace.New(stdouttrace.WithWriter(f)) - if err != nil { - return nil, err - } - return &fileExporter{ - writerExporter: stdoutExporter, - file: f, - }, nil -} - -func (e *fileExporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) error { - return e.writerExporter.ExportSpans(ctx, spans) -} - -func (e *fileExporter) Shutdown(ctx context.Context) error { - if err := e.writerExporter.Shutdown(ctx); err != nil { - return err - } - if err := e.file.Close(); err != nil { - return fmt.Errorf("closing trace file: %w", err) - } - return nil -} diff --git a/tracing/tracing.go b/tracing/tracing.go index 6cc666a83..061d5a7aa 100644 --- a/tracing/tracing.go +++ b/tracing/tracing.go @@ -3,20 +3,15 @@ package tracing import ( "context" "fmt" - "os" - "path" - "strings" + "github.com/ipfs/boxo/tracing" version "github.com/ipfs/kubo" "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/exporters/jaeger" - "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" - "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" - "go.opentelemetry.io/otel/exporters/zipkin" "go.opentelemetry.io/otel/sdk/resource" "go.opentelemetry.io/otel/sdk/trace" semconv "go.opentelemetry.io/otel/semconv/v1.4.0" traceapi "go.opentelemetry.io/otel/trace" + "go.opentelemetry.io/otel/trace/noop" ) // shutdownTracerProvider adds a shutdown method for tracer providers. @@ -24,6 +19,8 @@ import ( // Note that this doesn't directly use the provided TracerProvider interface // to avoid build breaking go-ipfs if new methods are added to it. type shutdownTracerProvider interface { + traceapi.TracerProvider + Tracer(instrumentationName string, opts ...traceapi.TracerOption) traceapi.Tracer Shutdown(ctx context.Context) error } @@ -33,92 +30,14 @@ type noopShutdownTracerProvider struct{ traceapi.TracerProvider } func (n *noopShutdownTracerProvider) Shutdown(ctx context.Context) error { return nil } -func buildExporters(ctx context.Context) ([]trace.SpanExporter, error) { - // These env vars are standardized but not yet supported by opentelemetry-go. - // Once supported, we can remove most of this code. - // - // Specs: - // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/sdk-environment-variables.md#exporter-selection - // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md - var exporters []trace.SpanExporter - for _, exporterStr := range strings.Split(os.Getenv("OTEL_TRACES_EXPORTER"), ",") { - switch exporterStr { - case "otlp": - protocol := "http/protobuf" - if v := os.Getenv("OTEL_EXPORTER_OTLP_PROTOCOL"); v != "" { - protocol = v - } - if v := os.Getenv("OTEL_EXPORTER_OTLP_TRACES_PROTOCOL"); v != "" { - protocol = v - } - - switch protocol { - case "http/protobuf": - exporter, err := otlptracehttp.New(ctx) - if err != nil { - return nil, fmt.Errorf("building OTLP HTTP exporter: %w", err) - } - exporters = append(exporters, exporter) - case "grpc": - exporter, err := otlptracegrpc.New(ctx) - if err != nil { - return nil, fmt.Errorf("building OTLP gRPC exporter: %w", err) - } - exporters = append(exporters, exporter) - default: - return nil, fmt.Errorf("unknown or unsupported OTLP exporter '%s'", exporterStr) - } - case "jaeger": - exporter, err := jaeger.New(jaeger.WithCollectorEndpoint()) - if err != nil { - return nil, fmt.Errorf("building Jaeger exporter: %w", err) - } - exporters = append(exporters, exporter) - case "zipkin": - exporter, err := zipkin.New("") - if err != nil { - return nil, fmt.Errorf("building Zipkin exporter: %w", err) - } - exporters = append(exporters, exporter) - case "file": - // This is not part of the spec, but provided for convenience - // so that you don't have to setup a collector, - // and because we don't support the stdout exporter. - filePath := os.Getenv("OTEL_EXPORTER_FILE_PATH") - if filePath == "" { - cwd, err := os.Getwd() - if err != nil { - return nil, fmt.Errorf("finding working directory for the OpenTelemetry file exporter: %w", err) - } - filePath = path.Join(cwd, "traces.json") - } - exporter, err := newFileExporter(filePath) - if err != nil { - return nil, err - } - exporters = append(exporters, exporter) - case "none": - continue - case "": - continue - case "stdout": - // stdout is already used for certain kinds of logging, so we don't support this - fallthrough - default: - return nil, fmt.Errorf("unknown or unsupported exporter '%s'", exporterStr) - } - } - return exporters, nil -} - // NewTracerProvider creates and configures a TracerProvider. func NewTracerProvider(ctx context.Context) (shutdownTracerProvider, error) { - exporters, err := buildExporters(ctx) + exporters, err := tracing.NewSpanExporters(ctx) if err != nil { return nil, err } if len(exporters) == 0 { - return &noopShutdownTracerProvider{TracerProvider: traceapi.NewNoopTracerProvider()}, nil + return &noopShutdownTracerProvider{TracerProvider: noop.NewTracerProvider()}, nil } options := []trace.TracerProviderOption{} @@ -130,7 +49,7 @@ func NewTracerProvider(ctx context.Context) (shutdownTracerProvider, error) { r, err := resource.Merge( resource.Default(), resource.NewSchemaless( - semconv.ServiceNameKey.String("go-ipfs"), + semconv.ServiceNameKey.String("Kubo"), semconv.ServiceVersionKey.String(version.CurrentVersionNumber), ), ) @@ -144,5 +63,5 @@ func NewTracerProvider(ctx context.Context) (shutdownTracerProvider, error) { // Span starts a new span using the standard IPFS tracing conventions. func Span(ctx context.Context, componentName string, spanName string, opts ...traceapi.SpanStartOption) (context.Context, traceapi.Span) { - return otel.Tracer("go-ipfs").Start(ctx, fmt.Sprintf("%s.%s", componentName, spanName), opts...) + return otel.Tracer("Kubo").Start(ctx, fmt.Sprintf("%s.%s", componentName, spanName), opts...) } diff --git a/version.go b/version.go index 1f0577fa3..fb1f2383a 100644 --- a/version.go +++ b/version.go @@ -7,11 +7,11 @@ import ( "github.com/ipfs/kubo/repo/fsrepo" ) -// CurrentCommit is the current git commit, this is set as a ldflag in the Makefile +// CurrentCommit is the current git commit, this is set as a ldflag in the Makefile. var CurrentCommit string -// CurrentVersionNumber is the current application's version literal -const CurrentVersionNumber = "0.19.0-dev" +// CurrentVersionNumber is the current application's version literal. +const CurrentVersionNumber = "0.33.0-dev" const ApiVersion = "/kubo/" + CurrentVersionNumber + "/" //nolint @@ -48,7 +48,7 @@ func GetVersionInfo() *VersionInfo { Version: CurrentVersionNumber, Commit: CurrentCommit, Repo: fmt.Sprint(fsrepo.RepoVersion), - System: runtime.GOARCH + "/" + runtime.GOOS, //TODO: Precise version here + System: runtime.GOARCH + "/" + runtime.GOOS, // TODO: Precise version here Golang: runtime.Version(), } }