diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml deleted file mode 100644 index 44fad65fc..000000000 --- a/.github/workflows/automerge.yml +++ /dev/null @@ -1,53 +0,0 @@ -# File managed by web3-bot. DO NOT EDIT. -# See https://github.com/protocol/.github/ for details. - -# Automatically merge pull requests opened by web3-bot, as soon as (and only if) all tests pass. -# This reduces the friction associated with updating with our workflows. - -on: [ pull_request ] -name: Automerge - -jobs: - automerge-check: - if: github.event.pull_request.user.login == 'web3-bot' - runs-on: ubuntu-latest - outputs: - status: ${{ steps.should-automerge.outputs.status }} - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - name: Check if we should automerge - id: should-automerge - run: | - for commit in $(git rev-list --first-parent origin/${{ github.event.pull_request.base.ref }}..${{ github.event.pull_request.head.sha }}); do - committer=$(git show --format=$'%ce' -s $commit) - echo "Committer: $committer" - if [[ "$committer" != "web3-bot@users.noreply.github.com" ]]; then - echo "Commit $commit wasn't committed by web3-bot, but by $committer." - echo "::set-output name=status::false" - exit - fi - done - echo "::set-output name=status::true" - automerge: - needs: automerge-check - runs-on: ubuntu-latest - # The check for the user is redundant here, as this job depends on the automerge-check job, - # but it prevents this job from spinning up, just to be skipped shortly after. - if: github.event.pull_request.user.login == 'web3-bot' && needs.automerge-check.outputs.status == 'true' - steps: - - name: Wait on tests - uses: lewagon/wait-on-check-action@bafe56a6863672c681c3cf671f5e10b20abf2eaa # v0.2 - with: - ref: ${{ github.event.pull_request.head.sha }} - repo-token: ${{ secrets.GITHUB_TOKEN }} - wait-interval: 10 - running-workflow-name: 'automerge' # the name of this job - - name: Merge PR - uses: pascalgn/automerge-action@741c311a47881be9625932b0a0de1b0937aab1ae # v0.13.1 - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - MERGE_LABELS: "" - MERGE_METHOD: "squash" - MERGE_DELETE_BRANCH: true diff --git a/.github/workflows/go-check.yml b/.github/workflows/go-check.yml index e127cf179..26f63bc12 100644 --- a/.github/workflows/go-check.yml +++ b/.github/workflows/go-check.yml @@ -1,74 +1,18 @@ -# File managed by web3-bot. DO NOT EDIT. -# See https://github.com/protocol/.github/ for details. - -on: [push, pull_request] name: Go Checks -jobs: - unit: - runs-on: ubuntu-latest - name: All - env: - RUNGOGENERATE: false - steps: - - uses: actions/checkout@v2 - with: - submodules: recursive - - uses: actions/setup-go@v2 - with: - go-version: "1.17.x" - - name: Run repo-specific setup - uses: ./.github/actions/go-check-setup - if: hashFiles('./.github/actions/go-check-setup') != '' - - name: Read config - if: hashFiles('./.github/workflows/go-check-config.json') != '' - run: | - if jq -re .gogenerate ./.github/workflows/go-check-config.json; then - echo "RUNGOGENERATE=true" >> $GITHUB_ENV - fi - - name: Install staticcheck - run: go install honnef.co/go/tools/cmd/staticcheck@c8caa92bad8c27ae734c6725b8a04932d54a147b # 2021.1.2 (v0.2.2) - - name: Check that go.mod is tidy - uses: protocol/multiple-go-modules@v1.2 - with: - run: | - go mod tidy - if [[ -n $(git ls-files --other --exclude-standard --directory -- go.sum) ]]; then - echo "go.sum was added by go mod tidy" - exit 1 - fi - git diff --exit-code -- go.sum go.mod - - name: gofmt - if: ${{ success() || failure() }} # run this step even if the previous one failed - run: | - out=$(gofmt -s -l .) - if [[ -n "$out" ]]; then - echo $out | awk '{print "::error file=" $0 ",line=0,col=0::File is not gofmt-ed."}' - exit 1 - fi - - name: go vet - if: ${{ success() || failure() }} # run this step even if the previous one failed - uses: protocol/multiple-go-modules@v1.2 - with: - run: go vet ./... - - name: staticcheck - if: ${{ success() || failure() }} # run this step even if the previous one failed - uses: protocol/multiple-go-modules@v1.2 - with: - run: | - set -o pipefail - staticcheck ./... | sed -e 's@\(.*\)\.go@./\1.go@g' - - name: go generate - uses: protocol/multiple-go-modules@v1.2 - if: (success() || failure()) && env.RUNGOGENERATE == 'true' - with: - run: | - git clean -fd # make sure there aren't untracked files / directories - go generate ./... - # check if go generate modified or added any files - if ! $(git add . && git diff-index HEAD --exit-code --quiet); then - echo "go generated caused changes to the repository:" - git status --short - exit 1 - fi +on: + pull_request: + push: + branches: ["master"] + workflow_dispatch: + +permissions: + contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event_name == 'push' && github.sha || github.ref }} + cancel-in-progress: true + +jobs: + go-check: + uses: ipdxco/unified-github-workflows/.github/workflows/go-check.yml@v1.0 diff --git a/.github/workflows/go-test.yml b/.github/workflows/go-test.yml index 606e0c7f3..778de6ed4 100644 --- a/.github/workflows/go-test.yml +++ b/.github/workflows/go-test.yml @@ -1,66 +1,20 @@ -# File managed by web3-bot. DO NOT EDIT. -# See https://github.com/protocol/.github/ for details. - -on: [push, pull_request] name: Go Test +on: + pull_request: + push: + branches: ["master"] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event_name == 'push' && github.sha || github.ref }} + cancel-in-progress: true + jobs: - unit: - strategy: - fail-fast: false - matrix: - os: [ "ubuntu", "windows", "macos" ] - go: [ "1.16.x", "1.17.x" ] - env: - COVERAGES: "" - runs-on: ${{ matrix.os }}-latest - name: ${{ matrix.os}} (go ${{ matrix.go }}) - steps: - - uses: actions/checkout@v2 - with: - submodules: recursive - - uses: actions/setup-go@v2 - with: - go-version: ${{ matrix.go }} - - name: Go information - run: | - go version - go env - - name: Use msys2 on windows - if: ${{ matrix.os == 'windows' }} - shell: bash - # The executable for msys2 is also called bash.cmd - # https://github.com/actions/virtual-environments/blob/main/images/win/Windows2019-Readme.md#shells - # If we prepend its location to the PATH - # subsequent 'shell: bash' steps will use msys2 instead of gitbash - run: echo "C:/msys64/usr/bin" >> $GITHUB_PATH - - name: Run repo-specific setup - uses: ./.github/actions/go-test-setup - if: hashFiles('./.github/actions/go-test-setup') != '' - - name: Run tests - uses: protocol/multiple-go-modules@v1.2 - with: - # Use -coverpkg=./..., so that we include cross-package coverage. - # If package ./A imports ./B, and ./A's tests also cover ./B, - # this means ./B's coverage will be significantly higher than 0%. - run: go test -v -coverprofile=module-coverage.txt -coverpkg=./... ./... - - name: Run tests (32 bit) - if: ${{ matrix.os != 'macos' }} # can't run 32 bit tests on OSX. - uses: protocol/multiple-go-modules@v1.2 - env: - GOARCH: 386 - with: - run: go test -v ./... - - name: Run tests with race detector - if: ${{ matrix.os == 'ubuntu' }} # speed things up. Windows and OSX VMs are slow - uses: protocol/multiple-go-modules@v1.2 - with: - run: go test -v -race ./... - - name: Collect coverage files - shell: bash - run: echo "COVERAGES=$(find . -type f -name 'module-coverage.txt' | tr -s '\n' ',' | sed 's/,$//')" >> $GITHUB_ENV - - name: Upload coverage to Codecov - uses: codecov/codecov-action@f32b3a3741e1053eb607407145bc9619351dc93b # v2.1.0 - with: - files: '${{ env.COVERAGES }}' - env_vars: OS=${{ matrix.os }}, GO=${{ matrix.go }} + go-test: + uses: ipdxco/unified-github-workflows/.github/workflows/go-test.yml@v1.0 + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/release-check.yml b/.github/workflows/release-check.yml index fde81c1f8..0b5ff6070 100644 --- a/.github/workflows/release-check.yml +++ b/.github/workflows/release-check.yml @@ -1,11 +1,19 @@ -# File managed by web3-bot. DO NOT EDIT. -# See https://github.com/protocol/.github/ for details. - name: Release Checker + on: - pull_request: + pull_request_target: paths: [ 'version.json' ] + types: [ opened, synchronize, reopened, labeled, unlabeled ] + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: release-check: - uses: protocol/.github/.github/workflows/release-check.yml@master + uses: ipdxco/unified-github-workflows/.github/workflows/release-check.yml@v1.0 diff --git a/.github/workflows/releaser.yml b/.github/workflows/releaser.yml index cdccbf873..2ebdbed31 100644 --- a/.github/workflows/releaser.yml +++ b/.github/workflows/releaser.yml @@ -1,11 +1,17 @@ -# File managed by web3-bot. DO NOT EDIT. -# See https://github.com/protocol/.github/ for details. - name: Releaser + on: push: paths: [ 'version.json' ] + workflow_dispatch: + +permissions: + contents: write + +concurrency: + group: ${{ github.workflow }}-${{ github.sha }} + cancel-in-progress: true jobs: releaser: - uses: protocol/.github/.github/workflows/releaser.yml@master + uses: ipdxco/unified-github-workflows/.github/workflows/releaser.yml@v1.0 diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 000000000..16d65d721 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,13 @@ +name: Close and mark stale issue + +on: + schedule: + - cron: '0 0 * * *' + +permissions: + issues: write + pull-requests: write + +jobs: + stale: + uses: pl-strflt/.github/.github/workflows/reusable-stale-issue.yml@v0.3 diff --git a/.github/workflows/tagpush.yml b/.github/workflows/tagpush.yml index d84996187..5ef3fb9ed 100644 --- a/.github/workflows/tagpush.yml +++ b/.github/workflows/tagpush.yml @@ -1,12 +1,18 @@ -# File managed by web3-bot. DO NOT EDIT. -# See https://github.com/protocol/.github/ for details. - name: Tag Push Checker + on: push: tags: - v* +permissions: + contents: read + issues: write + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: releaser: - uses: protocol/.github/.github/workflows/tagpush.yml@master + uses: ipdxco/unified-github-workflows/.github/workflows/tagpush.yml@v1.0 diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 000000000..43a81df84 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,15 @@ +# CODEOWNERS + +# default owner is the libp2p team +*.go @libp2p/go-libp2p-maintainers @guillaumemichel +/pb/ @libp2p/go-libp2p-maintainers @guillaumemichel + +# dual is an application for IPFS +/dual/ @libp2p/kubo-maintainers @guillaumemichel +# fullrt is IPFS specific +/fullrt/ @libp2p/kubo-maintainers @guillaumemichel +# providers describe the IPFS specific providers +/providers/ @libp2p/kubo-maintainers @guillaumemichel +# records are IPFS specific +/records.go @libp2p/kubo-maintainers @guillaumemichel +/records_test.go @libp2p/kubo-maintainers @guillaumemichel diff --git a/README.md b/README.md index c774998a3..b9b0a69b6 100644 --- a/README.md +++ b/README.md @@ -2,18 +2,18 @@ [![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](https://protocol.ai) [![](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](https://libp2p.io) -[![](https://img.shields.io/badge/freenode-%23libp2p-yellow.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23yellow) [![GoDoc](https://godoc.org/github.com/libp2p/go-libp2p-kad-dht?status.svg)](https://godoc.org/github.com/libp2p/go-libp2p-kad-dht) -[![Build Status](https://travis-ci.org/libp2p/go-libp2p-kad-dht.svg?branch=master)](https://travis-ci.org/libp2p/go-libp2p-kad-dht) [![Discourse posts](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg)](https://discuss.libp2p.io) -> A Kademlia DHT implementation on go-libp2p +> A Go implementation of [libp2p Kademlia DHT specification](https://github.com/libp2p/specs/tree/master/kad-dht) ## Table of Contents - [Install](#install) - [Usage](#usage) +- [Optimizations](#optimizations) - [Contribute](#contribute) +- [Maintainers](#maintainers) - [License](#license) ## Install @@ -22,6 +22,10 @@ go get github.com/libp2p/go-libp2p-kad-dht ``` +## Optimizations + +Client-side optimizations are described in [optimizations.md](./optimizations.md) + ## Usage Go to https://godoc.org/github.com/libp2p/go-libp2p-kad-dht. @@ -32,12 +36,16 @@ Contributions welcome. Please check out [the issues](https://github.com/libp2p/g Check out our [contributing document](https://github.com/libp2p/community/blob/master/CONTRIBUTE.md) for more information on how we work, and about contributing in general. Please be aware that all interactions related to libp2p are subject to the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md). -Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. + -## License +## Maintainers -[MIT](LICENSE) © Protocol Labs Inc. +- [@ipfs/kubo-maintainers](https://github.com/orgs/ipfs/teams/kubo-maintainers) +- [@libp2p/go-libp2p-maintainers](https://github.com/orgs/libp2p/teams/go-libp2p-maintainers) +- [@guillaumemichel](https://github.com/guillaumemichel) ---- +See [CODEOWNERS](./CODEOWNERS). -The last gx published version of this module was: 4.4.34: QmXuNFLZc6Nb5akB4sZsxK3doShsFKT1sZFvxLXJvZQwAW +## License + +[MIT](LICENSE) © Protocol Labs Inc. diff --git a/amino/defaults.go b/amino/defaults.go new file mode 100644 index 000000000..2d702450b --- /dev/null +++ b/amino/defaults.go @@ -0,0 +1,57 @@ +// Package amino provides protocol parameters and suggested default values for the [Amino DHT]. +// +// [Amino DHT] is an implementation of the Kademlia distributed hash table (DHT) algorithm, +// originally designed for use in IPFS (InterPlanetary File System) network. +// This package defines key constants and protocol identifiers used in the Amino DHT implementation. +// +// [Amino DHT]: https://probelab.io/ipfs/amino/ +package amino + +import ( + "time" + + "github.com/libp2p/go-libp2p/core/protocol" +) + +const ( + // ProtocolPrefix is the base prefix for Amono DHT protocols. + ProtocolPrefix protocol.ID = "/ipfs" + + // ProtocolID is the latest protocol identifier for the Amino DHT. + ProtocolID protocol.ID = "/ipfs/kad/1.0.0" + + // DefaultBucketSize is the Amino DHT bucket size (k in the Kademlia paper). + // It represents the maximum number of peers stored in each + // k-bucket of the routing table. + DefaultBucketSize = 20 + + // DefaultConcurrency is the suggested number of concurrent requests (alpha + // in the Kademlia paper) for a given query path in Amino DHT. It + // determines how many parallel lookups are performed during network + // traversal. + DefaultConcurrency = 10 + + // DefaultResiliency is the suggested number of peers closest to a target + // that must have responded in order for a given query path to complete in + // Amino DHT. This helps ensure reliable results by requiring multiple + // confirmations. + DefaultResiliency = 3 + + // DefaultProvideValidity is the default time that a Provider Record should + // last on Amino DHT before it needs to be refreshed or removed. This value + // is also known as Provider Record Expiration Interval. + DefaultProvideValidity = 48 * time.Hour + + // DefaultProviderAddrTTL is the TTL to keep the multi addresses of + // provider peers around. Those addresses are returned alongside provider. + // After it expires, the returned records will require an extra lookup, to + // find the multiaddress associated with the returned peer id. + DefaultProviderAddrTTL = 24 * time.Hour +) + +var ( + // Protocols is a slice containing all supported protocol IDs for Amino DHT. + // Currently, it only includes the main ProtocolID, but it's defined as a slice + // to allow for potential future protocol versions or variants. + Protocols = []protocol.ID{ProtocolID} +) diff --git a/crawler/crawler.go b/crawler/crawler.go index dae145382..9927df659 100644 --- a/crawler/crawler.go +++ b/crawler/crawler.go @@ -5,30 +5,43 @@ import ( "sync" "time" - "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/protocol" + "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/protocol" - logging "github.com/ipfs/go-log" - "github.com/libp2p/go-msgio/protoio" + logging "github.com/ipfs/go-log/v2" + "github.com/libp2p/go-msgio/pbio" pb "github.com/libp2p/go-libp2p-kad-dht/pb" kbucket "github.com/libp2p/go-libp2p-kbucket" ) -var logger = logging.Logger("dht-crawler") +var ( + logger = logging.Logger("dht-crawler") -// Crawler connects to hosts in the DHT to track routing tables of peers. -type Crawler struct { - parallelism int - connectTimeout time.Duration - host host.Host - dhtRPC *pb.ProtocolMessenger -} + _ Crawler = (*DefaultCrawler)(nil) +) -// New creates a new Crawler -func New(host host.Host, opts ...Option) (*Crawler, error) { +type ( + // Crawler connects to hosts in the DHT to track routing tables of peers. + Crawler interface { + // Run crawls the DHT starting from the startingPeers, and calls either handleSuccess or handleFail depending on whether a peer was successfully contacted or not. + Run(ctx context.Context, startingPeers []*peer.AddrInfo, handleSuccess HandleQueryResult, handleFail HandleQueryFail) + } + // DefaultCrawler provides a default implementation of Crawler. + DefaultCrawler struct { + parallelism int + connectTimeout time.Duration + queryTimeout time.Duration + host host.Host + dhtRPC *pb.ProtocolMessenger + dialAddressExtendDur time.Duration + } +) + +// NewDefaultCrawler creates a new DefaultCrawler +func NewDefaultCrawler(host host.Host, opts ...Option) (*DefaultCrawler, error) { o := new(options) if err := defaults(o); err != nil { return nil, err @@ -44,11 +57,13 @@ func New(host host.Host, opts ...Option) (*Crawler, error) { return nil, err } - return &Crawler{ - parallelism: o.parallelism, - connectTimeout: o.connectTimeout, - host: host, - dhtRPC: pm, + return &DefaultCrawler{ + parallelism: o.parallelism, + connectTimeout: o.connectTimeout, + queryTimeout: 3 * o.connectTimeout, + host: host, + dhtRPC: pm, + dialAddressExtendDur: o.dialAddressExtendDur, }, nil } @@ -61,19 +76,20 @@ type messageSender struct { // SendRequest sends a peer a message and waits for its response func (ms *messageSender) SendRequest(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { - s, err := ms.h.NewStream(ctx, p, ms.protocols...) + tctx, cancel := context.WithTimeout(ctx, ms.timeout) + defer cancel() + + s, err := ms.h.NewStream(tctx, p, ms.protocols...) if err != nil { return nil, err } - w := protoio.NewDelimitedWriter(s) + w := pbio.NewDelimitedWriter(s) if err := w.WriteMsg(pmes); err != nil { return nil, err } - r := protoio.NewDelimitedReader(s, network.MessageSizeMax) - tctx, cancel := context.WithTimeout(ctx, ms.timeout) - defer cancel() + r := pbio.NewDelimitedReader(s, network.MessageSizeMax) defer func() { _ = s.Close() }() msg := new(pb.Message) @@ -85,9 +101,9 @@ func (ms *messageSender) SendRequest(ctx context.Context, p peer.ID, pmes *pb.Me return msg, nil } -func ctxReadMsg(ctx context.Context, rc protoio.ReadCloser, mes *pb.Message) error { +func ctxReadMsg(ctx context.Context, rc pbio.ReadCloser, mes *pb.Message) error { errc := make(chan error, 1) - go func(r protoio.ReadCloser) { + go func(r pbio.ReadCloser) { defer close(errc) err := r.ReadMsg(mes) errc <- err @@ -109,7 +125,7 @@ func (ms *messageSender) SendMessage(ctx context.Context, p peer.ID, pmes *pb.Me } defer func() { _ = s.Close() }() - w := protoio.NewDelimitedWriter(s) + w := pbio.NewDelimitedWriter(s) return w.WriteMsg(pmes) } @@ -119,10 +135,8 @@ type HandleQueryResult func(p peer.ID, rtPeers []*peer.AddrInfo) // HandleQueryFail is a callback on failed peer query type HandleQueryFail func(p peer.ID, err error) -const dialAddressExtendDur time.Duration = time.Minute * 30 - // Run crawls dht peers from an initial seed of `startingPeers` -func (c *Crawler) Run(ctx context.Context, startingPeers []*peer.AddrInfo, handleSuccess HandleQueryResult, handleFail HandleQueryFail) { +func (c *DefaultCrawler) Run(ctx context.Context, startingPeers []*peer.AddrInfo, handleSuccess HandleQueryResult, handleFail HandleQueryFail) { jobs := make(chan peer.ID, 1) results := make(chan *queryResult, 1) @@ -133,7 +147,9 @@ func (c *Crawler) Run(ctx context.Context, startingPeers []*peer.AddrInfo, handl go func() { defer wg.Done() for p := range jobs { - res := c.queryPeer(ctx, p) + qctx, cancel := context.WithTimeout(ctx, c.queryTimeout) + res := c.queryPeer(qctx, p) + cancel() // do not defer, cleanup after each job results <- res } }() @@ -150,7 +166,7 @@ func (c *Crawler) Run(ctx context.Context, startingPeers []*peer.AddrInfo, handl extendAddrs := c.host.Peerstore().Addrs(ai.ID) if len(ai.Addrs) > 0 { extendAddrs = append(extendAddrs, ai.Addrs...) - c.host.Peerstore().AddAddrs(ai.ID, extendAddrs, dialAddressExtendDur) + c.host.Peerstore().AddAddrs(ai.ID, extendAddrs, c.dialAddressExtendDur) } if len(extendAddrs) == 0 { numSkipped++ @@ -182,7 +198,7 @@ func (c *Crawler) Run(ctx context.Context, startingPeers []*peer.AddrInfo, handl logger.Debugf("peer %v had %d peers", res.peer, len(res.data)) rtPeers := make([]*peer.AddrInfo, 0, len(res.data)) for p, ai := range res.data { - c.host.Peerstore().AddAddrs(p, ai.Addrs, dialAddressExtendDur) + c.host.Peerstore().AddAddrs(p, ai.Addrs, c.dialAddressExtendDur) if _, ok := peersSeen[p]; !ok { peersSeen[p] = struct{}{} toDial = append(toDial, ai) @@ -211,7 +227,7 @@ type queryResult struct { err error } -func (c *Crawler) queryPeer(ctx context.Context, nextPeer peer.ID) *queryResult { +func (c *DefaultCrawler) queryPeer(ctx context.Context, nextPeer peer.ID) *queryResult { tmpRT, err := kbucket.NewRoutingTable(20, kbucket.ConvertPeerID(nextPeer), time.Hour, c.host.Peerstore(), time.Hour, nil) if err != nil { logger.Errorf("error creating rt for peer %v : %v", nextPeer, err) diff --git a/crawler/options.go b/crawler/options.go index b33175009..649b4d6f6 100644 --- a/crawler/options.go +++ b/crawler/options.go @@ -3,26 +3,29 @@ package crawler import ( "time" - "github.com/libp2p/go-libp2p-core/protocol" + "github.com/libp2p/go-libp2p-kad-dht/amino" + "github.com/libp2p/go-libp2p/core/protocol" ) // Option DHT Crawler option type. type Option func(*options) error type options struct { - protocols []protocol.ID - parallelism int - connectTimeout time.Duration - perMsgTimeout time.Duration + protocols []protocol.ID + parallelism int + connectTimeout time.Duration + perMsgTimeout time.Duration + dialAddressExtendDur time.Duration } // defaults are the default crawler options. This option will be automatically // prepended to any options you pass to the crawler constructor. var defaults = func(o *options) error { - o.protocols = []protocol.ID{"/ipfs/kad/1.0.0"} + o.protocols = amino.Protocols o.parallelism = 1000 o.connectTimeout = time.Second * 5 o.perMsgTimeout = time.Second * 5 + o.dialAddressExtendDur = time.Minute * 30 return nil } @@ -58,3 +61,13 @@ func WithConnectTimeout(timeout time.Duration) Option { return nil } } + +// WithDialAddrExtendDuration sets the duration by which the TTL of dialed address in peer store are +// extended. +// Defaults to 30 minutes if unset. +func WithDialAddrExtendDuration(ext time.Duration) Option { + return func(o *options) error { + o.dialAddressExtendDur = ext + return nil + } +} diff --git a/dht.go b/dht.go index 830be3dd6..e241c9a81 100644 --- a/dht.go +++ b/dht.go @@ -8,17 +8,20 @@ import ( "sync" "time" - "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/protocol" - "github.com/libp2p/go-libp2p-core/routing" + "github.com/libp2p/go-libp2p-routing-helpers/tracing" + "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/protocol" + "github.com/libp2p/go-libp2p/core/routing" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" "github.com/libp2p/go-libp2p-kad-dht/internal" dhtcfg "github.com/libp2p/go-libp2p-kad-dht/internal/config" - "github.com/libp2p/go-libp2p-kad-dht/internal/net" "github.com/libp2p/go-libp2p-kad-dht/metrics" + "github.com/libp2p/go-libp2p-kad-dht/netsize" pb "github.com/libp2p/go-libp2p-kad-dht/pb" "github.com/libp2p/go-libp2p-kad-dht/providers" "github.com/libp2p/go-libp2p-kad-dht/rtrefresh" @@ -27,15 +30,19 @@ import ( record "github.com/libp2p/go-libp2p-record" recpb "github.com/libp2p/go-libp2p-record/pb" - "github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-datastore" - logging "github.com/ipfs/go-log" - "github.com/jbenet/goprocess" - goprocessctx "github.com/jbenet/goprocess/context" + logging "github.com/ipfs/go-log/v2" "github.com/multiformats/go-base32" ma "github.com/multiformats/go-multiaddr" "go.opencensus.io/tag" + "go.uber.org/multierr" "go.uber.org/zap" + "google.golang.org/protobuf/proto" +) + +const ( + tracer = tracing.Tracer("go-libp2p-kad-dht") + dhtName = "IpfsDHT" ) var ( @@ -68,11 +75,6 @@ const ( protectedBuckets = 2 ) -type addPeerRTReq struct { - p peer.ID - queryPeer bool -} - // IpfsDHT is an implementation of Kademlia with S/Kademlia modifications. // It is used to implement the base Routing module. type IpfsDHT struct { @@ -94,20 +96,18 @@ type IpfsDHT struct { Validator record.Validator - ctx context.Context - proc goprocess.Process + ctx context.Context + cancel context.CancelFunc + wg sync.WaitGroup protoMessenger *pb.ProtocolMessenger - msgSender pb.MessageSender - - plk sync.Mutex + msgSender pb.MessageSenderWithDisconnect stripedPutLocks [256]sync.Mutex // DHT protocols we query with. We'll only add peers to our routing // table if they speak these protocols. - protocols []protocol.ID - protocolsStrs []string + protocols []protocol.ID // DHT protocols we can respond to. serverProtocols []protocol.ID @@ -126,6 +126,12 @@ type IpfsDHT struct { autoRefresh bool + // timeout for the lookupCheck operation + lookupCheckTimeout time.Duration + // number of concurrent lookupCheck operations + lookupCheckCapacity int + lookupChecksLk sync.Mutex + // A function returning a set of bootstrap peers to fallback on if all other attempts to fix // the routing table fail (or, e.g., this is the first time this node is // connecting to the network). @@ -141,13 +147,26 @@ type IpfsDHT struct { disableFixLowPeers bool fixLowPeersChan chan struct{} - addPeerToRTChan chan addPeerRTReq + addPeerToRTChan chan peer.ID refreshFinishedCh chan struct{} rtFreezeTimeout time.Duration + // network size estimator + nsEstimator *netsize.Estimator + enableOptProv bool + + // a bound channel to limit asynchronicity of in-flight ADD_PROVIDER RPCs + optProvJobsPool chan struct{} + // configuration variables for tests testAddressUpdateProcessing bool + + // addrFilter is used to filter the addresses we put into the peer store. + // Mostly used to filter out localhost and local addresses. + addrFilter func([]ma.Multiaddr) []ma.Multiaddr + + onRequestHook func(ctx context.Context, s network.Stream, req *pb.Message) } // Assert that IPFS assumptions about interfaces aren't broken. These aren't a @@ -177,7 +196,7 @@ func New(ctx context.Context, h host.Host, options ...Option) (*IpfsDHT, error) return nil, err } - dht, err := makeDHT(ctx, h, cfg) + dht, err := makeDHT(h, cfg) if err != nil { return nil, fmt.Errorf("failed to create DHT, err=%s", err) } @@ -190,7 +209,7 @@ func New(ctx context.Context, h host.Host, options ...Option) (*IpfsDHT, error) dht.disableFixLowPeers = cfg.DisableFixLowPeers dht.Validator = cfg.Validator - dht.msgSender = net.NewMessageSenderImpl(h, dht.protocols) + dht.msgSender = cfg.MsgSenderBuilder(h, dht.protocols) dht.protoMessenger, err = pb.NewProtocolMessenger(dht.msgSender) if err != nil { return nil, err @@ -215,30 +234,27 @@ func New(ctx context.Context, h host.Host, options ...Option) (*IpfsDHT, error) } // register for event bus and network notifications - sn, err := newSubscriberNotifiee(dht) - if err != nil { + if err := dht.startNetworkSubscriber(); err != nil { return nil, err } - dht.proc.Go(sn.subscribe) - // handle providers - if mgr, ok := dht.providerStore.(interface{ Process() goprocess.Process }); ok { - dht.proc.AddChild(mgr.Process()) - } // go-routine to make sure we ALWAYS have RT peer addresses in the peerstore // since RT membership is decoupled from connectivity go dht.persistRTPeersInPeerStore() - dht.proc.Go(dht.rtPeerLoop) + dht.rtPeerLoop() // Fill routing table with currently connected peers that are DHT servers - dht.plk.Lock() for _, p := range dht.host.Network().Peers() { - dht.peerFound(dht.ctx, p, false) + dht.peerFound(p) } - dht.plk.Unlock() - dht.proc.Go(dht.populatePeers) + dht.rtRefreshManager.Start() + + // listens to the fix low peers chan and tries to fix the Routing Table + if !dht.disableFixLowPeers { + dht.runFixLowPeersLoop() + } return dht, nil } @@ -265,7 +281,7 @@ func NewDHTClient(ctx context.Context, h host.Host, dstore ds.Batching) *IpfsDHT return dht } -func makeDHT(ctx context.Context, h host.Host, cfg dhtcfg.Config) (*IpfsDHT, error) { +func makeDHT(h host.Host, cfg dhtcfg.Config) (*IpfsDHT, error) { var protocols, serverProtocols []protocol.ID v1proto := cfg.ProtocolPrefix + kad1 @@ -285,19 +301,24 @@ func makeDHT(ctx context.Context, h host.Host, cfg dhtcfg.Config) (*IpfsDHT, err host: h, birth: time.Now(), protocols: protocols, - protocolsStrs: protocol.ConvertToStrings(protocols), serverProtocols: serverProtocols, bucketSize: cfg.BucketSize, alpha: cfg.Concurrency, beta: cfg.Resiliency, + lookupCheckCapacity: cfg.LookupCheckConcurrency, queryPeerFilter: cfg.QueryPeerFilter, routingTablePeerFilter: cfg.RoutingTable.PeerFilter, rtPeerDiversityFilter: cfg.RoutingTable.DiversityFilter, + addrFilter: cfg.AddressFilter, + onRequestHook: cfg.OnRequestHook, fixLowPeersChan: make(chan struct{}, 1), - addPeerToRTChan: make(chan addPeerRTReq), + addPeerToRTChan: make(chan peer.ID), refreshFinishedCh: make(chan struct{}), + + enableOptProv: cfg.EnableOptimisticProvide, + optProvJobsPool: nil, } var maxLastSuccessfulOutboundThreshold time.Duration @@ -307,7 +328,7 @@ func makeDHT(ctx context.Context, h host.Host, cfg dhtcfg.Config) (*IpfsDHT, err // To grok the Math Wizardy that produced these exact equations, please be patient as a document explaining it will // be published soon. if cfg.Concurrency < cfg.BucketSize { // (alpha < K) - l1 := math.Log(float64(1) / float64(cfg.BucketSize)) //(Log(1/K)) + l1 := math.Log(float64(1) / float64(cfg.BucketSize)) // (Log(1/K)) l2 := math.Log(float64(1) - (float64(cfg.Concurrency) / float64(cfg.BucketSize))) // Log(1 - (alpha / K)) maxLastSuccessfulOutboundThreshold = time.Duration(l1 / l2 * float64(cfg.RoutingTable.RefreshInterval)) } else { @@ -323,27 +344,29 @@ func makeDHT(ctx context.Context, h host.Host, cfg dhtcfg.Config) (*IpfsDHT, err dht.routingTable = rt dht.bootstrapPeers = cfg.BootstrapPeers + dht.lookupCheckTimeout = cfg.RoutingTable.RefreshQueryTimeout + + // init network size estimator + dht.nsEstimator = netsize.NewEstimator(h.ID(), rt, cfg.BucketSize) + + if dht.enableOptProv { + dht.optProvJobsPool = make(chan struct{}, cfg.OptimisticProvideJobsPoolSize) + } + // rt refresh manager - rtRefresh, err := makeRtRefreshManager(dht, cfg, maxLastSuccessfulOutboundThreshold) + dht.rtRefreshManager, err = makeRtRefreshManager(dht, cfg, maxLastSuccessfulOutboundThreshold) if err != nil { return nil, fmt.Errorf("failed to construct RT Refresh Manager,err=%s", err) } - dht.rtRefreshManager = rtRefresh - - // create a DHT proc with the given context - dht.proc = goprocessctx.WithContextAndTeardown(ctx, func() error { - return rtRefresh.Close() - }) // create a tagged context derived from the original context - ctxTags := dht.newContextWithLocalTags(ctx) // the DHT context should be done when the process is closed - dht.ctx = goprocessctx.WithProcessClosing(ctxTags, dht.proc) + dht.ctx, dht.cancel = context.WithCancel(dht.newContextWithLocalTags(context.Background())) if cfg.ProviderStore != nil { dht.providerStore = cfg.ProviderStore } else { - dht.providerStore, err = providers.NewProviderManager(dht.ctx, h.ID(), dht.peerstore, cfg.Datastore) + dht.providerStore, err = providers.NewProviderManager(h.ID(), dht.peerstore, cfg.Datastore) if err != nil { return nil, fmt.Errorf("initializing default provider manager (%v)", err) } @@ -354,6 +377,20 @@ func makeDHT(ctx context.Context, h host.Host, cfg dhtcfg.Config) (*IpfsDHT, err return dht, nil } +// lookupCheck performs a lookup request to a remote peer.ID, verifying that it is able to +// answer it correctly +func (dht *IpfsDHT) lookupCheck(ctx context.Context, p peer.ID) error { + // lookup request to p requesting for its own peer.ID + peerids, err := dht.protoMessenger.GetClosestPeers(ctx, p, p) + // p is expected to return at least 1 peer id, unless our routing table has + // less than bucketSize peers, in which case we aren't picky about who we + // add to the routing table. + if err == nil && len(peerids) == 0 && dht.routingTable.Size() >= dht.bucketSize { + return fmt.Errorf("peer %s failed to return its closest peers, got %d", p, len(peerids)) + } + return err +} + func makeRtRefreshManager(dht *IpfsDHT, cfg dhtcfg.Config, maxLastSuccessfulOutboundThreshold time.Duration) (*rtrefresh.RtRefreshManager, error) { keyGenFnc := func(cpl uint) (string, error) { p, err := dht.routingTable.GenRandPeerID(cpl) @@ -369,6 +406,7 @@ func makeRtRefreshManager(dht *IpfsDHT, cfg dhtcfg.Config, maxLastSuccessfulOutb dht.host, dht.routingTable, cfg.RoutingTable.AutoRefresh, keyGenFnc, queryFnc, + dht.lookupCheck, cfg.RoutingTable.RefreshQueryTimeout, cfg.RoutingTable.RefreshInterval, maxLastSuccessfulOutboundThreshold, @@ -384,7 +422,6 @@ func makeRoutingTable(dht *IpfsDHT, cfg dhtcfg.Config, maxLastSuccessfulOutbound df, err := peerdiversity.NewFilter(dht.rtPeerDiversityFilter, "rt/diversity", func(p peer.ID) int { return kb.CommonPrefixLen(dht.selfKey, kb.ConvertPeerID(p)) }) - if err != nil { return nil, fmt.Errorf("failed to construct peer diversity filter: %w", err) } @@ -433,42 +470,32 @@ func (dht *IpfsDHT) Mode() ModeOpt { return dht.auto } -func (dht *IpfsDHT) populatePeers(_ goprocess.Process) { - if !dht.disableFixLowPeers { - dht.fixLowPeers(dht.ctx) - } - - if err := dht.rtRefreshManager.Start(); err != nil { - logger.Error(err) - } +// runFixLowPeersLoop manages simultaneous requests to fixLowPeers +func (dht *IpfsDHT) runFixLowPeersLoop() { + dht.wg.Add(1) + go func() { + defer dht.wg.Done() - // listens to the fix low peers chan and tries to fix the Routing Table - if !dht.disableFixLowPeers { - dht.proc.Go(dht.fixLowPeersRoutine) - } + dht.fixLowPeers() -} + ticker := time.NewTicker(periodicBootstrapInterval) + defer ticker.Stop() -// fixLowPeersRouting manages simultaneous requests to fixLowPeers -func (dht *IpfsDHT) fixLowPeersRoutine(proc goprocess.Process) { - ticker := time.NewTicker(periodicBootstrapInterval) - defer ticker.Stop() + for { + select { + case <-dht.fixLowPeersChan: + case <-ticker.C: + case <-dht.ctx.Done(): + return + } - for { - select { - case <-dht.fixLowPeersChan: - case <-ticker.C: - case <-proc.Closing(): - return + dht.fixLowPeers() } - - dht.fixLowPeers(dht.Context()) - } - + }() } // fixLowPeers tries to get more peers into the routing table if we're below the threshold -func (dht *IpfsDHT) fixLowPeers(ctx context.Context) { +func (dht *IpfsDHT) fixLowPeers() { if dht.routingTable.Size() > minRTRefreshThreshold { return } @@ -476,7 +503,7 @@ func (dht *IpfsDHT) fixLowPeers(ctx context.Context) { // we try to add all peers we are connected to to the Routing Table // in case they aren't already there. for _, p := range dht.host.Network().Peers() { - dht.peerFound(ctx, p, false) + dht.peerFound(p) } // TODO Active Bootstrapping @@ -493,7 +520,7 @@ func (dht *IpfsDHT) fixLowPeers(ctx context.Context) { found := 0 for _, i := range rand.Perm(len(bootstrapPeers)) { ai := bootstrapPeers[i] - err := dht.Host().Connect(ctx, ai) + err := dht.Host().Connect(dht.ctx, ai) if err == nil { found++ } else { @@ -578,81 +605,123 @@ func (dht *IpfsDHT) putLocal(ctx context.Context, key string, rec *recpb.Record) return dht.datastore.Put(ctx, mkDsKey(key), data) } -func (dht *IpfsDHT) rtPeerLoop(proc goprocess.Process) { - bootstrapCount := 0 - isBootsrapping := false - var timerCh <-chan time.Time +func (dht *IpfsDHT) rtPeerLoop() { + dht.wg.Add(1) + go func() { + defer dht.wg.Done() + + var bootstrapCount uint + var isBootsrapping bool + var timerCh <-chan time.Time + + for { + select { + case <-timerCh: + dht.routingTable.MarkAllPeersIrreplaceable() + case p := <-dht.addPeerToRTChan: + if dht.routingTable.Size() == 0 { + isBootsrapping = true + bootstrapCount = 0 + timerCh = nil + } + // queryPeer set to true as we only try to add queried peers to the RT + newlyAdded, err := dht.routingTable.TryAddPeer(p, true, isBootsrapping) + if err != nil { + // peer not added. + continue + } + if newlyAdded { + // peer was added to the RT, it can now be fixed if needed. + dht.fixRTIfNeeded() + } else { + // the peer is already in our RT, but we just successfully queried it and so let's give it a + // bump on the query time so we don't ping it too soon for a liveliness check. + dht.routingTable.UpdateLastSuccessfulOutboundQueryAt(p, time.Now()) + } + case <-dht.refreshFinishedCh: + bootstrapCount = bootstrapCount + 1 + if bootstrapCount == 2 { + timerCh = time.NewTimer(dht.rtFreezeTimeout).C + } - for { - select { - case <-timerCh: - dht.routingTable.MarkAllPeersIrreplaceable() - case addReq := <-dht.addPeerToRTChan: - prevSize := dht.routingTable.Size() - if prevSize == 0 { - isBootsrapping = true - bootstrapCount = 0 - timerCh = nil - } - newlyAdded, err := dht.routingTable.TryAddPeer(addReq.p, addReq.queryPeer, isBootsrapping) - if err != nil { - // peer not added. - continue - } - if !newlyAdded && addReq.queryPeer { - // the peer is already in our RT, but we just successfully queried it and so let's give it a - // bump on the query time so we don't ping it too soon for a liveliness check. - dht.routingTable.UpdateLastSuccessfulOutboundQueryAt(addReq.p, time.Now()) - } - case <-dht.refreshFinishedCh: - bootstrapCount = bootstrapCount + 1 - if bootstrapCount == 2 { - timerCh = time.NewTimer(dht.rtFreezeTimeout).C - } + old := isBootsrapping + isBootsrapping = false + if old { + dht.rtRefreshManager.RefreshNoWait() + } - old := isBootsrapping - isBootsrapping = false - if old { - dht.rtRefreshManager.RefreshNoWait() + case <-dht.ctx.Done(): + return } - - case <-proc.Closing(): - return } - } + }() } -// peerFound signals the routingTable that we've found a peer that -// might support the DHT protocol. -// If we have a connection a peer but no exchange of a query RPC -> -// LastQueriedAt=time.Now (so we don't ping it for some time for a liveliness check) -// LastUsefulAt=0 -// If we connect to a peer and then exchange a query RPC -> -// LastQueriedAt=time.Now (same reason as above) -// LastUsefulAt=time.Now (so we give it some life in the RT without immediately evicting it) -// If we query a peer we already have in our Routing Table -> -// LastQueriedAt=time.Now() -// LastUsefulAt remains unchanged -// If we connect to a peer we already have in the RT but do not exchange a query (rare) -// Do Nothing. -func (dht *IpfsDHT) peerFound(ctx context.Context, p peer.ID, queryPeer bool) { - if c := baseLogger.Check(zap.DebugLevel, "peer found"); c != nil { - c.Write(zap.String("peer", p.String())) +// peerFound verifies whether the found peer advertises DHT protocols +// and probe it to make sure it answers DHT queries as expected. If +// it fails to answer, it isn't added to the routingTable. +func (dht *IpfsDHT) peerFound(p peer.ID) { + // if the peer is already in the routing table or the appropriate bucket is + // already full, don't try to add the new peer.ID + if !dht.routingTable.UsefulNewPeer(p) { + return } + + // verify whether the remote peer advertises the right dht protocol b, err := dht.validRTPeer(p) if err != nil { logger.Errorw("failed to validate if peer is a DHT peer", "peer", p, "error", err) } else if b { - select { - case dht.addPeerToRTChan <- addPeerRTReq{p, queryPeer}: - case <-dht.ctx.Done(): + + // check if the maximal number of concurrent lookup checks is reached + dht.lookupChecksLk.Lock() + if dht.lookupCheckCapacity == 0 { + dht.lookupChecksLk.Unlock() + // drop the new peer.ID if the maximal number of concurrent lookup + // checks is reached return } + dht.lookupCheckCapacity-- + dht.lookupChecksLk.Unlock() + + go func() { + livelinessCtx, cancel := context.WithTimeout(dht.ctx, dht.lookupCheckTimeout) + defer cancel() + + // performing a FIND_NODE query + err := dht.lookupCheck(livelinessCtx, p) + + dht.lookupChecksLk.Lock() + dht.lookupCheckCapacity++ + dht.lookupChecksLk.Unlock() + + if err != nil { + logger.Debugw("connected peer not answering DHT request as expected", "peer", p, "error", err) + return + } + + // if the FIND_NODE succeeded, the peer is considered as valid + dht.validPeerFound(p) + }() + } +} + +// validPeerFound signals the routingTable that we've found a peer that +// supports the DHT protocol, and just answered correctly to a DHT FindPeers +func (dht *IpfsDHT) validPeerFound(p peer.ID) { + if c := baseLogger.Check(zap.DebugLevel, "peer found"); c != nil { + c.Write(zap.String("peer", p.String())) + } + + select { + case dht.addPeerToRTChan <- p: + case <-dht.ctx.Done(): + return } } // peerStoppedDHT signals the routing table that a peer is unable to responsd to DHT queries anymore. -func (dht *IpfsDHT) peerStoppedDHT(ctx context.Context, p peer.ID) { +func (dht *IpfsDHT) peerStoppedDHT(p peer.ID) { logger.Debugw("peer stopped dht", "peer", p) // A peer that does not support the DHT protocol is dead for us. // There's no point in talking to anymore till it starts supporting the DHT protocol again. @@ -667,13 +736,14 @@ func (dht *IpfsDHT) fixRTIfNeeded() { } // FindLocal looks for a peer with a given ID connected to this dht and returns the peer and the table it was found in. -func (dht *IpfsDHT) FindLocal(id peer.ID) peer.AddrInfo { - switch dht.host.Network().Connectedness(id) { - case network.Connected, network.CanConnect: +func (dht *IpfsDHT) FindLocal(ctx context.Context, id peer.ID) peer.AddrInfo { + _, span := internal.StartSpan(ctx, "IpfsDHT.FindLocal", trace.WithAttributes(attribute.Stringer("PeerID", id))) + defer span.End() + + if hasValidConnectedness(dht.host, id) { return dht.peerstore.PeerInfo(id) - default: - return peer.AddrInfo{} } + return peer.AddrInfo{} } // nearestPeersToQuery returns the routing tables closest peers. @@ -780,11 +850,6 @@ func (dht *IpfsDHT) Context() context.Context { return dht.ctx } -// Process returns the DHT's process. -func (dht *IpfsDHT) Process() goprocess.Process { - return dht.proc -} - // RoutingTable returns the DHT's routingTable. func (dht *IpfsDHT) RoutingTable() *kb.RoutingTable { return dht.routingTable @@ -792,7 +857,25 @@ func (dht *IpfsDHT) RoutingTable() *kb.RoutingTable { // Close calls Process Close. func (dht *IpfsDHT) Close() error { - return dht.proc.Close() + dht.cancel() + dht.wg.Wait() + + var wg sync.WaitGroup + closes := [...]func() error{ + dht.rtRefreshManager.Close, + dht.providerStore.Close, + } + var errors [len(closes)]error + wg.Add(len(errors)) + for i, c := range closes { + go func(i int, c func() error) { + defer wg.Done() + errors[i] = c() + }(i, c) + } + wg.Wait() + + return multierr.Combine(errors[:]...) } func mkDsKey(s string) ds.Key { @@ -816,16 +899,25 @@ func (dht *IpfsDHT) Host() host.Host { // Ping sends a ping message to the passed peer and waits for a response. func (dht *IpfsDHT) Ping(ctx context.Context, p peer.ID) error { + ctx, span := internal.StartSpan(ctx, "IpfsDHT.Ping", trace.WithAttributes(attribute.Stringer("PeerID", p))) + defer span.End() return dht.protoMessenger.Ping(ctx, p) } +// NetworkSize returns the most recent estimation of the DHT network size. +// EXPERIMENTAL: We do not provide any guarantees that this method will +// continue to exist in the codebase. Use it at your own risk. +func (dht *IpfsDHT) NetworkSize() (int32, error) { + return dht.nsEstimator.NetworkSize() +} + // newContextWithLocalTags returns a new context.Context with the InstanceID and // PeerID keys populated. It will also take any extra tags that need adding to // the context as tag.Mutators. func (dht *IpfsDHT) newContextWithLocalTags(ctx context.Context, extraTags ...tag.Mutator) context.Context { extraTags = append( extraTags, - tag.Upsert(metrics.KeyPeerID, dht.self.Pretty()), + tag.Upsert(metrics.KeyPeerID, dht.self.String()), tag.Upsert(metrics.KeyInstanceID, fmt.Sprintf("%p", dht)), ) ctx, _ = tag.New( @@ -837,8 +929,15 @@ func (dht *IpfsDHT) newContextWithLocalTags(ctx context.Context, extraTags ...ta func (dht *IpfsDHT) maybeAddAddrs(p peer.ID, addrs []ma.Multiaddr, ttl time.Duration) { // Don't add addresses for self or our connected peers. We have better ones. - if p == dht.self || dht.host.Network().Connectedness(p) == network.Connected { + if p == dht.self || hasValidConnectedness(dht.host, p) { return } - dht.peerstore.AddAddrs(p, addrs, ttl) + dht.peerstore.AddAddrs(p, dht.filterAddrs(addrs), ttl) +} + +func (dht *IpfsDHT) filterAddrs(addrs []ma.Multiaddr) []ma.Multiaddr { + if f := dht.addrFilter; f != nil { + return f(addrs) + } + return addrs } diff --git a/dht_bootstrap.go b/dht_bootstrap.go index 72d133af5..03029ad51 100644 --- a/dht_bootstrap.go +++ b/dht_bootstrap.go @@ -4,7 +4,7 @@ import ( "context" "time" - "github.com/libp2p/go-libp2p-core/peer" + "github.com/libp2p/go-libp2p/core/peer" "github.com/multiformats/go-multiaddr" ) @@ -57,7 +57,10 @@ func GetDefaultBootstrapPeerAddrInfos() []peer.AddrInfo { // Bootstrap tells the DHT to get into a bootstrapped state satisfying the // IpfsRouter interface. -func (dht *IpfsDHT) Bootstrap(ctx context.Context) error { +func (dht *IpfsDHT) Bootstrap(ctx context.Context) (err error) { + _, end := tracer.Bootstrap(dhtName, ctx) + defer func() { end(err) }() + dht.fixRTIfNeeded() dht.rtRefreshManager.RefreshNoWait() return nil diff --git a/dht_bootstrap_test.go b/dht_bootstrap_test.go index 8ea9c3609..e2236f5a1 100644 --- a/dht_bootstrap_test.go +++ b/dht_bootstrap_test.go @@ -5,9 +5,9 @@ import ( "testing" "time" - "github.com/libp2p/go-libp2p-core/event" - "github.com/libp2p/go-libp2p-core/peer" kb "github.com/libp2p/go-libp2p-kbucket" + "github.com/libp2p/go-libp2p/core/event" + "github.com/libp2p/go-libp2p/core/peer" "github.com/stretchr/testify/require" ) @@ -191,8 +191,8 @@ func TestBootstrappersReplacable(t *testing.T) { require.NoError(t, d.host.Network().ClosePeer(d5.self)) connectNoSync(t, ctx, d, d1) connectNoSync(t, ctx, d, d5) - d.peerFound(ctx, d5.self, true) - d.peerFound(ctx, d1.self, true) + d.peerFound(d5.self) + d.peerFound(d1.self) time.Sleep(1 * time.Second) require.Len(t, d.routingTable.ListPeers(), 2) diff --git a/dht_filters.go b/dht_filters.go index b6c041ea1..aa7c4799f 100644 --- a/dht_filters.go +++ b/dht_filters.go @@ -6,9 +6,9 @@ import ( "sync" "time" - "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/host" + "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/peer" "github.com/google/gopacket/routing" netroute "github.com/libp2p/go-netroute" @@ -238,3 +238,8 @@ func inAddrRange(ip net.IP, ipnets []*net.IPNet) bool { return false } + +func hasValidConnectedness(host host.Host, id peer.ID) bool { + connectedness := host.Network().Connectedness(id) + return connectedness == network.Connected || connectedness == network.Limited +} diff --git a/dht_filters_test.go b/dht_filters_test.go index 000d4572f..7714b8d9e 100644 --- a/dht_filters_test.go +++ b/dht_filters_test.go @@ -3,11 +3,12 @@ package dht import ( "context" "net" + "sync/atomic" "testing" - ic "github.com/libp2p/go-libp2p-core/crypto" - "github.com/libp2p/go-libp2p-core/network" - "github.com/libp2p/go-libp2p-core/peer" + ic "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/peer" ma "github.com/multiformats/go-multiaddr" manet "github.com/multiformats/go-multiaddr/net" ) @@ -31,21 +32,31 @@ func TestIsRelay(t *testing.T) { type mockConn struct { local peer.AddrInfo remote peer.AddrInfo + + isClosed atomic.Bool } var _ network.Conn = (*mockConn)(nil) -func (m *mockConn) ID() string { return "0" } -func (m *mockConn) Close() error { return nil } +func (m *mockConn) ID() string { return "0" } +func (m *mockConn) Close() error { + m.isClosed.Store(true) + return nil +} func (m *mockConn) NewStream(context.Context) (network.Stream, error) { return nil, nil } func (m *mockConn) GetStreams() []network.Stream { return []network.Stream{} } -func (m *mockConn) Stat() network.Stat { return network.Stat{Direction: network.DirOutbound} } -func (m *mockConn) LocalMultiaddr() ma.Multiaddr { return m.local.Addrs[0] } -func (m *mockConn) RemoteMultiaddr() ma.Multiaddr { return m.remote.Addrs[0] } -func (m *mockConn) LocalPeer() peer.ID { return m.local.ID } -func (m *mockConn) LocalPrivateKey() ic.PrivKey { return nil } -func (m *mockConn) RemotePeer() peer.ID { return m.remote.ID } -func (m *mockConn) RemotePublicKey() ic.PubKey { return nil } +func (m *mockConn) Stat() network.ConnStats { + return network.ConnStats{Stats: network.Stats{Direction: network.DirOutbound}} +} +func (m *mockConn) Scope() network.ConnScope { return &network.NullScope{} } +func (m *mockConn) LocalMultiaddr() ma.Multiaddr { return m.local.Addrs[0] } +func (m *mockConn) RemoteMultiaddr() ma.Multiaddr { return m.remote.Addrs[0] } +func (m *mockConn) LocalPeer() peer.ID { return m.local.ID } +func (m *mockConn) LocalPrivateKey() ic.PrivKey { return nil } +func (m *mockConn) RemotePeer() peer.ID { return m.remote.ID } +func (m *mockConn) RemotePublicKey() ic.PubKey { return nil } +func (m *mockConn) ConnState() network.ConnectionState { return network.ConnectionState{} } +func (m *mockConn) IsClosed() bool { return m.isClosed.Load() } func TestFilterCaching(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) diff --git a/dht_net.go b/dht_net.go index 18f4bd733..ff6a7e23e 100644 --- a/dht_net.go +++ b/dht_net.go @@ -4,11 +4,12 @@ import ( "io" "time" - "github.com/libp2p/go-libp2p-core/network" + "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p-kad-dht/internal/net" "github.com/libp2p/go-libp2p-kad-dht/metrics" pb "github.com/libp2p/go-libp2p-kad-dht/pb" + "google.golang.org/protobuf/proto" "github.com/libp2p/go-msgio" "go.opencensus.io/stats" @@ -44,7 +45,7 @@ func (dht *IpfsDHT) handleNewMessage(s network.Stream) bool { for { if dht.getMode() != modeServer { - logger.Errorf("ignoring incoming dht message while not in server mode") + logger.Debugf("ignoring incoming dht message while not in server mode") return false } @@ -72,7 +73,7 @@ func (dht *IpfsDHT) handleNewMessage(s network.Stream) bool { } return false } - err = req.Unmarshal(msgbytes) + err = proto.Unmarshal(msgbytes, &req) r.ReleaseMsg(msgbytes) if err != nil { if c := baseLogger.Check(zap.DebugLevel, "error unmarshaling message"); c != nil { @@ -100,6 +101,10 @@ func (dht *IpfsDHT) handleNewMessage(s network.Stream) bool { metrics.ReceivedBytes.M(int64(msgLen)), ) + if dht.onRequestHook != nil { + dht.onRequestHook(ctx, s, &req) + } + handler := dht.handlerForMsgType(req.GetType()) if handler == nil { stats.Record(ctx, metrics.ReceivedMessageErrors.M(1)) @@ -110,9 +115,6 @@ func (dht *IpfsDHT) handleNewMessage(s network.Stream) bool { return false } - // a peer has queried us, let's add it to RT - dht.peerFound(dht.ctx, mPeer, true) - if c := baseLogger.Check(zap.DebugLevel, "handling message"); c != nil { c.Write(zap.String("from", mPeer.String()), zap.Int32("type", int32(req.GetType())), diff --git a/dht_options.go b/dht_options.go index a87f320b6..793d9bf68 100644 --- a/dht_options.go +++ b/dht_options.go @@ -1,19 +1,24 @@ package dht import ( + "context" "fmt" "testing" "time" - "github.com/libp2p/go-libp2p-core/peer" - "github.com/libp2p/go-libp2p-core/protocol" + "github.com/libp2p/go-libp2p-kad-dht/amino" dhtcfg "github.com/libp2p/go-libp2p-kad-dht/internal/config" + pb "github.com/libp2p/go-libp2p-kad-dht/pb" "github.com/libp2p/go-libp2p-kad-dht/providers" - "github.com/libp2p/go-libp2p-kbucket/peerdiversity" record "github.com/libp2p/go-libp2p-record" + "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/protocol" ds "github.com/ipfs/go-datastore" + ma "github.com/multiformats/go-multiaddr" ) // ModeOpt describes what mode the dht should operate in @@ -32,7 +37,7 @@ const ( ) // DefaultPrefix is the application specific prefix attached to all DHT protocols by default. -const DefaultPrefix protocol.ID = "/ipfs" +const DefaultPrefix protocol.ID = amino.ProtocolPrefix type Option = dhtcfg.Option @@ -65,9 +70,9 @@ func RoutingTableRefreshQueryTimeout(timeout time.Duration) Option { // RoutingTableRefreshPeriod sets the period for refreshing buckets in the // routing table. The DHT will refresh buckets every period by: // -// 1. First searching for nearby peers to figure out how many buckets we should try to fill. -// 1. Then searching for a random key in each bucket that hasn't been queried in -// the last refresh period. +// 1. First searching for nearby peers to figure out how many buckets we should try to fill. +// 1. Then searching for a random key in each bucket that hasn't been queried in +// the last refresh period. func RoutingTableRefreshPeriod(period time.Duration) Option { return func(c *dhtcfg.Config) error { c.RoutingTable.RefreshInterval = period @@ -134,7 +139,7 @@ func NamespacedValidator(ns string, v record.Validator) Option { // ProtocolPrefix sets an application specific prefix to be attached to all DHT protocols. For example, // /myapp/kad/1.0.0 instead of /ipfs/kad/1.0.0. Prefix should be of the form /myapp. // -// Defaults to dht.DefaultPrefix +// Defaults to amino.ProtocolPrefix func ProtocolPrefix(prefix protocol.ID) Option { return func(c *dhtcfg.Config) error { c.ProtocolPrefix = prefix @@ -165,7 +170,7 @@ func V1ProtocolOverride(proto protocol.ID) Option { // BucketSize configures the bucket size (k in the Kademlia paper) of the routing table. // -// The default value is 20. +// The default value is amino.DefaultBucketSize func BucketSize(bucketSize int) Option { return func(c *dhtcfg.Config) error { c.BucketSize = bucketSize @@ -175,7 +180,7 @@ func BucketSize(bucketSize int) Option { // Concurrency configures the number of concurrent requests (alpha in the Kademlia paper) for a given query path. // -// The default value is 10. +// The default value is amino.DefaultConcurrency func Concurrency(alpha int) Option { return func(c *dhtcfg.Config) error { c.Concurrency = alpha @@ -186,7 +191,7 @@ func Concurrency(alpha int) Option { // Resiliency configures the number of peers closest to a target that must have responded in order for a given query // path to complete. // -// The default value is 3. +// The default value is amino.DefaultResiliency func Resiliency(beta int) Option { return func(c *dhtcfg.Config) error { c.Resiliency = beta @@ -194,6 +199,15 @@ func Resiliency(beta int) Option { } } +// LookupCheckConcurrency configures maximal number of go routines that can be used to +// perform a lookup check operation, before adding a new node to the routing table. +func LookupCheckConcurrency(n int) Option { + return func(c *dhtcfg.Config) error { + c.LookupCheckConcurrency = n + return nil + } +} + // MaxRecordAge specifies the maximum time that any node will hold onto a record ("PutValue record") // from the time its received. This does not apply to any other forms of validity that // the record may contain. @@ -309,3 +323,61 @@ func forceAddressUpdateProcessing(t *testing.T) Option { return nil } } + +// EnableOptimisticProvide enables an optimization that skips the last hops of the provide process. +// This works by using the network size estimator (which uses the keyspace density of queries) +// to optimistically send ADD_PROVIDER requests when we most likely have found the last hop. +// It will also run some ADD_PROVIDER requests asynchronously in the background after returning, +// this allows to optimistically return earlier if some threshold number of RPCs have succeeded. +// The number of background/in-flight queries can be configured with the OptimisticProvideJobsPoolSize +// option. +// +// EXPERIMENTAL: This is an experimental option and might be removed in the future. Use at your own risk. +func EnableOptimisticProvide() Option { + return func(c *dhtcfg.Config) error { + c.EnableOptimisticProvide = true + return nil + } +} + +// OptimisticProvideJobsPoolSize allows to configure the asynchronicity limit for in-flight ADD_PROVIDER RPCs. +// It makes sense to set it to a multiple of optProvReturnRatio * BucketSize. Check the description of +// EnableOptimisticProvide for more details. +// +// EXPERIMENTAL: This is an experimental option and might be removed in the future. Use at your own risk. +func OptimisticProvideJobsPoolSize(size int) Option { + return func(c *dhtcfg.Config) error { + c.OptimisticProvideJobsPoolSize = size + return nil + } +} + +// AddressFilter allows to configure the address filtering function. +// This function is run before addresses are added to the peerstore. +// It is most useful to avoid adding localhost / local addresses. +func AddressFilter(f func([]ma.Multiaddr) []ma.Multiaddr) Option { + return func(c *dhtcfg.Config) error { + c.AddressFilter = f + return nil + } +} + +// WithCustomMessageSender configures the pb.MessageSender of the IpfsDHT to use the +// custom implementation of the pb.MessageSender +func WithCustomMessageSender(messageSenderBuilder func(h host.Host, protos []protocol.ID) pb.MessageSenderWithDisconnect) Option { + return func(c *dhtcfg.Config) error { + c.MsgSenderBuilder = messageSenderBuilder + return nil + } +} + +// OnRequestHook registers a callback function that will be invoked for every +// incoming DHT protocol message. +// Note: Ensure that the callback executes efficiently, as it will block the +// entire message handler. +func OnRequestHook(f func(ctx context.Context, s network.Stream, req *pb.Message)) Option { + return func(c *dhtcfg.Config) error { + c.OnRequestHook = f + return nil + } +} diff --git a/dht_test.go b/dht_test.go index b172af35e..cb85d336a 100644 --- a/dht_test.go +++ b/dht_test.go @@ -14,14 +14,22 @@ import ( "testing" "time" - "github.com/libp2p/go-libp2p-core/event" - "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" + "github.com/libp2p/go-libp2p-kad-dht/internal" + "github.com/libp2p/go-libp2p-kad-dht/internal/net" + "github.com/libp2p/go-libp2p-kad-dht/providers" + "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/event" + "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/protocol" + "github.com/libp2p/go-libp2p/core/routing" + "github.com/libp2p/go-msgio" ma "github.com/multiformats/go-multiaddr" + manet "github.com/multiformats/go-multiaddr/net" "github.com/multiformats/go-multihash" "github.com/multiformats/go-multistream" + "google.golang.org/protobuf/proto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -31,11 +39,10 @@ import ( "github.com/ipfs/go-cid" detectrace "github.com/ipfs/go-detect-race" - u "github.com/ipfs/go-ipfs-util" kb "github.com/libp2p/go-libp2p-kbucket" record "github.com/libp2p/go-libp2p-record" - swarmt "github.com/libp2p/go-libp2p-swarm/testing" bhost "github.com/libp2p/go-libp2p/p2p/host/basic" + swarmt "github.com/libp2p/go-libp2p/p2p/net/swarm/testing" ) var testCaseCids []cid.Cid @@ -47,10 +54,10 @@ func init() { var newCid cid.Cid switch i % 3 { case 0: - mhv := u.Hash([]byte(v)) + mhv := internal.Hash([]byte(v)) newCid = cid.NewCidV0(mhv) case 1: - mhv := u.Hash([]byte(v)) + mhv := internal.Hash([]byte(v)) newCid = cid.NewCidV1(cid.DagCBOR, mhv) case 2: rawMh := make([]byte, 12) @@ -119,6 +126,7 @@ func setupDHT(ctx context.Context, t *testing.T, client bool, options ...Option) host, err := bhost.NewHost(swarmt.GenSwarm(t, swarmt.OptDisableReuseport), new(bhost.HostOpts)) require.NoError(t, err) + host.Start() t.Cleanup(func() { host.Close() }) d, err := New(ctx, host, append(baseOpts, options...)...) @@ -164,9 +172,7 @@ func connectNoSync(t *testing.T, ctx context.Context, a, b *IpfsDHT) { t.Fatal("peers setup incorrectly: no local address") } - a.peerstore.AddAddrs(idB, addrB, peerstore.TempAddrTTL) - pi := peer.AddrInfo{ID: idB} - if err := a.host.Connect(ctx, pi); err != nil { + if err := a.host.Connect(ctx, peer.AddrInfo{ID: idB, Addrs: addrB}); err != nil { t.Fatal(err) } } @@ -294,8 +300,6 @@ func TestValueGetSet(t *testing.T) { t.Fatalf("Expected 'world' got '%s'", string(val)) } - // late connect - connect(t, ctx, dhts[2], dhts[0]) connect(t, ctx, dhts[2], dhts[1]) @@ -561,12 +565,136 @@ func TestProvides(t *testing.T) { if prov.ID != dhts[3].self { t.Fatal("Got back wrong provider") } + if len(prov.Addrs) == 0 { + t.Fatal("Got no addresses back") + } case <-ctxT.Done(): t.Fatal("Did not get a provider back.") } } } +type testMessageSender struct { + sendRequest func(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) + sendMessage func(ctx context.Context, p peer.ID, pmes *pb.Message) error +} + +var _ pb.MessageSender = (*testMessageSender)(nil) + +func (t testMessageSender) SendRequest(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { + return t.sendRequest(ctx, p, pmes) +} + +func (t testMessageSender) SendMessage(ctx context.Context, p peer.ID, pmes *pb.Message) error { + return t.sendMessage(ctx, p, pmes) +} + +func TestProvideAddressFilter(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + dhts := setupDHTS(t, ctx, 2) + + connect(t, ctx, dhts[0], dhts[1]) + testMaddr := ma.StringCast("/ip4/99.99.99.99/tcp/9999") + + done := make(chan struct{}) + impl := net.NewMessageSenderImpl(dhts[0].host, dhts[0].protocols) + tms := &testMessageSender{ + sendMessage: func(ctx context.Context, p peer.ID, pmes *pb.Message) error { + defer close(done) + assert.Equal(t, pmes.Type, pb.Message_ADD_PROVIDER) + assert.Len(t, pmes.ProviderPeers[0].Addrs, 1) + assert.True(t, pmes.ProviderPeers[0].Addresses()[0].Equal(testMaddr)) + return impl.SendMessage(ctx, p, pmes) + }, + sendRequest: func(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) { + return impl.SendRequest(ctx, p, pmes) + }, + } + pm, err := pb.NewProtocolMessenger(tms) + require.NoError(t, err) + + dhts[0].protoMessenger = pm + dhts[0].addrFilter = func(multiaddrs []ma.Multiaddr) []ma.Multiaddr { + return []ma.Multiaddr{testMaddr} + } + + if err := dhts[0].Provide(ctx, testCaseCids[0], true); err != nil { + t.Fatal(err) + } + + select { + case <-done: + case <-time.After(5 * time.Second): + t.Fatal("timeout") + } +} + +type testProviderManager struct { + addProvider func(ctx context.Context, key []byte, prov peer.AddrInfo) error + getProviders func(ctx context.Context, key []byte) ([]peer.AddrInfo, error) + close func() error +} + +var _ providers.ProviderStore = (*testProviderManager)(nil) + +func (t *testProviderManager) AddProvider(ctx context.Context, key []byte, prov peer.AddrInfo) error { + return t.addProvider(ctx, key, prov) +} + +func (t *testProviderManager) GetProviders(ctx context.Context, key []byte) ([]peer.AddrInfo, error) { + return t.getProviders(ctx, key) +} + +func (t *testProviderManager) Close() error { + return t.close() +} + +func TestHandleAddProviderAddressFilter(t *testing.T) { + ctx := context.Background() + + d := setupDHT(ctx, t, false) + provider := setupDHT(ctx, t, false) + + testMaddr := ma.StringCast("/ip4/99.99.99.99/tcp/9999") + + d.addrFilter = func(multiaddrs []ma.Multiaddr) []ma.Multiaddr { + return []ma.Multiaddr{testMaddr} + } + + done := make(chan struct{}) + d.providerStore = &testProviderManager{ + addProvider: func(ctx context.Context, key []byte, prov peer.AddrInfo) error { + defer close(done) + assert.True(t, prov.Addrs[0].Equal(testMaddr)) + return nil + }, + close: func() error { return nil }, + } + + pmes := &pb.Message{ + Type: pb.Message_ADD_PROVIDER, + Key: []byte("test-key"), + ProviderPeers: pb.RawPeerInfosToPBPeers([]peer.AddrInfo{{ + ID: provider.self, + Addrs: []ma.Multiaddr{ + ma.StringCast("/ip4/55.55.55.55/tcp/5555"), + ma.StringCast("/ip4/66.66.66.66/tcp/6666"), + }, + }}), + } + + _, err := d.handleAddProvider(ctx, provider.self, pmes) + require.NoError(t, err) + + select { + case <-done: + case <-time.After(5 * time.Second): + t.Fatal("timeout") + } +} + func TestLocalProvides(t *testing.T) { // t.Skip("skipping test to debug another") ctx, cancel := context.WithCancel(context.Background()) @@ -603,30 +731,52 @@ func TestLocalProvides(t *testing.T) { } } +func TestAddressFilterProvide(t *testing.T) { + // t.Skip("skipping test to debug another") + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + testMaddr := ma.StringCast("/ip4/99.99.99.99/tcp/9999") + + d := setupDHT(ctx, t, false) + provider := setupDHT(ctx, t, false) + + d.addrFilter = func(maddrs []ma.Multiaddr) []ma.Multiaddr { + return []ma.Multiaddr{ + testMaddr, + } + } + + _, err := d.handleAddProvider(ctx, provider.self, &pb.Message{ + Type: pb.Message_ADD_PROVIDER, + Key: []byte("random-key"), + ProviderPeers: pb.PeerInfosToPBPeers(provider.host.Network(), []peer.AddrInfo{{ + ID: provider.self, + Addrs: provider.host.Addrs(), + }}), + }) + require.NoError(t, err) + + // because of the identify protocol we add all + // addresses to the peerstore, although the addresses + // will be filtered in the above handleAddProvider call + d.peerstore.AddAddrs(provider.self, provider.host.Addrs(), time.Hour) + + resp, err := d.handleGetProviders(ctx, d.self, &pb.Message{ + Type: pb.Message_GET_PROVIDERS, + Key: []byte("random-key"), + }) + require.NoError(t, err) + + assert.True(t, resp.ProviderPeers[0].Addresses()[0].Equal(testMaddr)) + assert.Len(t, resp.ProviderPeers[0].Addresses(), 1) +} + // if minPeers or avgPeers is 0, dont test for it. func waitForWellFormedTables(t *testing.T, dhts []*IpfsDHT, minPeers, avgPeers int, timeout time.Duration) { // test "well-formed-ness" (>= minPeers peers in every routing table) t.Helper() - checkTables := func() bool { - totalPeers := 0 - for _, dht := range dhts { - rtlen := dht.routingTable.Size() - totalPeers += rtlen - if minPeers > 0 && rtlen < minPeers { - //t.Logf("routing table for %s only has %d peers (should have >%d)", dht.self, rtlen, minPeers) - return false - } - } - actualAvgPeers := totalPeers / len(dhts) - t.Logf("avg rt size: %d", actualAvgPeers) - if avgPeers > 0 && actualAvgPeers < avgPeers { - t.Logf("avg rt size: %d < %d", actualAvgPeers, avgPeers) - return false - } - return true - } - timeoutA := time.After(timeout) for { select { @@ -634,7 +784,7 @@ func waitForWellFormedTables(t *testing.T, dhts []*IpfsDHT, minPeers, avgPeers i t.Errorf("failed to reach well-formed routing tables after %s", timeout) return case <-time.After(5 * time.Millisecond): - if checkTables() { + if checkForWellFormedTablesOnce(t, dhts, minPeers, avgPeers) { // succeeded return } @@ -642,6 +792,26 @@ func waitForWellFormedTables(t *testing.T, dhts []*IpfsDHT, minPeers, avgPeers i } } +func checkForWellFormedTablesOnce(t *testing.T, dhts []*IpfsDHT, minPeers, avgPeers int) bool { + t.Helper() + totalPeers := 0 + for _, dht := range dhts { + rtlen := dht.routingTable.Size() + totalPeers += rtlen + if minPeers > 0 && rtlen < minPeers { + // t.Logf("routing table for %s only has %d peers (should have >%d)", dht.self, rtlen, minPeers) + return false + } + } + actualAvgPeers := totalPeers / len(dhts) + t.Logf("avg rt size: %d", actualAvgPeers) + if avgPeers > 0 && actualAvgPeers < avgPeers { + t.Logf("avg rt size: %d < %d", actualAvgPeers, avgPeers) + return false + } + return true +} + func printRoutingTables(dhts []*IpfsDHT) { // the routing tables should be full now. let's inspect them. fmt.Printf("checking routing table of %d\n", len(dhts)) @@ -677,26 +847,18 @@ func TestRefresh(t *testing.T) { <-time.After(100 * time.Millisecond) // bootstrap a few times until we get good tables. t.Logf("bootstrapping them so they find each other %d", nDHTs) - ctxT, cancelT := context.WithTimeout(ctx, 5*time.Second) - defer cancelT() - for ctxT.Err() == nil { - bootstrap(t, ctxT, dhts) + for { + bootstrap(t, ctx, dhts) - // wait a bit. - select { - case <-time.After(50 * time.Millisecond): - continue // being explicit - case <-ctxT.Done(): - return + if checkForWellFormedTablesOnce(t, dhts, 7, 10) { + break } - } - - waitForWellFormedTables(t, dhts, 7, 10, 10*time.Second) - cancelT() + time.Sleep(time.Microsecond * 50) + } - if u.Debug { + if testing.Verbose() { // the routing tables should be full now. let's inspect them. printRoutingTables(dhts) } @@ -708,6 +870,7 @@ func TestRefreshBelowMinRTThreshold(t *testing.T) { host, err := bhost.NewHost(swarmt.GenSwarm(t, swarmt.OptDisableReuseport), new(bhost.HostOpts)) require.NoError(t, err) + host.Start() // enable auto bootstrap on A dhtA, err := New( @@ -763,7 +926,8 @@ func TestRefreshBelowMinRTThreshold(t *testing.T) { connect(t, ctx, dhtA, dhtD) // and because of the above bootstrap, A also discovers E ! - waitForWellFormedTables(t, []*IpfsDHT{dhtA}, 4, 4, 20*time.Second) + waitForWellFormedTables(t, []*IpfsDHT{dhtA}, 4, 4, 10*time.Second) + time.Sleep(100 * time.Millisecond) assert.Equal(t, dhtE.self, dhtA.routingTable.Find(dhtE.self), "A's routing table should have peer E!") } @@ -839,7 +1003,7 @@ func TestPeriodicRefresh(t *testing.T) { } } - if u.Debug { + if testing.Verbose() { printRoutingTables(dhts) } @@ -858,7 +1022,7 @@ func TestPeriodicRefresh(t *testing.T) { // until the routing tables look better, or some long timeout for the failure case. waitForWellFormedTables(t, dhts, 7, 10, 20*time.Second) - if u.Debug { + if testing.Verbose() { printRoutingTables(dhts) } } @@ -893,7 +1057,7 @@ func TestProvidesMany(t *testing.T) { defer cancel() bootstrap(t, ctxT, dhts) - if u.Debug { + if testing.Verbose() { // the routing tables should be full now. let's inspect them. t.Logf("checking routing table of %d", nDHTs) for _, dht := range dhts { @@ -1134,6 +1298,7 @@ func TestFindPeerWithQueryFilter(t *testing.T) { filteredPeer, err := bhost.NewHost(swarmt.GenSwarm(t, swarmt.OptDisableReuseport), new(bhost.HostOpts)) require.NoError(t, err) + filteredPeer.Start() defer filteredPeer.Close() dhts := setupDHTS(t, ctx, 4, QueryFilter(func(_ interface{}, ai peer.AddrInfo) bool { return ai.ID != filteredPeer.ID() @@ -1321,6 +1486,89 @@ func TestClientModeConnect(t *testing.T) { } } +func TestInvalidServer(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + s0 := setupDHT(ctx, t, false, BucketSize(2)) // server + s1 := setupDHT(ctx, t, false, BucketSize(2)) // server + m0 := setupDHT(ctx, t, false, BucketSize(2)) // misbehabing server + m1 := setupDHT(ctx, t, false, BucketSize(2)) // misbehabing server + + // make m0 and m1 advertise all dht server protocols, but hang on all requests + for _, protocol := range s0.serverProtocols { + for _, m := range []*IpfsDHT{m0, m1} { + // Hang on every request. + m.host.SetStreamHandler(protocol, func(s network.Stream) { + r := msgio.NewVarintReaderSize(s, network.MessageSizeMax) + msgbytes, err := r.ReadMsg() + if err != nil { + t.Fatal(err) + } + var req pb.Message + err = proto.Unmarshal(msgbytes, &req) + if err != nil { + t.Fatal(err) + } + + // answer with an empty response message + resp := pb.NewMessage(req.GetType(), nil, req.GetClusterLevel()) + + // send out response msg + err = net.WriteMsg(s, resp) + if err != nil { + t.Fatal(err) + } + }) + } + } + + // connect s0 and m0 + connectNoSync(t, ctx, s0, m0) + + // add a provider (p) for a key (k) to s0 + k := testCaseCids[0] + p := peer.ID("TestPeer") + s0.ProviderStore().AddProvider(ctx, k.Hash(), peer.AddrInfo{ID: p}) + time.Sleep(time.Millisecond * 5) // just in case... + + // find the provider for k from m0 + provs, err := m0.FindProviders(ctx, k) + if err != nil { + t.Fatal(err) + } + if len(provs) == 0 { + t.Fatal("Expected to get a provider back") + } + if provs[0].ID != p { + t.Fatal("expected it to be our test peer") + } + + // verify that m0 and s0 contain each other in their routing tables + if s0.routingTable.Find(m0.self) == "" { + // m0 is added to s0 routing table even though it is misbehaving, because + // s0's routing table is not well populated, so s0 isn't picky about who it adds. + t.Fatal("Misbehaving DHT servers should be added to routing table if not well populated") + } + if m0.routingTable.Find(s0.self) == "" { + t.Fatal("DHT server should have been added to the misbehaving server routing table") + } + + // connect s0 to both s1 and m1 + connectNoSync(t, ctx, s0, s1) + connectNoSync(t, ctx, s0, m1) + + // s1 should be added to s0's routing table. Then, because s0's routing table + // contains more than bucketSize (2) entries, lookupCheck is enabled and m1 + // shouldn't be added, because it fails the lookupCheck (hang on all requests). + if s0.routingTable.Find(s1.self) == "" { + t.Fatal("Well behaving DHT server should have been added to the server routing table") + } + if s0.routingTable.Find(m1.self) != "" { + t.Fatal("Misbehaving DHT servers should not be added to routing table if well populated") + } +} + func TestClientModeFindPeer(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() @@ -1529,9 +1777,7 @@ func TestProvideDisabled(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - var ( - optsA, optsB []Option - ) + var optsA, optsB []Option optsA = append(optsA, ProtocolPrefix("/provMaybeDisabled")) optsB = append(optsB, ProtocolPrefix("/provMaybeDisabled")) @@ -1603,6 +1849,7 @@ func TestHandleRemotePeerProtocolChanges(t *testing.T) { // start host 1 that speaks dht v1 hA, err := bhost.NewHost(swarmt.GenSwarm(t, swarmt.OptDisableReuseport), new(bhost.HostOpts)) require.NoError(t, err) + hA.Start() defer hA.Close() dhtA, err := New(ctx, hA, os...) require.NoError(t, err) @@ -1611,6 +1858,7 @@ func TestHandleRemotePeerProtocolChanges(t *testing.T) { // start host 2 that also speaks dht v1 hB, err := bhost.NewHost(swarmt.GenSwarm(t, swarmt.OptDisableReuseport), new(bhost.HostOpts)) require.NoError(t, err) + hB.Start() defer hB.Close() dhtB, err := New(ctx, hB, os...) require.NoError(t, err) @@ -1648,6 +1896,7 @@ func TestGetSetPluggedProtocol(t *testing.T) { hA, err := bhost.NewHost(swarmt.GenSwarm(t, swarmt.OptDisableReuseport), new(bhost.HostOpts)) require.NoError(t, err) + hA.Start() defer hA.Close() dhtA, err := New(ctx, hA, os...) require.NoError(t, err) @@ -1655,6 +1904,7 @@ func TestGetSetPluggedProtocol(t *testing.T) { hB, err := bhost.NewHost(swarmt.GenSwarm(t, swarmt.OptDisableReuseport), new(bhost.HostOpts)) require.NoError(t, err) + hB.Start() defer hB.Close() dhtB, err := New(ctx, hB, os...) require.NoError(t, err) @@ -1679,6 +1929,7 @@ func TestGetSetPluggedProtocol(t *testing.T) { hA, err := bhost.NewHost(swarmt.GenSwarm(t, swarmt.OptDisableReuseport), new(bhost.HostOpts)) require.NoError(t, err) + hA.Start() defer hA.Close() dhtA, err := New(ctx, hA, []Option{ ProtocolPrefix("/esh"), @@ -1691,6 +1942,7 @@ func TestGetSetPluggedProtocol(t *testing.T) { hB, err := bhost.NewHost(swarmt.GenSwarm(t, swarmt.OptDisableReuseport), new(bhost.HostOpts)) require.NoError(t, err) + hB.Start() defer hB.Close() dhtB, err := New(ctx, hB, []Option{ ProtocolPrefix("/lsr"), @@ -1735,7 +1987,7 @@ func TestClientModeAtInit(t *testing.T) { client := setupDHT(ctx, t, true) pinger.Host().Peerstore().AddAddrs(client.PeerID(), client.Host().Addrs(), peerstore.AddressTTL) err := pinger.Ping(context.Background(), client.PeerID()) - assert.True(t, errors.Is(err, multistream.ErrNotSupported)) + assert.True(t, errors.Is(err, multistream.ErrNotSupported[protocol.ID]{})) } func TestModeChange(t *testing.T) { @@ -1746,7 +1998,7 @@ func TestModeChange(t *testing.T) { clientToServer := setupDHT(ctx, t, true) clientOnly.Host().Peerstore().AddAddrs(clientToServer.PeerID(), clientToServer.Host().Addrs(), peerstore.AddressTTL) err := clientOnly.Ping(ctx, clientToServer.PeerID()) - assert.True(t, errors.Is(err, multistream.ErrNotSupported)) + assert.True(t, errors.Is(err, multistream.ErrNotSupported[protocol.ID]{})) err = clientToServer.setMode(modeServer) assert.Nil(t, err) err = clientOnly.Ping(ctx, clientToServer.PeerID()) @@ -1775,7 +2027,7 @@ func TestDynamicModeSwitching(t *testing.T) { assertDHTClient := func() { err = prober.Ping(ctx, node.PeerID()) - assert.True(t, errors.Is(err, multistream.ErrNotSupported)) + assert.True(t, errors.Is(err, multistream.ErrNotSupported[protocol.ID]{})) if l := len(prober.RoutingTable().ListPeers()); l != 0 { t.Errorf("expected routing table length to be 0; instead is %d", l) } @@ -1950,14 +2202,16 @@ func TestBootStrapWhenRTIsEmpty(t *testing.T) { // convert the bootstrap addresses to a p2p address bootstrapAddrs := make([]peer.AddrInfo, nBootStraps) for i := 0; i < nBootStraps; i++ { - b := peer.AddrInfo{ID: bootstrappers[i].self, - Addrs: bootstrappers[i].host.Addrs()} + b := peer.AddrInfo{ + ID: bootstrappers[i].self, + Addrs: bootstrappers[i].host.Addrs(), + } bootstrapAddrs[i] = b } { - //---------------- + // ---------------- // We will initialize a DHT with 1 bootstrapper, connect it to another DHT, // then remove the latter from the Routing Table // This should add the bootstrap peer and the peer that the bootstrap peer is conencted to @@ -1966,6 +2220,7 @@ func TestBootStrapWhenRTIsEmpty(t *testing.T) { h1, err := bhost.NewHost(swarmt.GenSwarm(t, swarmt.OptDisableReuseport), new(bhost.HostOpts)) require.NoError(t, err) + h1.Start() dht1, err := New( ctx, h1, @@ -1997,7 +2252,7 @@ func TestBootStrapWhenRTIsEmpty(t *testing.T) { { - //---------------- + // ---------------- // We will initialize a DHT with 2 bootstrappers, connect it to another DHT, // then remove the DHT handler from the other DHT which should make the first DHT's // routing table empty. @@ -2006,6 +2261,7 @@ func TestBootStrapWhenRTIsEmpty(t *testing.T) { // AutoRefresh needs to be enabled for this. h1, err := bhost.NewHost(swarmt.GenSwarm(t, swarmt.OptDisableReuseport), new(bhost.HostOpts)) require.NoError(t, err) + h1.Start() dht1, err := New( ctx, h1, @@ -2065,7 +2321,7 @@ func TestBootstrapPeersFunc(t *testing.T) { bootstrapPeersB = []peer.AddrInfo{addrA} lock.Unlock() - dhtB.fixLowPeers(ctx) + dhtB.fixLowPeers() require.NotEqual(t, 0, len(dhtB.host.Network().Peers())) } @@ -2082,9 +2338,11 @@ func TestPreconnectedNodes(t *testing.T) { // Create hosts h1, err := bhost.NewHost(swarmt.GenSwarm(t, swarmt.OptDisableReuseport), new(bhost.HostOpts)) require.NoError(t, err) + h1.Start() defer h1.Close() h2, err := bhost.NewHost(swarmt.GenSwarm(t, swarmt.OptDisableReuseport), new(bhost.HostOpts)) require.NoError(t, err) + h2.Start() defer h2.Close() // Setup first DHT @@ -2099,7 +2357,7 @@ func TestPreconnectedNodes(t *testing.T) { // Wait until we know identify has completed by checking for supported protocols // TODO: Is this needed? Could we do h2.Connect(h1) and that would wait for identify to complete. require.Eventually(t, func() bool { - h1Protos, err := h2.Peerstore().SupportsProtocols(h1.ID(), d1.protocolsStrs...) + h1Protos, err := h2.Peerstore().SupportsProtocols(h1.ID(), d1.protocols...) require.NoError(t, err) return len(h1Protos) > 0 @@ -2110,6 +2368,8 @@ func TestPreconnectedNodes(t *testing.T) { require.NoError(t, err) defer h2.Close() + connect(t, ctx, d1, d2) + // See if it works peers, err := d2.GetClosestPeers(ctx, "testkey") require.NoError(t, err) @@ -2117,3 +2377,92 @@ func TestPreconnectedNodes(t *testing.T) { require.Equal(t, len(peers), 1, "why is there more than one peer?") require.Equal(t, h1.ID(), peers[0], "could not find peer") } + +func TestAddrFilter(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // generate a bunch of addresses + publicAddrs := []ma.Multiaddr{ + ma.StringCast("/ip4/1.2.3.1/tcp/123"), + ma.StringCast("/ip4/160.160.160.160/tcp/1600"), + ma.StringCast("/ip6/2001::10/tcp/123"), + } + privAddrs := []ma.Multiaddr{ + ma.StringCast("/ip4/192.168.1.100/tcp/123"), + ma.StringCast("/ip4/172.16.10.10/tcp/123"), + ma.StringCast("/ip4/10.10.10.10/tcp/123"), + ma.StringCast("/ip6/fc00::10/tcp/123"), + } + loopbackAddrs := []ma.Multiaddr{ + ma.StringCast("/ip4/127.0.0.100/tcp/123"), + ma.StringCast("/ip6/::1/tcp/123"), + } + + allAddrs := append(publicAddrs, privAddrs...) + allAddrs = append(allAddrs, loopbackAddrs...) + + // generate different address filters + acceptAllFilter := AddressFilter(func(addrs []ma.Multiaddr) []ma.Multiaddr { + return addrs + }) + rejectAllFilter := AddressFilter(func(addrs []ma.Multiaddr) []ma.Multiaddr { + return []ma.Multiaddr{} + }) + publicIpFilter := AddressFilter(func(addrs []ma.Multiaddr) []ma.Multiaddr { + return ma.FilterAddrs(addrs, manet.IsPublicAddr) + }) + localIpFilter := AddressFilter(func(addrs []ma.Multiaddr) []ma.Multiaddr { + return ma.FilterAddrs(addrs, func(a ma.Multiaddr) bool { return !manet.IsIPLoopback(a) }) + }) + + // generate peerid for "remote" peer + _, pub, err := crypto.GenerateKeyPair( + crypto.Ed25519, // Select your key type. Ed25519 are nice short + -1, // Select key length when possible (i.e. RSA). + ) + require.NoError(t, err) + peerid, err := peer.IDFromPublicKey(pub) + require.NoError(t, err) + + // DHT accepting all addresses + d0 := setupDHT(ctx, t, false, acceptAllFilter) + + // peerstore should only contain self + require.Equal(t, 1, d0.host.Peerstore().Peers().Len()) + + d0.maybeAddAddrs(peerid, allAddrs, time.Minute) + require.Equal(t, 2, d0.host.Peerstore().Peers().Len()) + for _, a := range allAddrs { + // check that the peerstore contains all addresses of the remote peer + require.Contains(t, d0.host.Peerstore().Addrs(peerid), a) + } + + // DHT rejecting all addresses + d1 := setupDHT(ctx, t, false, rejectAllFilter) + d1.maybeAddAddrs(peerid, allAddrs, time.Minute) + // remote peer should not be added to peerstore (all addresses rejected) + require.Equal(t, 1, d1.host.Peerstore().Peers().Len()) + + // DHT accepting only public addresses + d2 := setupDHT(ctx, t, false, publicIpFilter) + d2.maybeAddAddrs(peerid, allAddrs, time.Minute) + for _, a := range publicAddrs { + // check that the peerstore contains only public addresses of the remote peer + require.Contains(t, d2.host.Peerstore().Addrs(peerid), a) + } + require.Equal(t, len(publicAddrs), len(d2.host.Peerstore().Addrs(peerid))) + + // DHT accepting only non-loopback addresses + d3 := setupDHT(ctx, t, false, localIpFilter) + d3.maybeAddAddrs(peerid, allAddrs, time.Minute) + for _, a := range publicAddrs { + // check that the peerstore contains only non-loopback addresses of the remote peer + require.Contains(t, d3.host.Peerstore().Addrs(peerid), a) + } + for _, a := range privAddrs { + // check that the peerstore contains only non-loopback addresses of the remote peer + require.Contains(t, d3.host.Peerstore().Addrs(peerid), a) + } + require.Equal(t, len(publicAddrs)+len(privAddrs), len(d3.host.Peerstore().Addrs(peerid))) +} diff --git a/dual/dual.go b/dual/dual.go index df88dcd14..0f94cf728 100644 --- a/dual/dual.go +++ b/dual/dual.go @@ -1,4 +1,4 @@ -// Package dual provides an implementaiton of a split or "dual" dht, where two parallel instances +// Package dual provides an implementation of a split or "dual" dht, where two parallel instances // are maintained for the global internet and the local LAN respectively. package dual @@ -8,21 +8,27 @@ import ( "sync" dht "github.com/libp2p/go-libp2p-kad-dht" + "github.com/libp2p/go-libp2p-kad-dht/internal" + "github.com/libp2p/go-libp2p-routing-helpers/tracing" "github.com/ipfs/go-cid" - ci "github.com/libp2p/go-libp2p-core/crypto" - "github.com/libp2p/go-libp2p-core/host" - "github.com/libp2p/go-libp2p-core/peer" - "github.com/libp2p/go-libp2p-core/protocol" - "github.com/libp2p/go-libp2p-core/routing" kb "github.com/libp2p/go-libp2p-kbucket" "github.com/libp2p/go-libp2p-kbucket/peerdiversity" helper "github.com/libp2p/go-libp2p-routing-helpers" + ci "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/host" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/protocol" + "github.com/libp2p/go-libp2p/core/routing" ma "github.com/multiformats/go-multiaddr" + manet "github.com/multiformats/go-multiaddr/net" "github.com/hashicorp/go-multierror" ) +const tracer = tracing.Tracer("go-libp2p-kad-dht/dual") +const dualName = "Dual" + // DHT implements the routing interface to provide two concrete DHT implementationts for use // in IPFS that are used to support both global network users and disjoint LAN usecases. type DHT struct { @@ -101,6 +107,8 @@ func New(ctx context.Context, h host.Host, options ...Option) (*DHT, error) { dht.QueryFilter(dht.PublicQueryFilter), dht.RoutingTableFilter(dht.PublicRoutingTableFilter), dht.RoutingTablePeerDiversityFilter(dht.NewRTPeerDiversityFilter(h, maxPrefixCountPerCpl, maxPrefixCount)), + // filter out all private addresses + dht.AddressFilter(func(addrs []ma.Multiaddr) []ma.Multiaddr { return ma.FilterAddrs(addrs, manet.IsPublicAddr) }), ), ) if err != nil { @@ -111,6 +119,10 @@ func New(ctx context.Context, h host.Host, options ...Option) (*DHT, error) { dht.ProtocolExtension(LanExtension), dht.QueryFilter(dht.PrivateQueryFilter), dht.RoutingTableFilter(dht.PrivateRoutingTableFilter), + // filter out localhost IP addresses + dht.AddressFilter(func(addrs []ma.Multiaddr) []ma.Multiaddr { + return ma.FilterAddrs(addrs, func(a ma.Multiaddr) bool { return !manet.IsIPLoopback(a) }) + }), ), ) if err != nil { @@ -151,7 +163,10 @@ func (dht *DHT) WANActive() bool { } // Provide adds the given cid to the content routing system. -func (dht *DHT) Provide(ctx context.Context, key cid.Cid, announce bool) error { +func (dht *DHT) Provide(ctx context.Context, key cid.Cid, announce bool) (err error) { + ctx, end := tracer.Provide(dualName, ctx, key, announce) + defer func() { end(err) }() + if dht.WANActive() { return dht.WAN.Provide(ctx, key, announce) } @@ -167,7 +182,10 @@ func (dht *DHT) GetRoutingTableDiversityStats() []peerdiversity.CplDiversityStat } // FindProvidersAsync searches for peers who are able to provide a given key -func (dht *DHT) FindProvidersAsync(ctx context.Context, key cid.Cid, count int) <-chan peer.AddrInfo { +func (dht *DHT) FindProvidersAsync(ctx context.Context, key cid.Cid, count int) (ch <-chan peer.AddrInfo) { + ctx, end := tracer.FindProvidersAsync(dualName, ctx, key, count) + defer func() { ch = end(ch, nil) }() + reqCtx, cancel := context.WithCancel(ctx) outCh := make(chan peer.AddrInfo) @@ -178,10 +196,13 @@ func (dht *DHT) FindProvidersAsync(ctx context.Context, key cid.Cid, count int) subCtx, evtCh = routing.RegisterForQueryEvents(reqCtx) } + subCtx, span := internal.StartSpan(subCtx, "Dual.worker") wanCh := dht.WAN.FindProvidersAsync(subCtx, key, count) lanCh := dht.LAN.FindProvidersAsync(subCtx, key, count) zeroCount := (count == 0) go func() { + defer span.End() + defer cancel() defer close(outCh) @@ -200,11 +221,13 @@ func (dht *DHT) FindProvidersAsync(ctx context.Context, key cid.Cid, count int) continue case pi, ok = <-wanCh: if !ok { + span.AddEvent("wan finished") wanCh = nil continue } case pi, ok = <-lanCh: if !ok { + span.AddEvent("lan finished") lanCh = nil continue } @@ -231,7 +254,10 @@ func (dht *DHT) FindProvidersAsync(ctx context.Context, key cid.Cid, count int) // FindPeer searches for a peer with given ID // Note: with signed peer records, we can change this to short circuit once either DHT returns. -func (dht *DHT) FindPeer(ctx context.Context, pid peer.ID) (peer.AddrInfo, error) { +func (dht *DHT) FindPeer(ctx context.Context, pid peer.ID) (pi peer.AddrInfo, err error) { + ctx, end := tracer.FindPeer(dualName, ctx, pid) + defer func() { end(pi, err) }() + var wg sync.WaitGroup wg.Add(2) var wanInfo, lanInfo peer.AddrInfo @@ -297,14 +323,20 @@ func combineErrors(erra, errb error) error { // Bootstrap allows callers to hint to the routing system to get into a // Boostrapped state and remain there. -func (dht *DHT) Bootstrap(ctx context.Context) error { +func (dht *DHT) Bootstrap(ctx context.Context) (err error) { + ctx, end := tracer.Bootstrap(dualName, ctx) + defer func() { end(err) }() + erra := dht.WAN.Bootstrap(ctx) errb := dht.LAN.Bootstrap(ctx) return combineErrors(erra, errb) } // PutValue adds value corresponding to given Key. -func (dht *DHT) PutValue(ctx context.Context, key string, val []byte, opts ...routing.Option) error { +func (dht *DHT) PutValue(ctx context.Context, key string, val []byte, opts ...routing.Option) (err error) { + ctx, end := tracer.PutValue(dualName, ctx, key, val, opts...) + defer func() { end(err) }() + if dht.WANActive() { return dht.WAN.PutValue(ctx, key, val, opts...) } @@ -312,7 +344,10 @@ func (dht *DHT) PutValue(ctx context.Context, key string, val []byte, opts ...ro } // GetValue searches for the value corresponding to given Key. -func (d *DHT) GetValue(ctx context.Context, key string, opts ...routing.Option) ([]byte, error) { +func (d *DHT) GetValue(ctx context.Context, key string, opts ...routing.Option) (result []byte, err error) { + ctx, end := tracer.GetValue(dualName, ctx, key, opts...) + defer func() { end(result, err) }() + lanCtx, cancelLan := context.WithCancel(ctx) defer cancelLan() @@ -342,7 +377,10 @@ func (d *DHT) GetValue(ctx context.Context, key string, opts ...routing.Option) } // SearchValue searches for better values from this value -func (dht *DHT) SearchValue(ctx context.Context, key string, opts ...routing.Option) (<-chan []byte, error) { +func (dht *DHT) SearchValue(ctx context.Context, key string, opts ...routing.Option) (ch <-chan []byte, err error) { + ctx, end := tracer.SearchValue(dualName, ctx, key, opts...) + defer func() { ch, err = end(ch, err) }() + p := helper.Parallel{Routers: []routing.Routing{dht.WAN, dht.LAN}, Validator: dht.WAN.Validator} return p.SearchValue(ctx, key, opts...) } diff --git a/dual/dual_test.go b/dual/dual_test.go index 892354931..a51738ac5 100644 --- a/dual/dual_test.go +++ b/dual/dual_test.go @@ -6,15 +6,15 @@ import ( "time" "github.com/ipfs/go-cid" - u "github.com/ipfs/go-ipfs-util" - "github.com/libp2p/go-libp2p-core/host" - "github.com/libp2p/go-libp2p-core/peer" - peerstore "github.com/libp2p/go-libp2p-core/peerstore" dht "github.com/libp2p/go-libp2p-kad-dht" + "github.com/libp2p/go-libp2p-kad-dht/internal" test "github.com/libp2p/go-libp2p-kad-dht/internal/testing" record "github.com/libp2p/go-libp2p-record" - swarmt "github.com/libp2p/go-libp2p-swarm/testing" + "github.com/libp2p/go-libp2p/core/host" + "github.com/libp2p/go-libp2p/core/peer" + peerstore "github.com/libp2p/go-libp2p/core/peerstore" bhost "github.com/libp2p/go-libp2p/p2p/host/basic" + swarmt "github.com/libp2p/go-libp2p/p2p/net/swarm/testing" "github.com/multiformats/go-multiaddr" "github.com/stretchr/testify/require" ) @@ -22,8 +22,8 @@ import ( var wancid, lancid cid.Cid func init() { - wancid = cid.NewCidV1(cid.DagCBOR, u.Hash([]byte("wan cid -- value"))) - lancid = cid.NewCidV1(cid.DagCBOR, u.Hash([]byte("lan cid -- value"))) + wancid = cid.NewCidV1(cid.DagCBOR, internal.Hash([]byte("wan cid -- value"))) + lancid = cid.NewCidV1(cid.DagCBOR, internal.Hash([]byte("lan cid -- value"))) } type blankValidator struct{} @@ -59,6 +59,7 @@ func MkFilterForPeer() (func(_ interface{}, p peer.ID) bool, *customRtHelper) { func setupDHTWithFilters(ctx context.Context, t *testing.T, options ...dht.Option) (*DHT, []*customRtHelper) { h, err := bhost.NewHost(swarmt.GenSwarm(t, swarmt.OptDisableReuseport), new(bhost.HostOpts)) require.NoError(t, err) + h.Start() t.Cleanup(func() { h.Close() }) wanFilter, wanRef := MkFilterForPeer() @@ -92,6 +93,7 @@ func setupDHT(ctx context.Context, t *testing.T, options ...dht.Option) *DHT { host, err := bhost.NewHost(swarmt.GenSwarm(t, swarmt.OptDisableReuseport), new(bhost.HostOpts)) require.NoError(t, err) + host.Start() t.Cleanup(func() { host.Close() }) baseOpts := []dht.Option{ @@ -127,7 +129,7 @@ func connect(ctx context.Context, t *testing.T, a, b *dht.IpfsDHT) { func wait(ctx context.Context, t *testing.T, a, b *dht.IpfsDHT) { t.Helper() for a.RoutingTable().Find(b.PeerID()) == "" { - //fmt.Fprintf(os.Stderr, "%v\n", a.RoutingTable().GetPeerInfos()) + // fmt.Fprintf(os.Stderr, "%v\n", a.RoutingTable().GetPeerInfos()) select { case <-ctx.Done(): t.Fatal(ctx.Err()) @@ -148,6 +150,7 @@ func setupTier(ctx context.Context, t *testing.T) (*DHT, *dht.IpfsDHT, *dht.Ipfs whost, err := bhost.NewHost(swarmt.GenSwarm(t, swarmt.OptDisableReuseport), new(bhost.HostOpts)) require.NoError(t, err) + whost.Start() t.Cleanup(func() { whost.Close() }) wan, err := dht.New( @@ -163,6 +166,7 @@ func setupTier(ctx context.Context, t *testing.T) (*DHT, *dht.IpfsDHT, *dht.Ipfs lhost, err := bhost.NewHost(swarmt.GenSwarm(t, swarmt.OptDisableReuseport), new(bhost.HostOpts)) require.NoError(t, err) + lhost.Start() t.Cleanup(func() { lhost.Close() }) lan, err := dht.New( diff --git a/events.go b/events.go index 9a1ff3c17..d6fe2e76f 100644 --- a/events.go +++ b/events.go @@ -7,8 +7,8 @@ import ( "github.com/google/uuid" - "github.com/libp2p/go-libp2p-core/peer" kbucket "github.com/libp2p/go-libp2p-kbucket" + "github.com/libp2p/go-libp2p/core/peer" ) // KeyKadID contains the Kademlia key in string and binary form. diff --git a/ext_test.go b/ext_test.go index 4a416b933..36d3ca93f 100644 --- a/ext_test.go +++ b/ext_test.go @@ -2,38 +2,24 @@ package dht import ( "context" - "math/rand" "testing" "time" - "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/protocol" - "github.com/libp2p/go-libp2p-core/routing" + "github.com/libp2p/go-libp2p/core/network" "github.com/stretchr/testify/require" - record "github.com/libp2p/go-libp2p-record" - swarmt "github.com/libp2p/go-libp2p-swarm/testing" - bhost "github.com/libp2p/go-libp2p/p2p/host/basic" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" - "github.com/libp2p/go-msgio/protoio" - - pb "github.com/libp2p/go-libp2p-kad-dht/pb" - - u "github.com/ipfs/go-ipfs-util" ) -// Test that one hung request to a peer doesn't prevent another request -// using that same peer from obeying its context. -func TestHungRequest(t *testing.T) { +func TestInvalidRemotePeers(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - mn, err := mocknet.FullMeshLinked(ctx, 2) + mn, err := mocknet.FullMeshLinked(5) if err != nil { t.Fatal(err) } + defer mn.Close() hosts := mn.Hosts() os := []Option{testPrefix, DisableAutoRefresh(), Mode(ModeServer)} @@ -44,7 +30,7 @@ func TestHungRequest(t *testing.T) { for _, proto := range d.serverProtocols { // Hang on every request. hosts[1].SetStreamHandler(proto, func(s network.Stream) { - defer s.Reset() //nolint + defer s.Reset() // nolint <-ctx.Done() }) } @@ -54,408 +40,9 @@ func TestHungRequest(t *testing.T) { t.Fatal("failed to connect peers", err) } - // Wait at a bit for a peer in our routing table. - for i := 0; i < 100 && d.routingTable.Size() == 0; i++ { - time.Sleep(10 * time.Millisecond) - } - if d.routingTable.Size() == 0 { - t.Fatal("failed to fill routing table") - } - - ctx1, cancel1 := context.WithTimeout(ctx, 1*time.Second) - defer cancel1() - - done := make(chan error, 1) - go func() { - _, err := d.GetClosestPeers(ctx1, testCaseCids[0].KeyString()) - done <- err - }() - time.Sleep(100 * time.Millisecond) - ctx2, cancel2 := context.WithTimeout(ctx, 100*time.Millisecond) - defer cancel2() - err = d.Provide(ctx2, testCaseCids[0], true) - if err != context.DeadlineExceeded { - t.Errorf("expected to fail with deadline exceeded, got: %s", ctx2.Err()) - } - select { - case err = <-done: - t.Error("GetClosestPeers should not have returned yet", err) - default: - err = <-done - if err != context.DeadlineExceeded { - t.Errorf("expected the deadline to be exceeded, got %s", err) - } - } - - if d.routingTable.Size() == 0 { - // make sure we didn't just disconnect - t.Fatal("expected peers in the routing table") - } -} - -func TestGetFailures(t *testing.T) { - if testing.Short() { - t.SkipNow() - } - - ctx := context.Background() - - host1, err := bhost.NewHost(swarmt.GenSwarm(t, swarmt.OptDisableReuseport), new(bhost.HostOpts)) - require.NoError(t, err) - host2, err := bhost.NewHost(swarmt.GenSwarm(t, swarmt.OptDisableReuseport), new(bhost.HostOpts)) - require.NoError(t, err) - - d, err := New(ctx, host1, testPrefix, DisableAutoRefresh(), Mode(ModeServer)) - require.NoError(t, err) - - // Reply with failures to every message - for _, proto := range d.serverProtocols { - host2.SetStreamHandler(proto, func(s network.Stream) { - time.Sleep(400 * time.Millisecond) - s.Close() - }) - } - - host1.Peerstore().AddAddrs(host2.ID(), host2.Addrs(), peerstore.ConnectedAddrTTL) - _, err = host1.Network().DialPeer(ctx, host2.ID()) - require.NoError(t, err) - time.Sleep(1 * time.Second) - - // This one should time out - ctx1, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond) - defer cancel() - if _, err := d.GetValue(ctx1, "test"); err != nil { - if merr, ok := err.(u.MultiErr); ok && len(merr) > 0 { - err = merr[0] - } - - if err != context.DeadlineExceeded { - t.Fatal("Got different error than we expected", err) - } - } else { - t.Fatal("Did not get expected error!") - } - - t.Log("Timeout test passed.") - - for _, proto := range d.serverProtocols { - // Reply with failures to every message - host2.SetStreamHandler(proto, func(s network.Stream) { - defer s.Close() - - pbr := protoio.NewDelimitedReader(s, network.MessageSizeMax) - pbw := protoio.NewDelimitedWriter(s) - - pmes := new(pb.Message) - if err := pbr.ReadMsg(pmes); err != nil { - // user gave up - return - } - - resp := &pb.Message{ - Type: pmes.Type, - } - _ = pbw.WriteMsg(resp) - }) - } - - // This one should fail with NotFound. - // long context timeout to ensure we dont end too early. - // the dht should be exhausting its query and returning not found. - // (was 3 seconds before which should be _plenty_ of time, but maybe - // travis machines really have a hard time...) - ctx2, cancel := context.WithTimeout(context.Background(), 20*time.Second) - defer cancel() - _, err = d.GetValue(ctx2, "test") - if err != nil { - if merr, ok := err.(u.MultiErr); ok && len(merr) > 0 { - err = merr[0] - } - if err != routing.ErrNotFound { - t.Fatalf("Expected ErrNotFound, got: %s", err) - } - } else { - t.Fatal("expected error, got none.") - } - - t.Log("ErrNotFound check passed!") - - // Now we test this DHT's handleGetValue failure - { - typ := pb.Message_GET_VALUE - str := "hello" - - rec := record.MakePutRecord(str, []byte("blah")) - req := pb.Message{ - Type: typ, - Key: []byte(str), - Record: rec, - } - - s, err := host2.NewStream(context.Background(), host1.ID(), d.protocols...) - if err != nil { - t.Fatal(err) - } - defer s.Close() - - pbr := protoio.NewDelimitedReader(s, network.MessageSizeMax) - pbw := protoio.NewDelimitedWriter(s) - - if err := pbw.WriteMsg(&req); err != nil { - t.Fatal(err) - } - - pmes := new(pb.Message) - if err := pbr.ReadMsg(pmes); err != nil { - t.Fatal(err) - } - if pmes.GetRecord() != nil { - t.Fatal("shouldnt have value") - } - if pmes.GetProviderPeers() != nil { - t.Fatal("shouldnt have provider peers") - } - } - - if d.routingTable.Size() == 0 { - // make sure we didn't just disconnect - t.Fatal("expected peers in the routing table") - } -} - -func TestNotFound(t *testing.T) { - // t.Skip("skipping test to debug another") - if testing.Short() { - t.SkipNow() - } - - ctx := context.Background() - mn, err := mocknet.FullMeshConnected(ctx, 16) - if err != nil { - t.Fatal(err) - } - hosts := mn.Hosts() - - os := []Option{testPrefix, DisableAutoRefresh(), Mode(ModeServer)} - d, err := New(ctx, hosts[0], os...) - if err != nil { - t.Fatal(err) - } - - // Reply with random peers to every message - for _, host := range hosts { - host := host // shadow loop var - for _, proto := range d.serverProtocols { - host.SetStreamHandler(proto, func(s network.Stream) { - defer s.Close() - pbr := protoio.NewDelimitedReader(s, network.MessageSizeMax) - pbw := protoio.NewDelimitedWriter(s) - - pmes := new(pb.Message) - if err := pbr.ReadMsg(pmes); err != nil { - // this isn't an error, it just means the stream has died. - return - } - - switch pmes.GetType() { - case pb.Message_GET_VALUE: - resp := &pb.Message{Type: pmes.Type} - - ps := []peer.AddrInfo{} - for i := 0; i < 7; i++ { - p := hosts[rand.Intn(len(hosts))].ID() - pi := host.Peerstore().PeerInfo(p) - ps = append(ps, pi) - } - - resp.CloserPeers = pb.PeerInfosToPBPeers(d.host.Network(), ps) - if err := pbw.WriteMsg(resp); err != nil { - return - } - default: - panic("Shouldnt recieve this.") - } - }) - } - for _, peer := range hosts { - if host == peer { - continue - } - _ = peer.Peerstore().AddProtocols(host.ID(), protocol.ConvertToStrings(d.serverProtocols)...) - } - } - - for _, p := range hosts { - d.peerFound(ctx, p.ID(), true) - } - - // long timeout to ensure timing is not at play. - ctx, cancel := context.WithTimeout(ctx, time.Second*20) - defer cancel() - v, err := d.GetValue(ctx, "hello") - logger.Debugf("get value got %v", v) - if err != nil { - if merr, ok := err.(u.MultiErr); ok && len(merr) > 0 { - err = merr[0] - } - switch err { - case routing.ErrNotFound: - if d.routingTable.Size() == 0 { - // make sure we didn't just disconnect - t.Fatal("expected peers in the routing table") - } - //Success! - return - case u.ErrTimeout: - t.Fatal("Should not have gotten timeout!") - default: - t.Fatalf("Got unexpected error: %s", err) - } - } - t.Fatal("Expected to recieve an error.") -} - -// If less than K nodes are in the entire network, it should fail when we make -// a GET rpc and nobody has the value -func TestLessThanKResponses(t *testing.T) { - // t.Skip("skipping test to debug another") - // t.Skip("skipping test because it makes a lot of output") - - ctx := context.Background() - mn, err := mocknet.FullMeshConnected(ctx, 6) - if err != nil { - t.Fatal(err) - } - hosts := mn.Hosts() - - os := []Option{testPrefix, DisableAutoRefresh(), Mode(ModeServer)} - d, err := New(ctx, hosts[0], os...) - if err != nil { - t.Fatal(err) - } - - for i := 1; i < 5; i++ { - d.peerFound(ctx, hosts[i].ID(), true) - } - - // Reply with random peers to every message - for _, host := range hosts { - host := host // shadow loop var - for _, proto := range d.serverProtocols { - host.SetStreamHandler(proto, func(s network.Stream) { - defer s.Close() - - pbr := protoio.NewDelimitedReader(s, network.MessageSizeMax) - pbw := protoio.NewDelimitedWriter(s) - - pmes := new(pb.Message) - if err := pbr.ReadMsg(pmes); err != nil { - panic(err) - } - - switch pmes.GetType() { - case pb.Message_GET_VALUE: - pi := host.Peerstore().PeerInfo(hosts[1].ID()) - resp := &pb.Message{ - Type: pmes.Type, - CloserPeers: pb.PeerInfosToPBPeers(d.host.Network(), []peer.AddrInfo{pi}), - } - - if err := pbw.WriteMsg(resp); err != nil { - panic(err) - } - default: - panic("Shouldnt recieve this.") - } - - }) - } - } - - ctx, cancel := context.WithTimeout(ctx, time.Second*30) - defer cancel() - if _, err := d.GetValue(ctx, "hello"); err != nil { - switch err { - case routing.ErrNotFound: - //Success! - return - case u.ErrTimeout: - t.Fatal("Should not have gotten timeout!") - default: - t.Fatalf("Got unexpected error: %s", err) - } - } - t.Fatal("Expected to recieve an error.") -} - -// Test multiple queries against a node that closes its stream after every query. -func TestMultipleQueries(t *testing.T) { - if testing.Short() { - t.SkipNow() - } - - ctx := context.Background() - mn, err := mocknet.FullMeshConnected(ctx, 2) - if err != nil { - t.Fatal(err) - } - hosts := mn.Hosts() - os := []Option{testPrefix, DisableAutoRefresh(), Mode(ModeServer)} - d, err := New(ctx, hosts[0], os...) - if err != nil { - t.Fatal(err) - } - - d.peerFound(ctx, hosts[1].ID(), true) - - for _, proto := range d.serverProtocols { - // It would be nice to be able to just get a value and succeed but then - // we'd need to deal with selectors and validators... - hosts[1].SetStreamHandler(proto, func(s network.Stream) { - defer s.Close() - - pbr := protoio.NewDelimitedReader(s, network.MessageSizeMax) - pbw := protoio.NewDelimitedWriter(s) - - pmes := new(pb.Message) - if err := pbr.ReadMsg(pmes); err != nil { - panic(err) - } - - switch pmes.GetType() { - case pb.Message_GET_VALUE: - pi := hosts[1].Peerstore().PeerInfo(hosts[0].ID()) - resp := &pb.Message{ - Type: pmes.Type, - CloserPeers: pb.PeerInfosToPBPeers(d.host.Network(), []peer.AddrInfo{pi}), - } - - if err := pbw.WriteMsg(resp); err != nil { - panic(err) - } - default: - panic("Shouldnt recieve this.") - } - }) - } - - // long timeout to ensure timing is not at play. - ctx, cancel := context.WithTimeout(ctx, time.Second*20) - defer cancel() - for i := 0; i < 10; i++ { - if _, err := d.GetValue(ctx, "hello"); err != nil { - switch err { - case routing.ErrNotFound: - //Success! - continue - case u.ErrTimeout: - t.Fatal("Should not have gotten timeout!") - default: - t.Fatalf("Got unexpected error: %s", err) - } - } - t.Fatal("Expected to recieve an error.") - } + // hosts[1] isn't added to the routing table because it isn't responding to + // the DHT request + require.Equal(t, 0, d.routingTable.Size()) } diff --git a/fullrt/dht.go b/fullrt/dht.go index 735209969..442d5c578 100644 --- a/fullrt/dht.go +++ b/fullrt/dht.go @@ -3,6 +3,7 @@ package fullrt import ( "bytes" "context" + "errors" "fmt" "math/rand" "sync" @@ -13,19 +14,22 @@ import ( "github.com/multiformats/go-multiaddr" "github.com/multiformats/go-multihash" - "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/protocol" - "github.com/libp2p/go-libp2p-core/routing" + "github.com/libp2p/go-libp2p-routing-helpers/tracing" + "github.com/libp2p/go-libp2p/core/event" + "github.com/libp2p/go-libp2p/core/host" + "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/peerstore" + "github.com/libp2p/go-libp2p/core/protocol" + "github.com/libp2p/go-libp2p/core/routing" + "github.com/libp2p/go-libp2p/p2p/host/eventbus" + swarm "github.com/libp2p/go-libp2p/p2p/net/swarm" - "github.com/gogo/protobuf/proto" "github.com/ipfs/go-cid" ds "github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-datastore/sync" - u "github.com/ipfs/go-ipfs-util" - logging "github.com/ipfs/go-log" + logging "github.com/ipfs/go-log/v2" + "google.golang.org/protobuf/proto" kaddht "github.com/libp2p/go-libp2p-kad-dht" "github.com/libp2p/go-libp2p-kad-dht/crawler" @@ -42,15 +46,19 @@ import ( "github.com/libp2p/go-libp2p-xor/kademlia" kadkey "github.com/libp2p/go-libp2p-xor/key" "github.com/libp2p/go-libp2p-xor/trie" - "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" ) -var Tracer = otel.Tracer("") - var logger = logging.Logger("fullrtdht") +const ( + tracer = tracing.Tracer("go-libp2p-kad-dht/fullrt") + dhtName = "FullRT" +) + +const rtRefreshLimitsMsg = `Accelerated DHT client was unable to fully refresh its routing table due to Resource Manager limits, which may degrade content routing. Consider increasing resource limits. See debug logs for the "dht-crawler" subsystem for details.` + // FullRT is an experimental DHT client that is under development. Expect breaking changes to occur in this client // until it stabilizes. // @@ -77,7 +85,7 @@ type FullRT struct { crawlerInterval time.Duration lastCrawlTime time.Time - crawler *crawler.Crawler + crawler crawler.Crawler protoMessenger *dht_pb.ProtocolMessenger messageSender dht_pb.MessageSender @@ -101,6 +109,10 @@ type FullRT struct { timeoutPerOp time.Duration bulkSendParallelism int + + self peer.ID + + peerConnectednessSubscriber event.Subscription } // NewFullRT creates a DHT client that tracks the full network. It takes a protocol prefix for the given network, @@ -114,7 +126,12 @@ type FullRT struct { // DHT options passed in should be suitable for your network (e.g. protocol prefix, validators, bucket size, and // bootstrap peers). func NewFullRT(h host.Host, protocolPrefix protocol.ID, options ...Option) (*FullRT, error) { - var fullrtcfg config + fullrtcfg := config{ + crawlInterval: time.Hour, + bulkSendParallelism: 20, + waitFrac: 0.3, + timeoutPerOp: 5 * time.Second, + } if err := fullrtcfg.apply(options...); err != nil { return nil, err } @@ -145,14 +162,22 @@ func NewFullRT(h host.Host, protocolPrefix protocol.ID, options ...Option) (*Ful return nil, err } - c, err := crawler.New(h, crawler.WithParallelism(200)) + if fullrtcfg.crawler == nil { + fullrtcfg.crawler, err = crawler.NewDefaultCrawler(h, crawler.WithParallelism(200)) + if err != nil { + return nil, err + } + } + + sub, err := h.EventBus().Subscribe(new(event.EvtPeerConnectednessChanged), eventbus.Name("fullrt-dht")) if err != nil { - return nil, err + return nil, fmt.Errorf("peer connectedness subscription failed: %w", err) } ctx, cancel := context.WithCancel(context.Background()) - pm, err := providers.NewProviderManager(ctx, h.ID(), h.Peerstore(), dhtcfg.Datastore) + self := h.ID() + pm, err := providers.NewProviderManager(self, h.Peerstore(), dhtcfg.Datastore, fullrtcfg.pmOpts...) if err != nil { cancel() return nil, err @@ -175,7 +200,7 @@ func NewFullRT(h host.Host, protocolPrefix protocol.ID, options ...Option) (*Ful ProviderManager: pm, datastore: dhtcfg.Datastore, h: h, - crawler: c, + crawler: fullrtcfg.crawler, messageSender: ms, protoMessenger: protoMessenger, filterFromTable: kaddht.PublicQueryFilter, @@ -188,17 +213,19 @@ func NewFullRT(h host.Host, protocolPrefix protocol.ID, options ...Option) (*Ful triggerRefresh: make(chan struct{}), - waitFrac: 0.3, - timeoutPerOp: 5 * time.Second, + waitFrac: fullrtcfg.waitFrac, + timeoutPerOp: fullrtcfg.timeoutPerOp, - crawlerInterval: time.Minute * 60, + crawlerInterval: fullrtcfg.crawlInterval, - bulkSendParallelism: 20, + bulkSendParallelism: fullrtcfg.bulkSendParallelism, + self: self, + peerConnectednessSubscriber: sub, } - rt.wg.Add(1) + rt.wg.Add(2) go rt.runCrawler(ctx) - + go rt.runSubscriber() return rt, nil } @@ -207,6 +234,31 @@ type crawlVal struct { key kadkey.Key } +func (dht *FullRT) runSubscriber() { + defer dht.wg.Done() + ms, ok := dht.messageSender.(dht_pb.MessageSenderWithDisconnect) + defer dht.peerConnectednessSubscriber.Close() + if !ok { + return + } + for { + select { + case e := <-dht.peerConnectednessSubscriber.Out(): + pc, ok := e.(event.EvtPeerConnectednessChanged) + if !ok { + logger.Errorf("invalid event message type: %T", e) + continue + } + + if pc.Connectedness != network.Connected { + ms.OnDisconnect(dht.ctx, pc.Peer) + } + case <-dht.ctx.Done(): + return + } + } +} + func (dht *FullRT) TriggerRefresh(ctx context.Context) error { select { case <-ctx.Done(): @@ -258,8 +310,8 @@ func (dht *FullRT) runCrawler(ctx context.Context) { defer dht.wg.Done() t := time.NewTicker(dht.crawlerInterval) - m := make(map[peer.ID]*crawlVal) - mxLk := sync.Mutex{} + foundPeers := make(map[peer.ID]*crawlVal) + foundPeersLk := sync.Mutex{} initialTrigger := make(chan struct{}, 1) initialTrigger <- struct{}{} @@ -275,18 +327,19 @@ func (dht *FullRT) runCrawler(ctx context.Context) { var addrs []*peer.AddrInfo dht.peerAddrsLk.Lock() - for k := range m { + for k := range foundPeers { addrs = append(addrs, &peer.AddrInfo{ID: k}) // Addrs: v.addrs } addrs = append(addrs, dht.bootstrapPeers...) dht.peerAddrsLk.Unlock() - for k := range m { - delete(m, k) + for k := range foundPeers { + delete(foundPeers, k) } start := time.Now() + limitErrOnce := sync.Once{} dht.crawler.Run(ctx, addrs, func(p peer.ID, rtPeers []*peer.AddrInfo) { conns := dht.h.Network().ConnsToPeer(p) @@ -306,24 +359,37 @@ func (dht *FullRT) runCrawler(ctx context.Context) { return } - mxLk.Lock() - defer mxLk.Unlock() - m[p] = &crawlVal{ + foundPeersLk.Lock() + defer foundPeersLk.Unlock() + foundPeers[p] = &crawlVal{ addrs: addrs, } }, - func(p peer.ID, err error) {}) + func(p peer.ID, err error) { + dialErr, ok := err.(*swarm.DialError) + if ok { + for _, transportErr := range dialErr.DialErrors { + if errors.Is(transportErr.Cause, network.ErrResourceLimitExceeded) { + limitErrOnce.Do(func() { logger.Errorf(rtRefreshLimitsMsg) }) + } + } + } + // note that DialError implements Unwrap() which returns the Cause, so this covers that case + if errors.Is(err, network.ErrResourceLimitExceeded) { + limitErrOnce.Do(func() { logger.Errorf(rtRefreshLimitsMsg) }) + } + }) dur := time.Since(start) logger.Infof("crawl took %v", dur) peerAddrs := make(map[peer.ID][]multiaddr.Multiaddr) kPeerMap := make(map[string]peer.ID) newRt := trie.New() - for k, v := range m { - v.key = kadkey.KbucketIDToKey(kb.ConvertPeerID(k)) - peerAddrs[k] = v.addrs - kPeerMap[string(v.key)] = k - newRt.Add(v.key) + for peerID, foundCrawlVal := range foundPeers { + foundCrawlVal.key = kadkey.KbucketIDToKey(kb.ConvertPeerID(peerID)) + peerAddrs[peerID] = foundCrawlVal.addrs + kPeerMap[string(foundCrawlVal.key)] = peerID + newRt.Add(foundCrawlVal.key) } dht.peerAddrsLk.Lock() @@ -343,17 +409,24 @@ func (dht *FullRT) runCrawler(ctx context.Context) { func (dht *FullRT) Close() error { dht.cancel() - err := dht.ProviderManager.Process().Close() dht.wg.Wait() - return err + return dht.ProviderManager.Close() } -func (dht *FullRT) Bootstrap(ctx context.Context) error { +func (dht *FullRT) Bootstrap(ctx context.Context) (err error) { + _, end := tracer.Bootstrap(dhtName, ctx) + defer func() { end(err) }() + + // TODO: This should block until the first crawl finish. + return nil } // CheckPeers return (success, total) func (dht *FullRT) CheckPeers(ctx context.Context, peers ...peer.ID) (int, int) { + ctx, span := internal.StartSpan(ctx, "FullRT.CheckPeers", trace.WithAttributes(attribute.Int("NumPeers", len(peers)))) + defer span.End() + var peerAddrs chan interface{} var total int if len(peers) == 0 { @@ -411,8 +484,7 @@ func workers(numWorkers int, fn func(interface{}), inputs <-chan interface{}) { } func (dht *FullRT) GetClosestPeers(ctx context.Context, key string) ([]peer.ID, error) { - ctx, span := Tracer.Start(ctx, "GetClosestPeers") - _ = ctx // not used, but we want to assign it _just_ in case we use it. + _, span := internal.StartSpan(ctx, "FullRT.GetClosestPeers", trace.WithAttributes(internal.KeyAsAttribute("Key", key))) defer span.End() kbID := kb.ConvertKey(key) @@ -442,6 +514,9 @@ func (dht *FullRT) GetClosestPeers(ctx context.Context, key string) ([]peer.ID, // PutValue adds value corresponding to given Key. // This is the top level "Store" operation of the DHT func (dht *FullRT) PutValue(ctx context.Context, key string, value []byte, opts ...routing.Option) (err error) { + ctx, end := tracer.PutValue(dhtName, ctx, key, value, opts...) + defer func() { end(err) }() + if !dht.enableValues { return routing.ErrNotSupported } @@ -472,7 +547,7 @@ func (dht *FullRT) PutValue(ctx context.Context, key string, value []byte, opts } rec := record.MakePutRecord(key, value) - rec.TimeReceived = u.FormatRFC3339(time.Now()) + rec.TimeReceived = internal.FormatRFC3339(time.Now()) err = dht.putLocal(ctx, key, rec) if err != nil { return err @@ -506,7 +581,10 @@ type RecvdVal struct { } // GetValue searches for the value corresponding to given Key. -func (dht *FullRT) GetValue(ctx context.Context, key string, opts ...routing.Option) (_ []byte, err error) { +func (dht *FullRT) GetValue(ctx context.Context, key string, opts ...routing.Option) (result []byte, err error) { + ctx, end := tracer.GetValue(dhtName, ctx, key, opts...) + defer func() { end(result, err) }() + if !dht.enableValues { return nil, routing.ErrNotSupported } @@ -540,7 +618,10 @@ func (dht *FullRT) GetValue(ctx context.Context, key string, opts ...routing.Opt } // SearchValue searches for the value corresponding to given Key and streams the results. -func (dht *FullRT) SearchValue(ctx context.Context, key string, opts ...routing.Option) (<-chan []byte, error) { +func (dht *FullRT) SearchValue(ctx context.Context, key string, opts ...routing.Option) (ch <-chan []byte, err error) { + ctx, end := tracer.SearchValue(dhtName, ctx, key, opts...) + defer func() { ch, err = end(ch, err) }() + if !dht.enableValues { return nil, routing.ErrNotSupported } @@ -561,6 +642,7 @@ func (dht *FullRT) SearchValue(ctx context.Context, key string, opts ...routing. out := make(chan []byte) go func() { defer close(out) + best, peersWithBest, aborted := dht.searchValueQuorum(ctx, key, valCh, stopCh, out, responsesNeeded) if best == nil || aborted { return @@ -582,7 +664,7 @@ func (dht *FullRT) SearchValue(ctx context.Context, key string, opts ...routing. return } - ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + ctx, cancel := context.WithTimeout(ctx, time.Second*5) dht.updatePeerValues(ctx, key, best, updatePeers) cancel() }() @@ -591,7 +673,8 @@ func (dht *FullRT) SearchValue(ctx context.Context, key string, opts ...routing. } func (dht *FullRT) searchValueQuorum(ctx context.Context, key string, valCh <-chan RecvdVal, stopCh chan struct{}, - out chan<- []byte, nvals int) ([]byte, map[peer.ID]struct{}, bool) { + out chan<- []byte, nvals int, +) ([]byte, map[peer.ID]struct{}, bool) { numResponses := 0 return dht.processValues(ctx, key, valCh, func(ctx context.Context, v RecvdVal, better bool) bool { @@ -613,7 +696,8 @@ func (dht *FullRT) searchValueQuorum(ctx context.Context, key string, valCh <-ch } func (dht *FullRT) processValues(ctx context.Context, key string, vals <-chan RecvdVal, - newVal func(ctx context.Context, v RecvdVal, better bool) bool) (best []byte, peersWithBest map[peer.ID]struct{}, aborted bool) { + newVal func(ctx context.Context, v RecvdVal, better bool) bool, +) (best []byte, peersWithBest map[peer.ID]struct{}, aborted bool) { loop: for { if aborted { @@ -659,7 +743,7 @@ func (dht *FullRT) updatePeerValues(ctx context.Context, key string, val []byte, fixupRec := record.MakePutRecord(key, val) for _, p := range peers { go func(p peer.ID) { - //TODO: Is this possible? + // TODO: Is this possible? if p == dht.h.ID() { err := dht.putLocal(ctx, key, fixupRec) if err != nil { @@ -766,8 +850,8 @@ func (dht *FullRT) getValues(ctx context.Context, key string, stopQuery chan str // Provide makes this node announce that it can provide a value for the given key func (dht *FullRT) Provide(ctx context.Context, key cid.Cid, brdcst bool) (err error) { - ctx, span := Tracer.Start(ctx, "Provide") - defer span.End() + ctx, end := tracer.Provide(dhtName, ctx, key, brdcst) + defer func() { end(err) }() if !dht.enableProviders { return routing.ErrNotSupported @@ -821,7 +905,10 @@ func (dht *FullRT) Provide(ctx context.Context, key cid.Cid, brdcst bool) (err e } successes := dht.execOnMany(ctx, func(ctx context.Context, p peer.ID) error { - err := dht.protoMessenger.PutProvider(ctx, p, keyMH, dht.h) + err := dht.protoMessenger.PutProviderAddrs(ctx, p, keyMH, peer.AddrInfo{ + ID: dht.self, + Addrs: dht.h.Addrs(), + }) return err }, peers, true) @@ -844,9 +931,6 @@ func (dht *FullRT) Provide(ctx context.Context, key cid.Cid, brdcst bool) (err e // execOnMany has returned (e.g. do not write to resources that might get closed or set to nil and therefore result in // a panic instead of just returning an error). func (dht *FullRT) execOnMany(ctx context.Context, fn func(context.Context, peer.ID) error, peers []peer.ID, sloppyExit bool) int { - ctx, span := Tracer.Start(ctx, "execOnMany") - defer span.End() - if len(peers) == 0 { return 0 } @@ -909,9 +993,9 @@ func (dht *FullRT) execOnMany(ctx context.Context, fn func(context.Context, peer return numSuccess } -func (dht *FullRT) ProvideMany(ctx context.Context, keys []multihash.Multihash) error { - ctx, span := Tracer.Start(ctx, "ProvideMany") - defer span.End() +func (dht *FullRT) ProvideMany(ctx context.Context, keys []multihash.Multihash) (err error) { + ctx, end := tracer.ProvideMany(dhtName, ctx, keys) + defer func() { end(err) }() if !dht.enableProviders { return routing.ErrNotSupported @@ -946,6 +1030,9 @@ func (dht *FullRT) ProvideMany(ctx context.Context, keys []multihash.Multihash) } func (dht *FullRT) PutMany(ctx context.Context, keys []string, values [][]byte) error { + ctx, span := internal.StartSpan(ctx, "FullRT.PutMany", trace.WithAttributes(attribute.Int("NumKeys", len(keys)))) + defer span.End() + if !dht.enableValues { return routing.ErrNotSupported } @@ -974,7 +1061,7 @@ func (dht *FullRT) PutMany(ctx context.Context, keys []string, values [][]byte) } func (dht *FullRT) bulkMessageSend(ctx context.Context, keys []peer.ID, fn func(ctx context.Context, target, k peer.ID) error, isProvRec bool) error { - ctx, span := Tracer.Start(ctx, "bulkMessageSend", trace.WithAttributes(attribute.Int("numKeys", len(keys)))) + ctx, span := internal.StartSpan(ctx, "FullRT.BulkMessageSend") defer span.End() if len(keys) == 0 { @@ -1194,18 +1281,17 @@ func (dht *FullRT) FindProviders(ctx context.Context, c cid.Cid) ([]peer.AddrInf // the search query completes. If count is zero then the query will run until it // completes. Note: not reading from the returned channel may block the query // from progressing. -func (dht *FullRT) FindProvidersAsync(ctx context.Context, key cid.Cid, count int) <-chan peer.AddrInfo { +func (dht *FullRT) FindProvidersAsync(ctx context.Context, key cid.Cid, count int) (ch <-chan peer.AddrInfo) { + ctx, end := tracer.FindProvidersAsync(dhtName, ctx, key, count) + defer func() { ch = end(ch, nil) }() + if !dht.enableProviders || !key.Defined() { peerOut := make(chan peer.AddrInfo) close(peerOut) return peerOut } - chSize := count - if count == 0 { - chSize = 1 - } - peerOut := make(chan peer.AddrInfo, chSize) + peerOut := make(chan peer.AddrInfo) keyMH := key.Hash() @@ -1215,14 +1301,29 @@ func (dht *FullRT) FindProvidersAsync(ctx context.Context, key cid.Cid, count in } func (dht *FullRT) findProvidersAsyncRoutine(ctx context.Context, key multihash.Multihash, count int, peerOut chan peer.AddrInfo) { + // use a span here because unlike tracer.FindProvidersAsync we know who told us about it and that intresting to log. + ctx, span := internal.StartSpan(ctx, "FullRT.FindProvidersAsyncRoutine") + defer span.End() + defer close(peerOut) findAll := count == 0 - var ps *peer.Set - if findAll { - ps = peer.NewSet() - } else { - ps = peer.NewLimitedSet(count) + ps := make(map[peer.ID]struct{}) + psLock := &sync.Mutex{} + psTryAdd := func(p peer.ID) bool { + psLock.Lock() + defer psLock.Unlock() + _, ok := ps[p] + if !ok && (len(ps) < count || findAll) { + ps[p] = struct{}{} + return true + } + return false + } + psSize := func() int { + psLock.Lock() + defer psLock.Unlock() + return len(ps) } provs, err := dht.ProviderManager.GetProviders(ctx, key) @@ -1231,9 +1332,13 @@ func (dht *FullRT) findProvidersAsyncRoutine(ctx context.Context, key multihash. } for _, p := range provs { // NOTE: Assuming that this list of peers is unique - if ps.TryAdd(p.ID) { + if psTryAdd(p.ID) { select { case peerOut <- p: + span.AddEvent("found provider", trace.WithAttributes( + attribute.Stringer("peer", p.ID), + attribute.Stringer("from", dht.self), + )) case <-ctx.Done(): return } @@ -1241,7 +1346,7 @@ func (dht *FullRT) findProvidersAsyncRoutine(ctx context.Context, key multihash. // If we have enough peers locally, don't bother with remote RPC // TODO: is this a DOS vector? - if !findAll && ps.Size() >= count { + if !findAll && psSize() >= count { return } } @@ -1272,17 +1377,21 @@ func (dht *FullRT) findProvidersAsyncRoutine(ctx context.Context, key multihash. for _, prov := range provs { dht.maybeAddAddrs(prov.ID, prov.Addrs, peerstore.TempAddrTTL) logger.Debugf("got provider: %s", prov) - if ps.TryAdd(prov.ID) { + if psTryAdd(prov.ID) { logger.Debugf("using provider: %s", prov) select { case peerOut <- *prov: + span.AddEvent("found provider", trace.WithAttributes( + attribute.Stringer("peer", prov.ID), + attribute.Stringer("from", p), + )) case <-ctx.Done(): logger.Debug("context timed out sending more providers") return ctx.Err() } } - if !findAll && ps.Size() >= count { - logger.Debugf("got enough providers (%d/%d)", ps.Size(), count) + if !findAll && psSize() >= count { + logger.Debugf("got enough providers (%d/%d)", psSize(), count) cancelquery() return nil } @@ -1303,7 +1412,10 @@ func (dht *FullRT) findProvidersAsyncRoutine(ctx context.Context, key multihash. } // FindPeer searches for a peer with given ID. -func (dht *FullRT) FindPeer(ctx context.Context, id peer.ID) (_ peer.AddrInfo, err error) { +func (dht *FullRT) FindPeer(ctx context.Context, id peer.ID) (pi peer.AddrInfo, err error) { + ctx, end := tracer.FindPeer(dhtName, ctx, id) + defer func() { end(pi, err) }() + if err := id.Validate(); err != nil { return peer.AddrInfo{}, err } @@ -1400,8 +1512,7 @@ func (dht *FullRT) FindPeer(ctx context.Context, id peer.ID) (_ peer.AddrInfo, e // Return peer information if we tried to dial the peer during the query or we are (or recently were) connected // to the peer. - connectedness := dht.h.Network().Connectedness(id) - if connectedness == network.Connected || connectedness == network.CanConnect { + if hasValidConnectedness(dht.h, id) { return dht.h.Peerstore().PeerInfo(id), nil } @@ -1479,18 +1590,21 @@ func (dht *FullRT) getRecordFromDatastore(ctx context.Context, dskey ds.Key) (*r // FindLocal looks for a peer with a given ID connected to this dht and returns the peer and the table it was found in. func (dht *FullRT) FindLocal(id peer.ID) peer.AddrInfo { - switch dht.h.Network().Connectedness(id) { - case network.Connected, network.CanConnect: + if hasValidConnectedness(dht.h, id) { return dht.h.Peerstore().PeerInfo(id) - default: - return peer.AddrInfo{} } + return peer.AddrInfo{} } func (dht *FullRT) maybeAddAddrs(p peer.ID, addrs []multiaddr.Multiaddr, ttl time.Duration) { // Don't add addresses for self or our connected peers. We have better ones. - if p == dht.h.ID() || dht.h.Network().Connectedness(p) == network.Connected { + if p == dht.h.ID() || hasValidConnectedness(dht.h, p) { return } dht.h.Peerstore().AddAddrs(p, addrs, ttl) } + +func hasValidConnectedness(host host.Host, id peer.ID) bool { + connectedness := host.Network().Connectedness(id) + return connectedness == network.Connected || connectedness == network.Limited +} diff --git a/fullrt/dht_test.go b/fullrt/dht_test.go index 94ee3e37a..23eef30b5 100644 --- a/fullrt/dht_test.go +++ b/fullrt/dht_test.go @@ -4,7 +4,7 @@ import ( "strconv" "testing" - "github.com/libp2p/go-libp2p-core/peer" + "github.com/libp2p/go-libp2p/core/peer" ) func TestDivideByChunkSize(t *testing.T) { diff --git a/fullrt/options.go b/fullrt/options.go index cd0f9ba59..275792f9e 100644 --- a/fullrt/options.go +++ b/fullrt/options.go @@ -2,11 +2,22 @@ package fullrt import ( "fmt" + "time" + kaddht "github.com/libp2p/go-libp2p-kad-dht" + "github.com/libp2p/go-libp2p-kad-dht/crawler" + "github.com/libp2p/go-libp2p-kad-dht/providers" ) type config struct { dhtOpts []kaddht.Option + + crawlInterval time.Duration + waitFrac float64 + bulkSendParallelism int + timeoutPerOp time.Duration + crawler crawler.Crawler + pmOpts []providers.Option } func (cfg *config) apply(opts ...Option) error { @@ -26,3 +37,62 @@ func DHTOption(opts ...kaddht.Option) Option { return nil } } + +// WithCrawler sets the crawler.Crawler to use in order to crawl the DHT network. +// Defaults to crawler.DefaultCrawler with parallelism of 200. +func WithCrawler(c crawler.Crawler) Option { + return func(opt *config) error { + opt.crawler = c + return nil + } +} + +// WithCrawlInterval sets the interval at which the DHT is crawled to refresh peer store. +// Defaults to 1 hour if unspecified. +func WithCrawlInterval(i time.Duration) Option { + return func(opt *config) error { + opt.crawlInterval = i + return nil + } +} + +// WithSuccessWaitFraction sets the fraction of peers to wait for before considering an operation a success defined as a number between (0, 1]. +// Defaults to 30% if unspecified. +func WithSuccessWaitFraction(f float64) Option { + return func(opt *config) error { + if f <= 0 || f > 1 { + return fmt.Errorf("success wait fraction must be larger than 0 and smaller or equal to 1; got: %f", f) + } + opt.waitFrac = f + return nil + } +} + +// WithBulkSendParallelism sets the maximum degree of parallelism at which messages are sent to other peers. It must be at least 1. +// Defaults to 20 if unspecified. +func WithBulkSendParallelism(b int) Option { + return func(opt *config) error { + if b < 1 { + return fmt.Errorf("bulk send parallelism must be at least 1; got: %d", b) + } + opt.bulkSendParallelism = b + return nil + } +} + +// WithTimeoutPerOperation sets the timeout per operation, where operations include putting providers and querying the DHT. +// Defaults to 5 seconds if unspecified. +func WithTimeoutPerOperation(t time.Duration) Option { + return func(opt *config) error { + opt.timeoutPerOp = t + return nil + } +} + +// WithProviderManagerOptions sets the options to use when instantiating providers.ProviderManager. +func WithProviderManagerOptions(pmOpts ...providers.Option) Option { + return func(opt *config) error { + opt.pmOpts = pmOpts + return nil + } +} diff --git a/go.mod b/go.mod index 6f5f43b24..321bc1e2f 100644 --- a/go.mod +++ b/go.mod @@ -1,41 +1,144 @@ module github.com/libp2p/go-libp2p-kad-dht -go 1.16 +go 1.22.0 + +retract v0.24.3 // this includes a breaking change and should have been released as v0.25.0 require ( - github.com/gogo/protobuf v1.3.2 github.com/google/gopacket v1.1.19 - github.com/google/uuid v1.3.0 + github.com/google/uuid v1.6.0 github.com/hashicorp/go-multierror v1.1.1 - github.com/hashicorp/golang-lru v0.5.4 - github.com/ipfs/go-cid v0.0.7 - github.com/ipfs/go-datastore v0.5.0 + github.com/hashicorp/golang-lru v1.0.2 + github.com/ipfs/boxo v0.27.2 + github.com/ipfs/go-cid v0.5.0 + github.com/ipfs/go-datastore v0.6.0 github.com/ipfs/go-detect-race v0.0.1 - github.com/ipfs/go-ipfs-util v0.0.2 - github.com/ipfs/go-ipns v0.1.2 - github.com/ipfs/go-log v1.0.5 - github.com/jbenet/goprocess v0.1.4 - github.com/libp2p/go-eventbus v0.2.1 - github.com/libp2p/go-libp2p v0.16.0 - github.com/libp2p/go-libp2p-core v0.11.0 - github.com/libp2p/go-libp2p-kbucket v0.4.7 - github.com/libp2p/go-libp2p-peerstore v0.4.0 - github.com/libp2p/go-libp2p-record v0.1.3 - github.com/libp2p/go-libp2p-routing-helpers v0.2.3 - github.com/libp2p/go-libp2p-swarm v0.8.0 - github.com/libp2p/go-libp2p-testing v0.5.0 - github.com/libp2p/go-libp2p-xor v0.0.0-20210714161855-5c005aca55db - github.com/libp2p/go-msgio v0.1.0 - github.com/libp2p/go-netroute v0.1.6 - github.com/multiformats/go-base32 v0.0.3 - github.com/multiformats/go-multiaddr v0.4.0 - github.com/multiformats/go-multibase v0.0.3 - github.com/multiformats/go-multihash v0.0.15 - github.com/multiformats/go-multistream v0.2.2 - github.com/stretchr/testify v1.7.0 + github.com/ipfs/go-log/v2 v2.5.1 + github.com/ipfs/go-test v0.0.4 + github.com/libp2p/go-libp2p v0.38.2 + github.com/libp2p/go-libp2p-kbucket v0.6.4 + github.com/libp2p/go-libp2p-record v0.3.1 + github.com/libp2p/go-libp2p-routing-helpers v0.7.4 + github.com/libp2p/go-libp2p-testing v0.12.0 + github.com/libp2p/go-libp2p-xor v0.1.0 + github.com/libp2p/go-msgio v0.3.0 + github.com/libp2p/go-netroute v0.2.2 + github.com/multiformats/go-base32 v0.1.0 + github.com/multiformats/go-multiaddr v0.14.0 + github.com/multiformats/go-multibase v0.2.0 + github.com/multiformats/go-multihash v0.2.3 + github.com/multiformats/go-multistream v0.6.0 + github.com/stretchr/testify v1.10.0 github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 - go.opencensus.io v0.23.0 - go.opentelemetry.io/otel v0.20.0 - go.opentelemetry.io/otel/trace v0.20.0 - go.uber.org/zap v1.19.0 + go.opencensus.io v0.24.0 + go.opentelemetry.io/otel v1.34.0 + go.opentelemetry.io/otel/trace v1.34.0 + go.uber.org/multierr v1.11.0 + go.uber.org/zap v1.27.0 + gonum.org/v1/gonum v0.15.1 + google.golang.org/protobuf v1.36.3 +) + +require ( + github.com/Jorropo/jsync v1.0.1 // indirect + github.com/benbjohnson/clock v1.3.5 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/containerd/cgroups v1.1.0 // indirect + github.com/coreos/go-systemd/v22 v22.5.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/elastic/gosigar v0.14.3 // indirect + github.com/flynn/noise v1.1.0 // indirect + github.com/francoispqt/gojay v1.2.13 // 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/godbus/dbus/v5 v5.1.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect + github.com/gorilla/websocket v1.5.3 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/huin/goupnp v1.3.0 // indirect + github.com/ipfs/go-block-format v0.2.0 // indirect + github.com/ipfs/go-ipfs-util v0.0.3 // indirect + github.com/ipfs/go-log v1.0.5 // indirect + github.com/ipld/go-ipld-prime v0.21.0 // indirect + github.com/jackpal/go-nat-pmp v1.0.2 // indirect + github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect + github.com/jbenet/goprocess v0.1.4 // indirect + github.com/klauspost/compress v1.17.11 // indirect + github.com/klauspost/cpuid/v2 v2.2.9 // indirect + github.com/koron/go-ssdp v0.0.4 // indirect + github.com/libp2p/go-buffer-pool v0.1.0 // indirect + github.com/libp2p/go-cidranger v1.1.0 // indirect + github.com/libp2p/go-flow-metrics v0.2.0 // indirect + github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect + github.com/libp2p/go-nat v0.2.0 // indirect + github.com/libp2p/go-reuseport v0.4.0 // indirect + github.com/libp2p/go-yamux/v4 v4.0.1 // indirect + github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect + github.com/mattn/go-isatty v0.0.20 // 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.1 // indirect + github.com/mr-tron/base58 v1.2.0 // indirect + github.com/multiformats/go-base36 v0.2.0 // indirect + github.com/multiformats/go-multiaddr-dns v0.4.1 // indirect + github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect + github.com/multiformats/go-multicodec v0.9.0 // indirect + github.com/multiformats/go-varint v0.0.7 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/onsi/ginkgo/v2 v2.22.0 // indirect + github.com/opencontainers/runtime-spec v1.2.0 // indirect + github.com/opentracing/opentracing-go v1.2.0 // indirect + github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect + github.com/pion/datachannel v1.5.10 // indirect + github.com/pion/dtls/v2 v2.2.12 // indirect + github.com/pion/ice/v2 v2.3.37 // 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.15 // indirect + github.com/pion/rtp v1.8.10 // indirect + github.com/pion/sctp v1.8.35 // 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/transport/v3 v3.0.7 // indirect + github.com/pion/turn/v2 v2.1.6 // indirect + github.com/pion/webrtc/v3 v3.3.5 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // 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.62.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.2 // 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/spaolacci/murmur3 v1.1.0 // indirect + github.com/wlynxg/anet v0.0.5 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/otel/metric v1.34.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 + golang.org/x/crypto v0.32.0 // indirect + golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect + golang.org/x/mod v0.22.0 // indirect + golang.org/x/net v0.34.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.29.0 // indirect + golang.org/x/text v0.21.0 // indirect + golang.org/x/tools v0.29.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + lukechampine.com/blake3 v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index 8a6be7432..539fa853e 100644 --- a/go.sum +++ b/go.sum @@ -2,365 +2,207 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT 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/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -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/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/Jorropo/jsync v1.0.1 h1:6HgRolFZnsdfzRUj+ImB9og1JYOxQoReSywkHOGSaUU= +github.com/Jorropo/jsync v1.0.1/go.mod h1:jCOZj3vrBCri3bSU3ErUYvevKlnbssrXeCivybS5ABQ= 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/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 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= +github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 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.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94= -github.com/btcsuite/btcd v0.22.0-beta h1:LTDpDKUM5EeOFBPM8IXpinEcmZ6FWfNZbE3lfrfdnWo= -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/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/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= 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 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= -github.com/cespare/xxhash/v2 v2.1.1/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/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/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/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.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 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= 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/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 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/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/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= +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/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-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/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/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +github.com/elastic/gosigar v0.12.0/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/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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 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.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 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= -github.com/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-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-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/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +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-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/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-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 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +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-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg= +github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/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.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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/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/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/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/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/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/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/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +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.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/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.2 h1:RfGLP+h3mvisuWEyybxNq5Eft3NWhHLPeUN72kpKZoI= -github.com/huin/goupnp v1.0.2/go.mod h1:0dxJBVBHqTMjIUMkESDTNgOOx/Mw5wYIfyFmdzSamkM= -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/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.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -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/boxo v0.27.2 h1:sGo4KdwBaMjdBjH08lqPJyt27Z4CO6sugne3ryX513s= +github.com/ipfs/boxo v0.27.2/go.mod h1:qEIRrGNr0bitDedTCzyzBHxzNWqYmyuHgK8LG9Q83EM= +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.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 h1:ysQJVJA3fNDF1qigJbsSQOdjhVLsOEoPdh0+R97k3jY= -github.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= +github.com/ipfs/go-cid v0.5.0 h1:goEKKhaGm0ul11IHA7I6p1GmKz8kEYniqFopaB5Otwg= +github.com/ipfs/go-cid v0.5.0/go.mod h1:0L7vmeNXpQpUS9vt+yEARkJ8rOg43DF3iPgn4GIN0mk= 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.4.5/go.mod h1:eXTcaaiN6uOlVCLS9GjJUJtlvJfM3xk23w3fyfrmmJs= -github.com/ipfs/go-datastore v0.5.0 h1:rQicVCEacWyk4JZ6G5bD9TKR7lZEG1MWcG7UdWYrFAU= -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-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.2.7/go.mod h1:02rnztVKA4aZwDuaRPTf8mpqcKmXP7mLl6JPxd14JHA= -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-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= github.com/ipfs/go-ipfs-util v0.0.1/go.mod h1:spsl5z8KUnrve+73pOhSVZND1SIxPW5RyBCNzQxlJBc= -github.com/ipfs/go-ipfs-util v0.0.2 h1:59Sswnk1MFaiq+VcaknX7aYEyGyGDAA73ilhEK2POp8= -github.com/ipfs/go-ipfs-util v0.0.2/go.mod h1:CbPtkWJzjLdEcezDns2XYaehFVNXG9zrdrtMecczcsQ= -github.com/ipfs/go-ipns v0.1.2 h1:O/s/0ht+4Jl9+VoxoUo0zaHjnZUS+aBQIKTuzdZ/ucI= -github.com/ipfs/go-ipns v0.1.2/go.mod h1:ioQ0j02o6jdIVW+bmi18f4k2gRf0AV3kZ9KeHYHICnQ= +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-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 h1:31Re/cPqFHpsRHgyVwjWADPoF0otB1WrjTy8ZFYwEZU= -github.com/ipfs/go-log/v2 v2.3.0/go.mod h1:QqGoj30OTpnKaG/LKTGTxoP2mmQtjVMEnK72gynbe/g= -github.com/ipld/go-ipld-prime v0.9.0 h1:N2OjJMb+fhyFPwPnVvJcWU/NsumP8etal+d2v3G4eww= -github.com/ipld/go-ipld-prime v0.9.0/go.mod h1:KvBLMr4PX1gWptgkzRjVZCrLmSGcZCb/jioOQwCqZN8= +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-test v0.0.4 h1:DKT66T6GBB6PsDFLoO56QZPrOmzJkqU1FZH5C9ySkew= +github.com/ipfs/go-test v0.0.4/go.mod h1:qhIM1EluEfElKKM6fnWxGn822/z9knUGM1+I/OAQNKI= +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/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.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= @@ -370,424 +212,241 @@ github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZl 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/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.11.7 h1:0hzRabrMN4tSTvMfnL3SCv1ZGeAP23ynzodBgaHeMeg= -github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +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.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= +github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= 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.2 h1:fL3wAoyT6hXHQlORyXUW4Q23kkQpJRgEAYcZB5BR71o= -github.com/koron/go-ssdp v0.0.2/go.mod h1:XoLfkAiA2KeZsYh4DbHxD7h3nR2AZNqVQOa+LJuqPYs= -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 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +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 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/libp2p/go-addr-util v0.0.2/go.mod h1:Ecd6Fb3yIuLzq4bD7VcywcVSBtefcAwnUISBM3WG15E= -github.com/libp2p/go-addr-util v0.1.0 h1:acKsntI33w2bTU7tC9a0SaPimJGfSI0bFKC18ChxeVI= -github.com/libp2p/go-addr-util v0.1.0/go.mod h1:6I3ZYuFr2O/9D+SoyM0zEw0EF3YkldtTX406BpdQMqw= +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-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ= -github.com/libp2p/go-buffer-pool v0.0.2 h1:QNK2iAFa8gjAe1SPz6mHSMuCcjs+X1wlHzeOSqcmlfs= github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= +github.com/libp2p/go-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.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 h1:9UCIKlBL1hC9u7nkMXpD1nkc/T53PKMAn3/k9ivBAVc= -github.com/libp2p/go-conn-security-multistream v0.3.0/go.mod h1:EEP47t4fw/bTelVmEzIDqSe69hO/ip52xBEhZMLWAHM= -github.com/libp2p/go-eventbus v0.2.1 h1:VanAdErQnpTioN2TowqNcOijf6YwhuODe4pPKSDpxGc= -github.com/libp2p/go-eventbus v0.2.1/go.mod h1:jc2S4SoEVPP48H9Wpzm5aiGwUCBMfGhVhhBjyhhCJs8= github.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZxBdp967ls1g+k8= -github.com/libp2p/go-flow-metrics v0.0.2/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs= -github.com/libp2p/go-flow-metrics v0.0.3 h1:8tAs/hSdNvUiLgtlSy3mxwxWP4I9y/jlkPFT7epKdeM= github.com/libp2p/go-flow-metrics v0.0.3/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs= -github.com/libp2p/go-libp2p v0.16.0 h1:aTxzQPllnW+nyC9mY8xaS20BbcrSYMt1HCkjZRHvdGY= -github.com/libp2p/go-libp2p v0.16.0/go.mod h1:ump42BsirwAWxKzsCiFnTtN1Yc+DuPu76fyMX364/O4= -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.1.0 h1:rABPCO77SjdbJ/eJ/ynIo8vWICy1VEnL5JAxJbQLo1E= -github.com/libp2p/go-libp2p-asn-util v0.1.0/go.mod h1:wu+AnM9Ii2KgO5jMmS1rz9dvzTdj8BXqsPR9HR0XB7I= -github.com/libp2p/go-libp2p-autonat v0.6.0 h1:+vbQ1pMzMGjE/RJopiQKK2FRjdCKHPNPrkPm8u+luQU= -github.com/libp2p/go-libp2p-autonat v0.6.0/go.mod h1:bFC6kY8jwzNNWoqc8iGE57vsfwyJ/lP4O4DOV1e0B2o= -github.com/libp2p/go-libp2p-blankhost v0.2.0 h1:3EsGAi0CBGcZ33GwRuXEYJLLPoVWyXJ1bcJzAJjINkk= -github.com/libp2p/go-libp2p-blankhost v0.2.0/go.mod h1:eduNKXGTioTuQAUcZ5epXi9vMl+t4d8ugUBRQ4SqaNQ= -github.com/libp2p/go-libp2p-circuit v0.4.0 h1:eqQ3sEYkGTtybWgr6JLqJY6QLtPWRErvFjFDfAOO1wc= -github.com/libp2p/go-libp2p-circuit v0.4.0/go.mod h1:t/ktoFIUzM6uLQ+o1G6NuBl2ANhBKN9Bc8jRIk31MoA= -github.com/libp2p/go-libp2p-core v0.0.1/go.mod h1:g/VxnTZ/1ygHxH3dKok7Vno1VfpvGcGip57wjTU4fco= -github.com/libp2p/go-libp2p-core v0.2.0/go.mod h1:X0eyB0Gy93v0DZtSYbEM7RnMChm9Uv3j7yRXjO77xSI= +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.38.2 h1:9SZQDOCi82A25An4kx30lEtr6kGTxrtoaDkbs5xrK5k= +github.com/libp2p/go-libp2p v0.38.2/go.mod h1:QWV4zGL3O9nXKdHirIC59DoRcZ446dfkjbOJ55NEWFo= +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.2.5/go.mod h1:6+5zJmKhsf7yHn1RbmYDu08qDUpIUxGdqHuEZckmZOA= github.com/libp2p/go-libp2p-core v0.3.0/go.mod h1:ACp3DmS3/N64c2jDzcV429ukDpicbL6+TrrxANBjPGw= -github.com/libp2p/go-libp2p-core v0.3.1/go.mod h1:thvWy0hvaSBhnVBaW37BvzgVV68OUhgJJLAa6almrII= -github.com/libp2p/go-libp2p-core v0.5.0/go.mod h1:49XGI+kc38oGVwqSBhDEwytaAxgZasHhFfQKibzTls0= -github.com/libp2p/go-libp2p-core v0.5.1/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y= -github.com/libp2p/go-libp2p-core v0.5.3/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y= -github.com/libp2p/go-libp2p-core v0.5.4/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y= -github.com/libp2p/go-libp2p-core v0.5.5/go.mod h1:vj3awlOr9+GMZJFH9s4mpt9RHHgGqeHCopzbYKZdRjM= -github.com/libp2p/go-libp2p-core v0.5.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.6/go.mod h1:dgHr0l0hIKfWpGpqAMbpo19pen9wJfdCGv51mTmdpmM= -github.com/libp2p/go-libp2p-core v0.9.0/go.mod h1:ESsbz31oC3C1AvMJoGx26RTuCkNhmkSRCqZ0kQtJ2/8= -github.com/libp2p/go-libp2p-core v0.10.0/go.mod h1:ECdxehoYosLYHgDDFa2N4yE8Y7aQRAMf0sX9mf2sbGg= -github.com/libp2p/go-libp2p-core v0.11.0 h1:75jAgdA+IChNa+/mZXogfmrGkgwxkVvxmIC7pV+F6sI= -github.com/libp2p/go-libp2p-core v0.11.0/go.mod h1:ECdxehoYosLYHgDDFa2N4yE8Y7aQRAMf0sX9mf2sbGg= -github.com/libp2p/go-libp2p-discovery v0.6.0 h1:1XdPmhMJr8Tmj/yUfkJMIi8mgwWrLUsCB3bMxdT+DSo= -github.com/libp2p/go-libp2p-discovery v0.6.0/go.mod h1:/u1voHt0tKIe5oIA1RHBKQLVCWPna2dXmPNHc2zR9S8= 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-loggables v0.1.0/go.mod h1:EyumB2Y6PrYjr55Q3/tiJ/o3xoDasoRYM7nOzEpoa90= -github.com/libp2p/go-libp2p-mplex v0.2.1/go.mod h1:SC99Rxs8Vuzrf/6WhmH41kNn13TiYdAWNYHrwImKLnE= -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.1 h1:/pyhkP1nLwjG3OM+VuaNJkQT/Pqq73WzB3aDN3Fx1sc= -github.com/libp2p/go-libp2p-mplex v0.4.1/go.mod h1:cmy+3GfqfM1PceHTLL7zQzAAYaryDu6iPSC+CIb094g= -github.com/libp2p/go-libp2p-nat v0.1.0 h1:vigUi2MEN+fwghe5ijpScxtbbDz+L/6y8XwlzYOJgSY= -github.com/libp2p/go-libp2p-nat v0.1.0/go.mod h1:DQzAG+QbDYjN1/C3B6vXucLtz3u9rEonLVPtZVzQqks= -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.3.0 h1:NCVH7evhVt9njbTQshzT7N1S3Q6fjj9M11FCgfH5+cA= -github.com/libp2p/go-libp2p-noise v0.3.0/go.mod h1:JNjHbociDJKHD64KTkzGnzqJ0FEV5gHJa6AB00kbCNQ= +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.6/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= -github.com/libp2p/go-libp2p-peerstore v0.2.8/go.mod h1:gGiPlXdz7mIHd2vfAsHzBNAMqSDkt2UBFwgcITgw1lA= -github.com/libp2p/go-libp2p-peerstore v0.4.0 h1:DOhRJLnM9Dc9lIXi3rPDZBf789LXy1BrzwIs7Tj0cKA= -github.com/libp2p/go-libp2p-peerstore v0.4.0/go.mod h1:rDJUFyzEWPpXpEwywkcTYYzDHlwza8riYMaUzaN6hX0= -github.com/libp2p/go-libp2p-pnet v0.2.0 h1:J6htxttBipJujEjz1y0a5+eYoiPcFHhSYHH6na5f0/k= -github.com/libp2p/go-libp2p-pnet v0.2.0/go.mod h1:Qqvq6JH/oMZGwqs3N1Fqhv8NVhrdYcO0BW4wssv21LA= -github.com/libp2p/go-libp2p-quic-transport v0.13.0/go.mod h1:39/ZWJ1TW/jx1iFkKzzUg00W6tDJh73FC0xYudjr7Hc= -github.com/libp2p/go-libp2p-quic-transport v0.15.0 h1:DR0mP6kcieowikBprWkcNtbquRKOPWb5dLZ4ahDZujk= -github.com/libp2p/go-libp2p-quic-transport v0.15.0/go.mod h1:wv4uGwjcqe8Mhjj7N/Ic0aKjA+/10UnMlSzLO0yRpYQ= -github.com/libp2p/go-libp2p-record v0.1.2/go.mod h1:pal0eNcT5nqZaTV7UGhqeGqxFgGdsU/9W//C8dqjQDk= -github.com/libp2p/go-libp2p-record v0.1.3 h1:R27hoScIhQf/A8XJZ8lYpnqh9LatJ5YbHs28kCIfql0= -github.com/libp2p/go-libp2p-record v0.1.3/go.mod h1:yNUff/adKIfPnYQXgp6FQmNu3gLJ6EMg7+/vv2+9pY4= -github.com/libp2p/go-libp2p-routing-helpers v0.2.3 h1:xY61alxJ6PurSi+MXbywZpelvuU4U4p/gPTxjqCqTzY= -github.com/libp2p/go-libp2p-routing-helpers v0.2.3/go.mod h1:795bh+9YeoFl99rMASoiVgHdi5bjack0N1+AFAdbvBw= -github.com/libp2p/go-libp2p-swarm v0.3.0/go.mod h1:hdv95GWCTmzkgeJpP+GK/9D9puJegb7H57B5hWQR5Kk= -github.com/libp2p/go-libp2p-swarm v0.8.0 h1:nRHNRhi86L7jhka02N4MoV+PSFFPoJFkHNQwCTFxNhw= -github.com/libp2p/go-libp2p-swarm v0.8.0/go.mod h1:sOMp6dPuqco0r0GHTzfVheVBh6UEL0L1lXUZ5ot2Fvc= -github.com/libp2p/go-libp2p-testing v0.0.3/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= -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.4.2/go.mod h1:Q+PFXYoiYFN5CAEG2w3gLPEzotlKsNSbKQ/lImlOWF0= -github.com/libp2p/go-libp2p-testing v0.5.0 h1:bTjC29TTQ/ODq0ld3+0KLq3irdA5cAH3OMbRi0/QsvE= -github.com/libp2p/go-libp2p-testing v0.5.0/go.mod h1:QBk8fqIL1XNcno/l3/hhaIEn4aLRijpYOR+zVjjlh+A= -github.com/libp2p/go-libp2p-tls v0.3.0/go.mod h1:fwF5X6PWGxm6IDRwF3V8AVCCj/hOd5oFlg+wo2FxJDY= -github.com/libp2p/go-libp2p-tls v0.3.1 h1:lsE2zYte+rZCEOHF72J1Fg3XK3dGQyKvI6i5ehJfEp0= -github.com/libp2p/go-libp2p-tls v0.3.1/go.mod h1:fwF5X6PWGxm6IDRwF3V8AVCCj/hOd5oFlg+wo2FxJDY= -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.3/go.mod h1:bpkldbOWXMrXhpZbSV1mQxTrefOg2Fi+k1ClDSA4ppw= -github.com/libp2p/go-libp2p-transport-upgrader v0.5.0 h1:7SDl3O2+AYOgfE40Mis83ClpfGNkNA6m4FwhbOHs+iI= -github.com/libp2p/go-libp2p-transport-upgrader v0.5.0/go.mod h1:Rc+XODlB3yce7dvFV4q/RmyJGsFcCZRkeZMu/Zdg0mo= -github.com/libp2p/go-libp2p-xor v0.0.0-20210714161855-5c005aca55db h1:EDoDKW8ZAHd6SIDeo+thU51PyQppqLYkBxx0ObvFj/w= -github.com/libp2p/go-libp2p-xor v0.0.0-20210714161855-5c005aca55db/go.mod h1:LSTM5yRnjGZbWNTA/hRwq2gGFrvRIbQJscoIL/u6InY= -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.6.0 h1:TKayW983n92JhCGdCo7ej7eEb+DQ0VYfKNOxlN/1kNQ= -github.com/libp2p/go-libp2p-yamux v0.6.0/go.mod h1:MRhd6mAYnFRnSISp4M8i0ClV/j+mWHo2mYLifWGw33k= -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 h1:4ACqZKw8AqiuJfwFGq1CYDFugfXTOos+qQ3DETkhtCE= -github.com/libp2p/go-maddr-filter v0.1.0/go.mod h1:VzZhTXkMucEGGEOSKddrwGiOv0tUhgnKqNEmIAz/bPU= -github.com/libp2p/go-mplex v0.1.0/go.mod h1:SXgmdki2kwCUlCCbfGLEgHjC4pFqhTp0ZoV6aiKgxDU= -github.com/libp2p/go-mplex v0.1.2/go.mod h1:Xgz2RDCi3co0LeZfgjm4OgUF15+sVR8SRcu3SFXI1lk= -github.com/libp2p/go-mplex v0.3.0 h1:U1T+vmCYJaEoDJPV1aq31N56hS+lJgb397GsylNSgrU= -github.com/libp2p/go-mplex v0.3.0/go.mod h1:0Oy/A9PQlwBytDRp4wSkFnzHYDKcpLot35JQ6msjvYQ= +github.com/libp2p/go-libp2p-record v0.3.1 h1:cly48Xi5GjNw5Wq+7gmjfBiG9HCzQVkiZOUZ8kUl+Fg= +github.com/libp2p/go-libp2p-record v0.3.1/go.mod h1:T8itUkLcWQLCYMqtX7Th6r7SexyUJpIyPgks757td/E= +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-xor v0.1.0 h1:hhQwT4uGrBcuAkUGXADuPltalOdpf9aag9kaYNT2tLA= +github.com/libp2p/go-libp2p-xor v0.1.0/go.mod h1:LSTM5yRnjGZbWNTA/hRwq2gGFrvRIbQJscoIL/u6InY= github.com/libp2p/go-msgio v0.0.4/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= -github.com/libp2p/go-msgio v0.0.6/go.mod h1:4ecVB6d9f4BDSL5fqvPiC4A3KivjWn+Venn/1ALLMWA= -github.com/libp2p/go-msgio v0.1.0 h1:8Q7g/528ivAlfXTFWvWhVjTE8XG8sDTkRUKPYh9+5Q8= -github.com/libp2p/go-msgio v0.1.0/go.mod h1:eNlv2vy9V2X/kNldcZ+SShFE++o2Yjxwx6RAYsmgJnE= -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 h1:ruPJStbYyXVYGQ81uzEDzuvbYRLKRrLvTYd33yomC38= -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.2 h1:Dejd8cQ47Qx2kRABg6lPwknU7+nBnFRpko45/fFPuZ8= +github.com/libp2p/go-netroute v0.2.2/go.mod h1:Rntq6jUAH0l9Gg17w5bFGhcC9a+vk4KNXs6s7IljKYE= github.com/libp2p/go-openssl v0.0.3/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= github.com/libp2p/go-openssl v0.0.4/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= -github.com/libp2p/go-openssl v0.0.5/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= -github.com/libp2p/go-openssl v0.0.7 h1:eCAzdLejcNVBzP/iZM9vqHnQm+XyCEbSSIheIPRGNsw= -github.com/libp2p/go-openssl v0.0.7/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= -github.com/libp2p/go-reuseport v0.0.1/go.mod h1:jn6RmB1ufnQwl0Q1f+YxAj8isJgDCQzaaxIFYDhcYEA= -github.com/libp2p/go-reuseport v0.1.0 h1:0ooKOx2iwyIkf339WCZ2HN3ujTDbkK0PjC7JVoP1AiM= -github.com/libp2p/go-reuseport v0.1.0/go.mod h1:bQVn9hmfcTaoo0c9v5pBhOarsU1eNOBZdaAd2hzXRKU= -github.com/libp2p/go-reuseport-transport v0.0.3/go.mod h1:Spv+MPft1exxARzP2Sruj2Wb5JSyHNncjf1Oi2dEbzM= -github.com/libp2p/go-reuseport-transport v0.1.0 h1:C3PHeHjmnz8m6f0uydObj02tMEoi7CyD1zuN7xQT8gc= -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 h1:yD80l2ZOdGksnOyHrhxDdTDFrf7Oy+v3FMVArIRgZxQ= -github.com/libp2p/go-sockaddr v0.1.1/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= -github.com/libp2p/go-stream-muxer-multistream v0.3.0 h1:TqnSHPJEIqDEO7h1wZZ0p3DXdvDSiLHQidKKUGZtiOY= -github.com/libp2p/go-stream-muxer-multistream v0.3.0/go.mod h1:yDh8abSIzmZtqtOt64gFJUXEryejzNb0lisTt+fAMJA= -github.com/libp2p/go-tcp-transport v0.2.0/go.mod h1:vX2U0CnWimU4h0SGSEsg++AzvBcroCGYw28kh94oLe0= -github.com/libp2p/go-tcp-transport v0.4.0 h1:VDyg4j6en3OuXf90gfDQh5Sy9KowO9udnd0OU8PP6zg= -github.com/libp2p/go-tcp-transport v0.4.0/go.mod h1:0y52Rwrn4076xdJYu/51/qJIdxz+EWDAOG2S45sV3VI= -github.com/libp2p/go-ws-transport v0.5.0 h1:cO6x4P0v6PfxbKnxmf5cY2Ny4OPDGYkUqNvZzp/zdlo= -github.com/libp2p/go-ws-transport v0.5.0/go.mod h1:I2juo1dNTbl8BKSBYo98XY85kU2xds1iamArLvl8kNg= -github.com/libp2p/go-yamux v1.4.0/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE= -github.com/libp2p/go-yamux v1.4.1 h1:P1Fe9vF4th5JOxxgQvfbOHkrGqIZniTLf+ddhZp8YTI= -github.com/libp2p/go-yamux v1.4.1/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE= -github.com/libp2p/go-yamux/v2 v2.3.0 h1:luRV68GS1vqqr6EFUjtu1kr51d+IbW0gSowu8emYWAI= -github.com/libp2p/go-yamux/v2 v2.3.0/go.mod h1:iTU+lOIn/2h0AgKcL49clNTwfEw+WSfDYrXe05EyKIs= -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.23.0/go.mod h1:paZuzjXCE5mj6sikVLMvqXk8lJV2AsqtJ6bDhjEfxx0= -github.com/lucas-clemente/quic-go v0.24.0 h1:ToR7SIIEdrgOhgVTHvPgdVRJfgVy+N0wQAagH7L4d5g= -github.com/lucas-clemente/quic-go v0.24.0/go.mod h1:paZuzjXCE5mj6sikVLMvqXk8lJV2AsqtJ6bDhjEfxx0= +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/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/qtls-go1-15 v0.1.4/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I= -github.com/marten-seemann/qtls-go1-16 v0.1.4 h1:xbHbOGGhrenVtII6Co8akhLEdrawwB2iHl5yhJRpnco= -github.com/marten-seemann/qtls-go1-16 v0.1.4/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk= -github.com/marten-seemann/qtls-go1-17 v0.1.0 h1:P9ggrs5xtwiqXv/FHNwntmuLMNq3KaSIG93AtAZ48xk= -github.com/marten-seemann/qtls-go1-17 v0.1.0/go.mod h1:fz4HIxByo+LlWcreM4CZOYNuz3taBQ8rN2X6FqvaWo8= 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.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.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA= -github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/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.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg= -github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= +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= 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 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g= github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= -github.com/minio/sha256-simd v0.0.0-20190328051042-05b4dd3047e5/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= -github.com/minio/sha256-simd v0.1.0/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/minio/sha256-simd v0.1.1/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/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/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= -github.com/mr-tron/base58 v1.1.1/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= -github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI= github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= -github.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ89tUg4F4= -github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM= -github.com/multiformats/go-multiaddr v0.0.1/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= -github.com/multiformats/go-multiaddr v0.0.2/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= -github.com/multiformats/go-multiaddr v0.0.4/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= +github.com/multiformats/go-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.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 h1:hL/K4ZJhJ5PTw3nwylq9lGU5yArzcAroZmex1ghSEkQ= -github.com/multiformats/go-multiaddr v0.4.0/go.mod h1:YcpyLH8ZPudLxQlemYBPhSm0/oCXAT8Z4mzFpyoPyRc= -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 v0.14.0 h1:bfrHrJhrRuh/NXH5mCnemjpbGjzRw/b+tJFOD41g2tU= +github.com/multiformats/go-multiaddr v0.14.0/go.mod h1:6EkVAxtznq2yC3QT5CM1UTAwG0GTP3EWAIcjHuzQ+r4= +github.com/multiformats/go-multiaddr-dns v0.4.1 h1:whi/uCLbDS3mSEUMb1MsoT4uzUeZB0N32yzufqS0i5M= +github.com/multiformats/go-multiaddr-dns v0.4.1/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.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 h1:l/B6bJDQjvQ5G52jw4QGSYeOTZoAwIO77RblWplfIqk= -github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc= -github.com/multiformats/go-multicodec v0.2.0 h1:MUzKZWxOFagwLLtlx96pub9zwDQAbMAf1k9fXOdc3so= -github.com/multiformats/go-multicodec v0.2.0/go.mod h1:/y4YVwkfMyry5kFbMTbLJKErhycTIftytRV+llXdyS4= +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.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 h1:hWOPdrNqDjwHDx82vsYGSDZNyktOJJ2dzZJzFkOV1jM= -github.com/multiformats/go-multihash v0.0.15/go.mod h1:D6aZrWNLFTV/ynMpKsNtB40mJzmCl4jb1alC0OvHiHg= -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 h1:TCYu1BHTDr1F/Qm75qwYISQdzGcRdC21nFgQW7l7GBo= -github.com/multiformats/go-multistream v0.2.2/go.mod h1:UIcnm7Zuo8HKG+HkWgfQsGL+/MIEhyTqbODbIUwSXKs= +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.6.0 h1:ZaHKbsL404720283o4c/IHQXiS6gb8qAN5EIJ4PN5EA= +github.com/multiformats/go-multistream v0.6.0/go.mod h1:MOyoG5otO24cHIg8kf9QW2/NozURlkP/rvi2FQJyCPg= 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/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/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/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/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.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 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= +github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.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 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak= -github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= -github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= -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/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= +github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= +github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk= +github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= -github.com/openzipkin-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/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/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +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/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= -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/pion/datachannel v1.5.10 h1:ly0Q26K1i6ZkGf42W7D4hQYR90pZwzFOjTq5AuCKk4o= +github.com/pion/datachannel v1.5.10/go.mod h1:p/jJfC9arb29W7WrxyKbepTU20CFgyx5oLo8Rs4Py/M= +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.37 h1:ObIdaNDu1rCo7hObhs34YSBcO7fjslJMZV0ux+uZWh0= +github.com/pion/ice/v2 v2.3.37/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.15 h1:LZQi2JbdipLOj4eBjK4wlVoQWfrZbh3Q6eHtWtJBZBo= +github.com/pion/rtcp v1.2.15/go.mod h1:jlGuAjHMEXwMUHK78RgX0UmEJFV4zUKOFHR7OP+D3D0= +github.com/pion/rtp v1.8.3/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= +github.com/pion/rtp v1.8.10 h1:puphjdbjPB+L+NFaVuZ5h6bt1g5q4kFIoI+r5q/g0CU= +github.com/pion/rtp v1.8.10/go.mod h1:8uMBJj32Pa1wwx8Fuv/AsFhn8jsgw+3rUC2PfoBZ8p4= +github.com/pion/sctp v1.8.35 h1:qwtKvNK1Wc5tHMIYgTDJhfZk7vATGVHhXbUDfHbYwzA= +github.com/pion/sctp v1.8.35/go.mod h1:EcXP8zCYVTRy3W9xtOF7wJm1L1aXfKRQzaM33SjQlzg= +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.5 h1:ZsSzaMz/i9nblPdiAkZoP+E6Kmjw+jnyq3bEmU3EtRg= +github.com/pion/webrtc/v3 v3.3.5/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/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 h1:CskT+S6Ay54OwxBGB0R3Rsx4Muto6UnEYTyKJbyRIAI= -github.com/polydawn/refmt v0.0.0-20190807091052-3d65705ee9f1/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.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 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +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.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.30.0 h1:JEkYlQnpzrzQFxi6gnukFPdQ+ac82oRhzMcIduJu/Ug= -github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= 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.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 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -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/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.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE= +github.com/quic-go/quic-go v0.48.2/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/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +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/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/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 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= @@ -813,237 +472,165 @@ 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/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -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/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +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/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/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +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 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 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/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 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 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-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-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/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/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/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/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= 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 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/otel v0.20.0 h1:eaP0Fqu7SXHwvjiqDq83zImeehOHX8doTvU9AwXON8g= -go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= -go.opentelemetry.io/otel/metric v0.20.0 h1:4kzhXFP+btKm4jwxpjIqjs41A7MakRFUS86bqLHTIw8= -go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= -go.opentelemetry.io/otel/oteltest v0.20.0 h1:HiITxCawalo5vQzdHfKeZurV8x7ljcqAgiWzF6Vaeaw= -go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= -go.opentelemetry.io/otel/trace v0.20.0 h1:1DL6EXUdcg95gukhuRRvLDO/4X5THh/5dIV52lqtnbw= -go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= -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.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= 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.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0= -go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -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/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.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.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 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec= -go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.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.0 h1:mZQZefskPPCMIBCSEH0v2/iUqqLrYtaeqwD6FUGUnFE= -go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= +go.uber.org/zap v1.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-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-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-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-20210813211128-0a44fdfbc16e h1:VvfwVmMH40bpMeizC9/K7ipM5Qjucuu16RWfneFPyhQ= -golang.org/x/crypto v0.0.0-20210813211128-0a44fdfbc16e/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.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.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= 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/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA= +golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= 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 h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= 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/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 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -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.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= +golang.org/x/mod v0.22.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-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-20190813141303-74dc4d7220e7/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-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-20210813160813-60bc85c4be6d h1:LO7XpTYMwTqxjLcGWPijK3vRXg1aWdlNOVOHRq45d7c= -golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +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.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.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= 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/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= @@ -1051,98 +638,67 @@ 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 h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +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.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-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-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-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-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-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-20200124204421-9fbb57f87de9/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-20201214210602-f9fddec55a1e/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-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-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-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-20210816183151-1e6c022a8912 h1:uCLL3g5wH2xjxVREVuAbP9JM5PPKjRbXKRa6IBjkzmU= -golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/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.0.0-20210630005230-0f9fa26af87c/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.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.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +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.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/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.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= 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= @@ -1152,146 +708,50 @@ golang.org/x/tools v0.0.0-20181130052023-1c3d964395ce/go.mod h1:n7NCudcB/nEzxVGm 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-20200103221440-774c71fcf114/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 h1:wGiQel/hW0NnEkJUk8lbzkX2gFJU6PFxf1v5OlCfuOs= -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.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.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= +golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= 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 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/gonum v0.15.1 h1:FNy7N6OUZVUaWG9pTiD+jlhdQ3lMP+/LcTpJ6+a8sQ0= +gonum.org/v1/gonum v0.15.1/go.mod h1:eZTZuRFrzu5pcyjN5wJhcIhnUdNijYxX1T2IcrOGY0o= 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/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-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/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 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= 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= @@ -1300,55 +760,36 @@ 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 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU= +google.golang.org/protobuf v1.36.3/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/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= 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 h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= 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= -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= +lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= +lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= 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/handlers.go b/handlers.go index afbcd9f6d..3428c0cd1 100644 --- a/handlers.go +++ b/handlers.go @@ -7,16 +7,15 @@ import ( "fmt" "time" - "github.com/libp2p/go-libp2p-core/peer" - pstore "github.com/libp2p/go-libp2p-peerstore" + "github.com/libp2p/go-libp2p/core/peer" + pstore "github.com/libp2p/go-libp2p/p2p/host/peerstore" - "github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-datastore" - u "github.com/ipfs/go-ipfs-util" "github.com/libp2p/go-libp2p-kad-dht/internal" pb "github.com/libp2p/go-libp2p-kad-dht/pb" recpb "github.com/libp2p/go-libp2p-record/pb" "github.com/multiformats/go-base32" + "google.golang.org/protobuf/proto" ) // dhthandler specifies the signature of functions that handle DHT messages. @@ -115,7 +114,7 @@ func (dht *IpfsDHT) checkLocalDatastore(ctx context.Context, k []byte) (*recpb.R } var recordIsBad bool - recvtime, err := u.ParseRFC3339(rec.GetTimeReceived()) + recvtime, err := internal.ParseRFC3339(rec.GetTimeReceived()) if err != nil { logger.Info("either no receive time set on record, or it was invalid: ", err) recordIsBad = true @@ -206,7 +205,7 @@ func (dht *IpfsDHT) handlePutValue(ctx context.Context, p peer.ID, pmes *pb.Mess } // record the time we receive every record - rec.TimeReceived = u.FormatRFC3339(time.Now()) + rec.TimeReceived = internal.FormatRFC3339(time.Now()) data, err := proto.Marshal(rec) if err != nil { @@ -262,30 +261,26 @@ func (dht *IpfsDHT) handleFindPeer(ctx context.Context, from peer.ID, pmes *pb.M // if looking for self... special case where we send it on CloserPeers. targetPid := peer.ID(pmes.GetKey()) - if targetPid == dht.self { - closest = []peer.ID{dht.self} - } else { - closest = dht.betterPeersToQuery(pmes, from, dht.bucketSize) - - // Never tell a peer about itself. - if targetPid != from { - // Add the target peer to the set of closest peers if - // not already present in our routing table. - // - // Later, when we lookup known addresses for all peers - // in this set, we'll prune this peer if we don't - // _actually_ know where it is. - found := false - for _, p := range closest { - if targetPid == p { - found = true - break - } - } - if !found { - closest = append(closest, targetPid) + closest = dht.betterPeersToQuery(pmes, from, dht.bucketSize) + + // Never tell a peer about itself. + if targetPid != from { + // Add the target peer to the set of closest peers if + // not already present in our routing table. + // + // Later, when we lookup known addresses for all peers + // in this set, we'll prune this peer if we don't + // _actually_ know where it is. + found := false + for _, p := range closest { + if targetPid == p { + found = true + break } } + if !found { + closest = append(closest, targetPid) + } } if closest == nil { @@ -321,7 +316,16 @@ func (dht *IpfsDHT) handleGetProviders(ctx context.Context, p peer.ID, pmes *pb. if err != nil { return nil, err } - resp.ProviderPeers = pb.PeerInfosToPBPeers(dht.host.Network(), providers) + + filtered := make([]peer.AddrInfo, len(providers)) + for i, provider := range providers { + filtered[i] = peer.AddrInfo{ + ID: provider.ID, + Addrs: dht.filterAddrs(provider.Addrs), + } + } + + resp.ProviderPeers = pb.PeerInfosToPBPeers(dht.host.Network(), filtered) // Also send closer peers. closer := dht.betterPeersToQuery(pmes, p, dht.bucketSize) @@ -342,7 +346,7 @@ func (dht *IpfsDHT) handleAddProvider(ctx context.Context, p peer.ID, pmes *pb.M return nil, fmt.Errorf("handleAddProvider key is empty") } - logger.Debugf("adding provider", "from", p, "key", internal.LoggableProviderRecordBytes(key)) + logger.Debugw("adding provider", "from", p, "key", internal.LoggableProviderRecordBytes(key)) // add provider should use the address given in the message pinfos := pb.PBPeersToPeerInfos(pmes.GetProviderPeers()) @@ -359,7 +363,10 @@ func (dht *IpfsDHT) handleAddProvider(ctx context.Context, p peer.ID, pmes *pb.M continue } - dht.providerStore.AddProvider(ctx, key, peer.AddrInfo{ID: p}) + // We run the addrs filter after checking for the length, + // this allows transient nodes with varying /p2p-circuit addresses to still have their anouncement go through. + addrs := dht.filterAddrs(pi.Addrs) + dht.providerStore.AddProvider(ctx, key, peer.AddrInfo{ID: pi.ID, Addrs: addrs}) } return nil, nil diff --git a/handlers_test.go b/handlers_test.go index 5a88dbcb8..6b3d71d26 100644 --- a/handlers_test.go +++ b/handlers_test.go @@ -8,13 +8,13 @@ import ( "testing" "time" - proto "github.com/gogo/protobuf/proto" "github.com/libp2p/go-libp2p" - crypto "github.com/libp2p/go-libp2p-core/crypto" - peer "github.com/libp2p/go-libp2p-core/peer" pb "github.com/libp2p/go-libp2p-kad-dht/pb" recpb "github.com/libp2p/go-libp2p-record/pb" + crypto "github.com/libp2p/go-libp2p/core/crypto" + peer "github.com/libp2p/go-libp2p/core/peer" ma "github.com/multiformats/go-multiaddr" + "google.golang.org/protobuf/proto" ) func TestCleanRecordSigned(t *testing.T) { @@ -111,7 +111,7 @@ func BenchmarkHandleFindPeer(b *testing.B) { panic(err) } - d.peerFound(ctx, id, true) + d.peerFound(id) peers = append(peers, id) a, err := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", 2000+i)) diff --git a/internal/config/config.go b/internal/config/config.go index 405115d12..0cfcfae97 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -1,24 +1,28 @@ package config import ( + "context" "fmt" "time" + "github.com/ipfs/boxo/ipns" ds "github.com/ipfs/go-datastore" dssync "github.com/ipfs/go-datastore/sync" - "github.com/ipfs/go-ipns" - "github.com/libp2p/go-libp2p-core/host" - "github.com/libp2p/go-libp2p-core/peer" - "github.com/libp2p/go-libp2p-core/protocol" + "github.com/libp2p/go-libp2p-kad-dht/amino" + "github.com/libp2p/go-libp2p-kad-dht/internal/net" + pb "github.com/libp2p/go-libp2p-kad-dht/pb" "github.com/libp2p/go-libp2p-kad-dht/providers" "github.com/libp2p/go-libp2p-kbucket/peerdiversity" record "github.com/libp2p/go-libp2p-record" + "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/protocol" + ma "github.com/multiformats/go-multiaddr" ) // DefaultPrefix is the application specific prefix attached to all DHT protocols by default. -const DefaultPrefix protocol.ID = "/ipfs" - -const defaultBucketSize = 20 +const DefaultPrefix protocol.ID = amino.ProtocolPrefix // ModeOpt describes what mode the dht should operate in type ModeOpt int @@ -32,20 +36,22 @@ type RouteTableFilterFunc func(dht interface{}, p peer.ID) bool // Config is a structure containing all the options that can be used when constructing a DHT. type Config struct { - Datastore ds.Batching - Validator record.Validator - ValidatorChanged bool // if true implies that the validator has been changed and that Defaults should not be used - Mode ModeOpt - ProtocolPrefix protocol.ID - V1ProtocolOverride protocol.ID - BucketSize int - Concurrency int - Resiliency int - MaxRecordAge time.Duration - EnableProviders bool - EnableValues bool - ProviderStore providers.ProviderStore - QueryPeerFilter QueryFilterFunc + Datastore ds.Batching + Validator record.Validator + ValidatorChanged bool // if true implies that the validator has been changed and that Defaults should not be used + Mode ModeOpt + ProtocolPrefix protocol.ID + V1ProtocolOverride protocol.ID + BucketSize int + Concurrency int + Resiliency int + MaxRecordAge time.Duration + EnableProviders bool + EnableValues bool + ProviderStore providers.ProviderStore + QueryPeerFilter QueryFilterFunc + LookupCheckConcurrency int + MsgSenderBuilder func(h host.Host, protos []protocol.ID) pb.MessageSenderWithDisconnect RoutingTable struct { RefreshQueryTimeout time.Duration @@ -58,10 +64,15 @@ type Config struct { } BootstrapPeers func() []peer.AddrInfo + AddressFilter func([]ma.Multiaddr) []ma.Multiaddr + OnRequestHook func(ctx context.Context, s network.Stream, req *pb.Message) // test specific Config options DisableFixLowPeers bool TestAddressUpdateProcessing bool + + EnableOptimisticProvide bool + OptimisticProvideJobsPoolSize int } func EmptyQueryFilter(_ interface{}, ai peer.AddrInfo) bool { return true } @@ -108,27 +119,34 @@ var Defaults = func(o *Config) error { o.EnableProviders = true o.EnableValues = true o.QueryPeerFilter = EmptyQueryFilter + o.MsgSenderBuilder = net.NewMessageSenderImpl - o.RoutingTable.LatencyTolerance = time.Minute - o.RoutingTable.RefreshQueryTimeout = 1 * time.Minute + o.RoutingTable.LatencyTolerance = 10 * time.Second + o.RoutingTable.RefreshQueryTimeout = 10 * time.Second o.RoutingTable.RefreshInterval = 10 * time.Minute o.RoutingTable.AutoRefresh = true o.RoutingTable.PeerFilter = EmptyRTFilter - o.MaxRecordAge = time.Hour * 36 - o.BucketSize = defaultBucketSize - o.Concurrency = 10 - o.Resiliency = 3 + o.MaxRecordAge = providers.ProvideValidity + + o.BucketSize = amino.DefaultBucketSize + o.Concurrency = amino.DefaultConcurrency + o.Resiliency = amino.DefaultResiliency + o.LookupCheckConcurrency = 256 + + // MAGIC: It makes sense to set it to a multiple of OptProvReturnRatio * BucketSize. We chose a multiple of 4. + o.OptimisticProvideJobsPoolSize = 60 return nil } func (c *Config) Validate() error { + // Configuration is validated and enforced only if prefix matches Amino DHT if c.ProtocolPrefix != DefaultPrefix { return nil } - if c.BucketSize != defaultBucketSize { - return fmt.Errorf("protocol prefix %s must use bucket size %d", DefaultPrefix, defaultBucketSize) + if c.BucketSize != amino.DefaultBucketSize { + return fmt.Errorf("protocol prefix %s must use bucket size %d", DefaultPrefix, amino.DefaultBucketSize) } if !c.EnableProviders { return fmt.Errorf("protocol prefix %s must have providers enabled", DefaultPrefix) diff --git a/internal/config/quorum.go b/internal/config/quorum.go index ce5fba2a8..63e399265 100644 --- a/internal/config/quorum.go +++ b/internal/config/quorum.go @@ -1,6 +1,6 @@ package config -import "github.com/libp2p/go-libp2p-core/routing" +import "github.com/libp2p/go-libp2p/core/routing" type QuorumOptionKey struct{} diff --git a/internal/net/message_manager.go b/internal/net/message_manager.go index 627c47a5b..e898e8018 100644 --- a/internal/net/message_manager.go +++ b/internal/net/message_manager.go @@ -8,14 +8,15 @@ import ( "sync" "time" - "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/protocol" + "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/protocol" - logging "github.com/ipfs/go-log" + logging "github.com/ipfs/go-log/v2" "github.com/libp2p/go-msgio" - "github.com/libp2p/go-msgio/protoio" + "github.com/libp2p/go-msgio/pbio" + "google.golang.org/protobuf/proto" "go.opencensus.io/stats" "go.opencensus.io/tag" @@ -41,7 +42,7 @@ type messageSenderImpl struct { protocols []protocol.ID } -func NewMessageSenderImpl(h host.Host, protos []protocol.ID) pb.MessageSender { +func NewMessageSenderImpl(h host.Host, protos []protocol.ID) pb.MessageSenderWithDisconnect { return &messageSenderImpl{ host: h, strmap: make(map[peer.ID]*peerMessageSender), @@ -83,6 +84,12 @@ func (m *messageSenderImpl) SendRequest(ctx context.Context, p peer.ID, pmes *pb return nil, err } + marshalled, err := proto.Marshal(pmes) + if err != nil { + logger.Debugw("failed to marshal request", "error", err, "to", p) + return nil, err + } + start := time.Now() rpmes, err := ms.SendRequest(ctx, pmes) @@ -97,7 +104,7 @@ func (m *messageSenderImpl) SendRequest(ctx context.Context, p peer.ID, pmes *pb stats.Record(ctx, metrics.SentRequests.M(1), - metrics.SentBytes.M(int64(pmes.Size())), + metrics.SentBytes.M(int64(len(marshalled))), metrics.OutboundRequestLatency.M(float64(time.Since(start))/float64(time.Millisecond)), ) m.host.Peerstore().RecordLatency(p, time.Since(start)) @@ -118,6 +125,12 @@ func (m *messageSenderImpl) SendMessage(ctx context.Context, p peer.ID, pmes *pb return err } + marshalled, err := proto.Marshal(pmes) + if err != nil { + logger.Debugw("failed to marshal message", "error", err, "to", p) + return err + } + if err := ms.SendMessage(ctx, pmes); err != nil { stats.Record(ctx, metrics.SentMessages.M(1), @@ -129,7 +142,7 @@ func (m *messageSenderImpl) SendMessage(ctx context.Context, p peer.ID, pmes *pb stats.Record(ctx, metrics.SentMessages.M(1), - metrics.SentBytes.M(int64(pmes.Size())), + metrics.SentBytes.M(int64(len(marshalled))), ) return nil } @@ -295,7 +308,10 @@ func (ms *peerMessageSender) SendRequest(ctx context.Context, pmes *pb.Message) if err := ms.ctxReadMsg(ctx, mes); err != nil { _ = ms.s.Reset() ms.s = nil - + if err == context.Canceled { + // retry would be same error + return nil, err + } if retry { logger.Debugw("error reading message", "error", err) return nil, err @@ -331,7 +347,7 @@ func (ms *peerMessageSender) ctxReadMsg(ctx context.Context, mes *pb.Message) er errc <- err return } - errc <- mes.Unmarshal(bytes) + errc <- proto.Unmarshal(bytes, mes) }(ms.r) t := time.NewTimer(dhtReadMessageTimeout) @@ -352,7 +368,7 @@ func (ms *peerMessageSender) ctxReadMsg(ctx context.Context, mes *pb.Message) er // packet for every single write. type bufferedDelimitedWriter struct { *bufio.Writer - protoio.WriteCloser + pbio.WriteCloser } var writerPool = sync.Pool{ @@ -360,7 +376,7 @@ var writerPool = sync.Pool{ w := bufio.NewWriter(nil) return &bufferedDelimitedWriter{ Writer: w, - WriteCloser: protoio.NewDelimitedWriter(w), + WriteCloser: pbio.NewDelimitedWriter(w), } }, } diff --git a/internal/net/message_manager_test.go b/internal/net/message_manager_test.go index 035bce201..5c61ec2de 100644 --- a/internal/net/message_manager_test.go +++ b/internal/net/message_manager_test.go @@ -4,11 +4,11 @@ import ( "context" "testing" - "github.com/libp2p/go-libp2p-core/peer" - "github.com/libp2p/go-libp2p-core/protocol" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/protocol" - swarmt "github.com/libp2p/go-libp2p-swarm/testing" bhost "github.com/libp2p/go-libp2p/p2p/host/basic" + swarmt "github.com/libp2p/go-libp2p/p2p/net/swarm/testing" "github.com/stretchr/testify/require" ) @@ -21,6 +21,7 @@ func TestInvalidMessageSenderTracking(t *testing.T) { h, err := bhost.NewHost(swarmt.GenSwarm(t, swarmt.OptDisableReuseport), new(bhost.HostOpts)) require.NoError(t, err) + h.Start() defer h.Close() msgSender := NewMessageSenderImpl(h, []protocol.ID{"/test/kad/1.0.0"}).(*messageSenderImpl) diff --git a/internal/tracing.go b/internal/tracing.go new file mode 100644 index 000000000..6b707f9cf --- /dev/null +++ b/internal/tracing.go @@ -0,0 +1,32 @@ +package internal + +import ( + "context" + "fmt" + "unicode/utf8" + + "github.com/multiformats/go-multibase" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" +) + +func StartSpan(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) { + return otel.Tracer("go-libp2p-kad-dht").Start(ctx, fmt.Sprintf("KademliaDHT.%s", name), opts...) +} + +// KeyAsAttribute format a DHT key into a suitable tracing attribute. +// DHT keys can be either valid utf-8 or binary, when they are derived from, for example, a multihash. +// Tracing (and notably OpenTelemetry+grpc exporter) requires valid utf-8 for string attributes. +func KeyAsAttribute(name string, key string) attribute.KeyValue { + b := []byte(key) + if utf8.Valid(b) { + return attribute.String(name, key) + } + encoded, err := multibase.Encode(multibase.Base58BTC, b) + if err != nil { + // should be unreachable + panic(err) + } + return attribute.String(name, encoded) +} diff --git a/internal/util.go b/internal/util.go new file mode 100644 index 000000000..9162fa177 --- /dev/null +++ b/internal/util.go @@ -0,0 +1,35 @@ +package internal + +import ( + "time" + + mh "github.com/multiformats/go-multihash" +) + +// Hash is the global IPFS hash function. uses multihash SHA2_256, 256 bits +func Hash(data []byte) mh.Multihash { + h, err := mh.Sum(data, mh.SHA2_256, -1) + if err != nil { + // this error can be safely ignored (panic) because multihash only fails + // from the selection of hash function. If the fn + length are valid, it + // won't error. + panic("multihash failed to hash using SHA2_256.") + } + return h +} + +// ParseRFC3339 parses an RFC3339Nano-formatted time stamp and +// returns the UTC time. +func ParseRFC3339(s string) (time.Time, error) { + t, err := time.Parse(time.RFC3339Nano, s) + if err != nil { + return time.Time{}, err + } + return t.UTC(), nil +} + +// FormatRFC3339 returns the string representation of the +// UTC value of the given time in RFC3339Nano format. +func FormatRFC3339(t time.Time) string { + return t.UTC().Format(time.RFC3339Nano) +} diff --git a/lookup.go b/lookup.go index 88695dc4a..03801325c 100644 --- a/lookup.go +++ b/lookup.go @@ -5,10 +5,13 @@ import ( "fmt" "time" - "github.com/libp2p/go-libp2p-core/peer" - "github.com/libp2p/go-libp2p-core/routing" - + "github.com/libp2p/go-libp2p-kad-dht/internal" + "github.com/libp2p/go-libp2p-kad-dht/metrics" + "github.com/libp2p/go-libp2p-kad-dht/qpeerset" kb "github.com/libp2p/go-libp2p-kbucket" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/routing" + "go.opentelemetry.io/otel/trace" ) // GetClosestPeers is a Kademlia 'node lookup' operation. Returns a channel of @@ -17,44 +20,66 @@ import ( // If the context is canceled, this function will return the context error along // with the closest K peers it has found so far. func (dht *IpfsDHT) GetClosestPeers(ctx context.Context, key string) ([]peer.ID, error) { + ctx, span := internal.StartSpan(ctx, "IpfsDHT.GetClosestPeers", trace.WithAttributes(internal.KeyAsAttribute("Key", key))) + defer span.End() + if key == "" { return nil, fmt.Errorf("can't lookup empty key") } - //TODO: I can break the interface! return []peer.ID - lookupRes, err := dht.runLookupWithFollowup(ctx, key, - func(ctx context.Context, p peer.ID) ([]*peer.AddrInfo, error) { - // For DHT query command - routing.PublishQueryEvent(ctx, &routing.QueryEvent{ - Type: routing.SendingQuery, - ID: p, - }) - - peers, err := dht.protoMessenger.GetClosestPeers(ctx, p, peer.ID(key)) - if err != nil { - logger.Debugf("error getting closer peers: %s", err) - return nil, err - } - // For DHT query command - routing.PublishQueryEvent(ctx, &routing.QueryEvent{ - Type: routing.PeerResponse, - ID: p, - Responses: peers, - }) - - return peers, err - }, - func() bool { return false }, - ) + //TODO: I can break the interface! return []peer.ID + lookupRes, err := dht.runLookupWithFollowup(ctx, key, dht.pmGetClosestPeers(key), func(*qpeerset.QueryPeerset) bool { return false }) if err != nil { return nil, err } - if ctx.Err() == nil && lookupRes.completed { - // refresh the cpl for this key as the query was successful - dht.routingTable.ResetCplRefreshedAtForID(kb.ConvertKey(key), time.Now()) + if err := ctx.Err(); err != nil || !lookupRes.completed { + return lookupRes.peers, err + } + + // tracking lookup results for network size estimator + if err = dht.nsEstimator.Track(key, lookupRes.closest); err != nil { + logger.Warnf("network size estimator track peers: %s", err) } - return lookupRes.peers, ctx.Err() + if ns, err := dht.nsEstimator.NetworkSize(); err == nil { + metrics.NetworkSize.M(int64(ns)) + } + + // refresh the cpl for this key as the query was successful + dht.routingTable.ResetCplRefreshedAtForID(kb.ConvertKey(key), time.Now()) + + return lookupRes.peers, nil +} + +// pmGetClosestPeers is the protocol messenger version of the GetClosestPeer queryFn. +func (dht *IpfsDHT) pmGetClosestPeers(key string) queryFn { + return func(ctx context.Context, p peer.ID) ([]*peer.AddrInfo, error) { + // For DHT query command + routing.PublishQueryEvent(ctx, &routing.QueryEvent{ + Type: routing.SendingQuery, + ID: p, + }) + + peers, err := dht.protoMessenger.GetClosestPeers(ctx, p, peer.ID(key)) + if err != nil { + logger.Debugf("error getting closer peers: %s", err) + routing.PublishQueryEvent(ctx, &routing.QueryEvent{ + Type: routing.QueryError, + ID: p, + Extra: err.Error(), + }) + return nil, err + } + + // For DHT query command + routing.PublishQueryEvent(ctx, &routing.QueryEvent{ + Type: routing.PeerResponse, + ID: p, + Responses: peers, + }) + + return peers, err + } } diff --git a/lookup_optim.go b/lookup_optim.go new file mode 100644 index 000000000..428e86f24 --- /dev/null +++ b/lookup_optim.go @@ -0,0 +1,313 @@ +package dht + +import ( + "context" + "fmt" + "math" + "sync" + "sync/atomic" + "time" + + "github.com/libp2p/go-libp2p-kad-dht/metrics" + "github.com/libp2p/go-libp2p-kad-dht/netsize" + "github.com/libp2p/go-libp2p-kad-dht/qpeerset" + kb "github.com/libp2p/go-libp2p-kbucket" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/multiformats/go-multihash" + ks "github.com/whyrusleeping/go-keyspace" + "gonum.org/v1/gonum/mathext" +) + +const ( + // optProvIndividualThresholdCertainty describes how sure we want to be that an individual peer that + // we find during walking the DHT actually belongs to the k-closest peers based on the current network size + // estimation. + optProvIndividualThresholdCertainty = 0.9 + + // optProvSetThresholdStrictness describes the probability that the set of closest peers is actually further + // away then the calculated set threshold. Put differently, what is the probability that we are too strict and + // don't terminate the process early because we can't find any closer peers. + optProvSetThresholdStrictness = 0.1 + + // optProvReturnRatio corresponds to how many ADD_PROVIDER RPCs must have completed (regardless of success) + // before we return to the user. The ratio of 0.75 equals 15 RPC as it is based on the Kademlia bucket size. + optProvReturnRatio = 0.75 +) + +type addProviderRPCState int + +const ( + scheduled addProviderRPCState = iota + 1 + success + failure +) + +type optimisticState struct { + // context for all ADD_PROVIDER RPCs + putCtx context.Context + + // reference to the DHT + dht *IpfsDHT + + // the most recent network size estimation + networkSize int32 + + // a channel indicating when an ADD_PROVIDER RPC completed (successful or not) + doneChan chan struct{} + + // tracks which peers we have stored the provider records with + peerStatesLk sync.RWMutex + peerStates map[peer.ID]addProviderRPCState + + // the key to provide + key string + + // the key to provide transformed into the Kademlia key space + ksKey ks.Key + + // distance threshold for individual peers. If peers are closer than this number we store + // the provider records right away. + individualThreshold float64 + + // distance threshold for the set of bucketSize closest peers. If the average distance of the bucketSize + // closest peers is below this number we stop the DHT walk and store the remaining provider records. + // "remaining" because we have likely already stored some on peers that were below the individualThreshold. + setThreshold float64 + + // number of completed (regardless of success) ADD_PROVIDER RPCs before we return control back to the user. + returnThreshold int + + // putProvDone counts the ADD_PROVIDER RPCs that have completed (successful and unsuccessful) + putProvDone atomic.Int32 +} + +func (dht *IpfsDHT) newOptimisticState(ctx context.Context, key string) (*optimisticState, error) { + // get network size and err out if there is no reasonable estimate + networkSize, err := dht.nsEstimator.NetworkSize() + if err != nil { + return nil, err + } + + individualThreshold := mathext.GammaIncRegInv(float64(dht.bucketSize), 1-optProvIndividualThresholdCertainty) / float64(networkSize) + setThreshold := mathext.GammaIncRegInv(float64(dht.bucketSize)/2.0+1, 1-optProvSetThresholdStrictness) / float64(networkSize) + returnThreshold := int(math.Ceil(float64(dht.bucketSize) * optProvReturnRatio)) + + return &optimisticState{ + putCtx: ctx, + dht: dht, + key: key, + doneChan: make(chan struct{}, returnThreshold), // buffered channel to not miss events + ksKey: ks.XORKeySpace.Key([]byte(key)), + networkSize: networkSize, + peerStates: map[peer.ID]addProviderRPCState{}, + individualThreshold: individualThreshold, + setThreshold: setThreshold, + returnThreshold: returnThreshold, + putProvDone: atomic.Int32{}, + }, nil +} + +func (dht *IpfsDHT) optimisticProvide(outerCtx context.Context, keyMH multihash.Multihash) error { + key := string(keyMH) + + if key == "" { + return fmt.Errorf("can't lookup empty key") + } + + // initialize new context for all putProvider operations. + // We don't want to give the outer context to the put operations as we return early before all + // put operations have finished to avoid the long tail of the latency distribution. If we + // provided the outer context the put operations may be cancelled depending on what happens + // with the context on the user side. + putCtx, putCtxCancel := context.WithTimeout(context.Background(), time.Minute) + + es, err := dht.newOptimisticState(putCtx, key) + if err != nil { + putCtxCancel() + return err + } + + // initialize context that finishes when this function returns + innerCtx, innerCtxCancel := context.WithCancel(outerCtx) + defer innerCtxCancel() + + go func() { + select { + case <-outerCtx.Done(): + // If the outer context gets cancelled while we're still in this function. We stop all + // pending put operations. + putCtxCancel() + case <-innerCtx.Done(): + // We have returned from this function. Ignore cancellations of the outer context and continue + // with the remaining put operations. + } + }() + + lookupRes, err := dht.runLookupWithFollowup(outerCtx, key, dht.pmGetClosestPeers(key), es.stopFn) + if err != nil { + return err + } + + // Store the provider records with all the closest peers we haven't already contacted/scheduled interaction with. + es.peerStatesLk.Lock() + for _, p := range lookupRes.peers { + if _, found := es.peerStates[p]; found { + continue + } + + go es.putProviderRecord(p) + es.peerStates[p] = scheduled + } + es.peerStatesLk.Unlock() + + // wait until a threshold number of RPCs have completed + es.waitForRPCs() + + if err := outerCtx.Err(); err != nil || !lookupRes.completed { // likely the "completed" field is false but that's not a given + return err + } + + // tracking lookup results for network size estimator as "completed" is true + if err = dht.nsEstimator.Track(key, lookupRes.closest); err != nil { + logger.Warnf("network size estimator track peers: %s", err) + } + + if ns, err := dht.nsEstimator.NetworkSize(); err == nil { + metrics.NetworkSize.M(int64(ns)) + } + + // refresh the cpl for this key as the query was successful + dht.routingTable.ResetCplRefreshedAtForID(kb.ConvertKey(key), time.Now()) + + return nil +} + +func (os *optimisticState) stopFn(qps *qpeerset.QueryPeerset) bool { + os.peerStatesLk.Lock() + defer os.peerStatesLk.Unlock() + + // get currently known closest peers and check if any of them is already very close. + // If so -> store provider records straight away. + closest := qps.GetClosestNInStates(os.dht.bucketSize, qpeerset.PeerHeard, qpeerset.PeerWaiting, qpeerset.PeerQueried) + distances := make([]float64, os.dht.bucketSize) + for i, p := range closest { + // calculate distance of peer p to the target key + distances[i] = netsize.NormedDistance(p, os.ksKey) + + // Check if we have already scheduled interaction or have actually interacted with that peer + if _, found := os.peerStates[p]; found { + continue + } + + // Check if peer is close enough to store the provider record with + if distances[i] > os.individualThreshold { + continue + } + + // peer is indeed very close already -> store the provider record directly with it! + go os.putProviderRecord(p) + + // keep track that we've scheduled storing a provider record with that peer + os.peerStates[p] = scheduled + } + + // count number of peers we have scheduled to contact or have already successfully contacted via the above method + scheduledAndSuccessCount := 0 + for _, s := range os.peerStates { + if s == scheduled || s == success { + scheduledAndSuccessCount += 1 + } + } + + // if we have already contacted/scheduled the RPC for more than bucketSize peers stop the procedure + if scheduledAndSuccessCount >= os.dht.bucketSize { + return true + } + + // calculate average distance of the set of closest peers + sum := 0.0 + for _, d := range distances { + sum += d + } + avg := sum / float64(len(distances)) + + // if the average is below the set threshold stop the procedure + return avg < os.setThreshold +} + +func (os *optimisticState) putProviderRecord(pid peer.ID) { + err := os.dht.protoMessenger.PutProviderAddrs(os.putCtx, pid, []byte(os.key), peer.AddrInfo{ + ID: os.dht.self, + Addrs: os.dht.filterAddrs(os.dht.host.Addrs()), + }) + os.peerStatesLk.Lock() + if err != nil { + os.peerStates[pid] = failure + } else { + os.peerStates[pid] = success + } + os.peerStatesLk.Unlock() + + // indicate that this ADD_PROVIDER RPC has completed + os.doneChan <- struct{}{} +} + +// waitForRPCs waits for a subset of ADD_PROVIDER RPCs to complete and then acquire a lease on +// a bound channel to return early back to the user and prevent unbound asynchronicity. If +// there are already too many requests in-flight we are just waiting for our current set to +// finish. +func (os *optimisticState) waitForRPCs() { + os.peerStatesLk.RLock() + rpcCount := len(os.peerStates) + os.peerStatesLk.RUnlock() + + // returnThreshold can't be larger than the total number issued RPCs + if os.returnThreshold > rpcCount { + os.returnThreshold = rpcCount + } + + // Wait until returnThreshold ADD_PROVIDER RPCs have returned + for range os.doneChan { + if int(os.putProvDone.Add(1)) == os.returnThreshold { + break + } + } + // At this point only a subset of all ADD_PROVIDER RPCs have completed. + // We want to give control back to the user as soon as possible because + // it is highly likely that at least one of the remaining RPCs will time + // out and thus slow down the whole processes. The provider records will + // already be available with less than the total number of RPCs having + // finished. This has been investigated here: + // https://github.com/protocol/network-measurements/blob/master/results/rfm17-provider-record-liveness.md + + // For the remaining ADD_PROVIDER RPCs try to acquire a lease on the optProvJobsPool channel. + // If that worked we need to consume the doneChan and release the acquired lease on the + // optProvJobsPool channel. + remaining := rpcCount - int(os.putProvDone.Load()) + for i := 0; i < remaining; i++ { + select { + case os.dht.optProvJobsPool <- struct{}{}: + // We were able to acquire a lease on the optProvJobsPool channel. + // Consume doneChan to release the acquired lease again. + go os.consumeDoneChan(rpcCount) + case <-os.doneChan: + // We were not able to acquire a lease but an ADD_PROVIDER RPC resolved. + if int(os.putProvDone.Add(1)) == rpcCount { + close(os.doneChan) + } + } + } +} + +func (os *optimisticState) consumeDoneChan(until int) { + // Wait for an RPC to finish + <-os.doneChan + + // Release acquired lease for other's to get a spot + <-os.dht.optProvJobsPool + + // If all RPCs have finished, close the channel. + if int(os.putProvDone.Add(1)) == until { + close(os.doneChan) + } +} diff --git a/lookup_optim_test.go b/lookup_optim_test.go new file mode 100644 index 000000000..e77dd0cac --- /dev/null +++ b/lookup_optim_test.go @@ -0,0 +1,106 @@ +package dht + +import ( + "context" + "math/rand" + "testing" + "time" + + "github.com/libp2p/go-libp2p-kad-dht/netsize" + "github.com/libp2p/go-libp2p/core/peer" +) + +func randInt(rng *rand.Rand, n, except int) int { + for { + r := rng.Intn(n) + if r != except { + return r + } + } +} + +func TestOptimisticProvide(t *testing.T) { + rng := rand.New(rand.NewSource(time.Now().UnixNano())) + + // Order of events: + // 1. setup DHTs + // 2. connect each DHT with three others (but not to itself) + // 3. select random DHT to be the privileged one (performs the optimistic provide) + // 4. initialize network size estimator of privileged DHT + // 5. perform provides + // 6. let all other DHTs perform the lookup for all provided CIDs + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + dhtCount := 21 + + dhts := setupDHTS(t, ctx, dhtCount, EnableOptimisticProvide()) + defer func() { + for i := 0; i < dhtCount; i++ { + dhts[i].Close() + defer dhts[i].host.Close() + } + }() + + // connect each DHT with three random others + for i, dht := range dhts { + for j := 0; j < 3; j++ { + r := randInt(rng, dhtCount, i) + connect(t, ctx, dhts[r], dht) + } + } + + // select privileged DHT that will perform the provide operation + privIdx := rng.Intn(dhtCount) + privDHT := dhts[privIdx] + + peerIDs := make([]peer.ID, 20) + for i := 0; i < dhtCount; i++ { + if i == privIdx { + continue + } + + if i >= privIdx { + peerIDs[i-1] = dhts[i-1].self + } else { + peerIDs[i] = dhts[i].self + } + } + nse := netsize.NewEstimator(privDHT.self, privDHT.routingTable, privDHT.bucketSize) + + for i := 0; i < 20; i++ { + err := nse.Track(string(testCaseCids[i].Bytes()), peerIDs) + if err != nil { + t.Fatal(err) + } + } + privDHT.nsEstimator = nse + + for _, k := range testCaseCids { + logger.Debugf("announcing provider for %s", k) + if err := privDHT.optimisticProvide(ctx, k.Hash()); err != nil { + t.Fatal(err) + } + } + + for _, c := range testCaseCids { + n := randInt(rng, dhtCount, privIdx) + + ctxT, cancel := context.WithTimeout(ctx, time.Second) + defer cancel() + provchan := dhts[n].FindProvidersAsync(ctxT, c, 1) + + select { + case prov := <-provchan: + if prov.ID == "" { + t.Fatal("Got back nil provider") + } + if prov.ID != privDHT.self { + t.Fatal("Got back wrong provider") + } + case <-ctxT.Done(): + t.Fatal("Did not get a provider back.") + } + } +} diff --git a/metrics/metrics.go b/metrics/metrics.go index 23bcbf9d9..8ff631167 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -39,6 +39,7 @@ var ( SentRequests = stats.Int64("libp2p.io/dht/kad/sent_requests", "Total number of requests sent per RPC", stats.UnitDimensionless) SentRequestErrors = stats.Int64("libp2p.io/dht/kad/sent_request_errors", "Total number of errors for requests sent per RPC", stats.UnitDimensionless) SentBytes = stats.Int64("libp2p.io/dht/kad/sent_bytes", "Total sent bytes per RPC", stats.UnitBytes) + NetworkSize = stats.Int64("libp2p.io/dht/kad/network_size", "Network size estimation", stats.UnitDimensionless) ) // Views @@ -93,6 +94,11 @@ var ( TagKeys: []tag.Key{KeyMessageType, KeyPeerID, KeyInstanceID}, Aggregation: defaultBytesDistribution, } + NetworkSizeView = &view.View{ + Measure: NetworkSize, + TagKeys: []tag.Key{KeyPeerID, KeyInstanceID}, + Aggregation: view.Count(), + } ) // DefaultViews with all views in it. @@ -107,4 +113,5 @@ var DefaultViews = []*view.View{ SentRequestsView, SentRequestErrorsView, SentBytesView, + NetworkSizeView, } diff --git a/netsize/netsize.go b/netsize/netsize.go new file mode 100644 index 000000000..02a0e6789 --- /dev/null +++ b/netsize/netsize.go @@ -0,0 +1,284 @@ +package netsize + +import ( + "fmt" + "math" + "math/big" + "sort" + "strings" + "sync" + "sync/atomic" + "time" + + logging "github.com/ipfs/go-log/v2" + kbucket "github.com/libp2p/go-libp2p-kbucket" + "github.com/libp2p/go-libp2p/core/peer" + ks "github.com/whyrusleeping/go-keyspace" +) + +// invalidEstimate indicates that we currently have no valid estimate cached. +const invalidEstimate int32 = -1 + +var ( + ErrNotEnoughData = fmt.Errorf("not enough data") + ErrWrongNumOfPeers = fmt.Errorf("expected bucket size number of peers") +) + +var ( + logger = logging.Logger("dht/netsize") + MaxMeasurementAge = 2 * time.Hour + MinMeasurementsThreshold = 5 + MaxMeasurementsThreshold = 150 + keyspaceMaxInt, _ = new(big.Int).SetString(strings.Repeat("1", 256), 2) + keyspaceMaxFloat = new(big.Float).SetInt(keyspaceMaxInt) +) + +type Estimator struct { + localID kbucket.ID + rt *kbucket.RoutingTable + bucketSize int + + measurementsLk sync.RWMutex + measurements map[int][]measurement + + netSizeCache int32 +} + +func NewEstimator(localID peer.ID, rt *kbucket.RoutingTable, bucketSize int) *Estimator { + // initialize map to hold measurement observations + measurements := map[int][]measurement{} + for i := 0; i < bucketSize; i++ { + measurements[i] = []measurement{} + } + + return &Estimator{ + localID: kbucket.ConvertPeerID(localID), + rt: rt, + bucketSize: bucketSize, + measurements: measurements, + netSizeCache: invalidEstimate, + } +} + +// NormedDistance calculates the normed XOR distance of the given keys (from 0 to 1). +func NormedDistance(p peer.ID, k ks.Key) float64 { + pKey := ks.XORKeySpace.Key([]byte(p)) + ksDistance := new(big.Float).SetInt(pKey.Distance(k)) + normedDist, _ := new(big.Float).Quo(ksDistance, keyspaceMaxFloat).Float64() + return normedDist +} + +type measurement struct { + distance float64 + weight float64 + timestamp time.Time +} + +// Track tracks the list of peers for the given key to incorporate in the next network size estimate. +// key is expected **NOT** to be in the kademlia keyspace and peers is expected to be a sorted list of +// the closest peers to the given key (the closest first). +// This function expects peers to have the same length as the routing table bucket size. It also +// strips old and limits the number of data points (favouring new). +func (e *Estimator) Track(key string, peers []peer.ID) error { + e.measurementsLk.Lock() + defer e.measurementsLk.Unlock() + + // sanity check + if len(peers) != e.bucketSize { + return ErrWrongNumOfPeers + } + + logger.Debugw("Tracking peers for key", "key", key) + + now := time.Now() + + // invalidate cache + atomic.StoreInt32(&e.netSizeCache, invalidEstimate) + + // Calculate weight for the peer distances. + weight := e.calcWeight(key, peers) + + // Map given key to the Kademlia key space (hash it) + ksKey := ks.XORKeySpace.Key([]byte(key)) + + // the maximum age timestamp of the measurement data points + maxAgeTs := now.Add(-MaxMeasurementAge) + + for i, p := range peers { + // Construct measurement struct + m := measurement{ + distance: NormedDistance(p, ksKey), + weight: weight, + timestamp: now, + } + + measurements := append(e.measurements[i], m) + + // find the smallest index of a measurement that is still in the allowed time window + // all measurements with a lower index should be discarded as they are too old + n := len(measurements) + idx := sort.Search(n, func(j int) bool { + return measurements[j].timestamp.After(maxAgeTs) + }) + + // if measurements are outside the allowed time window remove them. + // idx == n - there is no measurement in the allowed time window -> reset slice + // idx == 0 - the normal case where we only have valid entries + // idx != 0 - there is a mix of valid and obsolete entries + if idx != 0 { + x := make([]measurement, n-idx) + copy(x, measurements[idx:]) + measurements = x + } + + // if the number of data points exceed the max threshold, strip oldest measurement data points. + if len(measurements) > MaxMeasurementsThreshold { + measurements = measurements[len(measurements)-MaxMeasurementsThreshold:] + } + + e.measurements[i] = measurements + } + + return nil +} + +// NetworkSize instructs the Estimator to calculate the current network size estimate. +func (e *Estimator) NetworkSize() (int32, error) { + + // return cached calculation lock-free (fast path) + if estimate := atomic.LoadInt32(&e.netSizeCache); estimate != invalidEstimate { + logger.Debugw("Cached network size estimation", "estimate", estimate) + return estimate, nil + } + + e.measurementsLk.Lock() + defer e.measurementsLk.Unlock() + + // Check a second time. This is needed because we maybe had to wait on another goroutine doing the computation. + // Then the computation was just finished by the other goroutine, and we don't need to redo it. + if estimate := e.netSizeCache; estimate != invalidEstimate { + logger.Debugw("Cached network size estimation", "estimate", estimate) + return estimate, nil + } + + // remove obsolete data points + e.garbageCollect() + + // initialize slices for linear fit + xs := make([]float64, e.bucketSize) + ys := make([]float64, e.bucketSize) + yerrs := make([]float64, e.bucketSize) + + for i := 0; i < e.bucketSize; i++ { + observationCount := len(e.measurements[i]) + + // If we don't have enough data to reasonably calculate the network size, return early + if observationCount < MinMeasurementsThreshold { + return 0, ErrNotEnoughData + } + + // Calculate Average Distance + sumDistances := 0.0 + sumWeights := 0.0 + for _, m := range e.measurements[i] { + sumDistances += m.weight * m.distance + sumWeights += m.weight + } + distanceAvg := sumDistances / sumWeights + + // Calculate standard deviation + sumWeightedDiffs := 0.0 + for _, m := range e.measurements[i] { + diff := m.distance - distanceAvg + sumWeightedDiffs += m.weight * diff * diff + } + variance := sumWeightedDiffs / (float64(observationCount-1) / float64(observationCount) * sumWeights) + distanceStd := math.Sqrt(variance) + + // Track calculations + xs[i] = float64(i + 1) + ys[i] = distanceAvg + yerrs[i] = distanceStd + } + + // Calculate linear regression (assumes the line goes through the origin) + var x2Sum, xySum float64 + for i, xi := range xs { + yi := ys[i] + xySum += yerrs[i] * xi * yi + x2Sum += yerrs[i] * xi * xi + } + slope := xySum / x2Sum + + // calculate final network size + netSize := int32(1/slope - 1) + + // cache network size estimation + atomic.StoreInt32(&e.netSizeCache, netSize) + + logger.Debugw("New network size estimation", "estimate", netSize) + return netSize, nil +} + +// calcWeight weighs data points exponentially less if they fall into a non-full bucket. +// It weighs distance estimates based on their CPLs and bucket levels. +// Bucket Level: 20 -> 1/2^0 -> weight: 1 +// Bucket Level: 17 -> 1/2^3 -> weight: 1/8 +// Bucket Level: 10 -> 1/2^10 -> weight: 1/1024 +// +// It can happen that the routing table doesn't have a full bucket, but we are tracking here +// a list of peers that would theoretically have been suitable for that bucket. Let's imagine +// there are only 13 peers in bucket 3 although there is space for 20. Now, the Track function +// gets a peers list (len 20) where all peers fall into bucket 3. The weight of this set of peers +// should be 1 instead of 1/2^7. +// I actually thought this cannot happen as peers would have been added to the routing table before +// the Track function gets called. But they seem sometimes not to be added. +func (e *Estimator) calcWeight(key string, peers []peer.ID) float64 { + + cpl := kbucket.CommonPrefixLen(kbucket.ConvertKey(key), e.localID) + bucketLevel := e.rt.NPeersForCpl(uint(cpl)) + + if bucketLevel < e.bucketSize { + // routing table doesn't have a full bucket. Check how many peers would fit into that bucket + peerLevel := 0 + for _, p := range peers { + if cpl == kbucket.CommonPrefixLen(kbucket.ConvertPeerID(p), e.localID) { + peerLevel += 1 + } + } + + if peerLevel > bucketLevel { + return math.Pow(2, float64(peerLevel-e.bucketSize)) + } + } + + return math.Pow(2, float64(bucketLevel-e.bucketSize)) +} + +// garbageCollect removes all measurements from the list that fell out of the measurement time window. +func (e *Estimator) garbageCollect() { + logger.Debug("Running garbage collection") + + // the maximum age timestamp of the measurement data points + maxAgeTs := time.Now().Add(-MaxMeasurementAge) + + for i := 0; i < e.bucketSize; i++ { + + // find the smallest index of a measurement that is still in the allowed time window + // all measurements with a lower index should be discarded as they are too old + n := len(e.measurements[i]) + idx := sort.Search(n, func(j int) bool { + return e.measurements[i][j].timestamp.After(maxAgeTs) + }) + + // if measurements are outside the allowed time window remove them. + // idx == n - there is no measurement in the allowed time window -> reset slice + // idx == 0 - the normal case where we only have valid entries + // idx != 0 - there is a mix of valid and obsolete entries + if idx == n { + e.measurements[i] = []measurement{} + } else if idx != 0 { + e.measurements[i] = e.measurements[i][idx:] + } + } +} diff --git a/netsize/netsize_test.go b/netsize/netsize_test.go new file mode 100644 index 000000000..c6d3307f2 --- /dev/null +++ b/netsize/netsize_test.go @@ -0,0 +1,44 @@ +package netsize + +import ( + "testing" + "time" + + kbucket "github.com/libp2p/go-libp2p-kbucket" + pt "github.com/libp2p/go-libp2p/core/test" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + ks "github.com/whyrusleeping/go-keyspace" +) + +func TestNewEstimator(t *testing.T) { + bucketSize := 20 + + pid, err := pt.RandPeerID() + require.NoError(t, err) + + rt, err := kbucket.NewRoutingTable(bucketSize, kbucket.ConvertPeerID(pid), time.Second, nil, time.Second, nil) + require.NoError(t, err) + + e := NewEstimator(pid, rt, bucketSize) + + assert.Equal(t, rt, e.rt) + assert.Equal(t, kbucket.ConvertPeerID(pid), e.localID) + assert.Len(t, e.measurements, bucketSize) + assert.Equal(t, invalidEstimate, e.netSizeCache) +} + +func TestNormedDistance(t *testing.T) { + pid, err := pt.RandPeerID() + require.NoError(t, err) + + dist := NormedDistance(pid, ks.XORKeySpace.Key([]byte(pid))) + assert.Zero(t, dist) + + pid2, err := pt.RandPeerID() + require.NoError(t, err) + + dist = NormedDistance(pid, ks.XORKeySpace.Key([]byte(pid2))) + assert.Greater(t, 1.0, dist) + assert.Less(t, dist, 1.0) +} diff --git a/nofile_test.go b/nofile_test.go index 9c87f779a..10dca551d 100644 --- a/nofile_test.go +++ b/nofile_test.go @@ -1,5 +1,4 @@ //go:build !windows && !wasm -// +build !windows,!wasm package dht diff --git a/optimizations.md b/optimizations.md new file mode 100644 index 000000000..214c1796f --- /dev/null +++ b/optimizations.md @@ -0,0 +1,7 @@ +# Client-side optimizations + +This document reflects client-side optimizations that are implemented in this repository. Client-side optimizations are not part of the [Kademlia spec](https://github.com/libp2p/specs/tree/master/kad-dht), and are not required to be implemented on all clients. + +## Checking before Adding + +A Kademlia server should try to add remote peers querying it to its routing table. However, the Kademlia server has no guarantee that remote peers issuing requests are able to answer Kademlia requests correctly, even though they advertise speaking the Kademlia server protocol. It is important that only server nodes able to answer Kademlia requests end up in other peers' routing tables. Hence, before adding a remote peer to the Kademlia server's routing table, the Kademlia server will send a trivial `FIND_NODE` request to the remote peer, and add it to its routing table only if it is able to provide a valid response. \ No newline at end of file diff --git a/pb/Makefile b/pb/Makefile deleted file mode 100644 index eb14b5768..000000000 --- a/pb/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -PB = $(wildcard *.proto) -GO = $(PB:.proto=.pb.go) - -all: $(GO) - -%.pb.go: %.proto - protoc --proto_path=$(GOPATH)/src:. --gogofast_out=. $< - -clean: - rm -f *.pb.go - rm -f *.go diff --git a/pb/bytestring.go b/pb/bytestring.go deleted file mode 100644 index f20f1979e..000000000 --- a/pb/bytestring.go +++ /dev/null @@ -1,42 +0,0 @@ -package dht_pb - -import ( - "encoding/json" -) - -type byteString string - -func (b byteString) Marshal() ([]byte, error) { - return []byte(b), nil -} - -func (b *byteString) MarshalTo(data []byte) (int, error) { - return copy(data, *b), nil -} - -func (b *byteString) Unmarshal(data []byte) error { - *b = byteString(data) - return nil -} - -func (b *byteString) Size() int { - return len(*b) -} - -func (b byteString) MarshalJSON() ([]byte, error) { - return json.Marshal([]byte(b)) -} - -func (b *byteString) UnmarshalJSON(data []byte) error { - var buf []byte - err := json.Unmarshal(data, &buf) - if err != nil { - return err - } - *b = byteString(buf) - return nil -} - -func (b byteString) Equal(other byteString) bool { - return b == other -} diff --git a/pb/dht.pb.go b/pb/dht.pb.go index 30cb0dcf0..55766ccd6 100644 --- a/pb/dht.pb.go +++ b/pb/dht.pb.go @@ -1,28 +1,25 @@ -// Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: dht.proto +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.3 +// protoc v5.29.2 +// source: github.com/libp2p/go-libp2p-kad-dht/pb/dht.proto package dht_pb import ( - fmt "fmt" - _ "github.com/gogo/protobuf/gogoproto" - proto "github.com/gogo/protobuf/proto" pb "github.com/libp2p/go-libp2p-record/pb" - io "io" - math "math" - math_bits "math/bits" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" ) -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) type Message_MessageType int32 @@ -35,69 +32,114 @@ const ( Message_PING Message_MessageType = 5 ) -var Message_MessageType_name = map[int32]string{ - 0: "PUT_VALUE", - 1: "GET_VALUE", - 2: "ADD_PROVIDER", - 3: "GET_PROVIDERS", - 4: "FIND_NODE", - 5: "PING", -} +// Enum value maps for Message_MessageType. +var ( + Message_MessageType_name = map[int32]string{ + 0: "PUT_VALUE", + 1: "GET_VALUE", + 2: "ADD_PROVIDER", + 3: "GET_PROVIDERS", + 4: "FIND_NODE", + 5: "PING", + } + Message_MessageType_value = map[string]int32{ + "PUT_VALUE": 0, + "GET_VALUE": 1, + "ADD_PROVIDER": 2, + "GET_PROVIDERS": 3, + "FIND_NODE": 4, + "PING": 5, + } +) -var Message_MessageType_value = map[string]int32{ - "PUT_VALUE": 0, - "GET_VALUE": 1, - "ADD_PROVIDER": 2, - "GET_PROVIDERS": 3, - "FIND_NODE": 4, - "PING": 5, +func (x Message_MessageType) Enum() *Message_MessageType { + p := new(Message_MessageType) + *p = x + return p } func (x Message_MessageType) String() string { - return proto.EnumName(Message_MessageType_name, int32(x)) + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } +func (Message_MessageType) Descriptor() protoreflect.EnumDescriptor { + return file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_enumTypes[0].Descriptor() +} + +func (Message_MessageType) Type() protoreflect.EnumType { + return &file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_enumTypes[0] +} + +func (x Message_MessageType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Message_MessageType.Descriptor instead. func (Message_MessageType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_616a434b24c97ff4, []int{0, 0} + return file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_rawDescGZIP(), []int{0, 0} } type Message_ConnectionType int32 const ( - // sender does not have a connection to peer, and no extra information (default) + // sender does not have a connection to peer, and no extra information + // (default) Message_NOT_CONNECTED Message_ConnectionType = 0 // sender has a live connection to peer Message_CONNECTED Message_ConnectionType = 1 // sender recently connected to peer Message_CAN_CONNECT Message_ConnectionType = 2 // sender recently tried to connect to peer repeatedly but failed to connect - // ("try" here is loose, but this should signal "made strong effort, failed") + // ("try" here is loose, but this should signal "made strong effort, + // failed") Message_CANNOT_CONNECT Message_ConnectionType = 3 ) -var Message_ConnectionType_name = map[int32]string{ - 0: "NOT_CONNECTED", - 1: "CONNECTED", - 2: "CAN_CONNECT", - 3: "CANNOT_CONNECT", -} +// Enum value maps for Message_ConnectionType. +var ( + Message_ConnectionType_name = map[int32]string{ + 0: "NOT_CONNECTED", + 1: "CONNECTED", + 2: "CAN_CONNECT", + 3: "CANNOT_CONNECT", + } + Message_ConnectionType_value = map[string]int32{ + "NOT_CONNECTED": 0, + "CONNECTED": 1, + "CAN_CONNECT": 2, + "CANNOT_CONNECT": 3, + } +) -var Message_ConnectionType_value = map[string]int32{ - "NOT_CONNECTED": 0, - "CONNECTED": 1, - "CAN_CONNECT": 2, - "CANNOT_CONNECT": 3, +func (x Message_ConnectionType) Enum() *Message_ConnectionType { + p := new(Message_ConnectionType) + *p = x + return p } func (x Message_ConnectionType) String() string { - return proto.EnumName(Message_ConnectionType_name, int32(x)) + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Message_ConnectionType) Descriptor() protoreflect.EnumDescriptor { + return file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_enumTypes[1].Descriptor() +} + +func (Message_ConnectionType) Type() protoreflect.EnumType { + return &file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_enumTypes[1] } +func (x Message_ConnectionType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Message_ConnectionType.Descriptor instead. func (Message_ConnectionType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_616a434b24c97ff4, []int{0, 1} + return file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_rawDescGZIP(), []int{0, 1} } type Message struct { + state protoimpl.MessageState `protogen:"open.v1"` // defines what type of message it is. Type Message_MessageType `protobuf:"varint,1,opt,name=type,proto3,enum=dht.pb.Message_MessageType" json:"type,omitempty"` // defines what coral cluster level this query/response belongs to. @@ -111,848 +153,258 @@ type Message struct { Record *pb.Record `protobuf:"bytes,3,opt,name=record,proto3" json:"record,omitempty"` // Used to return peers closer to a key in a query // GET_VALUE, GET_PROVIDERS, FIND_NODE - CloserPeers []Message_Peer `protobuf:"bytes,8,rep,name=closerPeers,proto3" json:"closerPeers"` + CloserPeers []*Message_Peer `protobuf:"bytes,8,rep,name=closerPeers,proto3" json:"closerPeers,omitempty"` // Used to return Providers // GET_VALUE, ADD_PROVIDER, GET_PROVIDERS - ProviderPeers []Message_Peer `protobuf:"bytes,9,rep,name=providerPeers,proto3" json:"providerPeers"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + ProviderPeers []*Message_Peer `protobuf:"bytes,9,rep,name=providerPeers,proto3" json:"providerPeers,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } -func (m *Message) Reset() { *m = Message{} } -func (m *Message) String() string { return proto.CompactTextString(m) } -func (*Message) ProtoMessage() {} -func (*Message) Descriptor() ([]byte, []int) { - return fileDescriptor_616a434b24c97ff4, []int{0} -} -func (m *Message) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_Message.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *Message) XXX_Merge(src proto.Message) { - xxx_messageInfo_Message.Merge(m, src) +func (x *Message) Reset() { + *x = Message{} + mi := &file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } -func (m *Message) XXX_Size() int { - return m.Size() + +func (x *Message) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Message) XXX_DiscardUnknown() { - xxx_messageInfo_Message.DiscardUnknown(m) + +func (*Message) ProtoMessage() {} + +func (x *Message) ProtoReflect() protoreflect.Message { + mi := &file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_Message proto.InternalMessageInfo +// Deprecated: Use Message.ProtoReflect.Descriptor instead. +func (*Message) Descriptor() ([]byte, []int) { + return file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_rawDescGZIP(), []int{0} +} -func (m *Message) GetType() Message_MessageType { - if m != nil { - return m.Type +func (x *Message) GetType() Message_MessageType { + if x != nil { + return x.Type } return Message_PUT_VALUE } -func (m *Message) GetClusterLevelRaw() int32 { - if m != nil { - return m.ClusterLevelRaw +func (x *Message) GetClusterLevelRaw() int32 { + if x != nil { + return x.ClusterLevelRaw } return 0 } -func (m *Message) GetKey() []byte { - if m != nil { - return m.Key +func (x *Message) GetKey() []byte { + if x != nil { + return x.Key } return nil } -func (m *Message) GetRecord() *pb.Record { - if m != nil { - return m.Record +func (x *Message) GetRecord() *pb.Record { + if x != nil { + return x.Record } return nil } -func (m *Message) GetCloserPeers() []Message_Peer { - if m != nil { - return m.CloserPeers +func (x *Message) GetCloserPeers() []*Message_Peer { + if x != nil { + return x.CloserPeers } return nil } -func (m *Message) GetProviderPeers() []Message_Peer { - if m != nil { - return m.ProviderPeers +func (x *Message) GetProviderPeers() []*Message_Peer { + if x != nil { + return x.ProviderPeers } return nil } type Message_Peer struct { + state protoimpl.MessageState `protogen:"open.v1"` // ID of a given peer. - Id byteString `protobuf:"bytes,1,opt,name=id,proto3,customtype=byteString" json:"id"` + Id []byte `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // multiaddrs for a given peer Addrs [][]byte `protobuf:"bytes,2,rep,name=addrs,proto3" json:"addrs,omitempty"` // used to signal the sender's connection capabilities to the peer - Connection Message_ConnectionType `protobuf:"varint,3,opt,name=connection,proto3,enum=dht.pb.Message_ConnectionType" json:"connection,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Connection Message_ConnectionType `protobuf:"varint,3,opt,name=connection,proto3,enum=dht.pb.Message_ConnectionType" json:"connection,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } -func (m *Message_Peer) Reset() { *m = Message_Peer{} } -func (m *Message_Peer) String() string { return proto.CompactTextString(m) } -func (*Message_Peer) ProtoMessage() {} -func (*Message_Peer) Descriptor() ([]byte, []int) { - return fileDescriptor_616a434b24c97ff4, []int{0, 0} -} -func (m *Message_Peer) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *Message_Peer) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_Message_Peer.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *Message_Peer) XXX_Merge(src proto.Message) { - xxx_messageInfo_Message_Peer.Merge(m, src) +func (x *Message_Peer) Reset() { + *x = Message_Peer{} + mi := &file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } -func (m *Message_Peer) XXX_Size() int { - return m.Size() -} -func (m *Message_Peer) XXX_DiscardUnknown() { - xxx_messageInfo_Message_Peer.DiscardUnknown(m) -} - -var xxx_messageInfo_Message_Peer proto.InternalMessageInfo -func (m *Message_Peer) GetAddrs() [][]byte { - if m != nil { - return m.Addrs - } - return nil +func (x *Message_Peer) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Message_Peer) GetConnection() Message_ConnectionType { - if m != nil { - return m.Connection - } - return Message_NOT_CONNECTED -} +func (*Message_Peer) ProtoMessage() {} -func init() { - proto.RegisterEnum("dht.pb.Message_MessageType", Message_MessageType_name, Message_MessageType_value) - proto.RegisterEnum("dht.pb.Message_ConnectionType", Message_ConnectionType_name, Message_ConnectionType_value) - proto.RegisterType((*Message)(nil), "dht.pb.Message") - proto.RegisterType((*Message_Peer)(nil), "dht.pb.Message.Peer") -} - -func init() { proto.RegisterFile("dht.proto", fileDescriptor_616a434b24c97ff4) } - -var fileDescriptor_616a434b24c97ff4 = []byte{ - // 469 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x92, 0xb1, 0x6f, 0x9b, 0x40, - 0x18, 0xc5, 0x73, 0x80, 0xdd, 0xf8, 0x03, 0x3b, 0xe4, 0x94, 0x01, 0xb9, 0x92, 0x83, 0x3c, 0xd1, - 0xc1, 0x20, 0xd1, 0xb5, 0xaa, 0x6a, 0x03, 0x8d, 0x2c, 0xa5, 0xd8, 0xba, 0x38, 0xe9, 0x68, 0x19, - 0xb8, 0x12, 0x54, 0xd7, 0x87, 0x00, 0xa7, 0xf2, 0xd6, 0x3f, 0x2f, 0x63, 0xe7, 0x0e, 0x51, 0xe5, - 0xa9, 0x7f, 0x46, 0xc5, 0x11, 0x5a, 0xec, 0x25, 0x13, 0xef, 0x7d, 0xf7, 0x7e, 0xe2, 0xdd, 0xa7, - 0x83, 0x4e, 0x74, 0x5f, 0x98, 0x69, 0xc6, 0x0a, 0x86, 0xdb, 0x5c, 0x06, 0x7d, 0x3b, 0x4e, 0x8a, - 0xfb, 0x6d, 0x60, 0x86, 0xec, 0x9b, 0xb5, 0x4e, 0x82, 0xd4, 0x4e, 0xad, 0x98, 0x8d, 0x2a, 0x35, - 0xca, 0x68, 0xc8, 0xb2, 0xc8, 0x4a, 0x03, 0xab, 0x52, 0x15, 0xdb, 0x1f, 0x35, 0x98, 0x98, 0xc5, - 0xcc, 0xe2, 0xe3, 0x60, 0xfb, 0x85, 0x3b, 0x6e, 0xb8, 0xaa, 0xe2, 0xc3, 0x3f, 0x12, 0xbc, 0xfa, - 0x44, 0xf3, 0x7c, 0x15, 0x53, 0x6c, 0x81, 0x54, 0xec, 0x52, 0xaa, 0x21, 0x1d, 0x19, 0x3d, 0xfb, - 0xb5, 0x59, 0xb5, 0x30, 0x9f, 0x8f, 0xeb, 0xef, 0x62, 0x97, 0x52, 0xc2, 0x83, 0xd8, 0x80, 0xb3, - 0x70, 0xbd, 0xcd, 0x0b, 0x9a, 0x5d, 0xd3, 0x07, 0xba, 0x26, 0xab, 0xef, 0x1a, 0xe8, 0xc8, 0x68, - 0x91, 0xe3, 0x31, 0x56, 0x41, 0xfc, 0x4a, 0x77, 0x9a, 0xa0, 0x23, 0x43, 0x21, 0xa5, 0xc4, 0x6f, - 0xa0, 0x5d, 0xf5, 0xd6, 0x44, 0x1d, 0x19, 0xb2, 0x7d, 0x6e, 0xd6, 0xd7, 0x08, 0x4c, 0xc2, 0x15, - 0x79, 0x0e, 0xe0, 0x77, 0x20, 0x87, 0x6b, 0x96, 0xd3, 0x6c, 0x4e, 0x69, 0x96, 0x6b, 0xa7, 0xba, - 0x68, 0xc8, 0xf6, 0xc5, 0x71, 0xbd, 0xf2, 0x70, 0x22, 0x3d, 0x3e, 0x5d, 0x9e, 0x90, 0x66, 0x1c, - 0x7f, 0x80, 0x6e, 0x9a, 0xb1, 0x87, 0x24, 0xaa, 0xf9, 0xce, 0x8b, 0xfc, 0x21, 0xd0, 0xff, 0x81, - 0x40, 0x2a, 0x15, 0x1e, 0x82, 0x90, 0x44, 0x7c, 0x3d, 0xca, 0x04, 0x97, 0xc9, 0x5f, 0x4f, 0x97, - 0x10, 0xec, 0x0a, 0x7a, 0x53, 0x64, 0xc9, 0x26, 0x26, 0x42, 0x12, 0xe1, 0x0b, 0x68, 0xad, 0xa2, - 0x28, 0xcb, 0x35, 0x41, 0x17, 0x0d, 0x85, 0x54, 0x06, 0xbf, 0x07, 0x08, 0xd9, 0x66, 0x43, 0xc3, - 0x22, 0x61, 0x1b, 0x7e, 0xe3, 0x9e, 0x3d, 0x38, 0x6e, 0xe0, 0xfc, 0x4b, 0xf0, 0x1d, 0x37, 0x88, - 0x61, 0x02, 0x72, 0x63, 0xfd, 0xb8, 0x0b, 0x9d, 0xf9, 0xed, 0x62, 0x79, 0x37, 0xbe, 0xbe, 0xf5, - 0xd4, 0x93, 0xd2, 0x5e, 0x79, 0xb5, 0x45, 0x58, 0x05, 0x65, 0xec, 0xba, 0xcb, 0x39, 0x99, 0xdd, - 0x4d, 0x5d, 0x8f, 0xa8, 0x02, 0x3e, 0x87, 0x6e, 0x19, 0xa8, 0x27, 0x37, 0xaa, 0x58, 0x32, 0x1f, - 0xa7, 0xbe, 0xbb, 0xf4, 0x67, 0xae, 0xa7, 0x4a, 0xf8, 0x14, 0xa4, 0xf9, 0xd4, 0xbf, 0x52, 0x5b, - 0xc3, 0xcf, 0xd0, 0x3b, 0x2c, 0x52, 0xd2, 0xfe, 0x6c, 0xb1, 0x74, 0x66, 0xbe, 0xef, 0x39, 0x0b, - 0xcf, 0xad, 0xfe, 0xf8, 0xdf, 0x22, 0x7c, 0x06, 0xb2, 0x33, 0xf6, 0xeb, 0x84, 0x2a, 0x60, 0x0c, - 0x3d, 0x67, 0xec, 0x37, 0x28, 0x55, 0x9c, 0x28, 0x8f, 0xfb, 0x01, 0xfa, 0xb9, 0x1f, 0xa0, 0xdf, - 0xfb, 0x01, 0x0a, 0xda, 0xfc, 0xfd, 0xbd, 0xfd, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x8d, 0x1a, 0xa1, - 0xbe, 0xf7, 0x02, 0x00, 0x00, -} - -func (m *Message) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *Message) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *Message) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i -= len(m.XXX_unrecognized) - copy(dAtA[i:], m.XXX_unrecognized) - } - if m.ClusterLevelRaw != 0 { - i = encodeVarintDht(dAtA, i, uint64(m.ClusterLevelRaw)) - i-- - dAtA[i] = 0x50 - } - if len(m.ProviderPeers) > 0 { - for iNdEx := len(m.ProviderPeers) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.ProviderPeers[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintDht(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x4a +func (x *Message_Peer) ProtoReflect() protoreflect.Message { + mi := &file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) } + return ms } - if len(m.CloserPeers) > 0 { - for iNdEx := len(m.CloserPeers) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.CloserPeers[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintDht(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x42 - } - } - if m.Record != nil { - { - size, err := m.Record.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintDht(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x1a - } - if len(m.Key) > 0 { - i -= len(m.Key) - copy(dAtA[i:], m.Key) - i = encodeVarintDht(dAtA, i, uint64(len(m.Key))) - i-- - dAtA[i] = 0x12 - } - if m.Type != 0 { - i = encodeVarintDht(dAtA, i, uint64(m.Type)) - i-- - dAtA[i] = 0x8 - } - return len(dAtA) - i, nil -} - -func (m *Message_Peer) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil + return mi.MessageOf(x) } -func (m *Message_Peer) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) +// Deprecated: Use Message_Peer.ProtoReflect.Descriptor instead. +func (*Message_Peer) Descriptor() ([]byte, []int) { + return file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_rawDescGZIP(), []int{0, 0} } -func (m *Message_Peer) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i -= len(m.XXX_unrecognized) - copy(dAtA[i:], m.XXX_unrecognized) - } - if m.Connection != 0 { - i = encodeVarintDht(dAtA, i, uint64(m.Connection)) - i-- - dAtA[i] = 0x18 - } - if len(m.Addrs) > 0 { - for iNdEx := len(m.Addrs) - 1; iNdEx >= 0; iNdEx-- { - i -= len(m.Addrs[iNdEx]) - copy(dAtA[i:], m.Addrs[iNdEx]) - i = encodeVarintDht(dAtA, i, uint64(len(m.Addrs[iNdEx]))) - i-- - dAtA[i] = 0x12 - } - } - { - size := m.Id.Size() - i -= size - if _, err := m.Id.MarshalTo(dAtA[i:]); err != nil { - return 0, err - } - i = encodeVarintDht(dAtA, i, uint64(size)) +func (x *Message_Peer) GetId() []byte { + if x != nil { + return x.Id } - i-- - dAtA[i] = 0xa - return len(dAtA) - i, nil -} - -func encodeVarintDht(dAtA []byte, offset int, v uint64) int { - offset -= sovDht(v) - base := offset - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ - } - dAtA[offset] = uint8(v) - return base -} -func (m *Message) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Type != 0 { - n += 1 + sovDht(uint64(m.Type)) - } - l = len(m.Key) - if l > 0 { - n += 1 + l + sovDht(uint64(l)) - } - if m.Record != nil { - l = m.Record.Size() - n += 1 + l + sovDht(uint64(l)) - } - if len(m.CloserPeers) > 0 { - for _, e := range m.CloserPeers { - l = e.Size() - n += 1 + l + sovDht(uint64(l)) - } - } - if len(m.ProviderPeers) > 0 { - for _, e := range m.ProviderPeers { - l = e.Size() - n += 1 + l + sovDht(uint64(l)) - } - } - if m.ClusterLevelRaw != 0 { - n += 1 + sovDht(uint64(m.ClusterLevelRaw)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n + return nil } -func (m *Message_Peer) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = m.Id.Size() - n += 1 + l + sovDht(uint64(l)) - if len(m.Addrs) > 0 { - for _, b := range m.Addrs { - l = len(b) - n += 1 + l + sovDht(uint64(l)) - } - } - if m.Connection != 0 { - n += 1 + sovDht(uint64(m.Connection)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n -} - -func sovDht(x uint64) (n int) { - return (math_bits.Len64(x|1) + 6) / 7 -} -func sozDht(x uint64) (n int) { - return sovDht(uint64((x << 1) ^ uint64((int64(x) >> 63)))) -} -func (m *Message) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowDht - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: Message: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Message: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) - } - m.Type = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowDht - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Type |= Message_MessageType(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowDht - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthDht - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthDht - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...) - if m.Key == nil { - m.Key = []byte{} - } - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Record", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowDht - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthDht - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthDht - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Record == nil { - m.Record = &pb.Record{} - } - if err := m.Record.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 8: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field CloserPeers", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowDht - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthDht - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthDht - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.CloserPeers = append(m.CloserPeers, Message_Peer{}) - if err := m.CloserPeers[len(m.CloserPeers)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 9: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ProviderPeers", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowDht - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthDht - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthDht - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ProviderPeers = append(m.ProviderPeers, Message_Peer{}) - if err := m.ProviderPeers[len(m.ProviderPeers)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 10: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field ClusterLevelRaw", wireType) - } - m.ClusterLevelRaw = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowDht - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.ClusterLevelRaw |= int32(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipDht(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthDht - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF +func (x *Message_Peer) GetAddrs() [][]byte { + if x != nil { + return x.Addrs } return nil } -func (m *Message_Peer) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowDht - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: Peer: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Peer: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowDht - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthDht - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthDht - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if err := m.Id.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Addrs", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowDht - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthDht - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthDht - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Addrs = append(m.Addrs, make([]byte, postIndex-iNdEx)) - copy(m.Addrs[len(m.Addrs)-1], dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Connection", wireType) - } - m.Connection = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowDht - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Connection |= Message_ConnectionType(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipDht(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthDht - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - if iNdEx > l { - return io.ErrUnexpectedEOF +func (x *Message_Peer) GetConnection() Message_ConnectionType { + if x != nil { + return x.Connection } - return nil + return Message_NOT_CONNECTED } -func skipDht(dAtA []byte) (n int, err error) { - l := len(dAtA) - iNdEx := 0 - depth := 0 - for iNdEx < l { - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowDht - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - wireType := int(wire & 0x7) - switch wireType { - case 0: - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowDht - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - iNdEx++ - if dAtA[iNdEx-1] < 0x80 { - break - } - } - case 1: - iNdEx += 8 - case 2: - var length int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowDht - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - length |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if length < 0 { - return 0, ErrInvalidLengthDht - } - iNdEx += length - case 3: - depth++ - case 4: - if depth == 0 { - return 0, ErrUnexpectedEndOfGroupDht - } - depth-- - case 5: - iNdEx += 4 - default: - return 0, fmt.Errorf("proto: illegal wireType %d", wireType) - } - if iNdEx < 0 { - return 0, ErrInvalidLengthDht - } - if depth == 0 { - return iNdEx, nil - } - } - return 0, io.ErrUnexpectedEOF + +var File_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto protoreflect.FileDescriptor + +var file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_rawDesc = []byte{ + 0x0a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x62, + 0x70, 0x32, 0x70, 0x2f, 0x67, 0x6f, 0x2d, 0x6c, 0x69, 0x62, 0x70, 0x32, 0x70, 0x2d, 0x6b, 0x61, + 0x64, 0x2d, 0x64, 0x68, 0x74, 0x2f, 0x70, 0x62, 0x2f, 0x64, 0x68, 0x74, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x06, 0x64, 0x68, 0x74, 0x2e, 0x70, 0x62, 0x1a, 0x32, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x62, 0x70, 0x32, 0x70, 0x2f, 0x67, 0x6f, + 0x2d, 0x6c, 0x69, 0x62, 0x70, 0x32, 0x70, 0x2d, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x2f, 0x70, + 0x62, 0x2f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc7, + 0x04, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2f, 0x0a, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x64, 0x68, 0x74, 0x2e, 0x70, + 0x62, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x63, + 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x61, 0x77, 0x18, 0x0a, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4c, 0x65, 0x76, + 0x65, 0x6c, 0x52, 0x61, 0x77, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x06, 0x72, 0x65, 0x63, 0x6f, 0x72, + 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, + 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x06, 0x72, 0x65, 0x63, 0x6f, + 0x72, 0x64, 0x12, 0x36, 0x0a, 0x0b, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x72, 0x50, 0x65, 0x65, 0x72, + 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x64, 0x68, 0x74, 0x2e, 0x70, 0x62, + 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x52, 0x0b, 0x63, + 0x6c, 0x6f, 0x73, 0x65, 0x72, 0x50, 0x65, 0x65, 0x72, 0x73, 0x12, 0x3a, 0x0a, 0x0d, 0x70, 0x72, + 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x50, 0x65, 0x65, 0x72, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x14, 0x2e, 0x64, 0x68, 0x74, 0x2e, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x50, 0x65, 0x65, 0x72, 0x73, 0x1a, 0x6c, 0x0a, 0x04, 0x50, 0x65, 0x65, 0x72, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, + 0x0a, 0x05, 0x61, 0x64, 0x64, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x05, 0x61, + 0x64, 0x64, 0x72, 0x73, 0x12, 0x3e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x64, 0x68, 0x74, 0x2e, 0x70, + 0x62, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x69, 0x0a, 0x0b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, + 0x79, 0x70, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x50, 0x55, 0x54, 0x5f, 0x56, 0x41, 0x4c, 0x55, 0x45, + 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x47, 0x45, 0x54, 0x5f, 0x56, 0x41, 0x4c, 0x55, 0x45, 0x10, + 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x41, 0x44, 0x44, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, + 0x52, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x47, 0x45, 0x54, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, + 0x44, 0x45, 0x52, 0x53, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x49, 0x4e, 0x44, 0x5f, 0x4e, + 0x4f, 0x44, 0x45, 0x10, 0x04, 0x12, 0x08, 0x0a, 0x04, 0x50, 0x49, 0x4e, 0x47, 0x10, 0x05, 0x22, + 0x57, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x11, 0x0a, 0x0d, 0x4e, 0x4f, 0x54, 0x5f, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, + 0x45, 0x44, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x45, + 0x44, 0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x43, 0x41, 0x4e, 0x5f, 0x43, 0x4f, 0x4e, 0x4e, 0x45, + 0x43, 0x54, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x43, 0x41, 0x4e, 0x4e, 0x4f, 0x54, 0x5f, 0x43, + 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x10, 0x03, 0x42, 0x2f, 0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x62, 0x70, 0x32, 0x70, 0x2f, 0x67, 0x6f, + 0x2d, 0x6c, 0x69, 0x62, 0x70, 0x32, 0x70, 0x2d, 0x6b, 0x61, 0x64, 0x2d, 0x64, 0x68, 0x74, 0x2f, + 0x70, 0x62, 0x3b, 0x64, 0x68, 0x74, 0x5f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( - ErrInvalidLengthDht = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowDht = fmt.Errorf("proto: integer overflow") - ErrUnexpectedEndOfGroupDht = fmt.Errorf("proto: unexpected end of group") + file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_rawDescOnce sync.Once + file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_rawDescData = file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_rawDesc ) + +func file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_rawDescGZIP() []byte { + file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_rawDescOnce.Do(func() { + file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_rawDescData = protoimpl.X.CompressGZIP(file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_rawDescData) + }) + return file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_rawDescData +} + +var file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_goTypes = []any{ + (Message_MessageType)(0), // 0: dht.pb.Message.MessageType + (Message_ConnectionType)(0), // 1: dht.pb.Message.ConnectionType + (*Message)(nil), // 2: dht.pb.Message + (*Message_Peer)(nil), // 3: dht.pb.Message.Peer + (*pb.Record)(nil), // 4: record.pb.Record +} +var file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_depIdxs = []int32{ + 0, // 0: dht.pb.Message.type:type_name -> dht.pb.Message.MessageType + 4, // 1: dht.pb.Message.record:type_name -> record.pb.Record + 3, // 2: dht.pb.Message.closerPeers:type_name -> dht.pb.Message.Peer + 3, // 3: dht.pb.Message.providerPeers:type_name -> dht.pb.Message.Peer + 1, // 4: dht.pb.Message.Peer.connection:type_name -> dht.pb.Message.ConnectionType + 5, // [5:5] is the sub-list for method output_type + 5, // [5:5] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name +} + +func init() { file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_init() } +func file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_init() { + if File_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_rawDesc, + NumEnums: 2, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_goTypes, + DependencyIndexes: file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_depIdxs, + EnumInfos: file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_enumTypes, + MessageInfos: file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_msgTypes, + }.Build() + File_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto = out.File + file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_rawDesc = nil + file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_goTypes = nil + file_github_com_libp2p_go_libp2p_kad_dht_pb_dht_proto_depIdxs = nil +} diff --git a/pb/dht.proto b/pb/dht.proto index 18bfd7419..4df379d2f 100644 --- a/pb/dht.proto +++ b/pb/dht.proto @@ -1,72 +1,68 @@ -// In order to re-generate the golang packages for `Message` you will need... -// 1. Protobuf binary (tested with protoc 3.0.0). - https://github.com/gogo/protobuf/releases -// 2. Gogo Protobuf (tested with gogo 0.3). - https://github.com/gogo/protobuf -// 3. To have cloned `libp2p/go-libp2p-{record,kad-dht}` under the same directory. -// Now from `libp2p/go-libp2p-kad-dht/pb` you can run... -// `protoc --gogo_out=. --proto_path=../../go-libp2p-record/pb/ --proto_path=./ dht.proto` - syntax = "proto3"; package dht.pb; +option go_package = "github.com/libp2p/go-libp2p-kad-dht/pb;dht_pb"; + import "github.com/libp2p/go-libp2p-record/pb/record.proto"; -import "github.com/gogo/protobuf/gogoproto/gogo.proto"; message Message { - enum MessageType { - PUT_VALUE = 0; - GET_VALUE = 1; - ADD_PROVIDER = 2; - GET_PROVIDERS = 3; - FIND_NODE = 4; - PING = 5; - } - - enum ConnectionType { - // sender does not have a connection to peer, and no extra information (default) - NOT_CONNECTED = 0; - - // sender has a live connection to peer - CONNECTED = 1; - - // sender recently connected to peer - CAN_CONNECT = 2; - - // sender recently tried to connect to peer repeatedly but failed to connect - // ("try" here is loose, but this should signal "made strong effort, failed") - CANNOT_CONNECT = 3; - } - - message Peer { - // ID of a given peer. - bytes id = 1 [(gogoproto.customtype) = "byteString", (gogoproto.nullable) = false]; - - // multiaddrs for a given peer - repeated bytes addrs = 2; - - // used to signal the sender's connection capabilities to the peer - ConnectionType connection = 3; - } - - // defines what type of message it is. - MessageType type = 1; - - // defines what coral cluster level this query/response belongs to. - // in case we want to implement coral's cluster rings in the future. - int32 clusterLevelRaw = 10; - - // Used to specify the key associated with this message. - // PUT_VALUE, GET_VALUE, ADD_PROVIDER, GET_PROVIDERS - bytes key = 2; - - // Used to return a value - // PUT_VALUE, GET_VALUE - record.pb.Record record = 3; - - // Used to return peers closer to a key in a query - // GET_VALUE, GET_PROVIDERS, FIND_NODE - repeated Peer closerPeers = 8 [(gogoproto.nullable) = false]; - - // Used to return Providers - // GET_VALUE, ADD_PROVIDER, GET_PROVIDERS - repeated Peer providerPeers = 9 [(gogoproto.nullable) = false]; + enum MessageType { + PUT_VALUE = 0; + GET_VALUE = 1; + ADD_PROVIDER = 2; + GET_PROVIDERS = 3; + FIND_NODE = 4; + PING = 5; + } + + enum ConnectionType { + // sender does not have a connection to peer, and no extra information + // (default) + NOT_CONNECTED = 0; + + // sender has a live connection to peer + CONNECTED = 1; + + // sender recently connected to peer + CAN_CONNECT = 2; + + // sender recently tried to connect to peer repeatedly but failed to connect + // ("try" here is loose, but this should signal "made strong effort, + // failed") + CANNOT_CONNECT = 3; + } + + message Peer { + // ID of a given peer. + bytes id = 1; + + // multiaddrs for a given peer + repeated bytes addrs = 2; + + // used to signal the sender's connection capabilities to the peer + ConnectionType connection = 3; + } + + // defines what type of message it is. + MessageType type = 1; + + // defines what coral cluster level this query/response belongs to. + // in case we want to implement coral's cluster rings in the future. + int32 clusterLevelRaw = 10; + + // Used to specify the key associated with this message. + // PUT_VALUE, GET_VALUE, ADD_PROVIDER, GET_PROVIDERS + bytes key = 2; + + // Used to return a value + // PUT_VALUE, GET_VALUE + record.pb.Record record = 3; + + // Used to return peers closer to a key in a query + // GET_VALUE, GET_PROVIDERS, FIND_NODE + repeated Peer closerPeers = 8; + + // Used to return Providers + // GET_VALUE, ADD_PROVIDER, GET_PROVIDERS + repeated Peer providerPeers = 9; } diff --git a/pb/gen.go b/pb/gen.go new file mode 100644 index 000000000..6269c3de4 --- /dev/null +++ b/pb/gen.go @@ -0,0 +1,22 @@ +// These commands work around namespace conflicts that occur when multiple +// repositories depend on .proto files with generic filenames. Due to the way +// protobuf registers files (e.g., foo.proto or pb/foo.proto), naming +// collisions can occur when the same filename is used across different +// packages. +// +// The only way to generate a *.pb.go file that includes the full package path +// (e.g., github.com/org/repo/pb/foo.proto) is to place the .proto file in a +// directory structure that mirrors its package path. +// +// References: +// - https://protobuf.dev/reference/go/faq#namespace-conflict +// - https://github.com/golang/protobuf/issues/1122#issuecomment-2045945265 +// +//go:generate mkdir -p github.com/libp2p/go-libp2p-kad-dht/pb +//go:generate git -c advice.detachedHead=false clone --depth 1 --branch release-v0.3.1 https://github.com/libp2p/go-libp2p-record.git github.com/libp2p/go-libp2p-record +//go:generate ln -f dht.proto github.com/libp2p/go-libp2p-kad-dht/pb/ +//go:generate protoc --go_opt=Mgithub.com/libp2p/go-libp2p-record/pb/record.proto=github.com/libp2p/go-libp2p-record/pb --go_out=. github.com/libp2p/go-libp2p-kad-dht/pb/dht.proto +//go:generate mv -f github.com/libp2p/go-libp2p-kad-dht/pb/dht.pb.go . +//go:generate rm -rf github.com + +package dht_pb diff --git a/pb/message.go b/pb/message.go index d9d2aeda0..5f98f5e39 100644 --- a/pb/message.go +++ b/pb/message.go @@ -1,10 +1,10 @@ package dht_pb import ( - "github.com/libp2p/go-libp2p-core/network" - "github.com/libp2p/go-libp2p-core/peer" + "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/peer" - logging "github.com/ipfs/go-log" + logging "github.com/ipfs/go-log/v2" ma "github.com/multiformats/go-multiaddr" ) @@ -25,31 +25,31 @@ func NewMessage(typ Message_MessageType, key []byte, level int) *Message { return m } -func peerRoutingInfoToPBPeer(p PeerRoutingInfo) Message_Peer { - var pbp Message_Peer +func peerRoutingInfoToPBPeer(p PeerRoutingInfo) *Message_Peer { + pbp := &Message_Peer{} pbp.Addrs = make([][]byte, len(p.Addrs)) for i, maddr := range p.Addrs { pbp.Addrs[i] = maddr.Bytes() // Bytes, not String. Compressed. } - pbp.Id = byteString(p.ID) + pbp.Id = []byte(p.ID) pbp.Connection = ConnectionType(p.Connectedness) return pbp } -func peerInfoToPBPeer(p peer.AddrInfo) Message_Peer { - var pbp Message_Peer +func peerInfoToPBPeer(p peer.AddrInfo) *Message_Peer { + pbp := &Message_Peer{} pbp.Addrs = make([][]byte, len(p.Addrs)) for i, maddr := range p.Addrs { pbp.Addrs[i] = maddr.Bytes() // Bytes, not String. Compressed. } - pbp.Id = byteString(p.ID) + pbp.Id = []byte(p.ID) return pbp } // PBPeerToPeer turns a *Message_Peer into its peer.AddrInfo counterpart -func PBPeerToPeerInfo(pbp Message_Peer) peer.AddrInfo { +func PBPeerToPeerInfo(pbp *Message_Peer) peer.AddrInfo { return peer.AddrInfo{ ID: peer.ID(pbp.Id), Addrs: pbp.Addresses(), @@ -58,8 +58,8 @@ func PBPeerToPeerInfo(pbp Message_Peer) peer.AddrInfo { // RawPeerInfosToPBPeers converts a slice of Peers into a slice of *Message_Peers, // ready to go out on the wire. -func RawPeerInfosToPBPeers(peers []peer.AddrInfo) []Message_Peer { - pbpeers := make([]Message_Peer, len(peers)) +func RawPeerInfosToPBPeers(peers []peer.AddrInfo) []*Message_Peer { + pbpeers := make([]*Message_Peer, len(peers)) for i, p := range peers { pbpeers[i] = peerInfoToPBPeer(p) } @@ -70,17 +70,17 @@ func RawPeerInfosToPBPeers(peers []peer.AddrInfo) []Message_Peer { // which can be written to a message and sent out. the key thing this function // does (in addition to PeersToPBPeers) is set the ConnectionType with // information from the given network.Network. -func PeerInfosToPBPeers(n network.Network, peers []peer.AddrInfo) []Message_Peer { +func PeerInfosToPBPeers(n network.Network, peers []peer.AddrInfo) []*Message_Peer { pbps := RawPeerInfosToPBPeers(peers) - for i, pbp := range pbps { + for i := range pbps { c := ConnectionType(n.Connectedness(peers[i].ID)) - pbp.Connection = c + pbps[i].Connection = c } return pbps } -func PeerRoutingInfosToPBPeers(peers []PeerRoutingInfo) []Message_Peer { - pbpeers := make([]Message_Peer, len(peers)) +func PeerRoutingInfosToPBPeers(peers []PeerRoutingInfo) []*Message_Peer { + pbpeers := make([]*Message_Peer, len(peers)) for i, p := range peers { pbpeers[i] = peerRoutingInfoToPBPeer(p) } @@ -89,7 +89,7 @@ func PeerRoutingInfosToPBPeers(peers []PeerRoutingInfo) []Message_Peer { // PBPeersToPeerInfos converts given []*Message_Peer into []peer.AddrInfo // Invalid addresses will be silently omitted. -func PBPeersToPeerInfos(pbps []Message_Peer) []*peer.AddrInfo { +func PBPeersToPeerInfos(pbps []*Message_Peer) []*peer.AddrInfo { peers := make([]*peer.AddrInfo, 0, len(pbps)) for _, pbp := range pbps { ai := PBPeerToPeerInfo(pbp) @@ -146,10 +146,6 @@ func ConnectionType(c network.Connectedness) Message_ConnectionType { return Message_NOT_CONNECTED case network.Connected: return Message_CONNECTED - case network.CanConnect: - return Message_CAN_CONNECT - case network.CannotConnect: - return Message_CANNOT_CONNECT } } @@ -163,9 +159,5 @@ func Connectedness(c Message_ConnectionType) network.Connectedness { return network.NotConnected case Message_CONNECTED: return network.Connected - case Message_CAN_CONNECT: - return network.CanConnect - case Message_CANNOT_CONNECT: - return network.CannotConnect } } diff --git a/pb/protocol_messenger.go b/pb/protocol_messenger.go index 883a86872..7971db68e 100644 --- a/pb/protocol_messenger.go +++ b/pb/protocol_messenger.go @@ -6,11 +6,13 @@ import ( "errors" "fmt" - logging "github.com/ipfs/go-log" - "github.com/libp2p/go-libp2p-core/host" - "github.com/libp2p/go-libp2p-core/peer" + logging "github.com/ipfs/go-log/v2" recpb "github.com/libp2p/go-libp2p-record/pb" + "github.com/libp2p/go-libp2p/core/host" + "github.com/libp2p/go-libp2p/core/peer" "github.com/multiformats/go-multihash" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" "github.com/libp2p/go-libp2p-kad-dht/internal" ) @@ -45,6 +47,12 @@ func NewProtocolMessenger(msgSender MessageSender, opts ...ProtocolMessengerOpti return pm, nil } +type MessageSenderWithDisconnect interface { + MessageSender + + OnDisconnect(context.Context, peer.ID) +} + // MessageSender handles sending wire protocol messages to a given peer type MessageSender interface { // SendRequest sends a peer a message and waits for its response @@ -54,7 +62,18 @@ type MessageSender interface { } // PutValue asks a peer to store the given key/value pair. -func (pm *ProtocolMessenger) PutValue(ctx context.Context, p peer.ID, rec *recpb.Record) error { +func (pm *ProtocolMessenger) PutValue(ctx context.Context, p peer.ID, rec *recpb.Record) (err error) { + ctx, span := internal.StartSpan(ctx, "ProtocolMessenger.PutValue") + defer span.End() + if span.IsRecording() { + span.SetAttributes(attribute.Stringer("to", p), attribute.Stringer("record", rec)) + defer func() { + if err != nil { + span.SetStatus(codes.Error, err.Error()) + } + }() + } + pmes := NewMessage(Message_PUT_VALUE, rec.Key, 0) pmes.Record = rec rpmes, err := pm.m.SendRequest(ctx, p, pmes) @@ -74,7 +93,27 @@ func (pm *ProtocolMessenger) PutValue(ctx context.Context, p peer.ID, rec *recpb // GetValue asks a peer for the value corresponding to the given key. Also returns the K closest peers to the key // as described in GetClosestPeers. -func (pm *ProtocolMessenger) GetValue(ctx context.Context, p peer.ID, key string) (*recpb.Record, []*peer.AddrInfo, error) { +func (pm *ProtocolMessenger) GetValue(ctx context.Context, p peer.ID, key string) (record *recpb.Record, closerPeers []*peer.AddrInfo, err error) { + ctx, span := internal.StartSpan(ctx, "ProtocolMessenger.GetValue") + defer span.End() + if span.IsRecording() { + span.SetAttributes(attribute.Stringer("to", p), internal.KeyAsAttribute("key", key)) + defer func() { + if err != nil { + span.SetStatus(codes.Error, err.Error()) + } else { + peers := make([]string, len(closerPeers)) + for i, v := range closerPeers { + peers[i] = v.String() + } + span.SetAttributes( + attribute.Stringer("record", record), + attribute.StringSlice("closestPeers", peers), + ) + } + }() + } + pmes := NewMessage(Message_GET_VALUE, []byte(key), 0) respMsg, err := pm.m.SendRequest(ctx, p, pmes) if err != nil { @@ -103,7 +142,24 @@ func (pm *ProtocolMessenger) GetValue(ctx context.Context, p peer.ID, key string // GetClosestPeers asks a peer to return the K (a DHT-wide parameter) DHT server peers closest in XOR space to the id // Note: If the peer happens to know another peer whose peerID exactly matches the given id it will return that peer // even if that peer is not a DHT server node. -func (pm *ProtocolMessenger) GetClosestPeers(ctx context.Context, p peer.ID, id peer.ID) ([]*peer.AddrInfo, error) { +func (pm *ProtocolMessenger) GetClosestPeers(ctx context.Context, p peer.ID, id peer.ID) (closerPeers []*peer.AddrInfo, err error) { + ctx, span := internal.StartSpan(ctx, "ProtocolMessenger.GetClosestPeers") + defer span.End() + if span.IsRecording() { + span.SetAttributes(attribute.Stringer("to", p), attribute.Stringer("key", id)) + defer func() { + if err != nil { + span.SetStatus(codes.Error, err.Error()) + } else { + peers := make([]string, len(closerPeers)) + for i, v := range closerPeers { + peers[i] = v.String() + } + span.SetAttributes(attribute.StringSlice("peers", peers)) + } + }() + } + pmes := NewMessage(Message_FIND_NODE, []byte(id), 0) respMsg, err := pm.m.SendRequest(ctx, p, pmes) if err != nil { @@ -113,40 +169,86 @@ func (pm *ProtocolMessenger) GetClosestPeers(ctx context.Context, p peer.ID, id return peers, nil } -// PutProvider asks a peer to store that we are a provider for the given key. -func (pm *ProtocolMessenger) PutProvider(ctx context.Context, p peer.ID, key multihash.Multihash, host host.Host) error { - pi := peer.AddrInfo{ - ID: host.ID(), - Addrs: host.Addrs(), +// PutProvider is deprecated please use [ProtocolMessenger.PutProviderAddrs]. +func (pm *ProtocolMessenger) PutProvider(ctx context.Context, p peer.ID, key multihash.Multihash, h host.Host) error { + return pm.PutProviderAddrs(ctx, p, key, peer.AddrInfo{ + ID: h.ID(), + Addrs: h.Addrs(), + }) +} + +// PutProviderAddrs asks a peer to store that we are a provider for the given key. +func (pm *ProtocolMessenger) PutProviderAddrs(ctx context.Context, p peer.ID, key multihash.Multihash, self peer.AddrInfo) (err error) { + ctx, span := internal.StartSpan(ctx, "ProtocolMessenger.PutProvider") + defer span.End() + if span.IsRecording() { + span.SetAttributes(attribute.Stringer("to", p), attribute.Stringer("key", key)) + defer func() { + if err != nil { + span.SetStatus(codes.Error, err.Error()) + } + }() } // TODO: We may want to limit the type of addresses in our provider records // For example, in a WAN-only DHT prohibit sharing non-WAN addresses (e.g. 192.168.0.100) - if len(pi.Addrs) < 1 { + if len(self.Addrs) < 1 { return fmt.Errorf("no known addresses for self, cannot put provider") } pmes := NewMessage(Message_ADD_PROVIDER, key, 0) - pmes.ProviderPeers = RawPeerInfosToPBPeers([]peer.AddrInfo{pi}) + pmes.ProviderPeers = RawPeerInfosToPBPeers([]peer.AddrInfo{self}) return pm.m.SendMessage(ctx, p, pmes) } // GetProviders asks a peer for the providers it knows of for a given key. Also returns the K closest peers to the key // as described in GetClosestPeers. -func (pm *ProtocolMessenger) GetProviders(ctx context.Context, p peer.ID, key multihash.Multihash) ([]*peer.AddrInfo, []*peer.AddrInfo, error) { +func (pm *ProtocolMessenger) GetProviders(ctx context.Context, p peer.ID, key multihash.Multihash) (provs []*peer.AddrInfo, closerPeers []*peer.AddrInfo, err error) { + ctx, span := internal.StartSpan(ctx, "ProtocolMessenger.GetProviders") + defer span.End() + if span.IsRecording() { + span.SetAttributes(attribute.Stringer("to", p), attribute.Stringer("key", key)) + defer func() { + if err != nil { + span.SetStatus(codes.Error, err.Error()) + } else { + provsStr := make([]string, len(provs)) + for i, v := range provs { + provsStr[i] = v.String() + } + closerPeersStr := make([]string, len(provs)) + for i, v := range provs { + closerPeersStr[i] = v.String() + } + span.SetAttributes(attribute.StringSlice("provs", provsStr), attribute.StringSlice("closestPeers", closerPeersStr)) + } + }() + } + pmes := NewMessage(Message_GET_PROVIDERS, key, 0) respMsg, err := pm.m.SendRequest(ctx, p, pmes) if err != nil { return nil, nil, err } - provs := PBPeersToPeerInfos(respMsg.GetProviderPeers()) - closerPeers := PBPeersToPeerInfos(respMsg.GetCloserPeers()) + provs = PBPeersToPeerInfos(respMsg.GetProviderPeers()) + closerPeers = PBPeersToPeerInfos(respMsg.GetCloserPeers()) return provs, closerPeers, nil } // Ping sends a ping message to the passed peer and waits for a response. -func (pm *ProtocolMessenger) Ping(ctx context.Context, p peer.ID) error { +func (pm *ProtocolMessenger) Ping(ctx context.Context, p peer.ID) (err error) { + ctx, span := internal.StartSpan(ctx, "ProtocolMessenger.Ping") + defer span.End() + if span.IsRecording() { + span.SetAttributes(attribute.Stringer("to", p)) + defer func() { + if err != nil { + span.SetStatus(codes.Error, err.Error()) + } + }() + } + req := NewMessage(Message_PING, nil, 0) resp, err := pm.m.SendRequest(ctx, p, req) if err != nil { diff --git a/protocol.go b/protocol.go index a68f01c19..3081a57d9 100644 --- a/protocol.go +++ b/protocol.go @@ -1,12 +1,13 @@ package dht import ( - "github.com/libp2p/go-libp2p-core/protocol" + "github.com/libp2p/go-libp2p-kad-dht/amino" + "github.com/libp2p/go-libp2p/core/protocol" ) var ( // ProtocolDHT is the default DHT protocol. - ProtocolDHT protocol.ID = "/ipfs/kad/1.0.0" + ProtocolDHT protocol.ID = amino.ProtocolID // DefaultProtocols spoken by the DHT. - DefaultProtocols = []protocol.ID{ProtocolDHT} + DefaultProtocols = amino.Protocols ) diff --git a/providers/provider_set.go b/providers/provider_set.go index 4b5295078..d1745ef86 100644 --- a/providers/provider_set.go +++ b/providers/provider_set.go @@ -3,7 +3,7 @@ package providers import ( "time" - "github.com/libp2p/go-libp2p-core/peer" + "github.com/libp2p/go-libp2p/core/peer" ) // A providerSet has the list of providers and the time that they were added diff --git a/providers/providers_manager.go b/providers/providers_manager.go index ad5929203..d740297a7 100644 --- a/providers/providers_manager.go +++ b/providers/providers_manager.go @@ -4,29 +4,39 @@ import ( "context" "encoding/binary" "fmt" + "io" "strings" + "sync" "time" - "github.com/libp2p/go-libp2p-core/peer" - peerstore "github.com/libp2p/go-libp2p-core/peerstore" - peerstoreImpl "github.com/libp2p/go-libp2p-peerstore" - lru "github.com/hashicorp/golang-lru/simplelru" ds "github.com/ipfs/go-datastore" - autobatch "github.com/ipfs/go-datastore/autobatch" + "github.com/ipfs/go-datastore/autobatch" dsq "github.com/ipfs/go-datastore/query" - logging "github.com/ipfs/go-log" - goprocess "github.com/jbenet/goprocess" - goprocessctx "github.com/jbenet/goprocess/context" - base32 "github.com/multiformats/go-base32" + logging "github.com/ipfs/go-log/v2" + "github.com/libp2p/go-libp2p-kad-dht/amino" + "github.com/libp2p/go-libp2p-kad-dht/internal" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/peerstore" + peerstoreImpl "github.com/libp2p/go-libp2p/p2p/host/peerstore" + "github.com/multiformats/go-base32" ) -// ProvidersKeyPrefix is the prefix/namespace for ALL provider record -// keys stored in the data store. -const ProvidersKeyPrefix = "/providers/" +const ( + // ProvidersKeyPrefix is the prefix/namespace for ALL provider record + // keys stored in the data store. + ProvidersKeyPrefix = "/providers/" + + // ProviderAddrTTL is the TTL to keep the multi addresses of provider + // peers around. Those addresses are returned alongside provider. After + // it expires, the returned records will require an extra lookup, to + // find the multiaddress associated with the returned peer id. + ProviderAddrTTL = amino.DefaultProviderAddrTTL +) -// ProvideValidity is the default time that a provider record should last -var ProvideValidity = time.Hour * 24 +// ProvideValidity is the default time that a Provider Record should last on DHT +// This value is also known as Provider Record Expiration Interval. +var ProvideValidity = amino.DefaultProvideValidity var defaultCleanupInterval = time.Hour var lruCacheSize = 256 var batchBufferSize = 256 @@ -36,6 +46,7 @@ var log = logging.Logger("providers") type ProviderStore interface { AddProvider(ctx context.Context, key []byte, prov peer.AddrInfo) error GetProviders(ctx context.Context, key []byte) ([]peer.AddrInfo, error) + io.Closer } // ProviderManager adds and pulls providers out of the datastore, @@ -50,9 +61,12 @@ type ProviderManager struct { newprovs chan *addProv getprovs chan *getProv - proc goprocess.Process cleanupInterval time.Duration + + ctx context.Context + cancel context.CancelFunc + wg sync.WaitGroup } var _ ProviderStore = (*ProviderManager)(nil) @@ -100,7 +114,7 @@ type getProv struct { } // NewProviderManager constructor -func NewProviderManager(ctx context.Context, local peer.ID, ps peerstore.Peerstore, dstore ds.Batching, opts ...Option) (*ProviderManager, error) { +func NewProviderManager(local peer.ID, ps peerstore.Peerstore, dstore ds.Batching, opts ...Option) (*ProviderManager, error) { pm := new(ProviderManager) pm.self = local pm.getprovs = make(chan *getProv) @@ -116,123 +130,130 @@ func NewProviderManager(ctx context.Context, local peer.ID, ps peerstore.Peersto if err := pm.applyOptions(opts...); err != nil { return nil, err } - pm.proc = goprocessctx.WithContext(ctx) - pm.proc.Go(func(proc goprocess.Process) { pm.run(ctx, proc) }) + pm.ctx, pm.cancel = context.WithCancel(context.Background()) + pm.run() return pm, nil } -// Process returns the ProviderManager process -func (pm *ProviderManager) Process() goprocess.Process { - return pm.proc -} +func (pm *ProviderManager) run() { + pm.wg.Add(1) + go func() { + defer pm.wg.Done() -func (pm *ProviderManager) run(ctx context.Context, proc goprocess.Process) { - var ( - gcQuery dsq.Results - gcQueryRes <-chan dsq.Result - gcSkip map[string]struct{} - gcTime time.Time - gcTimer = time.NewTimer(pm.cleanupInterval) - ) - - defer func() { - gcTimer.Stop() - if gcQuery != nil { - // don't really care if this fails. - _ = gcQuery.Close() - } - if err := pm.dstore.Flush(ctx); err != nil { - log.Error("failed to flush datastore: ", err) - } - }() + var gcQuery dsq.Results + gcTimer := time.NewTimer(pm.cleanupInterval) - for { - select { - case np := <-pm.newprovs: - err := pm.addProv(np.ctx, np.key, np.val) - if err != nil { - log.Error("error adding new providers: ", err) - continue - } - if gcSkip != nil { - // we have an gc, tell it to skip this provider - // as we've updated it since the GC started. - gcSkip[mkProvKeyFor(np.key, np.val)] = struct{}{} + defer func() { + gcTimer.Stop() + if gcQuery != nil { + // don't really care if this fails. + _ = gcQuery.Close() } - case gp := <-pm.getprovs: - provs, err := pm.getProvidersForKey(gp.ctx, gp.key) - if err != nil && err != ds.ErrNotFound { - log.Error("error reading providers: ", err) + if err := pm.dstore.Flush(context.Background()); err != nil { + log.Error("failed to flush datastore: ", err) } - - // set the cap so the user can't append to this. - gp.resp <- provs[0:len(provs):len(provs)] - case res, ok := <-gcQueryRes: - if !ok { - if err := gcQuery.Close(); err != nil { - log.Error("failed to close provider GC query: ", err) + }() + + var gcQueryRes <-chan dsq.Result + var gcSkip map[string]struct{} + var gcTime time.Time + for { + select { + case np := <-pm.newprovs: + err := pm.addProv(np.ctx, np.key, np.val) + if err != nil { + log.Error("error adding new providers: ", err) + continue + } + if gcSkip != nil { + // we have an gc, tell it to skip this provider + // as we've updated it since the GC started. + gcSkip[mkProvKeyFor(np.key, np.val)] = struct{}{} + } + case gp := <-pm.getprovs: + provs, err := pm.getProvidersForKey(gp.ctx, gp.key) + if err != nil && err != ds.ErrNotFound { + log.Error("error reading providers: ", err) } - gcTimer.Reset(pm.cleanupInterval) - // cleanup GC round - gcQueryRes = nil - gcSkip = nil - gcQuery = nil - continue - } - if res.Error != nil { - log.Error("got error from GC query: ", res.Error) - continue - } - if _, ok := gcSkip[res.Key]; ok { - // We've updated this record since starting the - // GC round, skip it. - continue - } + // set the cap so the user can't append to this. + gp.resp <- provs[0:len(provs):len(provs)] + case res, ok := <-gcQueryRes: + if !ok { + if err := gcQuery.Close(); err != nil { + log.Error("failed to close provider GC query: ", err) + } + gcTimer.Reset(pm.cleanupInterval) + + // cleanup GC round + gcQueryRes = nil + gcSkip = nil + gcQuery = nil + continue + } + if res.Error != nil { + log.Error("got error from GC query: ", res.Error) + continue + } + if _, ok := gcSkip[res.Key]; ok { + // We've updated this record since starting the + // GC round, skip it. + continue + } - // check expiration time - t, err := readTimeValue(res.Value) - switch { - case err != nil: - // couldn't parse the time - log.Error("parsing providers record from disk: ", err) - fallthrough - case gcTime.Sub(t) > ProvideValidity: - // or expired - err = pm.dstore.Delete(ctx, ds.RawKey(res.Key)) - if err != nil && err != ds.ErrNotFound { - log.Error("failed to remove provider record from disk: ", err) + // check expiration time + t, err := readTimeValue(res.Value) + switch { + case err != nil: + // couldn't parse the time + log.Error("parsing providers record from disk: ", err) + fallthrough + case gcTime.Sub(t) > ProvideValidity: + // or expired + err = pm.dstore.Delete(pm.ctx, ds.RawKey(res.Key)) + if err != nil && err != ds.ErrNotFound { + log.Error("failed to remove provider record from disk: ", err) + } } - } - case gcTime = <-gcTimer.C: - // You know the wonderful thing about caches? You can - // drop them. - // - // Much faster than GCing. - pm.cache.Purge() - - // Now, kick off a GC of the datastore. - q, err := pm.dstore.Query(ctx, dsq.Query{ - Prefix: ProvidersKeyPrefix, - }) - if err != nil { - log.Error("provider record GC query failed: ", err) - continue + case gcTime = <-gcTimer.C: + // You know the wonderful thing about caches? You can + // drop them. + // + // Much faster than GCing. + pm.cache.Purge() + + // Now, kick off a GC of the datastore. + q, err := pm.dstore.Query(pm.ctx, dsq.Query{ + Prefix: ProvidersKeyPrefix, + }) + if err != nil { + log.Error("provider record GC query failed: ", err) + continue + } + gcQuery = q + gcQueryRes = q.Next() + gcSkip = make(map[string]struct{}) + case <-pm.ctx.Done(): + return } - gcQuery = q - gcQueryRes = q.Next() - gcSkip = make(map[string]struct{}) - case <-proc.Closing(): - return } - } + }() +} + +func (pm *ProviderManager) Close() error { + pm.cancel() + pm.wg.Wait() + return nil } // AddProvider adds a provider func (pm *ProviderManager) AddProvider(ctx context.Context, k []byte, provInfo peer.AddrInfo) error { + ctx, span := internal.StartSpan(ctx, "ProviderManager.AddProvider") + defer span.End() + if provInfo.ID != pm.self { // don't add own addrs. - pm.pstore.AddAddrs(provInfo.ID, provInfo.Addrs, peerstore.ProviderAddrTTL) + pm.pstore.AddAddrs(provInfo.ID, provInfo.Addrs, ProviderAddrTTL) } prov := &addProv{ ctx: ctx, @@ -278,6 +299,9 @@ func mkProvKey(k []byte) string { // GetProviders returns the set of providers for the given key. // This method _does not_ copy the set. Do not modify it. func (pm *ProviderManager) GetProviders(ctx context.Context, k []byte) ([]peer.AddrInfo, error) { + ctx, span := internal.StartSpan(ctx, "ProviderManager.GetProviders") + defer span.End() + gp := &getProv{ ctx: ctx, key: k, diff --git a/providers/providers_manager_test.go b/providers/providers_manager_test.go index c793d92f2..8b52e722c 100644 --- a/providers/providers_manager_test.go +++ b/providers/providers_manager_test.go @@ -3,20 +3,20 @@ package providers import ( "context" "fmt" - "io/ioutil" + "io" "os" "testing" "time" - "github.com/libp2p/go-libp2p-core/peer" - "github.com/libp2p/go-libp2p-peerstore/pstoremem" + "github.com/libp2p/go-libp2p-kad-dht/internal" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoremem" mh "github.com/multiformats/go-multihash" ds "github.com/ipfs/go-datastore" dsq "github.com/ipfs/go-datastore/query" dssync "github.com/ipfs/go-datastore/sync" - u "github.com/ipfs/go-ipfs-util" // // used by TestLargeProvidersSet: do not remove // lds "github.com/ipfs/go-ds-leveldb" @@ -31,11 +31,11 @@ func TestProviderManager(t *testing.T) { if err != nil { t.Fatal(err) } - p, err := NewProviderManager(ctx, mid, ps, dssync.MutexWrap(ds.NewMapDatastore())) + p, err := NewProviderManager(mid, ps, dssync.MutexWrap(ds.NewMapDatastore())) if err != nil { t.Fatal(err) } - a := u.Hash([]byte("test")) + a := internal.Hash([]byte("test")) p.AddProvider(ctx, a, peer.AddrInfo{ID: peer.ID("testingprovider")}) // Not cached @@ -60,7 +60,7 @@ func TestProviderManager(t *testing.T) { t.Fatalf("Should have got 3 providers, got %d", len(resp)) } - p.proc.Close() + p.Close() } func TestProvidersDatastore(t *testing.T) { @@ -77,16 +77,16 @@ func TestProvidersDatastore(t *testing.T) { t.Fatal(err) } - p, err := NewProviderManager(ctx, mid, ps, dssync.MutexWrap(ds.NewMapDatastore())) + p, err := NewProviderManager(mid, ps, dssync.MutexWrap(ds.NewMapDatastore())) if err != nil { t.Fatal(err) } - defer p.proc.Close() + defer p.Close() friend := peer.ID("friend") var mhs []mh.Multihash for i := 0; i < 100; i++ { - h := u.Hash([]byte(fmt.Sprint(i))) + h := internal.Hash([]byte(fmt.Sprint(i))) mhs = append(mhs, h) p.AddProvider(ctx, h, peer.AddrInfo{ID: friend}) } @@ -105,7 +105,7 @@ func TestProvidersDatastore(t *testing.T) { func TestProvidersSerialization(t *testing.T) { dstore := dssync.MutexWrap(ds.NewMapDatastore()) - k := u.Hash(([]byte("my key!"))) + k := internal.Hash(([]byte("my key!"))) p1 := peer.ID("peer one") p2 := peer.ID("peer two") pt1 := time.Now() @@ -166,7 +166,7 @@ func TestProvidesExpire(t *testing.T) { if err != nil { t.Fatal(err) } - p, err := NewProviderManager(ctx, mid, ps, ds) + p, err := NewProviderManager(mid, ps, ds) if err != nil { t.Fatal(err) } @@ -174,7 +174,7 @@ func TestProvidesExpire(t *testing.T) { peers := []peer.ID{"a", "b"} var mhs []mh.Multihash for i := 0; i < 10; i++ { - h := u.Hash([]byte(fmt.Sprint(i))) + h := internal.Hash([]byte(fmt.Sprint(i))) mhs = append(mhs, h) } @@ -216,7 +216,7 @@ func TestProvidesExpire(t *testing.T) { time.Sleep(time.Second / 2) // Stop to prevent data races - p.Process().Close() + p.Close() if p.cache.Len() != 0 { t.Fatal("providers map not cleaned up") @@ -235,8 +235,10 @@ func TestProvidesExpire(t *testing.T) { } } -var _ = ioutil.NopCloser -var _ = os.DevNull +var ( + _ = io.NopCloser + _ = os.DevNull +) // TestLargeProvidersSet can be used for profiling. // The datastore can be switched to levelDB by uncommenting the section below and the import above @@ -248,24 +250,23 @@ func TestLargeProvidersSet(t *testing.T) { dstore := ds.NewMapDatastore() - //dirn, err := ioutil.TempDir("", "provtest") - //if err != nil { + //dirn, err := os.MkdirTemp("", "provtest") // t.Fatal(err) - //} + // } // - //opts := &lds.Options{ + // opts := &lds.Options{ // NoSync: true, // Compression: 1, - //} - //lds, err := lds.NewDatastore(dirn, opts) - //if err != nil { + // } + // lds, err := lds.NewDatastore(dirn, opts) + // if err != nil { // t.Fatal(err) - //} - //dstore = lds + // } + // dstore = lds // - //defer func() { + // defer func() { // os.RemoveAll(dirn) - //}() + // }() ctx := context.Background() var peers []peer.ID @@ -279,15 +280,15 @@ func TestLargeProvidersSet(t *testing.T) { t.Fatal(err) } - p, err := NewProviderManager(ctx, mid, ps, dstore) + p, err := NewProviderManager(mid, ps, dstore) if err != nil { t.Fatal(err) } - defer p.proc.Close() + defer p.Close() var mhs []mh.Multihash for i := 0; i < 1000; i++ { - h := u.Hash([]byte(fmt.Sprint(i))) + h := internal.Hash([]byte(fmt.Sprint(i))) mhs = append(mhs, h) for _, pid := range peers { p.AddProvider(ctx, h, peer.AddrInfo{ID: pid}) @@ -312,14 +313,14 @@ func TestUponCacheMissProvidersAreReadFromDatastore(t *testing.T) { defer cancel() p1, p2 := peer.ID("a"), peer.ID("b") - h1 := u.Hash([]byte("1")) - h2 := u.Hash([]byte("2")) + h1 := internal.Hash([]byte("1")) + h2 := internal.Hash([]byte("2")) ps, err := pstoremem.NewPeerstore() if err != nil { t.Fatal(err) } - pm, err := NewProviderManager(ctx, p1, ps, dssync.MutexWrap(ds.NewMapDatastore())) + pm, err := NewProviderManager(p1, ps, dssync.MutexWrap(ds.NewMapDatastore())) if err != nil { t.Fatal(err) } @@ -342,13 +343,13 @@ func TestWriteUpdatesCache(t *testing.T) { defer cancel() p1, p2 := peer.ID("a"), peer.ID("b") - h1 := u.Hash([]byte("1")) + h1 := internal.Hash([]byte("1")) ps, err := pstoremem.NewPeerstore() if err != nil { t.Fatal(err) } - pm, err := NewProviderManager(ctx, p1, ps, dssync.MutexWrap(ds.NewMapDatastore())) + pm, err := NewProviderManager(p1, ps, dssync.MutexWrap(ds.NewMapDatastore())) if err != nil { t.Fatal(err) } diff --git a/qpeerset/qpeerset.go b/qpeerset/qpeerset.go index b940a0430..d22a074b7 100644 --- a/qpeerset/qpeerset.go +++ b/qpeerset/qpeerset.go @@ -4,7 +4,7 @@ import ( "math/big" "sort" - "github.com/libp2p/go-libp2p-core/peer" + "github.com/libp2p/go-libp2p/core/peer" ks "github.com/whyrusleeping/go-keyspace" ) diff --git a/qpeerset/qpeerset_test.go b/qpeerset/qpeerset_test.go index bd40413af..5ce49628e 100644 --- a/qpeerset/qpeerset_test.go +++ b/qpeerset/qpeerset_test.go @@ -3,8 +3,8 @@ package qpeerset import ( "testing" - "github.com/libp2p/go-libp2p-core/peer" - "github.com/libp2p/go-libp2p-core/test" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/test" kb "github.com/libp2p/go-libp2p-kbucket" diff --git a/query.go b/query.go index a6a73935e..7c01a2af2 100644 --- a/query.go +++ b/query.go @@ -8,12 +8,15 @@ import ( "sync" "time" - "github.com/libp2p/go-libp2p-core/network" - "github.com/libp2p/go-libp2p-core/peer" - pstore "github.com/libp2p/go-libp2p-core/peerstore" - "github.com/libp2p/go-libp2p-core/routing" + "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/peer" + pstore "github.com/libp2p/go-libp2p/core/peerstore" + "github.com/libp2p/go-libp2p/core/routing" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" "github.com/google/uuid" + "github.com/libp2p/go-libp2p-kad-dht/internal" "github.com/libp2p/go-libp2p-kad-dht/qpeerset" kb "github.com/libp2p/go-libp2p-kbucket" ) @@ -22,7 +25,7 @@ import ( var ErrNoPeersQueried = errors.New("failed to query any peers") type queryFn func(context.Context, peer.ID) ([]*peer.AddrInfo, error) -type stopFn func() bool +type stopFn func(*qpeerset.QueryPeerset) bool // query represents a single DHT query. type query struct { @@ -61,8 +64,9 @@ type query struct { } type lookupWithFollowupResult struct { - peers []peer.ID // the top K not unreachable peers at the end of the query - state []qpeerset.PeerState // the peer states at the end of the query + peers []peer.ID // the top K not unreachable peers at the end of the query + state []qpeerset.PeerState // the peer states at the end of the query of the peers slice (not closest) + closest []peer.ID // the top K peers at the end of the query // indicates that neither the lookup nor the followup has been prematurely terminated by an external condition such // as context cancellation or the stop function being called. @@ -77,8 +81,11 @@ type lookupWithFollowupResult struct { // After the lookup is complete the query function is run (unless stopped) against all of the top K peers from the // lookup that have not already been successfully queried. func (dht *IpfsDHT) runLookupWithFollowup(ctx context.Context, target string, queryFn queryFn, stopFn stopFn) (*lookupWithFollowupResult, error) { + ctx, span := internal.StartSpan(ctx, "IpfsDHT.RunLookupWithFollowup", trace.WithAttributes(internal.KeyAsAttribute("Target", target))) + defer span.End() + // run the query - lookupRes, err := dht.runQuery(ctx, target, queryFn, stopFn) + lookupRes, qps, err := dht.runQuery(ctx, target, queryFn, stopFn) if err != nil { return nil, err } @@ -99,7 +106,7 @@ func (dht *IpfsDHT) runLookupWithFollowup(ctx context.Context, target string, qu } // return if the lookup has been externally stopped - if ctx.Err() != nil || stopFn() { + if ctx.Err() != nil || stopFn(qps) { lookupRes.completed = false return lookupRes, nil } @@ -122,7 +129,7 @@ processFollowUp: select { case <-doneCh: followupsCompleted++ - if stopFn() { + if stopFn(qps) { cancelFollowUp() if i < len(queryPeers)-1 { lookupRes.completed = false @@ -145,7 +152,10 @@ processFollowUp: return lookupRes, nil } -func (dht *IpfsDHT) runQuery(ctx context.Context, target string, queryFn queryFn, stopFn stopFn) (*lookupWithFollowupResult, error) { +func (dht *IpfsDHT) runQuery(ctx context.Context, target string, queryFn queryFn, stopFn stopFn) (*lookupWithFollowupResult, *qpeerset.QueryPeerset, error) { + ctx, span := internal.StartSpan(ctx, "IpfsDHT.RunQuery") + defer span.End() + // pick the K closest peers to the key in our Routing table. targetKadID := kb.ConvertKey(target) seedPeers := dht.routingTable.NearestPeers(targetKadID, dht.bucketSize) @@ -154,7 +164,7 @@ func (dht *IpfsDHT) runQuery(ctx context.Context, target string, queryFn queryFn Type: routing.QueryError, Extra: kb.ErrLookupFailure.Error(), }) - return nil, kb.ErrLookupFailure + return nil, nil, kb.ErrLookupFailure } q := &query{ @@ -178,7 +188,7 @@ func (dht *IpfsDHT) runQuery(ctx context.Context, target string, queryFn queryFn } res := q.constructLookupResult(targetKadID) - return res, nil + return res, q.queryPeers, nil } func (q *query) recordPeerIsValuable(p peer.ID) { @@ -235,11 +245,14 @@ func (q *query) constructLookupResult(target kb.ID) *lookupWithFollowupResult { sortedPeers = sortedPeers[:q.dht.bucketSize] } + closest := q.queryPeers.GetClosestNInStates(q.dht.bucketSize, qpeerset.PeerHeard, qpeerset.PeerWaiting, qpeerset.PeerQueried, qpeerset.PeerUnreachable) + // return the top K not unreachable peers as well as their states at the end of the query res := &lookupWithFollowupResult{ peers: sortedPeers, state: make([]qpeerset.PeerState, len(sortedPeers)), completed: completed, + closest: closest, } for i, p := range sortedPeers { @@ -259,7 +272,10 @@ type queryUpdate struct { } func (q *query) run() { - pathCtx, cancelPath := context.WithCancel(q.ctx) + ctx, span := internal.StartSpan(q.ctx, "IpfsDHT.Query.Run") + defer span.End() + + pathCtx, cancelPath := context.WithCancel(ctx) defer cancelPath() alpha := q.dht.alpha @@ -303,6 +319,12 @@ func (q *query) run() { // spawnQuery starts one query, if an available heard peer is found func (q *query) spawnQuery(ctx context.Context, cause peer.ID, queryPeer peer.ID, ch chan<- *queryUpdate) { + ctx, span := internal.StartSpan(ctx, "IpfsDHT.SpawnQuery", trace.WithAttributes( + attribute.String("Cause", cause.String()), + attribute.String("QueryPeer", queryPeer.String()), + )) + defer span.End() + PublishLookupEvent(ctx, NewLookupEvent( q.dht.self, @@ -327,7 +349,7 @@ func (q *query) spawnQuery(ctx context.Context, cause peer.ID, queryPeer peer.ID func (q *query) isReadyToTerminate(ctx context.Context, nPeersToQuery int) (bool, LookupTerminationReason, []peer.ID) { // give the application logic a chance to terminate - if q.stopFn() { + if q.stopFn(q.queryPeers) { return true, LookupStopped, nil } if q.isStarvationTermination() { @@ -369,6 +391,9 @@ func (q *query) isStarvationTermination() bool { } func (q *query) terminate(ctx context.Context, cancel context.CancelFunc, reason LookupTerminationReason) { + ctx, span := internal.StartSpan(ctx, "IpfsDHT.Query.Terminate", trace.WithAttributes(attribute.Stringer("Reason", reason))) + defer span.End() + if q.terminated { return } @@ -391,13 +416,17 @@ func (q *query) terminate(ctx context.Context, cancel context.CancelFunc, reason // queryPeer does not access the query state in queryPeers! func (q *query) queryPeer(ctx context.Context, ch chan<- *queryUpdate, p peer.ID) { defer q.waitGroup.Done() + + ctx, span := internal.StartSpan(ctx, "IpfsDHT.QueryPeer") + defer span.End() + dialCtx, queryCtx := ctx, ctx // dial the peer if err := q.dht.dialPeer(dialCtx, p); err != nil { // remove the peer if there was a dial failure..but not because of a context cancellation if dialCtx.Err() == nil { - q.dht.peerStoppedDHT(q.dht.ctx, p) + q.dht.peerStoppedDHT(p) } ch <- &queryUpdate{cause: p, unreachable: []peer.ID{p}} return @@ -408,7 +437,7 @@ func (q *query) queryPeer(ctx context.Context, ch chan<- *queryUpdate, p peer.ID newPeers, err := q.queryFn(queryCtx, p) if err != nil { if queryCtx.Err() == nil { - q.dht.peerStoppedDHT(q.dht.ctx, p) + q.dht.peerStoppedDHT(p) } ch <- &queryUpdate{cause: p, unreachable: []peer.ID{p}} return @@ -417,7 +446,7 @@ func (q *query) queryPeer(ctx context.Context, ch chan<- *queryUpdate, p peer.ID queryDuration := time.Since(startQuery) // query successful, try to add to RT - q.dht.peerFound(q.dht.ctx, p, true) + q.dht.validPeerFound(p) // process new peers saw := []peer.ID{} @@ -497,6 +526,9 @@ func (q *query) updateState(ctx context.Context, up *queryUpdate) { } func (dht *IpfsDHT) dialPeer(ctx context.Context, p peer.ID) error { + ctx, span := internal.StartSpan(ctx, "IpfsDHT.DialPeer", trace.WithAttributes(attribute.String("PeerID", p.String()))) + defer span.End() + // short-circuit if we're already connected. if dht.host.Network().Connectedness(p) == network.Connected { return nil diff --git a/query_test.go b/query_test.go index e79af514f..91669641a 100644 --- a/query_test.go +++ b/query_test.go @@ -34,7 +34,7 @@ func TestRTEvictionOnFailedQuery(t *testing.T) { // peers should be in the RT because of fixLowPeers require.NoError(t, tu.WaitFor(ctx, func() error { if !checkRoutingTable(d1, d2) { - return fmt.Errorf("should have routes") + return fmt.Errorf("should have routes") } return nil })) @@ -45,7 +45,7 @@ func TestRTEvictionOnFailedQuery(t *testing.T) { // peers will still be in the RT because we have decoupled membership from connectivity require.NoError(t, tu.WaitFor(ctx, func() error { if !checkRoutingTable(d1, d2) { - return fmt.Errorf("should have routes") + return fmt.Errorf("should have routes") } return nil })) diff --git a/records.go b/records.go index 23d6ee7da..271425d25 100644 --- a/records.go +++ b/records.go @@ -4,10 +4,12 @@ import ( "context" "fmt" - "github.com/libp2p/go-libp2p-core/peer" - "github.com/libp2p/go-libp2p-core/routing" - - ci "github.com/libp2p/go-libp2p-core/crypto" + "github.com/libp2p/go-libp2p-kad-dht/internal" + ci "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/routing" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" ) type pubkrs struct { @@ -18,6 +20,9 @@ type pubkrs struct { // GetPublicKey gets the public key when given a Peer ID. It will extract from // the Peer ID if inlined or ask the node it belongs to or ask the DHT. func (dht *IpfsDHT) GetPublicKey(ctx context.Context, p peer.ID) (ci.PubKey, error) { + ctx, span := internal.StartSpan(ctx, "IpfsDHT.GetPublicKey", trace.WithAttributes(attribute.Stringer("PeerID", p))) + defer span.End() + if !dht.enableValues { return nil, routing.ErrNotSupported } diff --git a/records_test.go b/records_test.go index 8e1ebb747..69603b0b4 100644 --- a/records_test.go +++ b/records_test.go @@ -7,14 +7,15 @@ import ( "testing" "time" - "github.com/libp2p/go-libp2p-core/test" + "github.com/libp2p/go-libp2p-kad-dht/internal" + "github.com/libp2p/go-libp2p/core/test" - u "github.com/ipfs/go-ipfs-util" - ci "github.com/libp2p/go-libp2p-core/crypto" - "github.com/libp2p/go-libp2p-core/peer" - "github.com/libp2p/go-libp2p-core/routing" + "github.com/ipfs/go-test/random" record "github.com/libp2p/go-libp2p-record" tnet "github.com/libp2p/go-libp2p-testing/net" + ci "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/routing" ) // Check that GetPublicKey() correctly extracts a public key @@ -151,7 +152,7 @@ func TestPubkeyNotFound(t *testing.T) { connect(t, ctx, dhtA, dhtB) - r := u.NewSeededRand(15) // generate deterministic keypair + r := random.NewSeededRand(15) // generate deterministic keypair _, pubk, err := ci.GenerateKeyPairWithReader(ci.RSA, 2048, r) if err != nil { t.Fatal(err) @@ -204,7 +205,7 @@ func TestPubkeyBadKeyFromDHT(t *testing.T) { // Store incorrect public key on node B rec := record.MakePutRecord(pkkey, wrongbytes) - rec.TimeReceived = u.FormatRFC3339(time.Now()) + rec.TimeReceived = internal.FormatRFC3339(time.Now()) err = dhtB.putLocal(ctx, pkkey, rec) if err != nil { t.Fatal(err) @@ -243,7 +244,7 @@ func TestPubkeyBadKeyFromDHTGoodKeyDirect(t *testing.T) { // Store incorrect public key on node B rec := record.MakePutRecord(pkkey, wrongbytes) - rec.TimeReceived = u.FormatRFC3339(time.Now()) + rec.TimeReceived = internal.FormatRFC3339(time.Now()) err = dhtB.putLocal(ctx, pkkey, rec) if err != nil { t.Fatal(err) @@ -316,9 +317,7 @@ func TestValuesDisabled(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - var ( - optsA, optsB []Option - ) + var optsA, optsB []Option optsA = append(optsA, ProtocolPrefix("/valuesMaybeDisabled")) optsB = append(optsB, ProtocolPrefix("/valuesMaybeDisabled")) diff --git a/routing.go b/routing.go index c6df5e224..2753d7087 100644 --- a/routing.go +++ b/routing.go @@ -3,19 +3,21 @@ package dht import ( "bytes" "context" + "errors" "fmt" "sync" "time" - "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" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/peerstore" + "github.com/libp2p/go-libp2p/core/routing" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" "github.com/ipfs/go-cid" - u "github.com/ipfs/go-ipfs-util" "github.com/libp2p/go-libp2p-kad-dht/internal" internalConfig "github.com/libp2p/go-libp2p-kad-dht/internal/config" + "github.com/libp2p/go-libp2p-kad-dht/netsize" "github.com/libp2p/go-libp2p-kad-dht/qpeerset" kb "github.com/libp2p/go-libp2p-kbucket" record "github.com/libp2p/go-libp2p-record" @@ -29,6 +31,9 @@ import ( // PutValue adds value corresponding to given Key. // This is the top level "Store" operation of the DHT func (dht *IpfsDHT) PutValue(ctx context.Context, key string, value []byte, opts ...routing.Option) (err error) { + ctx, end := tracer.PutValue(dhtName, ctx, key, value, opts...) + defer func() { end(err) }() + if !dht.enableValues { return routing.ErrNotSupported } @@ -59,7 +64,7 @@ func (dht *IpfsDHT) PutValue(ctx context.Context, key string, value []byte, opts } rec := record.MakePutRecord(key, value) - rec.TimeReceived = u.FormatRFC3339(time.Now()) + rec.TimeReceived = internal.FormatRFC3339(time.Now()) err = dht.putLocal(ctx, key, rec) if err != nil { return err @@ -100,7 +105,10 @@ type recvdVal struct { } // GetValue searches for the value corresponding to given Key. -func (dht *IpfsDHT) GetValue(ctx context.Context, key string, opts ...routing.Option) (_ []byte, err error) { +func (dht *IpfsDHT) GetValue(ctx context.Context, key string, opts ...routing.Option) (result []byte, err error) { + ctx, end := tracer.GetValue(dhtName, ctx, key, opts...) + defer func() { end(result, err) }() + if !dht.enableValues { return nil, routing.ErrNotSupported } @@ -134,7 +142,10 @@ func (dht *IpfsDHT) GetValue(ctx context.Context, key string, opts ...routing.Op } // SearchValue searches for the value corresponding to given Key and streams the results. -func (dht *IpfsDHT) SearchValue(ctx context.Context, key string, opts ...routing.Option) (<-chan []byte, error) { +func (dht *IpfsDHT) SearchValue(ctx context.Context, key string, opts ...routing.Option) (ch <-chan []byte, err error) { + ctx, end := tracer.SearchValue(dhtName, ctx, key, opts...) + defer func() { ch, err = end(ch, err) }() + if !dht.enableValues { return nil, routing.ErrNotSupported } @@ -183,7 +194,8 @@ func (dht *IpfsDHT) SearchValue(ctx context.Context, key string, opts ...routing } func (dht *IpfsDHT) searchValueQuorum(ctx context.Context, key string, valCh <-chan recvdVal, stopCh chan struct{}, - out chan<- []byte, nvals int) ([]byte, map[peer.ID]struct{}, bool) { + out chan<- []byte, nvals int, +) ([]byte, map[peer.ID]struct{}, bool) { numResponses := 0 return dht.processValues(ctx, key, valCh, func(ctx context.Context, v recvdVal, better bool) bool { @@ -205,7 +217,8 @@ func (dht *IpfsDHT) searchValueQuorum(ctx context.Context, key string, valCh <-c } func (dht *IpfsDHT) processValues(ctx context.Context, key string, vals <-chan recvdVal, - newVal func(ctx context.Context, v recvdVal, better bool) bool) (best []byte, peersWithBest map[peer.ID]struct{}, aborted bool) { + newVal func(ctx context.Context, v recvdVal, better bool) bool, +) (best []byte, peersWithBest map[peer.ID]struct{}, aborted bool) { loop: for { if aborted { @@ -251,7 +264,7 @@ func (dht *IpfsDHT) updatePeerValues(ctx context.Context, key string, val []byte fixupRec := record.MakePutRecord(key, val) for _, p := range peers { go func(p peer.ID) { - //TODO: Is this possible? + // TODO: Is this possible? if p == dht.self { err := dht.putLocal(ctx, key, fixupRec) if err != nil { @@ -298,6 +311,7 @@ func (dht *IpfsDHT) getValues(ctx context.Context, key string, stopQuery chan st rec, peers, err := dht.protoMessenger.GetValue(ctx, p, key) if err != nil { + logger.Debugf("error getting closer peers: %s", err) return nil, err } @@ -335,7 +349,7 @@ func (dht *IpfsDHT) getValues(ctx context.Context, key string, stopQuery chan st return peers, nil }, - func() bool { + func(*qpeerset.QueryPeerset) bool { select { case <-stopQuery: return true @@ -344,7 +358,6 @@ func (dht *IpfsDHT) getValues(ctx context.Context, key string, stopQuery chan st } }, ) - if err != nil { return } @@ -371,6 +384,9 @@ func (dht *IpfsDHT) refreshRTIfNoShortcut(key kb.ID, lookupRes *lookupWithFollow // Provide makes this node announce that it can provide a value for the given key func (dht *IpfsDHT) Provide(ctx context.Context, key cid.Cid, brdcst bool) (err error) { + ctx, end := tracer.Provide(dhtName, ctx, key, brdcst) + defer func() { end(err) }() + if !dht.enableProviders { return routing.ErrNotSupported } else if !key.Defined() { @@ -385,6 +401,18 @@ func (dht *IpfsDHT) Provide(ctx context.Context, key cid.Cid, brdcst bool) (err return nil } + if dht.enableOptProv { + err := dht.optimisticProvide(ctx, keyMH) + if errors.Is(err, netsize.ErrNotEnoughData) { + logger.Debugln("not enough data for optimistic provide taking classic approach") + return dht.classicProvide(ctx, keyMH) + } + return err + } + return dht.classicProvide(ctx, keyMH) +} + +func (dht *IpfsDHT) classicProvide(ctx context.Context, keyMH multihash.Multihash) error { closerCtx := ctx if deadline, ok := ctx.Deadline(); ok { now := time.Now() @@ -428,7 +456,10 @@ func (dht *IpfsDHT) Provide(ctx context.Context, key cid.Cid, brdcst bool) (err go func(p peer.ID) { defer wg.Done() logger.Debugf("putProvider(%s, %s)", internal.LoggableProviderRecordBytes(keyMH), p) - err := dht.protoMessenger.PutProvider(ctx, p, keyMH, dht.host) + err := dht.protoMessenger.PutProviderAddrs(ctx, p, keyMH, peer.AddrInfo{ + ID: dht.self, + Addrs: dht.filterAddrs(dht.host.Addrs()), + }) if err != nil { logger.Debug(err) } @@ -461,18 +492,17 @@ func (dht *IpfsDHT) FindProviders(ctx context.Context, c cid.Cid) ([]peer.AddrIn // the search query completes. If count is zero then the query will run until it // completes. Note: not reading from the returned channel may block the query // from progressing. -func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key cid.Cid, count int) <-chan peer.AddrInfo { +func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key cid.Cid, count int) (ch <-chan peer.AddrInfo) { + ctx, end := tracer.FindProvidersAsync(dhtName, ctx, key, count) + defer func() { ch = end(ch, nil) }() + if !dht.enableProviders || !key.Defined() { peerOut := make(chan peer.AddrInfo) close(peerOut) return peerOut } - chSize := count - if count == 0 { - chSize = 1 - } - peerOut := make(chan peer.AddrInfo, chSize) + peerOut := make(chan peer.AddrInfo) keyMH := key.Hash() @@ -482,14 +512,30 @@ func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key cid.Cid, count i } func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key multihash.Multihash, count int, peerOut chan peer.AddrInfo) { + // use a span here because unlike tracer.FindProvidersAsync we know who told us about it and that intresting to log. + ctx, span := internal.StartSpan(ctx, "IpfsDHT.FindProvidersAsyncRoutine") + defer span.End() + defer close(peerOut) findAll := count == 0 - var ps *peer.Set - if findAll { - ps = peer.NewSet() - } else { - ps = peer.NewLimitedSet(count) + + ps := make(map[peer.ID]peer.AddrInfo) + psLock := &sync.Mutex{} + psTryAdd := func(p peer.AddrInfo) bool { + psLock.Lock() + defer psLock.Unlock() + pi, ok := ps[p.ID] + if (!ok || ((len(pi.Addrs) == 0) && len(p.Addrs) > 0)) && (len(ps) < count || findAll) { + ps[p.ID] = p + return true + } + return false + } + psSize := func() int { + psLock.Lock() + defer psLock.Unlock() + return len(ps) } provs, err := dht.providerStore.GetProviders(ctx, key) @@ -498,9 +544,16 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key multihash } for _, p := range provs { // NOTE: Assuming that this list of peers is unique - if ps.TryAdd(p.ID) { + if psTryAdd(p) { select { case peerOut <- p: + // Add tracing event for finding a provider + span.AddEvent("found provider", trace.WithAttributes( + attribute.Stringer("peer", p.ID), + attribute.Stringer("from", dht.self), + attribute.Int("provider_addrs_count", len(p.Addrs)), + attribute.Bool("found_in_provider_store", true), + )) case <-ctx.Done(): return } @@ -508,7 +561,7 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key multihash // If we have enough peers locally, don't bother with remote RPC // TODO: is this a DOS vector? - if !findAll && ps.Size() >= count { + if !findAll && len(ps) >= count { return } } @@ -532,17 +585,22 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key multihash for _, prov := range provs { dht.maybeAddAddrs(prov.ID, prov.Addrs, peerstore.TempAddrTTL) logger.Debugf("got provider: %s", prov) - if ps.TryAdd(prov.ID) { + if psTryAdd(*prov) { logger.Debugf("using provider: %s", prov) select { case peerOut <- *prov: + span.AddEvent("found provider", trace.WithAttributes( + attribute.Stringer("peer", prov.ID), + attribute.Stringer("from", p), + attribute.Int("provider_addrs_count", len(prov.Addrs)), + )) case <-ctx.Done(): logger.Debug("context timed out sending more providers") return nil, ctx.Err() } } - if !findAll && ps.Size() >= count { - logger.Debugf("got enough providers (%d/%d)", ps.Size(), count) + if !findAll && psSize() >= count { + logger.Debugf("got enough providers (%d/%d)", psSize(), count) return nil, nil } } @@ -558,8 +616,8 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key multihash return closest, nil }, - func() bool { - return !findAll && ps.Size() >= count + func(*qpeerset.QueryPeerset) bool { + return !findAll && psSize() >= count }, ) @@ -569,7 +627,10 @@ func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key multihash } // FindPeer searches for a peer with given ID. -func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (_ peer.AddrInfo, err error) { +func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (pi peer.AddrInfo, err error) { + ctx, end := tracer.FindPeer(dhtName, ctx, id) + defer func() { end(pi, err) }() + if err := id.Validate(); err != nil { return peer.AddrInfo{}, err } @@ -577,7 +638,7 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (_ peer.AddrInfo, logger.Debugw("finding peer", "peer", id) // Check if were already connected to them - if pi := dht.FindLocal(id); pi.ID != "" { + if pi := dht.FindLocal(ctx, id); pi.ID != "" { return pi, nil } @@ -604,11 +665,10 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (_ peer.AddrInfo, return peers, err }, - func() bool { - return dht.host.Network().Connectedness(id) == network.Connected + func(*qpeerset.QueryPeerset) bool { + return hasValidConnectedness(dht.host, id) }, ) - if err != nil { return peer.AddrInfo{}, err } @@ -626,8 +686,7 @@ func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (_ peer.AddrInfo, // Return peer information if we tried to dial the peer during the query or we are (or recently were) connected // to the peer. - connectedness := dht.host.Network().Connectedness(id) - if dialedPeerDuringQuery || connectedness == network.Connected || connectedness == network.CanConnect { + if dialedPeerDuringQuery || hasValidConnectedness(dht.host, id) { return dht.peerstore.PeerInfo(id), nil } diff --git a/routing_options.go b/routing_options.go index 7352c098b..bccc6d62e 100644 --- a/routing_options.go +++ b/routing_options.go @@ -1,8 +1,8 @@ package dht import ( - "github.com/libp2p/go-libp2p-core/routing" internalConfig "github.com/libp2p/go-libp2p-kad-dht/internal/config" + "github.com/libp2p/go-libp2p/core/routing" ) // Quorum is a DHT option that tells the DHT how many peers it needs to get diff --git a/rt_diversity_filter.go b/rt_diversity_filter.go index 06c3116e6..45aedd221 100644 --- a/rt_diversity_filter.go +++ b/rt_diversity_filter.go @@ -3,8 +3,8 @@ package dht import ( "sync" - "github.com/libp2p/go-libp2p-core/host" - "github.com/libp2p/go-libp2p-core/peer" + "github.com/libp2p/go-libp2p/core/host" + "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p-kbucket/peerdiversity" diff --git a/rt_diversity_filter_test.go b/rt_diversity_filter_test.go index 8caf14004..9faad6363 100644 --- a/rt_diversity_filter_test.go +++ b/rt_diversity_filter_test.go @@ -7,8 +7,8 @@ import ( kb "github.com/libp2p/go-libp2p-kbucket" "github.com/libp2p/go-libp2p-kbucket/peerdiversity" - swarmt "github.com/libp2p/go-libp2p-swarm/testing" bhost "github.com/libp2p/go-libp2p/p2p/host/basic" + swarmt "github.com/libp2p/go-libp2p/p2p/net/swarm/testing" "github.com/stretchr/testify/require" ) @@ -16,6 +16,7 @@ import ( func TestRTPeerDiversityFilter(t *testing.T) { h, err := bhost.NewHost(swarmt.GenSwarm(t, swarmt.OptDisableReuseport), new(bhost.HostOpts)) require.NoError(t, err) + h.Start() defer h.Close() r := NewRTPeerDiversityFilter(h, 2, 3) @@ -57,6 +58,7 @@ func TestRoutingTableEndToEndMaxPerCpl(t *testing.T) { ctx := context.Background() h, err := bhost.NewHost(swarmt.GenSwarm(t, swarmt.OptDisableReuseport), new(bhost.HostOpts)) require.NoError(t, err) + h.Start() defer h.Close() r := NewRTPeerDiversityFilter(h, 1, 2) @@ -116,6 +118,7 @@ func TestRoutingTableEndToEndMaxPerTable(t *testing.T) { h, err := bhost.NewHost(swarmt.GenSwarm(t, swarmt.OptDisableReuseport), new(bhost.HostOpts)) require.NoError(t, err) + h.Start() defer h.Close() r := NewRTPeerDiversityFilter(h, 100, 3) diff --git a/rtrefresh/rt_refresh_manager.go b/rtrefresh/rt_refresh_manager.go index 1dc9b7e7e..c81e9e6ed 100644 --- a/rtrefresh/rt_refresh_manager.go +++ b/rtrefresh/rt_refresh_manager.go @@ -4,16 +4,19 @@ import ( "context" "fmt" "sync" + "sync/atomic" "time" - "github.com/libp2p/go-libp2p-core/host" - "github.com/libp2p/go-libp2p-core/peer" - - kbucket "github.com/libp2p/go-libp2p-kbucket" - "github.com/hashicorp/go-multierror" - logging "github.com/ipfs/go-log" + logging "github.com/ipfs/go-log/v2" + "github.com/libp2p/go-libp2p-kad-dht/internal" + kbucket "github.com/libp2p/go-libp2p-kbucket" + "github.com/libp2p/go-libp2p/core/host" + "github.com/libp2p/go-libp2p/core/peer" "github.com/multiformats/go-base32" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/trace" ) var logger = logging.Logger("dht/RtRefreshManager") @@ -28,10 +31,9 @@ type triggerRefreshReq struct { } type RtRefreshManager struct { - ctx context.Context - cancel context.CancelFunc - refcount sync.WaitGroup - closeOnce sync.Once + ctx context.Context + cancel context.CancelFunc + refcount sync.WaitGroup // peerId of this DHT peer i.e. self peerId. h host.Host @@ -41,6 +43,7 @@ type RtRefreshManager struct { enableAutoRefresh bool // should run periodic refreshes ? refreshKeyGenFnc func(cpl uint) (string, error) // generate the key for the query to refresh this cpl refreshQueryFnc func(ctx context.Context, key string) error // query to run for a refresh. + refreshPingFnc func(ctx context.Context, p peer.ID) error // request to check liveness of remote peer refreshQueryTimeout time.Duration // timeout for one refresh query // interval between two periodic refreshes. @@ -57,6 +60,7 @@ type RtRefreshManager struct { func NewRtRefreshManager(h host.Host, rt *kbucket.RoutingTable, autoRefresh bool, refreshKeyGenFnc func(cpl uint) (string, error), refreshQueryFnc func(ctx context.Context, key string) error, + refreshPingFnc func(ctx context.Context, p peer.ID) error, refreshQueryTimeout time.Duration, refreshInterval time.Duration, successfulOutboundQueryGracePeriod time.Duration, @@ -73,6 +77,7 @@ func NewRtRefreshManager(h host.Host, rt *kbucket.RoutingTable, autoRefresh bool enableAutoRefresh: autoRefresh, refreshKeyGenFnc: refreshKeyGenFnc, refreshQueryFnc: refreshQueryFnc, + refreshPingFnc: refreshPingFnc, refreshQueryTimeout: refreshQueryTimeout, refreshInterval: refreshInterval, @@ -83,17 +88,14 @@ func NewRtRefreshManager(h host.Host, rt *kbucket.RoutingTable, autoRefresh bool }, nil } -func (r *RtRefreshManager) Start() error { +func (r *RtRefreshManager) Start() { r.refcount.Add(1) go r.loop() - return nil } func (r *RtRefreshManager) Close() error { - r.closeOnce.Do(func() { - r.cancel() - r.refcount.Wait() - }) + r.cancel() + r.refcount.Wait() return nil } @@ -111,6 +113,7 @@ func (r *RtRefreshManager) Refresh(force bool) <-chan error { case r.triggerRefresh <- &triggerRefreshReq{respCh: resp, forceCplRefresh: force}: case <-r.ctx.Done(): resp <- r.ctx.Err() + close(resp) } }() @@ -126,12 +129,60 @@ func (r *RtRefreshManager) RefreshNoWait() { } } +// pingAndEvictPeers pings Routing Table peers that haven't been heard of/from +// in the interval they should have been and evict them if they don't reply. +func (r *RtRefreshManager) pingAndEvictPeers(ctx context.Context) { + ctx, span := internal.StartSpan(ctx, "RefreshManager.PingAndEvictPeers") + defer span.End() + + var peersChecked int + var alive int64 + var wg sync.WaitGroup + peers := r.rt.GetPeerInfos() + for _, ps := range peers { + if time.Since(ps.LastSuccessfulOutboundQueryAt) <= r.successfulOutboundQueryGracePeriod { + continue + } + + peersChecked++ + wg.Add(1) + go func(ps kbucket.PeerInfo) { + defer wg.Done() + + livelinessCtx, cancel := context.WithTimeout(ctx, peerPingTimeout) + defer cancel() + peerIdStr := ps.Id.String() + livelinessCtx, span := internal.StartSpan(livelinessCtx, "RefreshManager.PingAndEvictPeers.worker", trace.WithAttributes(attribute.String("peer", peerIdStr))) + defer span.End() + + if err := r.h.Connect(livelinessCtx, peer.AddrInfo{ID: ps.Id}); err != nil { + logger.Debugw("evicting peer after failed connection", "peer", peerIdStr, "error", err) + span.RecordError(err) + r.rt.RemovePeer(ps.Id) + return + } + + if err := r.refreshPingFnc(livelinessCtx, ps.Id); err != nil { + logger.Debugw("evicting peer after failed ping", "peer", peerIdStr, "error", err) + span.RecordError(err) + r.rt.RemovePeer(ps.Id) + return + } + + atomic.AddInt64(&alive, 1) + }(ps) + } + wg.Wait() + + span.SetAttributes(attribute.Int("NumPeersChecked", peersChecked), attribute.Int("NumPeersSkipped", len(peers)-peersChecked), attribute.Int64("NumPeersAlive", alive)) +} + func (r *RtRefreshManager) loop() { defer r.refcount.Done() var refreshTickrCh <-chan time.Time if r.enableAutoRefresh { - err := r.doRefresh(true) + err := r.doRefresh(r.ctx, true) if err != nil { logger.Warn("failed when refreshing routing table", err) } @@ -168,29 +219,12 @@ func (r *RtRefreshManager) loop() { } } - // EXECUTE the refresh - - // ping Routing Table peers that haven't been heard of/from in the interval they should have been. - // and evict them if they don't reply. - var wg sync.WaitGroup - for _, ps := range r.rt.GetPeerInfos() { - if time.Since(ps.LastSuccessfulOutboundQueryAt) > r.successfulOutboundQueryGracePeriod { - wg.Add(1) - go func(ps kbucket.PeerInfo) { - defer wg.Done() - livelinessCtx, cancel := context.WithTimeout(r.ctx, peerPingTimeout) - if err := r.h.Connect(livelinessCtx, peer.AddrInfo{ID: ps.Id}); err != nil { - logger.Debugw("evicting peer after failed ping", "peer", ps.Id, "error", err) - r.rt.RemovePeer(ps.Id) - } - cancel() - }(ps) - } - } - wg.Wait() + ctx, span := internal.StartSpan(r.ctx, "RefreshManager.Refresh") + + r.pingAndEvictPeers(ctx) // Query for self and refresh the required buckets - err := r.doRefresh(forced) + err := r.doRefresh(ctx, forced) for _, w := range waiting { w <- err close(w) @@ -198,13 +232,18 @@ func (r *RtRefreshManager) loop() { if err != nil { logger.Warnw("failed when refreshing routing table", "error", err) } + + span.End() } } -func (r *RtRefreshManager) doRefresh(forceRefresh bool) error { +func (r *RtRefreshManager) doRefresh(ctx context.Context, forceRefresh bool) error { + ctx, span := internal.StartSpan(ctx, "RefreshManager.doRefresh") + defer span.End() + var merr error - if err := r.queryForSelf(); err != nil { + if err := r.queryForSelf(ctx); err != nil { merr = multierror.Append(merr, err) } @@ -212,9 +251,9 @@ func (r *RtRefreshManager) doRefresh(forceRefresh bool) error { rfnc := func(cpl uint) (err error) { if forceRefresh { - err = r.refreshCpl(cpl) + err = r.refreshCpl(ctx, cpl) } else { - err = r.refreshCplIfEligible(cpl, refreshCpls[cpl]) + err = r.refreshCplIfEligible(ctx, cpl, refreshCpls[cpl]) } return } @@ -245,8 +284,8 @@ func (r *RtRefreshManager) doRefresh(forceRefresh bool) error { select { case r.refreshDoneCh <- struct{}{}: - case <-r.ctx.Done(): - return r.ctx.Err() + case <-ctx.Done(): + return ctx.Err() } return merr @@ -260,42 +299,53 @@ func min(a int, b int) int { return b } -func (r *RtRefreshManager) refreshCplIfEligible(cpl uint, lastRefreshedAt time.Time) error { +func (r *RtRefreshManager) refreshCplIfEligible(ctx context.Context, cpl uint, lastRefreshedAt time.Time) error { if time.Since(lastRefreshedAt) <= r.refreshInterval { logger.Debugf("not running refresh for cpl %d as time since last refresh not above interval", cpl) return nil } - return r.refreshCpl(cpl) + return r.refreshCpl(ctx, cpl) } -func (r *RtRefreshManager) refreshCpl(cpl uint) error { +func (r *RtRefreshManager) refreshCpl(ctx context.Context, cpl uint) error { + ctx, span := internal.StartSpan(ctx, "RefreshManager.refreshCpl", trace.WithAttributes(attribute.Int("cpl", int(cpl)))) + defer span.End() + // gen a key for the query to refresh the cpl key, err := r.refreshKeyGenFnc(cpl) if err != nil { + span.SetStatus(codes.Error, err.Error()) return fmt.Errorf("failed to generated query key for cpl=%d, err=%s", cpl, err) } logger.Infof("starting refreshing cpl %d with key %s (routing table size was %d)", cpl, loggableRawKeyString(key), r.rt.Size()) - if err := r.runRefreshDHTQuery(key); err != nil { + if err := r.runRefreshDHTQuery(ctx, key); err != nil { + span.SetStatus(codes.Error, err.Error()) return fmt.Errorf("failed to refresh cpl=%d, err=%s", cpl, err) } - logger.Infof("finished refreshing cpl %d, routing table size is now %d", cpl, r.rt.Size()) + sz := r.rt.Size() + logger.Infof("finished refreshing cpl %d, routing table size is now %d", cpl, sz) + span.SetAttributes(attribute.Int("NewSize", sz)) return nil } -func (r *RtRefreshManager) queryForSelf() error { - if err := r.runRefreshDHTQuery(string(r.dhtPeerId)); err != nil { +func (r *RtRefreshManager) queryForSelf(ctx context.Context) error { + ctx, span := internal.StartSpan(ctx, "RefreshManager.queryForSelf") + defer span.End() + + if err := r.runRefreshDHTQuery(ctx, string(r.dhtPeerId)); err != nil { + span.SetStatus(codes.Error, err.Error()) return fmt.Errorf("failed to query for self, err=%s", err) } return nil } -func (r *RtRefreshManager) runRefreshDHTQuery(key string) error { - queryCtx, cancel := context.WithTimeout(r.ctx, r.refreshQueryTimeout) +func (r *RtRefreshManager) runRefreshDHTQuery(ctx context.Context, key string) error { + queryCtx, cancel := context.WithTimeout(ctx, r.refreshQueryTimeout) defer cancel() err := r.refreshQueryFnc(queryCtx, key) diff --git a/rtrefresh/rt_refresh_manager_test.go b/rtrefresh/rt_refresh_manager_test.go index 7bfc14351..994e0d1ac 100644 --- a/rtrefresh/rt_refresh_manager_test.go +++ b/rtrefresh/rt_refresh_manager_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" - "github.com/libp2p/go-libp2p-core/test" + "github.com/libp2p/go-libp2p/core/test" kb "github.com/libp2p/go-libp2p-kbucket" - pstore "github.com/libp2p/go-libp2p-peerstore" + pstore "github.com/libp2p/go-libp2p/p2p/host/peerstore" "github.com/stretchr/testify/require" ) @@ -65,7 +65,7 @@ func TestSkipRefreshOnGapCpls(t *testing.T) { b, _ := rt.TryAddPeer(p, true, false) require.True(t, b) r.refreshQueryFnc = qFuncWithIgnore(rt, icpl) - require.NoError(t, r.doRefresh(true)) + require.NoError(t, r.doRefresh(ctx, true)) for i := uint(0); i < lastCpl+1; i++ { if i == icpl { @@ -88,7 +88,7 @@ func TestSkipRefreshOnGapCpls(t *testing.T) { b, _ = rt.TryAddPeer(p, true, false) require.True(t, b) r.refreshQueryFnc = qFuncWithIgnore(rt, icpl) - require.NoError(t, r.doRefresh(true)) + require.NoError(t, r.doRefresh(ctx, true)) for i := uint(0); i < 10; i++ { if i == icpl { diff --git a/subscriber_notifee.go b/subscriber_notifee.go index 00ff4ba03..c1eb69387 100644 --- a/subscriber_notifee.go +++ b/subscriber_notifee.go @@ -1,28 +1,15 @@ package dht import ( - "context" "fmt" - "github.com/libp2p/go-libp2p-core/event" - "github.com/libp2p/go-libp2p-core/network" - "github.com/libp2p/go-libp2p-core/peer" - - "github.com/libp2p/go-eventbus" - - "github.com/jbenet/goprocess" - ma "github.com/multiformats/go-multiaddr" + "github.com/libp2p/go-libp2p/core/event" + "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/p2p/host/eventbus" ) -// subscriberNotifee implements network.Notifee and also manages the subscriber to the event bus. We consume peer -// identification events to trigger inclusion in the routing table, and we consume Disconnected events to eject peers -// from it. -type subscriberNotifee struct { - dht *IpfsDHT - subs event.Subscription -} - -func newSubscriberNotifiee(dht *IpfsDHT) (*subscriberNotifee, error) { +func (dht *IpfsDHT) startNetworkSubscriber() error { bufSize := eventbus.BufSize(256) evts := []interface{}{ @@ -36,6 +23,9 @@ func newSubscriberNotifiee(dht *IpfsDHT) (*subscriberNotifee, error) { // register for event bus notifications for when our local address/addresses change so we can // advertise those to the network new(event.EvtLocalAddressesUpdated), + + // we want to know when we are disconnecting from other peers. + new(event.EvtPeerConnectednessChanged), } // register for event bus local routability changes in order to trigger switching between client and server modes @@ -46,61 +36,57 @@ func newSubscriberNotifiee(dht *IpfsDHT) (*subscriberNotifee, error) { subs, err := dht.host.EventBus().Subscribe(evts, bufSize) if err != nil { - return nil, fmt.Errorf("dht could not subscribe to eventbus events; err: %s", err) + return fmt.Errorf("dht could not subscribe to eventbus events: %w", err) } - nn := &subscriberNotifee{ - dht: dht, - subs: subs, - } - - // register for network notifications - dht.host.Network().Notify(nn) - - return nn, nil -} + dht.wg.Add(1) + go func() { + defer dht.wg.Done() + defer subs.Close() -func (nn *subscriberNotifee) subscribe(proc goprocess.Process) { - dht := nn.dht - defer dht.host.Network().StopNotify(nn) - defer nn.subs.Close() - - for { - select { - case e, more := <-nn.subs.Out(): - if !more { - return - } - - switch evt := e.(type) { - case event.EvtLocalAddressesUpdated: - // when our address changes, we should proactively tell our closest peers about it so - // we become discoverable quickly. The Identify protocol will push a signed peer record - // with our new address to all peers we are connected to. However, we might not necessarily be connected - // to our closet peers & so in the true spirit of Zen, searching for ourself in the network really is the best way - // to to forge connections with those matter. - if dht.autoRefresh || dht.testAddressUpdateProcessing { - dht.rtRefreshManager.RefreshNoWait() + for { + select { + case e, more := <-subs.Out(): + if !more { + return } - case event.EvtPeerProtocolsUpdated: - handlePeerChangeEvent(dht, evt.Peer) - case event.EvtPeerIdentificationCompleted: - handlePeerChangeEvent(dht, evt.Peer) - case event.EvtLocalReachabilityChanged: - if dht.auto == ModeAuto || dht.auto == ModeAutoServer { - handleLocalReachabilityChangedEvent(dht, evt) - } else { - // something has gone really wrong if we get an event we did not subscribe to - logger.Errorf("received LocalReachabilityChanged event that was not subscribed to") + + switch evt := e.(type) { + case event.EvtLocalAddressesUpdated: + // when our address changes, we should proactively tell our closest peers about it so + // we become discoverable quickly. The Identify protocol will push a signed peer record + // with our new address to all peers we are connected to. However, we might not necessarily be connected + // to our closet peers & so in the true spirit of Zen, searching for ourself in the network really is the best way + // to to forge connections with those matter. + if dht.autoRefresh || dht.testAddressUpdateProcessing { + dht.rtRefreshManager.RefreshNoWait() + } + case event.EvtPeerProtocolsUpdated: + handlePeerChangeEvent(dht, evt.Peer) + case event.EvtPeerIdentificationCompleted: + handlePeerChangeEvent(dht, evt.Peer) + case event.EvtPeerConnectednessChanged: + if evt.Connectedness != network.Connected { + dht.msgSender.OnDisconnect(dht.ctx, evt.Peer) + } + case event.EvtLocalReachabilityChanged: + if dht.auto == ModeAuto || dht.auto == ModeAutoServer { + handleLocalReachabilityChangedEvent(dht, evt) + } else { + // something has gone really wrong if we get an event we did not subscribe to + logger.Errorf("received LocalReachabilityChanged event that was not subscribed to") + } + default: + // something has gone really wrong if we get an event for another type + logger.Errorf("got wrong type from subscription: %T", e) } - default: - // something has gone really wrong if we get an event for another type - logger.Errorf("got wrong type from subscription: %T", e) + case <-dht.ctx.Done(): + return } - case <-proc.Closing(): - return } - } + }() + + return nil } func handlePeerChangeEvent(dht *IpfsDHT, p peer.ID) { @@ -109,10 +95,9 @@ func handlePeerChangeEvent(dht *IpfsDHT, p peer.ID) { logger.Errorf("could not check peerstore for protocol support: err: %s", err) return } else if valid { - dht.peerFound(dht.ctx, p, false) - dht.fixRTIfNeeded() + dht.peerFound(p) } else { - dht.peerStoppedDHT(dht.ctx, p) + dht.peerStoppedDHT(p) } } @@ -147,48 +132,10 @@ func handleLocalReachabilityChangedEvent(dht *IpfsDHT, e event.EvtLocalReachabil // supporting the primary protocols, we do not want to add peers that are speaking obsolete secondary protocols to our // routing table func (dht *IpfsDHT) validRTPeer(p peer.ID) (bool, error) { - b, err := dht.peerstore.FirstSupportedProtocol(p, dht.protocolsStrs...) + b, err := dht.peerstore.FirstSupportedProtocol(p, dht.protocols...) if len(b) == 0 || err != nil { return false, err } return dht.routingTablePeerFilter == nil || dht.routingTablePeerFilter(dht, p), nil } - -type disconnector interface { - OnDisconnect(ctx context.Context, p peer.ID) -} - -func (nn *subscriberNotifee) Disconnected(n network.Network, v network.Conn) { - dht := nn.dht - - ms, ok := dht.msgSender.(disconnector) - if !ok { - return - } - - select { - case <-dht.Process().Closing(): - return - default: - } - - p := v.RemotePeer() - - // Lock and check to see if we're still connected. We lock to make sure - // we don't concurrently process a connect event. - dht.plk.Lock() - defer dht.plk.Unlock() - if dht.host.Network().Connectedness(p) == network.Connected { - // We're still connected. - return - } - - ms.OnDisconnect(dht.Context(), p) -} - -func (nn *subscriberNotifee) Connected(network.Network, network.Conn) {} -func (nn *subscriberNotifee) OpenedStream(network.Network, network.Stream) {} -func (nn *subscriberNotifee) ClosedStream(network.Network, network.Stream) {} -func (nn *subscriberNotifee) Listen(network.Network, ma.Multiaddr) {} -func (nn *subscriberNotifee) ListenClose(network.Network, ma.Multiaddr) {} diff --git a/version.json b/version.json index 7f358a70c..a99fc2b18 100644 --- a/version.json +++ b/version.json @@ -1,3 +1,3 @@ { - "version": "v0.15.0" + "version": "v0.29.0" }