From 0f1181d23218fe2d8263eb1eab9c415ff5ea57c0 Mon Sep 17 00:00:00 2001 From: galargh Date: Mon, 6 Mar 2023 12:30:18 +0000 Subject: [PATCH 01/26] chore: update version --- version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.go b/version.go index 1f0577fa3..71912f891 100644 --- a/version.go +++ b/version.go @@ -11,7 +11,7 @@ import ( var CurrentCommit string // CurrentVersionNumber is the current application's version literal -const CurrentVersionNumber = "0.19.0-dev" +const CurrentVersionNumber = "0.19.0-rc1" const ApiVersion = "/kubo/" + CurrentVersionNumber + "/" //nolint From adcee3e515b231632249d397cb2e866e06d6ddc4 Mon Sep 17 00:00:00 2001 From: Will Hawkins Date: Mon, 14 Nov 2022 14:17:04 +0000 Subject: [PATCH 02/26] fix: typo in documentation for install path By default, `go install` will install go $GOBIN and not $GOPATH/bin. In most cases there is no functional difference -- `go install` falls back to $GOPATH/bin when $GOBIN is empty. --- Rules.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rules.mk b/Rules.mk index f7e962549..3d3d9c139 100644 --- a/Rules.mk +++ b/Rules.mk @@ -118,7 +118,7 @@ help: @echo ' all - print this help message' @echo ' build - Build binary at ./cmd/ipfs/ipfs' @echo ' nofuse - Build binary with no fuse support' - @echo ' install - Build binary and install into $$GOPATH/bin' + @echo ' install - Build binary and install into $$GOBIN' # @echo ' dist_install - TODO: c.f. ./cmd/ipfs/dist/README.md' @echo '' @echo 'CLEANING TARGETS:' From c30ab94c53eed391a841f8a1e6f83e22c8fcc655 Mon Sep 17 00:00:00 2001 From: Piotr Galar Date: Mon, 6 Mar 2023 20:03:19 +0100 Subject: [PATCH 03/26] Merge pull request #9689 from ipfs/no-testground ci: remove disabled testground workflow --- .github/workflows/testground-on-push.yml | 38 ------------------------ 1 file changed, 38 deletions(-) delete mode 100644 .github/workflows/testground-on-push.yml diff --git a/.github/workflows/testground-on-push.yml b/.github/workflows/testground-on-push.yml deleted file mode 100644 index 7b55dbdee..000000000 --- a/.github/workflows/testground-on-push.yml +++ /dev/null @@ -1,38 +0,0 @@ ---- -name: Testground PR Checker - -on: - workflow_dispatch: - push: - - -jobs: - testground: - if: github.repository == 'ipfs/kubo' || github.event_name == 'workflow_dispatch' - runs-on: ubuntu-latest - name: ${{ matrix.composition_file }} - strategy: - matrix: - include: - - backend_addr: ci.testground.ipfs.team - backend_proto: https - plan_directory: testplans/bitswap - composition_file: testplans/bitswap/_compositions/small-k8s.toml - - backend_addr: ci.testground.ipfs.team - backend_proto: https - plan_directory: testplans/bitswap - composition_file: testplans/bitswap/_compositions/medium-k8s.toml - - backend_addr: ci.testground.ipfs.team - backend_proto: https - plan_directory: testplans/bitswap - composition_file: testplans/bitswap/_compositions/large-k8s.toml - steps: - - uses: actions/checkout@v2 - - name: testground run - uses: testground/testground-github-action@v1 - timeout-minutes: 5 - with: - backend_addr: ${{ matrix.backend_addr }} - backend_proto: ${{ matrix.backend_proto }} - plan_directory: ${{ matrix.plan_directory }} - composition_file: ${{ matrix.composition_file }} From ff019366f5c7335656e33c5dc985dd68bd21db61 Mon Sep 17 00:00:00 2001 From: Piotr Galar Date: Mon, 6 Mar 2023 20:19:34 +0100 Subject: [PATCH 04/26] Merge pull request #9699 from ipfs/early-testers docs: add bifrost to early testers --- docs/EARLY_TESTERS.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/EARLY_TESTERS.md b/docs/EARLY_TESTERS.md index fb2fedc2a..d0dd4a867 100644 --- a/docs/EARLY_TESTERS.md +++ b/docs/EARLY_TESTERS.md @@ -22,15 +22,16 @@ We will ask early testers to participate at two points in the process: ## Who has signed up? -- [ ] pacman.store (@RubenKelevra) -- [ ] Infura (@MichaelMure) -- [ ] Textile (@sanderpick) -- [ ] Pinata (@obo20) -- [ ] RTrade (@postables) -- [ ] Siderus (@koalalorenzo) - [ ] Charity Engine (@rytiss, @tristanolive) - [ ] Fission (@bmann) +- [ ] Infura (@MichaelMure) - [ ] OrbitDB (@aphelionz) +- [ ] pacman.store (@RubenKelevra) +- [ ] Pinata (@obo20) +- [ ] PL EngRes bifrost (@gmasgras) +- [ ] RTrade (@postables) +- [ ] Siderus (@koalalorenzo) +- [ ] Textile (@sanderpick) ## How to sign up? From 8fe7d779c1b977bebc20e549a8c5227f436673d6 Mon Sep 17 00:00:00 2001 From: Piotr Galar Date: Wed, 8 Mar 2023 10:19:28 +0100 Subject: [PATCH 05/26] Merge pull request #9696 from ipfs/galargh-patch-2 fix: t0116-gateway-cache.sh --- test/sharness/t0116-gateway-cache.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/sharness/t0116-gateway-cache.sh b/test/sharness/t0116-gateway-cache.sh index 508fc73c2..0cb1a94eb 100755 --- a/test/sharness/t0116-gateway-cache.sh +++ b/test/sharness/t0116-gateway-cache.sh @@ -204,7 +204,7 @@ test_expect_success "Prepare IPNS unixfs content path for testing" ' grep -E "< Etag: \"DirIndex-.+_CID-${ROOT3_CID}\"" curl_ipfs_dir_listing_output ' test_expect_success "GET /ipns/ dir response has special Etag for generated dir listing" ' - test_should_contain "< Etag: \"DirIndex" curl_ipfs_dir_listing_output && + test_should_contain "< Etag: \"DirIndex" curl_ipns_dir_listing_output && grep -E "< Etag: \"DirIndex-.+_CID-${ROOT3_CID}\"" curl_ipns_dir_listing_output ' From d339059a7fb44467b8d3bb92359236cf76538c91 Mon Sep 17 00:00:00 2001 From: Jorropo Date: Tue, 7 Mar 2023 19:28:52 +0100 Subject: [PATCH 06/26] chore: bump go-libipfs v0.6.1 This does nothing, just move from an untagged commit to a tagged commit but contain the same things. --- docs/examples/kubo-as-a-library/go.mod | 2 +- docs/examples/kubo-as-a-library/go.sum | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/examples/kubo-as-a-library/go.mod b/docs/examples/kubo-as-a-library/go.mod index 056aea334..1c19dd3eb 100644 --- a/docs/examples/kubo-as-a-library/go.mod +++ b/docs/examples/kubo-as-a-library/go.mod @@ -7,7 +7,7 @@ go 1.18 replace github.com/ipfs/kubo => ./../../.. require ( - github.com/ipfs/go-libipfs v0.6.1-0.20230228004237-36918f45f260 + github.com/ipfs/go-libipfs v0.6.1 github.com/ipfs/interface-go-ipfs-core v0.11.0 github.com/ipfs/kubo v0.0.0-00010101000000-000000000000 github.com/libp2p/go-libp2p v0.26.2 diff --git a/docs/examples/kubo-as-a-library/go.sum b/docs/examples/kubo-as-a-library/go.sum index 8a07bfbe8..c927b58d9 100644 --- a/docs/examples/kubo-as-a-library/go.sum +++ b/docs/examples/kubo-as-a-library/go.sum @@ -569,8 +569,8 @@ github.com/ipfs/go-ipld-legacy v0.1.1 h1:BvD8PEuqwBHLTKqlGFTHSwrwFOMkVESEvwIYwR2 github.com/ipfs/go-ipld-legacy v0.1.1/go.mod h1:8AyKFCjgRPsQFf15ZQgDB8Din4DML/fOmKZkkFkrIEg= github.com/ipfs/go-ipns v0.3.0 h1:ai791nTgVo+zTuq2bLvEGmWP1M0A6kGTXUsgv/Yq67A= github.com/ipfs/go-ipns v0.3.0/go.mod h1:3cLT2rbvgPZGkHJoPO1YMJeh6LtkxopCkKFcio/wE24= -github.com/ipfs/go-libipfs v0.6.1-0.20230228004237-36918f45f260 h1:QRLcCoITO9ZQo2pvjmrfngqKhUKjPopBva3MVH62LT8= -github.com/ipfs/go-libipfs v0.6.1-0.20230228004237-36918f45f260/go.mod h1:3OoEQs95UkqFEf65SbRDpiMwuzI+C/jTsYQaHfBbJXI= +github.com/ipfs/go-libipfs v0.6.1 h1:OSO9cm1H3r4OXfP0MP1Q5UhTnhd2fByGl6CVYyz/Rhk= +github.com/ipfs/go-libipfs v0.6.1/go.mod h1:FmhKgxMOQA572TK5DA3MZ5GL44ZqsMHIrkgK4gLn4A8= github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM= github.com/ipfs/go-log v1.0.2/go.mod h1:1MNjMxe0u6xvJZgeqbJ8vdo2TKaGwZ1a0Bpza+sr2Sk= github.com/ipfs/go-log v1.0.3/go.mod h1:OsLySYkwIbiSUR/yBTdv1qPtcE4FW3WPWk/ewz9Ru+A= diff --git a/go.mod b/go.mod index 34603ebd2..673dd3793 100644 --- a/go.mod +++ b/go.mod @@ -45,7 +45,7 @@ require ( github.com/ipfs/go-ipld-git v0.1.1 github.com/ipfs/go-ipld-legacy v0.1.1 github.com/ipfs/go-ipns v0.3.0 - github.com/ipfs/go-libipfs v0.6.1-0.20230228004237-36918f45f260 + github.com/ipfs/go-libipfs v0.6.1 github.com/ipfs/go-log v1.0.5 github.com/ipfs/go-log/v2 v2.5.1 github.com/ipfs/go-merkledag v0.9.0 diff --git a/go.sum b/go.sum index 6f7800a2e..16329d6ad 100644 --- a/go.sum +++ b/go.sum @@ -591,8 +591,8 @@ github.com/ipfs/go-ipld-legacy v0.1.1 h1:BvD8PEuqwBHLTKqlGFTHSwrwFOMkVESEvwIYwR2 github.com/ipfs/go-ipld-legacy v0.1.1/go.mod h1:8AyKFCjgRPsQFf15ZQgDB8Din4DML/fOmKZkkFkrIEg= github.com/ipfs/go-ipns v0.3.0 h1:ai791nTgVo+zTuq2bLvEGmWP1M0A6kGTXUsgv/Yq67A= github.com/ipfs/go-ipns v0.3.0/go.mod h1:3cLT2rbvgPZGkHJoPO1YMJeh6LtkxopCkKFcio/wE24= -github.com/ipfs/go-libipfs v0.6.1-0.20230228004237-36918f45f260 h1:QRLcCoITO9ZQo2pvjmrfngqKhUKjPopBva3MVH62LT8= -github.com/ipfs/go-libipfs v0.6.1-0.20230228004237-36918f45f260/go.mod h1:3OoEQs95UkqFEf65SbRDpiMwuzI+C/jTsYQaHfBbJXI= +github.com/ipfs/go-libipfs v0.6.1 h1:OSO9cm1H3r4OXfP0MP1Q5UhTnhd2fByGl6CVYyz/Rhk= +github.com/ipfs/go-libipfs v0.6.1/go.mod h1:FmhKgxMOQA572TK5DA3MZ5GL44ZqsMHIrkgK4gLn4A8= github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM= github.com/ipfs/go-log v1.0.2/go.mod h1:1MNjMxe0u6xvJZgeqbJ8vdo2TKaGwZ1a0Bpza+sr2Sk= github.com/ipfs/go-log v1.0.3/go.mod h1:OsLySYkwIbiSUR/yBTdv1qPtcE4FW3WPWk/ewz9Ru+A= From bfa425fc67b5a6125412cd2453d6fa7f83a4bc96 Mon Sep 17 00:00:00 2001 From: Gus Eggert Date: Wed, 8 Mar 2023 15:48:56 -0500 Subject: [PATCH 07/26] test: port legacy DHT tests to Go --- test/cli/dht_legacy_test.go | 137 ++++++++++++++++++++++++++++++ test/cli/harness/harness.go | 2 +- test/cli/harness/node.go | 44 +++++++--- test/cli/harness/nodes.go | 16 ++++ test/cli/harness/run.go | 8 +- test/sharness/t0170-legacy-dht.sh | 121 -------------------------- 6 files changed, 192 insertions(+), 136 deletions(-) create mode 100644 test/cli/dht_legacy_test.go delete mode 100755 test/sharness/t0170-legacy-dht.sh diff --git a/test/cli/dht_legacy_test.go b/test/cli/dht_legacy_test.go new file mode 100644 index 000000000..437b62ae4 --- /dev/null +++ b/test/cli/dht_legacy_test.go @@ -0,0 +1,137 @@ +package cli + +import ( + "sort" + "sync" + "testing" + + "github.com/ipfs/kubo/test/cli/harness" + "github.com/ipfs/kubo/test/cli/testutils" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestLegacyDHT(t *testing.T) { + nodes := harness.NewT(t).NewNodes(5).Init() + nodes.ForEachPar(func(node *harness.Node) { + node.IPFS("config", "Routing.Type", "dht") + }) + nodes.StartDaemons().Connect() + + t.Run("ipfs dht findpeer", func(t *testing.T) { + t.Parallel() + res := nodes[1].RunIPFS("dht", "findpeer", nodes[0].PeerID().String()) + assert.Equal(t, 0, res.ExitCode()) + + swarmAddr := nodes[0].SwarmAddrsWithoutPeerIDs()[0] + require.Equal(t, swarmAddr.String(), res.Stdout.Trimmed()) + }) + + t.Run("ipfs dht get ", func(t *testing.T) { + t.Parallel() + hash := nodes[2].IPFSAddStr("hello world") + nodes[2].IPFS("name", "publish", "/ipfs/"+hash) + + res := nodes[1].IPFS("dht", "get", "/ipns/"+nodes[2].PeerID().String()) + assert.Contains(t, res.Stdout.String(), "/ipfs/"+hash) + + t.Run("put round trips (#3124)", func(t *testing.T) { + t.Parallel() + nodes[0].WriteBytes("get_result", res.Stdout.Bytes()) + res := nodes[0].IPFS("dht", "put", "/ipns/"+nodes[2].PeerID().String(), "get_result") + assert.Greater(t, len(res.Stdout.Lines()), 0, "should put to at least one node") + }) + + t.Run("put with bad keys fails (issue #5113, #4611)", func(t *testing.T) { + t.Parallel() + keys := []string{"foo", "/pk/foo", "/ipns/foo"} + for _, key := range keys { + key := key + t.Run(key, func(t *testing.T) { + t.Parallel() + res := nodes[0].RunIPFS("dht", "put", key) + assert.Equal(t, 1, res.ExitCode()) + assert.Contains(t, res.Stderr.String(), "invalid") + assert.Empty(t, res.Stdout.String()) + }) + } + }) + + t.Run("get with bad keys (issue #4611)", func(t *testing.T) { + for _, key := range []string{"foo", "/pk/foo"} { + key := key + t.Run(key, func(t *testing.T) { + t.Parallel() + res := nodes[0].RunIPFS("dht", "get", key) + assert.Equal(t, 1, res.ExitCode()) + assert.Contains(t, res.Stderr.String(), "invalid") + assert.Empty(t, res.Stdout.String()) + }) + } + }) + }) + + t.Run("ipfs dht findprovs", func(t *testing.T) { + t.Parallel() + hash := nodes[3].IPFSAddStr("some stuff") + res := nodes[4].IPFS("dht", "findprovs", hash) + assert.Equal(t, nodes[3].PeerID().String(), res.Stdout.Trimmed()) + }) + + t.Run("ipfs dht query ", func(t *testing.T) { + t.Parallel() + t.Run("normal DHT configuration", func(t *testing.T) { + t.Parallel() + hash := nodes[0].IPFSAddStr("some other stuff") + peerCounts := map[string]int{} + peerCountsMut := sync.Mutex{} + harness.Nodes(nodes).ForEachPar(func(node *harness.Node) { + res := node.IPFS("dht", "query", hash) + closestPeer := res.Stdout.Lines()[0] + // check that it's a valid peer ID + _, err := peer.Decode(closestPeer) + require.NoError(t, err) + + peerCountsMut.Lock() + peerCounts[closestPeer]++ + peerCountsMut.Unlock() + }) + // 4 nodes should see the same peer ID + // 1 node (the closest) should see a different one + var counts []int + for _, count := range peerCounts { + counts = append(counts, count) + } + sort.IntSlice(counts).Sort() + assert.Equal(t, []int{1, 4}, counts) + }) + + }) + + t.Run("dht commands fail when offline", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + + // these cannot be run in parallel due to repo locking (seems like a bug) + + t.Run("dht findprovs", func(t *testing.T) { + res := node.RunIPFS("dht", "findprovs", testutils.CIDEmptyDir) + assert.Equal(t, 1, res.ExitCode()) + assert.Contains(t, res.Stderr.String(), "this command must be run in online mode") + }) + + t.Run("dht findpeer", func(t *testing.T) { + res := node.RunIPFS("dht", "findpeer", testutils.CIDEmptyDir) + assert.Equal(t, 1, res.ExitCode()) + assert.Contains(t, res.Stderr.String(), "this command must be run in online mode") + }) + + t.Run("dht put", func(t *testing.T) { + node.WriteBytes("foo", []byte("foo")) + res := node.RunIPFS("dht", "put", "/ipns/"+node.PeerID().String(), "foo") + assert.Equal(t, 1, res.ExitCode()) + assert.Contains(t, res.Stderr.String(), "this action must be run in online mode") + }) + }) +} diff --git a/test/cli/harness/harness.go b/test/cli/harness/harness.go index de962e1c1..a35fead35 100644 --- a/test/cli/harness/harness.go +++ b/test/cli/harness/harness.go @@ -171,7 +171,7 @@ func (h *Harness) Mkdirs(paths ...string) { } } -func (h *Harness) Sh(expr string) RunResult { +func (h *Harness) Sh(expr string) *RunResult { return h.Runner.Run(RunRequest{ Path: "bash", Args: []string{"-c", expr}, diff --git a/test/cli/harness/node.go b/test/cli/harness/node.go index 0d0295307..181fca99b 100644 --- a/test/cli/harness/node.go +++ b/test/cli/harness/node.go @@ -129,23 +129,23 @@ func (n *Node) UpdateConfigAndUserSuppliedResourceManagerOverrides(f func(cfg *c n.WriteUserSuppliedResourceOverrides(overrides) } -func (n *Node) IPFS(args ...string) RunResult { +func (n *Node) IPFS(args ...string) *RunResult { res := n.RunIPFS(args...) n.Runner.AssertNoError(res) return res } -func (n *Node) PipeStrToIPFS(s string, args ...string) RunResult { +func (n *Node) PipeStrToIPFS(s string, args ...string) *RunResult { return n.PipeToIPFS(strings.NewReader(s), args...) } -func (n *Node) PipeToIPFS(reader io.Reader, args ...string) RunResult { +func (n *Node) PipeToIPFS(reader io.Reader, args ...string) *RunResult { res := n.RunPipeToIPFS(reader, args...) n.Runner.AssertNoError(res) return res } -func (n *Node) RunPipeToIPFS(reader io.Reader, args ...string) RunResult { +func (n *Node) RunPipeToIPFS(reader io.Reader, args ...string) *RunResult { return n.Runner.Run(RunRequest{ Path: n.IPFSBin, Args: args, @@ -153,7 +153,7 @@ func (n *Node) RunPipeToIPFS(reader io.Reader, args ...string) RunResult { }) } -func (n *Node) RunIPFS(args ...string) RunResult { +func (n *Node) RunIPFS(args ...string) *RunResult { return n.Runner.Run(RunRequest{ Path: n.IPFSBin, Args: args, @@ -216,7 +216,7 @@ func (n *Node) StartDaemon(ipfsArgs ...string) *Node { RunFunc: (*exec.Cmd).Start, }) - n.Daemon = &res + n.Daemon = res log.Debugf("node %d started, checking API", n.ID) n.WaitOnAPI() @@ -399,8 +399,6 @@ func (n *Node) SwarmAddrs() []multiaddr.Multiaddr { Path: n.IPFSBin, Args: []string{"swarm", "addrs", "local"}, }) - ipfsProtocol := multiaddr.ProtocolWithCode(multiaddr.P_IPFS).Name - peerID := n.PeerID() out := strings.TrimSpace(res.Stdout.String()) outLines := strings.Split(out, "\n") var addrs []multiaddr.Multiaddr @@ -409,9 +407,18 @@ func (n *Node) SwarmAddrs() []multiaddr.Multiaddr { if err != nil { panic(err) } + addrs = append(addrs, ma) + } + return addrs +} +func (n *Node) SwarmAddrsWithPeerIDs() []multiaddr.Multiaddr { + ipfsProtocol := multiaddr.ProtocolWithCode(multiaddr.P_IPFS).Name + peerID := n.PeerID() + var addrs []multiaddr.Multiaddr + for _, ma := range n.SwarmAddrs() { // add the peer ID to the multiaddr if it doesn't have it - _, err = ma.ValueForProtocol(multiaddr.P_IPFS) + _, err := ma.ValueForProtocol(multiaddr.P_IPFS) if errors.Is(err, multiaddr.ErrProtocolNotFound) { comp, err := multiaddr.NewComponent(ipfsProtocol, peerID.String()) if err != nil { @@ -424,10 +431,27 @@ func (n *Node) SwarmAddrs() []multiaddr.Multiaddr { return addrs } +func (n *Node) SwarmAddrsWithoutPeerIDs() []multiaddr.Multiaddr { + var addrs []multiaddr.Multiaddr + for _, ma := range n.SwarmAddrs() { + var components []multiaddr.Multiaddr + multiaddr.ForEach(ma, func(c multiaddr.Component) bool { + if c.Protocol().Code == multiaddr.P_IPFS { + return true + } + components = append(components, &c) + return true + }) + ma = multiaddr.Join(components...) + addrs = append(addrs, ma) + } + return addrs +} + func (n *Node) Connect(other *Node) *Node { n.Runner.MustRun(RunRequest{ Path: n.IPFSBin, - Args: []string{"swarm", "connect", other.SwarmAddrs()[0].String()}, + Args: []string{"swarm", "connect", other.SwarmAddrsWithPeerIDs()[0].String()}, }) return n } diff --git a/test/cli/harness/nodes.go b/test/cli/harness/nodes.go index dbc7de16b..872d77679 100644 --- a/test/cli/harness/nodes.go +++ b/test/cli/harness/nodes.go @@ -4,6 +4,7 @@ import ( "sync" "github.com/multiformats/go-multiaddr" + "golang.org/x/sync/errgroup" ) // Nodes is a collection of Kubo nodes along with operations on groups of nodes. @@ -16,6 +17,21 @@ func (n Nodes) Init(args ...string) Nodes { return n } +func (n Nodes) ForEachPar(f func(*Node)) { + group := &errgroup.Group{} + for _, node := range n { + node := node + group.Go(func() error { + f(node) + return nil + }) + } + err := group.Wait() + if err != nil { + panic(err) + } +} + func (n Nodes) Connect() Nodes { wg := sync.WaitGroup{} for i, node := range n { diff --git a/test/cli/harness/run.go b/test/cli/harness/run.go index 9cbb871bc..c2a3662be 100644 --- a/test/cli/harness/run.go +++ b/test/cli/harness/run.go @@ -51,7 +51,7 @@ func environToMap(environ []string) map[string]string { return m } -func (r *Runner) Run(req RunRequest) RunResult { +func (r *Runner) Run(req RunRequest) *RunResult { cmd := exec.Command(req.Path, req.Args...) stdout := &Buffer{} stderr := &Buffer{} @@ -86,17 +86,17 @@ func (r *Runner) Run(req RunRequest) RunResult { result.ExitErr = exitErr } - return result + return &result } // MustRun runs the command and fails the test if the command fails. -func (r *Runner) MustRun(req RunRequest) RunResult { +func (r *Runner) MustRun(req RunRequest) *RunResult { result := r.Run(req) r.AssertNoError(result) return result } -func (r *Runner) AssertNoError(result RunResult) { +func (r *Runner) AssertNoError(result *RunResult) { if result.ExitErr != nil { log.Panicf("'%s' returned error, code: %d, err: %s\nstdout:%s\nstderr:%s\n", result.Cmd.Args, result.ExitErr.ExitCode(), result.ExitErr.Error(), result.Stdout.String(), result.Stderr.String()) diff --git a/test/sharness/t0170-legacy-dht.sh b/test/sharness/t0170-legacy-dht.sh deleted file mode 100755 index fc11b9044..000000000 --- a/test/sharness/t0170-legacy-dht.sh +++ /dev/null @@ -1,121 +0,0 @@ -#!/usr/bin/env bash - -# Legacy / deprecated, see: t0170-routing-dht.sh -test_description="Test dht command" - -. lib/test-lib.sh - -test_dht() { - NUM_NODES=5 - - test_expect_success 'init iptb' ' - rm -rf .iptb/ && - iptb testbed create -type localipfs -count $NUM_NODES -init - ' - - test_expect_success 'DHT-only routing' ' - iptb run -- ipfs config Routing.Type dht - ' - - startup_cluster $NUM_NODES $@ - - test_expect_success 'peer ids' ' - PEERID_0=$(iptb attr get 0 id) && - PEERID_2=$(iptb attr get 2 id) - ' - - # ipfs dht findpeer - test_expect_success 'findpeer' ' - ipfsi 1 dht findpeer $PEERID_0 | sort >actual && - ipfsi 0 id -f "" | cut -d / -f 1-5 | sort >expected && - test_cmp actual expected - ' - - # ipfs dht get - test_expect_success 'get with good keys works' ' - HASH="$(echo "hello world" | ipfsi 2 add -q)" && - ipfsi 2 name publish "/ipfs/$HASH" && - ipfsi 1 dht get "/ipns/$PEERID_2" >get_result - ' - - test_expect_success 'get with good keys contains the right value' ' - cat get_result | grep -aq "/ipfs/$HASH" - ' - - test_expect_success 'put round trips (#3124)' ' - ipfsi 0 dht put "/ipns/$PEERID_2" get_result | sort >putted && - [ -s putted ] || - test_fsh cat putted - ' - - test_expect_success 'put with bad keys fails (issue #5113)' ' - ipfsi 0 dht put "foo" <<putted - ipfsi 0 dht put "/pk/foo" <<>putted - ipfsi 0 dht put "/ipns/foo" <<>putted - [ ! -s putted ] || - test_fsh cat putted - ' - - test_expect_success 'put with bad keys returns error (issue #4611)' ' - test_must_fail ipfsi 0 dht put "foo" << afile && - HASH=$(ipfsi 3 add -q afile) - ' - - # ipfs dht findprovs - test_expect_success 'findprovs' ' - ipfsi 4 dht findprovs $HASH > provs && - iptb attr get 3 id > expected && - test_cmp provs expected - ' - - - # ipfs dht query - # - # We test all nodes. 4 nodes should see the same peer ID, one node (the - # closest) should see a different one. - - for i in $(test_seq 0 4); do - test_expect_success "query from $i" ' - ipfsi "$i" dht query "$HASH" | head -1 >closest-$i - ' - done - - test_expect_success "collecting results" ' - cat closest-* | sort | uniq -c | sed -e "s/ *\([0-9]\+\) .*/\1/g" | sort -g > actual && - echo 1 > expected && - echo 4 >> expected - ' - - test_expect_success "checking results" ' - test_cmp actual expected - ' - - test_expect_success 'stop iptb' ' - iptb stop - ' - - test_expect_success "dht commands fail when offline" ' - test_must_fail ipfsi 0 dht findprovs "$HASH" 2>err_findprovs && - test_must_fail ipfsi 0 dht findpeer "$HASH" 2>err_findpeer && - test_must_fail ipfsi 0 dht put "/ipns/$PEERID_2" "get_result" 2>err_put && - test_should_contain "this command must be run in online mode" err_findprovs && - test_should_contain "this command must be run in online mode" err_findpeer && - test_should_contain "this action must be run in online mode" err_put - ' -} - -test_dht -test_dht --enable-pubsub-experiment --enable-namesys-pubsub - -test_done From 840eaa75990c3efb67e4404c9f0e7d25f091c152 Mon Sep 17 00:00:00 2001 From: Gus Eggert Date: Wed, 8 Mar 2023 16:25:45 -0500 Subject: [PATCH 08/26] test: parallelize more of rcmgr Go tests --- test/cli/harness/node.go | 8 ---- test/cli/rcmgr_test.go | 86 ++++++++++++++++++++++++---------------- 2 files changed, 52 insertions(+), 42 deletions(-) diff --git a/test/cli/harness/node.go b/test/cli/harness/node.go index 181fca99b..cc251e11b 100644 --- a/test/cli/harness/node.go +++ b/test/cli/harness/node.go @@ -121,14 +121,6 @@ func (n *Node) UpdateUserSuppliedResourceManagerOverrides(f func(overrides *rcmg n.WriteUserSuppliedResourceOverrides(overrides) } -func (n *Node) UpdateConfigAndUserSuppliedResourceManagerOverrides(f func(cfg *config.Config, overrides *rcmgr.PartialLimitConfig)) { - overrides := n.ReadUserResourceOverrides() - cfg := n.ReadConfig() - f(cfg, overrides) - n.WriteConfig(cfg) - n.WriteUserSuppliedResourceOverrides(overrides) -} - func (n *Node) IPFS(args ...string) *RunResult { res := n.RunIPFS(args...) n.Runner.AssertNoError(res) diff --git a/test/cli/rcmgr_test.go b/test/cli/rcmgr_test.go index fb644e1a7..51b2b0452 100644 --- a/test/cli/rcmgr_test.go +++ b/test/cli/rcmgr_test.go @@ -49,6 +49,7 @@ func TestRcmgr(t *testing.T) { }) t.Run("Very high connmgr highwater", func(t *testing.T) { + t.Parallel() node := harness.NewT(t).NewNode().Init() node.UpdateConfig(func(cfg *config.Config) { cfg.Swarm.ConnMgr.HighWater = config.NewOptionalInteger(1000) @@ -74,6 +75,7 @@ func TestRcmgr(t *testing.T) { node.StartDaemon() t.Run("conns and streams are above 800 for default connmgr settings", func(t *testing.T) { + t.Parallel() res := node.RunIPFS("swarm", "resources", "--enc=json") require.Equal(t, 0, res.ExitCode()) limits := unmarshalLimits(t, res.Stdout.Bytes()) @@ -87,6 +89,7 @@ func TestRcmgr(t *testing.T) { }) t.Run("limits should succeed", func(t *testing.T) { + t.Parallel() res := node.RunIPFS("swarm", "resources", "--enc=json") assert.Equal(t, 0, res.ExitCode()) @@ -106,6 +109,7 @@ func TestRcmgr(t *testing.T) { }) t.Run("swarm stats works", func(t *testing.T) { + t.Parallel() res := node.RunIPFS("swarm", "resources", "--enc=json") require.Equal(t, 0, res.ExitCode()) @@ -123,6 +127,7 @@ func TestRcmgr(t *testing.T) { }) t.Run("smoke test transient scope", func(t *testing.T) { + t.Parallel() node := harness.NewT(t).NewNode().Init() node.UpdateUserSuppliedResourceManagerOverrides(func(overrides *rcmgr.PartialLimitConfig) { overrides.Transient.Memory = 88888 @@ -135,6 +140,7 @@ func TestRcmgr(t *testing.T) { }) t.Run("smoke test service scope", func(t *testing.T) { + t.Parallel() node := harness.NewT(t).NewNode().Init() node.UpdateUserSuppliedResourceManagerOverrides(func(overrides *rcmgr.PartialLimitConfig) { overrides.Service = map[string]rcmgr.ResourceLimits{"foo": {Memory: 77777}} @@ -147,6 +153,7 @@ func TestRcmgr(t *testing.T) { }) t.Run("smoke test protocol scope", func(t *testing.T) { + t.Parallel() node := harness.NewT(t).NewNode().Init() node.UpdateUserSuppliedResourceManagerOverrides(func(overrides *rcmgr.PartialLimitConfig) { overrides.Protocol = map[protocol.ID]rcmgr.ResourceLimits{"foo": {Memory: 66666}} @@ -159,6 +166,7 @@ func TestRcmgr(t *testing.T) { }) t.Run("smoke test peer scope", func(t *testing.T) { + t.Parallel() validPeerID, err := peer.Decode("QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN") assert.NoError(t, err) node := harness.NewT(t).NewNode().Init() @@ -172,13 +180,17 @@ func TestRcmgr(t *testing.T) { assert.Equal(t, rcmgr.LimitVal64(55555), limits.Peers[validPeerID].Memory) }) - t.Run("", func(t *testing.T) { + t.Run("blocking and allowlists", func(t *testing.T) { + t.Parallel() nodes := harness.NewT(t).NewNodes(3).Init() node0, node1, node2 := nodes[0], nodes[1], nodes[2] - // peerID0, peerID1, peerID2 := node0.PeerID(), node1.PeerID(), node2.PeerID() peerID1, peerID2 := node1.PeerID().String(), node2.PeerID().String() - node0.UpdateConfigAndUserSuppliedResourceManagerOverrides(func(cfg *config.Config, overrides *rcmgr.PartialLimitConfig) { + node0.UpdateConfig(func(cfg *config.Config) { + cfg.Swarm.ResourceMgr.Enabled = config.True + cfg.Swarm.ResourceMgr.Allowlist = []string{"/ip4/0.0.0.0/ipcidr/0/p2p/" + peerID2} + }) + node0.UpdateUserSuppliedResourceManagerOverrides(func(overrides *rcmgr.PartialLimitConfig) { *overrides = rcmgr.PartialLimitConfig{ System: rcmgr.ResourceLimits{ Conns: rcmgr.BlockAllLimit, @@ -186,91 +198,97 @@ func TestRcmgr(t *testing.T) { ConnsOutbound: rcmgr.BlockAllLimit, }, } - cfg.Swarm.ResourceMgr.Enabled = config.True - cfg.Swarm.ResourceMgr.Allowlist = []string{"/ip4/0.0.0.0/ipcidr/0/p2p/" + peerID2} }) nodes.StartDaemons() - t.Parallel() - t.Run("node 0 should fail to connect to node 1", func(t *testing.T) { + t.Run("node 0 should fail to connect to and ping node 1", func(t *testing.T) { + t.Parallel() res := node0.Runner.Run(harness.RunRequest{ Path: node0.IPFSBin, - Args: []string{"swarm", "connect", node1.SwarmAddrs()[0].String()}, + Args: []string{"swarm", "connect", node1.SwarmAddrsWithPeerIDs()[0].String()}, }) assert.Equal(t, 1, res.ExitCode()) assert.Contains(t, res.Stderr.String(), "failed to find any peer in table") - }) - t.Run("node 0 should connect to node 2 since it is allowlisted", func(t *testing.T) { - res := node0.Runner.Run(harness.RunRequest{ - Path: node0.IPFSBin, - Args: []string{"swarm", "connect", node2.SwarmAddrs()[0].String()}, - }) - assert.Equal(t, 0, res.ExitCode()) - }) - - t.Run("node 0 should fail to ping node 1", func(t *testing.T) { - res := node0.RunIPFS("ping", "-n2", peerID1) + res = node0.RunIPFS("ping", "-n2", peerID1) assert.Equal(t, 1, res.ExitCode()) assert.Contains(t, res.Stderr.String(), "Error: ping failed") }) - t.Run("node 0 should be able to ping node 2", func(t *testing.T) { - res := node0.RunIPFS("ping", "-n2", peerID2) + t.Run("node 0 should connect to and ping node 2 since it is allowlisted", func(t *testing.T) { + t.Parallel() + res := node0.Runner.Run(harness.RunRequest{ + Path: node0.IPFSBin, + Args: []string{"swarm", "connect", node2.SwarmAddrsWithPeerIDs()[0].String()}, + }) + assert.Equal(t, 0, res.ExitCode()) + + res = node0.RunIPFS("ping", "-n2", peerID2) assert.Equal(t, 0, res.ExitCode()) }) }) t.Run("daemon should refuse to start if connmgr.highwater < resources inbound", func(t *testing.T) { - t.Parallel() t.Run("system conns", func(t *testing.T) { + t.Parallel() node := harness.NewT(t).NewNode().Init() - node.UpdateConfigAndUserSuppliedResourceManagerOverrides(func(cfg *config.Config, overrides *rcmgr.PartialLimitConfig) { + node.UpdateConfig(func(cfg *config.Config) { + cfg.Swarm.ConnMgr.HighWater = config.NewOptionalInteger(128) + cfg.Swarm.ConnMgr.LowWater = config.NewOptionalInteger(64) + }) + node.UpdateUserSuppliedResourceManagerOverrides(func(overrides *rcmgr.PartialLimitConfig) { *overrides = rcmgr.PartialLimitConfig{ System: rcmgr.ResourceLimits{Conns: 128}, } - cfg.Swarm.ConnMgr.HighWater = config.NewOptionalInteger(128) - cfg.Swarm.ConnMgr.LowWater = config.NewOptionalInteger(64) }) res := node.RunIPFS("daemon") assert.Equal(t, 1, res.ExitCode()) }) t.Run("system conns inbound", func(t *testing.T) { + t.Parallel() node := harness.NewT(t).NewNode().Init() - node.UpdateConfigAndUserSuppliedResourceManagerOverrides(func(cfg *config.Config, overrides *rcmgr.PartialLimitConfig) { + node.UpdateConfig(func(cfg *config.Config) { + cfg.Swarm.ConnMgr.HighWater = config.NewOptionalInteger(128) + cfg.Swarm.ConnMgr.LowWater = config.NewOptionalInteger(64) + }) + node.UpdateUserSuppliedResourceManagerOverrides(func(overrides *rcmgr.PartialLimitConfig) { *overrides = rcmgr.PartialLimitConfig{ System: rcmgr.ResourceLimits{ConnsInbound: 128}, } - cfg.Swarm.ConnMgr.HighWater = config.NewOptionalInteger(128) - cfg.Swarm.ConnMgr.LowWater = config.NewOptionalInteger(64) }) res := node.RunIPFS("daemon") assert.Equal(t, 1, res.ExitCode()) }) t.Run("system streams", func(t *testing.T) { + t.Parallel() node := harness.NewT(t).NewNode().Init() - node.UpdateConfigAndUserSuppliedResourceManagerOverrides(func(cfg *config.Config, overrides *rcmgr.PartialLimitConfig) { + node.UpdateConfig(func(cfg *config.Config) { + cfg.Swarm.ConnMgr.HighWater = config.NewOptionalInteger(128) + cfg.Swarm.ConnMgr.LowWater = config.NewOptionalInteger(64) + }) + node.UpdateUserSuppliedResourceManagerOverrides(func(overrides *rcmgr.PartialLimitConfig) { *overrides = rcmgr.PartialLimitConfig{ System: rcmgr.ResourceLimits{Streams: 128}, } - cfg.Swarm.ConnMgr.HighWater = config.NewOptionalInteger(128) - cfg.Swarm.ConnMgr.LowWater = config.NewOptionalInteger(64) }) res := node.RunIPFS("daemon") assert.Equal(t, 1, res.ExitCode()) }) t.Run("system streams inbound", func(t *testing.T) { + t.Parallel() node := harness.NewT(t).NewNode().Init() - node.UpdateConfigAndUserSuppliedResourceManagerOverrides(func(cfg *config.Config, overrides *rcmgr.PartialLimitConfig) { + node.UpdateConfig(func(cfg *config.Config) { + cfg.Swarm.ConnMgr.HighWater = config.NewOptionalInteger(128) + cfg.Swarm.ConnMgr.LowWater = config.NewOptionalInteger(64) + }) + node.UpdateUserSuppliedResourceManagerOverrides(func(overrides *rcmgr.PartialLimitConfig) { *overrides = rcmgr.PartialLimitConfig{ System: rcmgr.ResourceLimits{StreamsInbound: 128}, } - cfg.Swarm.ConnMgr.HighWater = config.NewOptionalInteger(128) - cfg.Swarm.ConnMgr.LowWater = config.NewOptionalInteger(64) }) res := node.RunIPFS("daemon") From 2510f063643086f83a5ac3f70a2f56d1d373bf21 Mon Sep 17 00:00:00 2001 From: Gus Eggert Date: Wed, 8 Mar 2023 16:26:47 -0500 Subject: [PATCH 09/26] feat: add "autoclient" routing type This routing type is the same as "auto" but it creates the DHT in "client" mode and hence does not start a DHT server. --- cmd/ipfs/daemon.go | 61 ++++++++++++++----------- config/routing.go | 2 +- core/node/libp2p/routingopt.go | 5 +- docs/changelogs/v0.19.md | 6 +++ docs/config.md | 6 ++- test/cli/delegated_routing_http_test.go | 2 +- test/cli/dht_autoclient_test.go | 39 ++++++++++++++++ 7 files changed, 88 insertions(+), 33 deletions(-) create mode 100644 test/cli/dht_autoclient_test.go diff --git a/cmd/ipfs/daemon.go b/cmd/ipfs/daemon.go index 21495c498..880d26b0e 100644 --- a/cmd/ipfs/daemon.go +++ b/cmd/ipfs/daemon.go @@ -45,32 +45,33 @@ import ( ) const ( - adjustFDLimitKwd = "manage-fdlimit" - enableGCKwd = "enable-gc" - initOptionKwd = "init" - initConfigOptionKwd = "init-config" - initProfileOptionKwd = "init-profile" - ipfsMountKwd = "mount-ipfs" - ipnsMountKwd = "mount-ipns" - migrateKwd = "migrate" - mountKwd = "mount" - offlineKwd = "offline" // global option - routingOptionKwd = "routing" - routingOptionSupernodeKwd = "supernode" - routingOptionDHTClientKwd = "dhtclient" - routingOptionDHTKwd = "dht" - routingOptionDHTServerKwd = "dhtserver" - routingOptionNoneKwd = "none" - routingOptionCustomKwd = "custom" - routingOptionDefaultKwd = "default" - routingOptionAutoKwd = "auto" - unencryptTransportKwd = "disable-transport-encryption" - unrestrictedAPIAccessKwd = "unrestricted-api" - writableKwd = "writable" - enablePubSubKwd = "enable-pubsub-experiment" - enableIPNSPubSubKwd = "enable-namesys-pubsub" - enableMultiplexKwd = "enable-mplex-experiment" - agentVersionSuffix = "agent-version-suffix" + adjustFDLimitKwd = "manage-fdlimit" + enableGCKwd = "enable-gc" + initOptionKwd = "init" + initConfigOptionKwd = "init-config" + initProfileOptionKwd = "init-profile" + ipfsMountKwd = "mount-ipfs" + ipnsMountKwd = "mount-ipns" + migrateKwd = "migrate" + mountKwd = "mount" + offlineKwd = "offline" // global option + routingOptionKwd = "routing" + routingOptionSupernodeKwd = "supernode" + routingOptionDHTClientKwd = "dhtclient" + routingOptionDHTKwd = "dht" + routingOptionDHTServerKwd = "dhtserver" + routingOptionNoneKwd = "none" + routingOptionCustomKwd = "custom" + routingOptionDefaultKwd = "default" + routingOptionAutoKwd = "auto" + routingOptionAutoClientKwd = "autoclient" + unencryptTransportKwd = "disable-transport-encryption" + unrestrictedAPIAccessKwd = "unrestricted-api" + writableKwd = "writable" + enablePubSubKwd = "enable-pubsub-experiment" + enableIPNSPubSubKwd = "enable-namesys-pubsub" + enableMultiplexKwd = "enable-mplex-experiment" + agentVersionSuffix = "agent-version-suffix" // apiAddrKwd = "address-api" // swarmAddrKwd = "address-swarm" ) @@ -416,6 +417,14 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment cfg.Identity.PeerID, cfg.Addresses.Swarm, cfg.Identity.PrivKey, + libp2p.DHTOption, + ) + case routingOptionAutoClientKwd: + ncfg.Routing = libp2p.ConstructDefaultRouting( + cfg.Identity.PeerID, + cfg.Addresses.Swarm, + cfg.Identity.PrivKey, + libp2p.DHTClientOption, ) case routingOptionDHTClientKwd: ncfg.Routing = libp2p.DHTClientOption diff --git a/config/routing.go b/config/routing.go index f19414ff3..1210bb3ce 100644 --- a/config/routing.go +++ b/config/routing.go @@ -10,7 +10,7 @@ import ( type Routing struct { // Type sets default daemon routing mode. // - // Can be one of "auto", "dht", "dhtclient", "dhtserver", "none", or "custom". + // Can be one of "auto", "autoclient", "dht", "dhtclient", "dhtserver", "none", or "custom". // When unset or set to "auto", DHT and implicit routers are used. // When "custom" is set, user-provided Routing.Routers is used. Type *OptionalString `json:",omitempty"` diff --git a/core/node/libp2p/routingopt.go b/core/node/libp2p/routingopt.go index bfb45971c..d54f37acc 100644 --- a/core/node/libp2p/routingopt.go +++ b/core/node/libp2p/routingopt.go @@ -40,7 +40,7 @@ func init() { } // ConstructDefaultRouting returns routers used when Routing.Type is unset or set to "auto" -func ConstructDefaultRouting(peerID string, addrs []string, privKey string) func( +func ConstructDefaultRouting(peerID string, addrs []string, privKey string, routingOpt RoutingOption) func( ctx context.Context, host host.Host, dstore datastore.Batching, @@ -58,8 +58,7 @@ func ConstructDefaultRouting(peerID string, addrs []string, privKey string) func // Different trade-offs can be made by setting Routing.Type = "custom" with own Routing.Routers var routers []*routinghelpers.ParallelRouter - // Run the default DHT routing (same as Routing.Type = "dht") - dhtRouting, err := DHTOption(ctx, host, dstore, validator, bootstrapPeers...) + dhtRouting, err := routingOpt(ctx, host, dstore, validator, bootstrapPeers...) if err != nil { return nil, err } diff --git a/docs/changelogs/v0.19.md b/docs/changelogs/v0.19.md index 7663308a6..fede7a454 100644 --- a/docs/changelogs/v0.19.md +++ b/docs/changelogs/v0.19.md @@ -7,6 +7,7 @@ - [Overview](#overview) - [🔦 Highlights](#-highlights) - [Improving the libp2p resource management integration](#improving-the-libp2p-resource-management-integration) + - [Addition of "autoclient" router type](#addition-of-autoclient-router-type) - [📝 Changelog](#-changelog) - [👨‍👩‍👧‍👦 Contributors](#-contributors) @@ -22,6 +23,11 @@ and [0.18.1](https://github.com/ipfs/kubo/blob/master/docs/changelogs/v0.18.md#i - Note: we don't expect most users to need these capablities, but they are there if so. 1. [Doc updates](https://github.com/ipfs/kubo/blob/master/docs/libp2p-resource-management.md). +#### Addition of "autoclient" router type +A new routing type "autoclient" has been added. This mode is similar to "auto", in that it is a hybrid of content routers (including Kademlia and HTTP routers), but it does not run a DHT server. This is similar to the difference between "dhtclient" and "dht" router types. + +See the [Routing.Type documentation](https://github.com/ipfs/kubo/blob/master/docs/config.md#routingtype) for more information. + ### 📝 Changelog ### 👨‍👩‍👧‍👦 Contributors diff --git a/docs/config.md b/docs/config.md index 30a4e7601..de47e5445 100644 --- a/docs/config.md +++ b/docs/config.md @@ -1349,11 +1349,13 @@ Contains options for content, peer, and IPNS routing mechanisms. ### `Routing.Type` -There are multiple routing options: "auto", "none", "dht" and "custom". +There are multiple routing options: "auto", "autoclient", "none", "dht", "dhtclient", and "custom". * **DEFAULT:** If unset, or set to "auto", your node will use the IPFS DHT and parallel HTTP routers listed below for additional speed. +* If set to "autoclient", your node will behave as in "auto" but without running a DHT server. + * If set to "none", your node will use _no_ routing system. You'll have to explicitly connect to peers that have the content you're looking for. @@ -1379,7 +1381,7 @@ To force a specific DHT-only mode, client or server, set `Routing.Type` to `dhtclient` or `dhtserver` respectively. Please do not set this to `dhtserver` unless you're sure your node is reachable from the public network. -When `Routing.Type` is set to `auto` your node will accelerate some types of routing +When `Routing.Type` is set to `auto` or `autoclient` your node will accelerate some types of routing by leveraging HTTP endpoints compatible with [IPIP-337](https://github.com/ipfs/specs/pull/337) in addition to the IPFS DHT. By default, an instance of [IPNI](https://github.com/ipni/specs/blob/main/IPNI.md#readme) diff --git a/test/cli/delegated_routing_http_test.go b/test/cli/delegated_routing_http_test.go index 0b39a9b12..446ea5150 100644 --- a/test/cli/delegated_routing_http_test.go +++ b/test/cli/delegated_routing_http_test.go @@ -94,7 +94,7 @@ func TestHTTPDelegatedRouting(t *testing.T) { })) t.Cleanup(server.Close) - node.IPFS("config", "Routing.Type", "--json", `"custom"`) + node.IPFS("config", "Routing.Type", "custom") node.IPFS("config", "Routing.Routers.TestDelegatedRouter", "--json", ToJSONStr(JSONObj{ "Type": "http", "Parameters": JSONObj{ diff --git a/test/cli/dht_autoclient_test.go b/test/cli/dht_autoclient_test.go new file mode 100644 index 000000000..749e34b34 --- /dev/null +++ b/test/cli/dht_autoclient_test.go @@ -0,0 +1,39 @@ +package cli + +import ( + "bytes" + "testing" + + "github.com/ipfs/kubo/test/cli/harness" + "github.com/ipfs/kubo/test/cli/testutils" + "github.com/stretchr/testify/assert" +) + +func TestDHTAutoclient(t *testing.T) { + t.Parallel() + nodes := harness.NewT(t).NewNodes(10).Init() + harness.Nodes(nodes[8:]).ForEachPar(func(node *harness.Node) { + node.IPFS("config", "Routing.Type", "autoclient") + }) + nodes.StartDaemons().Connect() + + t.Run("file added on node in client mode is retrievable from node in client mode", func(t *testing.T) { + t.Parallel() + randomBytes := testutils.RandomBytes(1000) + hash := nodes[8].IPFSAdd(bytes.NewReader(randomBytes)) + + res := nodes[9].IPFS("cat", hash) + assert.Equal(t, randomBytes, []byte(res.Stdout.Trimmed())) + }) + + t.Run("file added on node in server mode is retrievable from all nodes", func(t *testing.T) { + t.Parallel() + randomBytes := testutils.RandomBytes(1000) + hash := nodes[0].IPFSAdd(bytes.NewReader(randomBytes)) + + for i := 0; i < 10; i++ { + res := nodes[i].IPFS("cat", hash) + assert.Equal(t, randomBytes, []byte(res.Stdout.Trimmed())) + } + }) +} From 6bab5ce37ca9a525a9ce16c21a1251947529e0e1 Mon Sep 17 00:00:00 2001 From: Laurent Senta Date: Thu, 9 Mar 2023 15:21:50 +0100 Subject: [PATCH 10/26] test: name --verify forgets the verified key --- test/sharness/t0100-name.sh | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/sharness/t0100-name.sh b/test/sharness/t0100-name.sh index 44c2afb2d..34f33ea4d 100755 --- a/test/sharness/t0100-name.sh +++ b/test/sharness/t0100-name.sh @@ -314,4 +314,32 @@ test_name_with_key 'rsa' test_name_with_key 'ed25519_b58' test_name_with_key 'ed25519_b36' + +# `ipfs name inspect --verify` using the wrong RSA key should not succeed + +test_init_ipfs +test_launch_ipfs_daemon + +test_expect_success "prepare RSA keys" ' + export KEY_1=`ipfs key gen --type=rsa --size=4096 key1` && + export KEY_2=`ipfs key gen --type=rsa --size=4096 key2` && + export PEERID_1=`ipfs key list --ipns-base=base36 -l | grep key1 | cut -d " " -f1` && + export PEERID_2=`ipfs key list --ipns-base=base36 -l | grep key2 | cut -d " " -f1` +' + +test_expect_success "ipfs name publish --allow-offline --key= ' succeeds" ' + ipfs name publish --allow-offline --key=${KEY_1} "/ipfs/$( echo "helloworld" | ipfs add --inline -q )" && + ipfs routing get "/ipns/$PEERID_1" > ipns_record +' + +test_expect_success "ipfs name inspect --verify' has '.Validation.Validity' set to 'true' with correct Peer ID" ' + ipfs name inspect --verify $PEERID_1 --enc json < ipns_record | jq -e ".Validation.Valid == true" +' + +test_expect_success "ipfs name inspect --verify' has '.Validation.Validity' set to 'false' when we verify the wrong Peer ID" ' + ipfs name inspect --verify $PEERID_2 --enc json < ipns_record | jq -e ".Validation.Valid == false" +' + +test_kill_ipfs_daemon + test_done From d001ed74513eab7aee0fbef5f3c5ea0d94ca43e9 Mon Sep 17 00:00:00 2001 From: Laurent Senta Date: Thu, 9 Mar 2023 15:23:52 +0100 Subject: [PATCH 11/26] fix: --verify forgets the verified key --- core/commands/name/name.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/core/commands/name/name.go b/core/commands/name/name.go index 7999aee3d..0e4fa6cb5 100644 --- a/core/commands/name/name.go +++ b/core/commands/name/name.go @@ -222,6 +222,20 @@ Passing --verify will verify signature against provided public key. // Peer ID. if len(entry.PubKey) > 0 { pub, err = ic.UnmarshalPublicKey(entry.PubKey) + if err != nil { + return err + } + + // Verify the public key matches the name we are verifying. + entryID, err := peer.IDFromPublicKey(pub) + + if err != nil { + return err + } + + if id != entryID { + return fmt.Errorf("record public key does not match the verified name") + } } } if err != nil { From 5f766619cf5c8856edc06139d4338753c444577c Mon Sep 17 00:00:00 2001 From: Eng Zer Jun Date: Wed, 10 Aug 2022 22:04:19 +0800 Subject: [PATCH 12/26] test: use `T.TempDir` to create temporary test directory This commit replaces `os.MkdirTemp` with `t.TempDir` in tests. The directory created by `t.TempDir` is automatically removed when the test and all its subtests complete. Prior to this commit, temporary directory created using `os.MkdirTemp` needs to be removed manually by calling `os.RemoveAll`, which is omitted in some tests. The error handling boilerplate e.g. defer func() { if err := os.RemoveAll(dir); err != nil { t.Fatal(err) } } is also tedious, but `t.TempDir` handles this for us nicely. Reference: https://pkg.go.dev/testing#T.TempDir Signed-off-by: Eng Zer Jun --- fuse/node/mount_test.go | 10 +++------- repo/fsrepo/config_test.go | 25 ++++--------------------- repo/fsrepo/fsrepo_test.go | 21 ++++++--------------- test/bench/bench_cli_ipfs_add/main.go | 7 +------ test/bench/offline_add/main.go | 6 +----- 5 files changed, 15 insertions(+), 54 deletions(-) diff --git a/fuse/node/mount_test.go b/fuse/node/mount_test.go index 12313ae3e..1691cfa5b 100644 --- a/fuse/node/mount_test.go +++ b/fuse/node/mount_test.go @@ -4,6 +4,7 @@ package node import ( + "context" "os" "strings" "testing" @@ -11,8 +12,6 @@ import ( "bazil.org/fuse" - "context" - core "github.com/ipfs/kubo/core" ipns "github.com/ipfs/kubo/fuse/ipns" mount "github.com/ipfs/kubo/fuse/mount" @@ -52,11 +51,8 @@ func TestExternalUnmount(t *testing.T) { t.Fatal(err) } - // get the test dir paths (/tmp/fusetestXXXX) - dir, err := os.MkdirTemp("", "fusetest") - if err != nil { - t.Fatal(err) - } + // get the test dir paths (/tmp/TestExternalUnmount) + dir := t.TempDir() ipfsDir := dir + "/ipfs" ipnsDir := dir + "/ipns" diff --git a/repo/fsrepo/config_test.go b/repo/fsrepo/config_test.go index 03af75a96..060d0222a 100644 --- a/repo/fsrepo/config_test.go +++ b/repo/fsrepo/config_test.go @@ -2,7 +2,6 @@ package fsrepo_test import ( "encoding/json" - "os" "reflect" "testing" @@ -88,11 +87,7 @@ func TestDefaultDatastoreConfig(t *testing.T) { t.Fatal(err) } - dir, err := os.MkdirTemp("", "ipfs-datastore-config-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) // clean up + dir := t.TempDir() config := new(config.Datastore) err = json.Unmarshal(defaultConfig, config) @@ -126,11 +121,7 @@ func TestLevelDbConfig(t *testing.T) { if err != nil { t.Fatal(err) } - dir, err := os.MkdirTemp("", "ipfs-datastore-config-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) // clean up + dir := t.TempDir() spec := make(map[string]interface{}) err = json.Unmarshal(leveldbConfig, &spec) @@ -164,11 +155,7 @@ func TestFlatfsConfig(t *testing.T) { if err != nil { t.Fatal(err) } - dir, err := os.MkdirTemp("", "ipfs-datastore-config-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) // clean up + dir := t.TempDir() spec := make(map[string]interface{}) err = json.Unmarshal(flatfsConfig, &spec) @@ -202,11 +189,7 @@ func TestMeasureConfig(t *testing.T) { if err != nil { t.Fatal(err) } - dir, err := os.MkdirTemp("", "ipfs-datastore-config-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) // clean up + dir := t.TempDir() spec := make(map[string]interface{}) err = json.Unmarshal(measureConfig, &spec) diff --git a/repo/fsrepo/fsrepo_test.go b/repo/fsrepo/fsrepo_test.go index 7c7551d20..6b30b107a 100644 --- a/repo/fsrepo/fsrepo_test.go +++ b/repo/fsrepo/fsrepo_test.go @@ -13,18 +13,9 @@ import ( config "github.com/ipfs/kubo/config" ) -// swap arg order -func testRepoPath(p string, t *testing.T) string { - name, err := os.MkdirTemp("", p) - if err != nil { - t.Fatal(err) - } - return name -} - func TestInitIdempotence(t *testing.T) { t.Parallel() - path := testRepoPath("", t) + path := t.TempDir() for i := 0; i < 10; i++ { assert.Nil(Init(path, &config.Config{Datastore: config.DefaultDatastoreConfig()}), t, "multiple calls to init should succeed") } @@ -37,8 +28,8 @@ func Remove(repoPath string) error { func TestCanManageReposIndependently(t *testing.T) { t.Parallel() - pathA := testRepoPath("a", t) - pathB := testRepoPath("b", t) + pathA := t.TempDir() + pathB := t.TempDir() t.Log("initialize two repos") assert.Nil(Init(pathA, &config.Config{Datastore: config.DefaultDatastoreConfig()}), t, "a", "should initialize successfully") @@ -65,7 +56,7 @@ func TestCanManageReposIndependently(t *testing.T) { func TestDatastoreGetNotAllowedAfterClose(t *testing.T) { t.Parallel() - path := testRepoPath("test", t) + path := t.TempDir() assert.True(!IsInitialized(path), t, "should NOT be initialized") assert.Nil(Init(path, &config.Config{Datastore: config.DefaultDatastoreConfig()}), t, "should initialize successfully") @@ -83,7 +74,7 @@ func TestDatastoreGetNotAllowedAfterClose(t *testing.T) { func TestDatastorePersistsFromRepoToRepo(t *testing.T) { t.Parallel() - path := testRepoPath("test", t) + path := t.TempDir() assert.Nil(Init(path, &config.Config{Datastore: config.DefaultDatastoreConfig()}), t) r1, err := Open(path) @@ -104,7 +95,7 @@ func TestDatastorePersistsFromRepoToRepo(t *testing.T) { func TestOpenMoreThanOnceInSameProcess(t *testing.T) { t.Parallel() - path := testRepoPath("", t) + path := t.TempDir() assert.Nil(Init(path, &config.Config{Datastore: config.DefaultDatastoreConfig()}), t) r1, err := Open(path) diff --git a/test/bench/bench_cli_ipfs_add/main.go b/test/bench/bench_cli_ipfs_add/main.go index a089410ef..e7fe90e04 100644 --- a/test/bench/bench_cli_ipfs_add/main.go +++ b/test/bench/bench_cli_ipfs_add/main.go @@ -45,12 +45,7 @@ func benchmarkAdd(amount int64) (*testing.BenchmarkResult, error) { b.SetBytes(amount) for i := 0; i < b.N; i++ { b.StopTimer() - tmpDir, err := os.MkdirTemp("", "") - if err != nil { - benchmarkError = err - b.Fatal(err) - } - defer os.RemoveAll(tmpDir) + tmpDir := b.TempDir() env := append( []string{fmt.Sprintf("%s=%s", config.EnvDir, path.Join(tmpDir, config.DefaultPathName))}, // first in order to override diff --git a/test/bench/offline_add/main.go b/test/bench/offline_add/main.go index a15ebcffd..338a5f6ac 100644 --- a/test/bench/offline_add/main.go +++ b/test/bench/offline_add/main.go @@ -37,11 +37,7 @@ func benchmarkAdd(amount int64) (*testing.BenchmarkResult, error) { b.SetBytes(amount) for i := 0; i < b.N; i++ { b.StopTimer() - tmpDir, err := os.MkdirTemp("", "") - if err != nil { - b.Fatal(err) - } - defer os.RemoveAll(tmpDir) + tmpDir := b.TempDir() env := append(os.Environ(), fmt.Sprintf("%s=%s", config.EnvDir, path.Join(tmpDir, config.DefaultPathName))) setupCmd := func(cmd *exec.Cmd) { From b4211be7d6147193882efb6e4da1aecabe71a563 Mon Sep 17 00:00:00 2001 From: Gus Eggert Date: Fri, 16 Dec 2022 06:55:03 -0500 Subject: [PATCH 13/26] test: port peering test from sharness to Go This is the slowest test in the sharness test suite, because it has very long sleeps. It usually takes 2+ minutes to run. This new impl runs all peering tests in about 20 seconds, since it polls for conditions instead of sleeping, and runs the tests in parallel. This also has an additional test case for a peer that was never online and then connects. --- test/cli/harness/harness.go | 21 +++++ test/cli/harness/log.go | 155 +++++++++++++++++++++++++++++++++ test/cli/harness/node.go | 25 +++++- test/cli/harness/nodes.go | 20 +---- test/cli/peering_test.go | 141 ++++++++++++++++++++++++++++++ test/cli/testutils/strings.go | 14 +++ test/sharness/t0171-peering.sh | 127 --------------------------- 7 files changed, 358 insertions(+), 145 deletions(-) create mode 100644 test/cli/harness/log.go create mode 100644 test/cli/peering_test.go delete mode 100755 test/sharness/t0171-peering.sh diff --git a/test/cli/harness/harness.go b/test/cli/harness/harness.go index a35fead35..e68116b5e 100644 --- a/test/cli/harness/harness.go +++ b/test/cli/harness/harness.go @@ -11,6 +11,8 @@ import ( logging "github.com/ipfs/go-log/v2" . "github.com/ipfs/kubo/test/cli/testutils" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/multiformats/go-multiaddr" ) // Harness tracks state for a test, such as temp dirs and IFPS nodes, and cleans them up after the test. @@ -188,3 +190,22 @@ func (h *Harness) Cleanup() { log.Panicf("removing temp dir %s: %s", h.Dir, err) } } + +// ExtractPeerID extracts a peer ID from the given multiaddr, and fatals if it does not contain a peer ID. +func (h *Harness) ExtractPeerID(m multiaddr.Multiaddr) peer.ID { + var peerIDStr string + multiaddr.ForEach(m, func(c multiaddr.Component) bool { + if c.Protocol().Code == multiaddr.P_P2P { + peerIDStr = c.Value() + } + return true + }) + if peerIDStr == "" { + panic(multiaddr.ErrProtocolNotFound) + } + peerID, err := peer.Decode(peerIDStr) + if err != nil { + panic(err) + } + return peerID +} diff --git a/test/cli/harness/log.go b/test/cli/harness/log.go new file mode 100644 index 000000000..d76bb2747 --- /dev/null +++ b/test/cli/harness/log.go @@ -0,0 +1,155 @@ +package harness + +import ( + "fmt" + "path/filepath" + "runtime" + "sort" + "strings" + "sync" + "testing" + "time" +) + +type event struct { + timestamp time.Time + msg string +} + +type events []*event + +func (e events) Len() int { return len(e) } +func (e events) Less(i, j int) bool { return e[i].timestamp.Before(e[j].timestamp) } +func (e events) Swap(i, j int) { e[i], e[j] = e[j], e[i] } + +// TestLogger is a logger for tests. +// It buffers output and only writes the output if the test fails or output is explicitly turned on. +// The purpose of this logger is to allow Go test to run with the verbose flag without printing logs. +// The verbose flag is useful since it streams test progress, but also printing logs makes the output too verbose. +// +// You can also add prefixes that are prepended to each log message, for extra logging context. +// +// This is implemented as a hierarchy of loggers, with children flushing log entries back to parents. +// This works because t.Cleanup() processes entries in LIFO order, so children always flush first. +// +// Obviously this logger should never be used in production systems. +type TestLogger struct { + parent *TestLogger + children []*TestLogger + prefixes []string + prefixesIface []any + t *testing.T + buf events + m sync.Mutex + logsEnabled bool +} + +func NewTestLogger(t *testing.T) *TestLogger { + l := &TestLogger{t: t, buf: make(events, 0)} + t.Cleanup(l.flush) + return l +} + +func (t *TestLogger) buildPrefix(timestamp time.Time) string { + d := timestamp.Format("2006-01-02T15:04:05.999999") + _, file, lineno, _ := runtime.Caller(2) + file = filepath.Base(file) + caller := fmt.Sprintf("%s:%d", file, lineno) + + if len(t.prefixes) == 0 { + return fmt.Sprintf("%s\t%s\t", d, caller) + } + + prefixes := strings.Join(t.prefixes, ":") + return fmt.Sprintf("%s\t%s\t%s: ", d, caller, prefixes) +} + +func (t *TestLogger) Log(args ...any) { + timestamp := time.Now() + e := t.buildPrefix(timestamp) + fmt.Sprint(args...) + t.add(&event{timestamp: timestamp, msg: e}) +} + +func (t *TestLogger) Logf(format string, args ...any) { + timestamp := time.Now() + e := t.buildPrefix(timestamp) + fmt.Sprintf(format, args...) + t.add(&event{timestamp: timestamp, msg: e}) +} + +func (t *TestLogger) Fatal(args ...any) { + timestamp := time.Now() + e := t.buildPrefix(timestamp) + fmt.Sprint(append([]any{"fatal: "}, args...)...) + t.add(&event{timestamp: timestamp, msg: e}) + t.t.FailNow() +} + +func (t *TestLogger) Fatalf(format string, args ...any) { + timestamp := time.Now() + e := t.buildPrefix(timestamp) + fmt.Sprintf(fmt.Sprintf("fatal: %s", format), args...) + t.add(&event{timestamp: timestamp, msg: e}) + t.t.FailNow() +} + +func (t *TestLogger) add(e *event) { + t.m.Lock() + defer t.m.Unlock() + t.buf = append(t.buf, e) +} + +func (t *TestLogger) AddPrefix(prefix string) *TestLogger { + l := &TestLogger{ + prefixes: append(t.prefixes, prefix), + prefixesIface: append(t.prefixesIface, prefix), + t: t.t, + parent: t, + logsEnabled: t.logsEnabled, + } + t.m.Lock() + defer t.m.Unlock() + + t.children = append(t.children, l) + t.t.Cleanup(l.flush) + + return l +} + +func (t *TestLogger) EnableLogs() { + t.m.Lock() + defer t.m.Unlock() + t.logsEnabled = true + if t.parent != nil { + if t.parent.logsEnabled { + t.parent.EnableLogs() + } + } + fmt.Printf("enabling %d children\n", len(t.children)) + for _, c := range t.children { + if !c.logsEnabled { + c.EnableLogs() + } + } +} + +func (t *TestLogger) flush() { + if t.t.Failed() || t.logsEnabled { + t.m.Lock() + defer t.m.Unlock() + // if this is a child, send the events to the parent + // the root parent will print all the events in sorted order + if t.parent != nil { + for _, e := range t.buf { + t.parent.add(e) + } + } else { + // we're the root, sort all the events and then print them + sort.Sort(t.buf) + fmt.Println() + fmt.Printf("Logs for test %q:\n\n", t.t.Name()) + for _, e := range t.buf { + fmt.Println(e.msg) + } + fmt.Println() + } + t.buf = nil + } +} diff --git a/test/cli/harness/node.go b/test/cli/harness/node.go index cc251e11b..f740ab1b1 100644 --- a/test/cli/harness/node.go +++ b/test/cli/harness/node.go @@ -453,9 +453,8 @@ func (n *Node) Peers() []multiaddr.Multiaddr { Path: n.IPFSBin, Args: []string{"swarm", "peers"}, }) - lines := strings.Split(strings.TrimSpace(res.Stdout.String()), "\n") var addrs []multiaddr.Multiaddr - for _, line := range lines { + for _, line := range res.Stdout.Lines() { ma, err := multiaddr.NewMultiaddr(line) if err != nil { panic(err) @@ -465,6 +464,28 @@ func (n *Node) Peers() []multiaddr.Multiaddr { return addrs } +func (n *Node) PeerWith(other *Node) { + n.UpdateConfig(func(cfg *config.Config) { + var addrs []multiaddr.Multiaddr + for _, addrStr := range other.ReadConfig().Addresses.Swarm { + ma, err := multiaddr.NewMultiaddr(addrStr) + if err != nil { + panic(err) + } + addrs = append(addrs, ma) + } + + cfg.Peering.Peers = append(cfg.Peering.Peers, peer.AddrInfo{ + ID: other.PeerID(), + Addrs: addrs, + }) + }) +} + +func (n *Node) Disconnect(other *Node) { + n.IPFS("swarm", "disconnect", "/p2p/"+other.PeerID().String()) +} + // GatewayURL waits for the gateway file and then returns its contents or times out. func (n *Node) GatewayURL() string { timer := time.NewTimer(1 * time.Second) diff --git a/test/cli/harness/nodes.go b/test/cli/harness/nodes.go index 872d77679..78662afbb 100644 --- a/test/cli/harness/nodes.go +++ b/test/cli/harness/nodes.go @@ -3,6 +3,7 @@ package harness import ( "sync" + . "github.com/ipfs/kubo/test/cli/testutils" "github.com/multiformats/go-multiaddr" "golang.org/x/sync/errgroup" ) @@ -11,9 +12,7 @@ import ( type Nodes []*Node func (n Nodes) Init(args ...string) Nodes { - for _, node := range n { - node.Init() - } + ForEachPar(n, func(node *Node) { node.Init(args...) }) return n } @@ -59,22 +58,11 @@ func (n Nodes) Connect() Nodes { } func (n Nodes) StartDaemons() Nodes { - wg := sync.WaitGroup{} - for _, node := range n { - wg.Add(1) - node := node - go func() { - defer wg.Done() - node.StartDaemon() - }() - } - wg.Wait() + ForEachPar(n, func(node *Node) { node.StartDaemon() }) return n } func (n Nodes) StopDaemons() Nodes { - for _, node := range n { - node.StopDaemon() - } + ForEachPar(n, func(node *Node) { node.StopDaemon() }) return n } diff --git a/test/cli/peering_test.go b/test/cli/peering_test.go new file mode 100644 index 000000000..f3e797fae --- /dev/null +++ b/test/cli/peering_test.go @@ -0,0 +1,141 @@ +package cli + +import ( + "fmt" + "math/rand" + "testing" + "time" + + "github.com/ipfs/kubo/config" + "github.com/ipfs/kubo/test/cli/harness" + . "github.com/ipfs/kubo/test/cli/testutils" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/stretchr/testify/assert" +) + +func TestPeering(t *testing.T) { + t.Parallel() + + type peering struct { + from int + to int + } + + newRandPort := func() int { + n := rand.Int() + return 3000 + (n % 1000) + } + + containsPeerID := func(p peer.ID, peers []peer.ID) bool { + for _, peerID := range peers { + if p == peerID { + return true + } + } + return false + } + + assertPeered := func(h *harness.Harness, from *harness.Node, to *harness.Node) { + assert.Eventuallyf(t, func() bool { + fromPeers := from.Peers() + if len(fromPeers) == 0 { + return false + } + var fromPeerIDs []peer.ID + for _, p := range fromPeers { + fromPeerIDs = append(fromPeerIDs, h.ExtractPeerID(p)) + } + return containsPeerID(to.PeerID(), fromPeerIDs) + }, 20*time.Second, 10*time.Millisecond, "%d -> %d not peered", from.ID, to.ID) + } + + assertNotPeered := func(h *harness.Harness, from *harness.Node, to *harness.Node) { + assert.Eventuallyf(t, func() bool { + fromPeers := from.Peers() + if len(fromPeers) == 0 { + return false + } + var fromPeerIDs []peer.ID + for _, p := range fromPeers { + fromPeerIDs = append(fromPeerIDs, h.ExtractPeerID(p)) + } + return !containsPeerID(to.PeerID(), fromPeerIDs) + }, 20*time.Second, 10*time.Millisecond, "%d -> %d peered", from.ID, to.ID) + } + + assertPeerings := func(h *harness.Harness, nodes []*harness.Node, peerings []peering) { + ForEachPar(peerings, func(peering peering) { + assertPeered(h, nodes[peering.from], nodes[peering.to]) + }) + } + + createNodes := func(t *testing.T, n int, peerings []peering) (*harness.Harness, harness.Nodes) { + h := harness.NewT(t) + nodes := h.NewNodes(n).Init() + nodes.ForEachPar(func(node *harness.Node) { + node.UpdateConfig(func(cfg *config.Config) { + cfg.Routing.Type = config.NewOptionalString("none") + cfg.Addresses.Swarm = []string{fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", newRandPort())} + }) + + }) + + for _, peering := range peerings { + nodes[peering.from].PeerWith(nodes[peering.to]) + } + + return h, nodes + } + + t.Run("bidirectional peering should work (simultaneous connect)", func(t *testing.T) { + t.Parallel() + peerings := []peering{{from: 0, to: 1}, {from: 1, to: 0}, {from: 1, to: 2}} + h, nodes := createNodes(t, 3, peerings) + + nodes.StartDaemons() + assertPeerings(h, nodes, peerings) + + nodes[0].Disconnect(nodes[1]) + assertPeerings(h, nodes, peerings) + }) + + t.Run("1 should reconnect to 2 when 2 disconnects from 1", func(t *testing.T) { + t.Parallel() + peerings := []peering{{from: 0, to: 1}, {from: 1, to: 0}, {from: 1, to: 2}} + h, nodes := createNodes(t, 3, peerings) + + nodes.StartDaemons() + assertPeerings(h, nodes, peerings) + + nodes[2].Disconnect(nodes[1]) + assertPeerings(h, nodes, peerings) + }) + + t.Run("1 will peer with 2 when it comes online", func(t *testing.T) { + t.Parallel() + peerings := []peering{{from: 0, to: 1}, {from: 1, to: 0}, {from: 1, to: 2}} + h, nodes := createNodes(t, 3, peerings) + + nodes[0].StartDaemon() + nodes[1].StartDaemon() + assertPeerings(h, nodes, []peering{{from: 0, to: 1}, {from: 1, to: 0}}) + + nodes[2].StartDaemon() + assertPeerings(h, nodes, peerings) + }) + + t.Run("1 will re-peer with 2 when it disconnects and then comes back online", func(t *testing.T) { + t.Parallel() + peerings := []peering{{from: 0, to: 1}, {from: 1, to: 0}, {from: 1, to: 2}} + h, nodes := createNodes(t, 3, peerings) + + nodes.StartDaemons() + assertPeerings(h, nodes, peerings) + + nodes[2].StopDaemon() + assertNotPeered(h, nodes[1], nodes[2]) + + nodes[2].StartDaemon() + assertPeerings(h, nodes, peerings) + }) +} diff --git a/test/cli/testutils/strings.go b/test/cli/testutils/strings.go index 1fb151248..110051e67 100644 --- a/test/cli/testutils/strings.go +++ b/test/cli/testutils/strings.go @@ -7,6 +7,7 @@ import ( "net/netip" "net/url" "strings" + "sync" "github.com/multiformats/go-multiaddr" manet "github.com/multiformats/go-multiaddr/net" @@ -75,3 +76,16 @@ func URLStrToMultiaddr(u string) multiaddr.Multiaddr { } return ma } + +// ForEachPar invokes f in a new goroutine for each element of s and waits for all to complete. +func ForEachPar[T any](s []T, f func(T)) { + wg := sync.WaitGroup{} + wg.Add(len(s)) + for _, x := range s { + go func(x T) { + defer wg.Done() + f(x) + }(x) + } + wg.Wait() +} diff --git a/test/sharness/t0171-peering.sh b/test/sharness/t0171-peering.sh deleted file mode 100755 index 207b27980..000000000 --- a/test/sharness/t0171-peering.sh +++ /dev/null @@ -1,127 +0,0 @@ -#!/usr/bin/env bash - -test_description="Test peering service" - -. lib/test-lib.sh - -NUM_NODES=3 - -test_expect_success 'init iptb' ' - rm -rf .iptb/ && - iptb testbed create -type localipfs -count $NUM_NODES -init -' - -test_expect_success 'disabling routing' ' - iptb run -- ipfs config Routing.Type none -' - -for i in $(seq 0 2); do - ADDR="$(printf '["/ip4/127.0.0.1/tcp/%s"]' "$(( 3000 + ( RANDOM % 1000 ) ))")" - test_expect_success "configuring node $i to listen on $ADDR" ' - ipfsi "$i" config --json Addresses.Swarm "$ADDR" - ' -done - -peer_id() { - ipfsi "$1" config Identity.PeerID -} - -peer_addrs() { - ipfsi "$1" config Addresses.Swarm -} - -peer() { - PEER1="$1" && - PEER2="$2" && - PEER_LIST="$(ipfsi "$PEER1" config Peering.Peers || true)" && - { [[ "$PEER_LIST" == "null" ]] || PEER_LIST_INNER="${PEER_LIST:1:-1}"; } && - ADDR_INFO="$(printf '[%s{"ID": "%s", "Addrs": %s}]' \ - "${PEER_LIST_INNER:+${PEER_LIST_INNER},}" \ - "$(peer_id "$PEER2")" \ - "$(peer_addrs "$PEER2")")" && - ipfsi "$PEER1" config --json Peering.Peers "${ADDR_INFO}" -} - -# Peer: -# - 0 <-> 1 -# - 1 -> 2 -test_expect_success 'configure peering' ' - peer 0 1 && - peer 1 0 && - peer 1 2 -' - -list_peers() { - ipfsi "$1" swarm peers | sed 's|.*/p2p/\([^/]*\)$|\1|' | sort -u -} - -check_peers() { - sleep 20 # give it some time to settle. - test_expect_success 'verifying peering for peer 0' ' - list_peers 0 > peers_0_actual && - peer_id 1 > peers_0_expected && - test_cmp peers_0_expected peers_0_actual - ' - - test_expect_success 'verifying peering for peer 1' ' - list_peers 1 > peers_1_actual && - { peer_id 0 && peer_id 2 ; } | sort -u > peers_1_expected && - test_cmp peers_1_expected peers_1_actual - ' - - test_expect_success 'verifying peering for peer 2' ' - list_peers 2 > peers_2_actual && - peer_id 1 > peers_2_expected && - test_cmp peers_2_expected peers_2_actual - ' -} - -test_expect_success 'startup cluster' ' - iptb start -wait && - iptb run -- ipfs log level peering debug -' - -check_peers - -disconnect() { - ipfsi "$1" swarm disconnect "/p2p/$(peer_id "$2")" -} - -# Bidirectional peering shouldn't cause problems (e.g., simultaneous connect -# issues). -test_expect_success 'disconnecting 0->1' ' - disconnect 0 1 -' - -check_peers - -# 1 should reconnect to 2 when 2 disconnects from 1. -test_expect_success 'disconnecting 2->1' ' - disconnect 2 1 -' - -check_peers - -# 2 isn't peering. This test ensures that 1 will re-peer with 2 when it comes -# back online. -test_expect_success 'stopping 2' ' - iptb stop 2 -' - -# Wait to disconnect -sleep 30 - -test_expect_success 'starting 2' ' - iptb start 2 -' - -# Wait for backoff -sleep 30 - -check_peers - -test_expect_success "stop testbed" ' - iptb stop -' - -test_done From cc5e1325b4646e0eb651a63ad04ac86086d2589b Mon Sep 17 00:00:00 2001 From: Jorropo Date: Wed, 15 Mar 2023 03:23:41 +0100 Subject: [PATCH 14/26] chore: deprecate the pubsub api Fixes #9717 --- README.md | 3 +-- cmd/ipfs/daemon.go | 2 +- core/commands/pubsub.go | 20 ++++++++++---------- docs/changelogs/v0.19.md | 7 +++++++ docs/config.md | 12 +++++++++++- docs/experimental-features.md | 31 ------------------------------- 6 files changed, 30 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index e734fef66..94e5e42af 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Kubo was the first IPFS implementation and is the most widely used one today. Im Featureset - Runs an IPFS-Node as a network service - [Command Line Interface](https://docs.ipfs.tech/reference/kubo/cli/) to IPFS-Nodes -- Local [Web2-to-Web3 HTTP Gateway functionality](https://github.com/ipfs/specs/tree/main/http-gateways#readme) +- Local [Web2-to-Web3 HTTP Gateway functionality](https://github.com/ipfs/specs/tree/main/http-gateways#readme) - HTTP RPC API (`/api/v0`) to access and control the daemon - IPFS's internal Webgui can be used to manage the Kubo nodes @@ -381,7 +381,6 @@ Some places to get you started on the codebase: - libp2p - libp2p: https://github.com/libp2p/go-libp2p - DHT: https://github.com/libp2p/go-libp2p-kad-dht - - PubSub: https://github.com/libp2p/go-libp2p-pubsub - [IPFS : The `Add` command demystified](https://github.com/ipfs/kubo/tree/master/docs/add-code-flow.md) ### Map of Implemented Subsystems diff --git a/cmd/ipfs/daemon.go b/cmd/ipfs/daemon.go index 880d26b0e..52addcc07 100644 --- a/cmd/ipfs/daemon.go +++ b/cmd/ipfs/daemon.go @@ -171,7 +171,7 @@ Headers. cmds.BoolOption(enableGCKwd, "Enable automatic periodic repo garbage collection"), cmds.BoolOption(adjustFDLimitKwd, "Check and raise file descriptor limits if needed").WithDefault(true), cmds.BoolOption(migrateKwd, "If true, assume yes at the migrate prompt. If false, assume no."), - cmds.BoolOption(enablePubSubKwd, "Enable experimental pubsub feature. Overrides Pubsub.Enabled config."), + cmds.BoolOption(enablePubSubKwd, "DEPRECATED"), cmds.BoolOption(enableIPNSPubSubKwd, "Enable IPNS over pubsub. Implicitly enables pubsub, overrides Ipns.UsePubsub config."), cmds.BoolOption(enableMultiplexKwd, "DEPRECATED"), cmds.StringOption(agentVersionSuffix, "Optional suffix to the AgentVersion presented by `ipfs id` and also advertised through BitSwap."), diff --git a/core/commands/pubsub.go b/core/commands/pubsub.go index ef8afcb44..ee795e078 100644 --- a/core/commands/pubsub.go +++ b/core/commands/pubsub.go @@ -16,14 +16,14 @@ import ( ) var PubsubCmd = &cmds.Command{ - Status: cmds.Experimental, + Status: cmds.Deprecated, Helptext: cmds.HelpText{ Tagline: "An experimental publish-subscribe system on ipfs.", ShortDescription: ` ipfs pubsub allows you to publish messages to a given topic, and also to subscribe to new messages on a given topic. -EXPERIMENTAL FEATURE +DEPRECATED FEATURE (see https://github.com/ipfs/kubo/issues/9717) It is not intended in its current state to be used in a production environment. To use, the daemon must be run with @@ -46,13 +46,13 @@ type pubsubMessage struct { } var PubsubSubCmd = &cmds.Command{ - Status: cmds.Experimental, + Status: cmds.Deprecated, Helptext: cmds.HelpText{ Tagline: "Subscribe to messages on a given topic.", ShortDescription: ` ipfs pubsub sub subscribes to messages on a given topic. -EXPERIMENTAL FEATURE +DEPRECATED FEATURE (see https://github.com/ipfs/kubo/issues/9717) It is not intended in its current state to be used in a production environment. To use, the daemon must be run with @@ -145,14 +145,14 @@ TOPIC AND DATA ENCODING } var PubsubPubCmd = &cmds.Command{ - Status: cmds.Experimental, + Status: cmds.Deprecated, Helptext: cmds.HelpText{ Tagline: "Publish data to a given pubsub topic.", ShortDescription: ` ipfs pubsub pub publishes a message to a specified topic. It reads binary data from stdin or a file. -EXPERIMENTAL FEATURE +DEPRECATED FEATURE (see https://github.com/ipfs/kubo/issues/9717) It is not intended in its current state to be used in a production environment. To use, the daemon must be run with @@ -201,13 +201,13 @@ HTTP RPC ENCODING } var PubsubLsCmd = &cmds.Command{ - Status: cmds.Experimental, + Status: cmds.Deprecated, Helptext: cmds.HelpText{ Tagline: "List subscribed topics by name.", ShortDescription: ` ipfs pubsub ls lists out the names of topics you are currently subscribed to. -EXPERIMENTAL FEATURE +DEPRECATED FEATURE (see https://github.com/ipfs/kubo/issues/9717) It is not intended in its current state to be used in a production environment. To use, the daemon must be run with @@ -273,7 +273,7 @@ func safeTextListEncoder(req *cmds.Request, w io.Writer, list *stringList) error } var PubsubPeersCmd = &cmds.Command{ - Status: cmds.Experimental, + Status: cmds.Deprecated, Helptext: cmds.HelpText{ Tagline: "List peers we are currently pubsubbing with.", ShortDescription: ` @@ -281,7 +281,7 @@ ipfs pubsub peers with no arguments lists out the pubsub peers you are currently connected to. If given a topic, it will list connected peers who are subscribed to the named topic. -EXPERIMENTAL FEATURE +DEPRECATED FEATURE (see https://github.com/ipfs/kubo/issues/9717) It is not intended in its current state to be used in a production environment. To use, the daemon must be run with diff --git a/docs/changelogs/v0.19.md b/docs/changelogs/v0.19.md index fede7a454..12331cae7 100644 --- a/docs/changelogs/v0.19.md +++ b/docs/changelogs/v0.19.md @@ -8,6 +8,7 @@ - [🔦 Highlights](#-highlights) - [Improving the libp2p resource management integration](#improving-the-libp2p-resource-management-integration) - [Addition of "autoclient" router type](#addition-of-autoclient-router-type) + - [Deprecation of the `ipfs pubsub` commands and matching HTTP endpoints](#deprecation-of-the-ipfs-pubsub-commands-and-matching-http-endpoints) - [📝 Changelog](#-changelog) - [👨‍👩‍👧‍👦 Contributors](#-contributors) @@ -28,6 +29,12 @@ A new routing type "autoclient" has been added. This mode is similar to "auto", See the [Routing.Type documentation](https://github.com/ipfs/kubo/blob/master/docs/config.md#routingtype) for more information. +#### Deprecation of the `ipfs pubsub` commands and matching HTTP endpoints + +We are deprecating `ipfs pubsub` and all `/api/v0/pubsub/` RPC endpoints and will remove them in the next release. + +For more information and rational see [#9717](https://github.com/ipfs/kubo/issues/9717). + ### 📝 Changelog ### 👨‍👩‍👧‍👦 Contributors diff --git a/docs/config.md b/docs/config.md index de47e5445..28331c2ae 100644 --- a/docs/config.md +++ b/docs/config.md @@ -1165,13 +1165,15 @@ Type: `duration` ## `Pubsub` +**DEPRECATED**: See [#9717](https://github.com/ipfs/kubo/issues/9717) + Pubsub configures the `ipfs pubsub` subsystem. To use, it must be enabled by passing the `--enable-pubsub-experiment` flag to the daemon or via the `Pubsub.Enabled` flag below. ### `Pubsub.Enabled` -**EXPERIMENTAL:** read about current limitations at [experimental-features.md#ipfs-pubsub](./experimental-features.md#ipfs-pubsub). +**DEPRECATED**: See [#9717](https://github.com/ipfs/kubo/issues/9717) Enables the pubsub system. @@ -1181,6 +1183,8 @@ Type: `flag` ### `Pubsub.Router` +**DEPRECATED**: See [#9717](https://github.com/ipfs/kubo/issues/9717) + Sets the default router used by pubsub to route messages to peers. This can be one of: * `"floodsub"` - floodsub is a basic router that simply _floods_ messages to all @@ -1196,6 +1200,8 @@ Type: `string` (one of `"floodsub"`, `"gossipsub"`, or `""` (apply default)) ### `Pubsub.DisableSigning` +**DEPRECATED**: See [#9717](https://github.com/ipfs/kubo/issues/9717) + Disables message signing and signature verification. Enable this option if you're operating in a completely trusted network. @@ -1209,6 +1215,8 @@ Type: `bool` ### `Pubsub.SeenMessagesTTL` +**DEPRECATED**: See [#9717](https://github.com/ipfs/kubo/issues/9717) + Controls the time window within which duplicate messages, identified by Message ID, will be identified and won't be emitted again. @@ -1228,6 +1236,8 @@ Type: `optionalDuration` ### `Pubsub.SeenMessagesStrategy` +**DEPRECATED**: See [#9717](https://github.com/ipfs/kubo/issues/9717) + Determines how the time-to-live (TTL) countdown for deduplicating Pubsub messages is calculated. diff --git a/docs/experimental-features.md b/docs/experimental-features.md index 41e72f1df..296f6ca1e 100644 --- a/docs/experimental-features.md +++ b/docs/experimental-features.md @@ -12,7 +12,6 @@ When you add a new experimental feature to kubo or change an experimental feature, you MUST please make a PR updating this document, and link the PR in the above issue. -- [ipfs pubsub](#ipfs-pubsub) - [Raw leaves for unixfs files](#raw-leaves-for-unixfs-files) - [ipfs filestore](#ipfs-filestore) - [ipfs urlstore](#ipfs-urlstore) @@ -31,36 +30,6 @@ the above issue. --- -## ipfs pubsub - -### State - -Candidate, disabled by default but will be enabled by default in 0.6.0. - -### In Version - -0.4.5 (`--enable-pubsub-experiment`) -0.11.0 (`Pubsub.Enabled` flag in config) - -### How to enable - -Run your daemon with the `--enable-pubsub-experiment` flag -or modify your ipfs config and restart the daemon: -``` -ipfs config --json Pubsub.Enabled true -``` - -Then use the `ipfs pubsub` commands. - -NOTE: `--enable-pubsub-experiment` CLI flag overrides `Pubsub.Enabled` config. - -Configuration documentation can be found in [kubo/docs/config.md](./config.md#pubsub) - -### Road to being a real feature - -- [ ] Needs to not impact peers who don't use pubsub: - https://github.com/libp2p/go-libp2p-pubsub/issues/332 - ## Raw Leaves for unixfs files Allows files to be added with no formatting in the leaf nodes of the graph. From c78c9886ad1c647f33846908b4bf01ecc769cb12 Mon Sep 17 00:00:00 2001 From: Gus Eggert Date: Wed, 15 Mar 2023 14:57:23 -0400 Subject: [PATCH 15/26] test: fix flaky rcmgr test --- test/cli/rcmgr_test.go | 6 +++++- test/cli/testutils/asserts.go | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 test/cli/testutils/asserts.go diff --git a/test/cli/rcmgr_test.go b/test/cli/rcmgr_test.go index 51b2b0452..9d76a7dd8 100644 --- a/test/cli/rcmgr_test.go +++ b/test/cli/rcmgr_test.go @@ -7,6 +7,7 @@ import ( "github.com/ipfs/kubo/config" "github.com/ipfs/kubo/core/node/libp2p" "github.com/ipfs/kubo/test/cli/harness" + "github.com/ipfs/kubo/test/cli/testutils" "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/protocol" rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager" @@ -209,7 +210,10 @@ func TestRcmgr(t *testing.T) { Args: []string{"swarm", "connect", node1.SwarmAddrsWithPeerIDs()[0].String()}, }) assert.Equal(t, 1, res.ExitCode()) - assert.Contains(t, res.Stderr.String(), "failed to find any peer in table") + testutils.AssertStringContainsOneOf(t, res.Stderr.String(), + "failed to find any peer in table", + "resource limit exceeded", + ) res = node0.RunIPFS("ping", "-n2", peerID1) assert.Equal(t, 1, res.ExitCode()) diff --git a/test/cli/testutils/asserts.go b/test/cli/testutils/asserts.go new file mode 100644 index 000000000..cf840c20e --- /dev/null +++ b/test/cli/testutils/asserts.go @@ -0,0 +1,15 @@ +package testutils + +import ( + "strings" + "testing" +) + +func AssertStringContainsOneOf(t *testing.T, str string, ss ...string) { + for _, s := range ss { + if strings.Contains(str, s) { + return + } + } + t.Errorf("%q does not contain one of %v", str, ss) +} From 6ff764f9fb1bfbff2625f46c732d3515b0443c70 Mon Sep 17 00:00:00 2001 From: Jorropo Date: Wed, 15 Mar 2023 04:40:16 +0100 Subject: [PATCH 16/26] fix: preserve Unlimited StreamsInbound in connmgr reconciliation Fixes #9695 --- core/node/libp2p/rcmgr.go | 4 ++++ core/node/libp2p/rcmgr_defaults.go | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/core/node/libp2p/rcmgr.go b/core/node/libp2p/rcmgr.go index 54bff2852..3e1624531 100644 --- a/core/node/libp2p/rcmgr.go +++ b/core/node/libp2p/rcmgr.go @@ -459,6 +459,7 @@ func ensureConnMgrMakeSenseVsResourceMgr(concreteLimits rcmgr.ConcreteLimitConfi return fmt.Errorf(` Unable to initialize libp2p due to conflicting resource manager limit configuration. resource manager System.Conns (%d) must be bigger than ConnMgr.HighWater (%d) +See: https://github.com/ipfs/kubo/blob/master/docs/libp2p-resource-management.md#how-does-the-resource-manager-resourcemgr-relate-to-the-connection-manager-connmgr `, rcm.System.Conns, highWater) } if rcm.System.ConnsInbound != rcmgr.Unlimited && int64(rcm.System.ConnsInbound) <= highWater { @@ -466,6 +467,7 @@ resource manager System.Conns (%d) must be bigger than ConnMgr.HighWater (%d) return fmt.Errorf(` Unable to initialize libp2p due to conflicting resource manager limit configuration. resource manager System.ConnsInbound (%d) must be bigger than ConnMgr.HighWater (%d) +See: https://github.com/ipfs/kubo/blob/master/docs/libp2p-resource-management.md#how-does-the-resource-manager-resourcemgr-relate-to-the-connection-manager-connmgr `, rcm.System.ConnsInbound, highWater) } if rcm.System.Streams != rcmgr.Unlimited && int64(rcm.System.Streams) <= highWater { @@ -473,6 +475,7 @@ resource manager System.ConnsInbound (%d) must be bigger than ConnMgr.HighWater return fmt.Errorf(` Unable to initialize libp2p due to conflicting resource manager limit configuration. resource manager System.Streams (%d) must be bigger than ConnMgr.HighWater (%d) +See: https://github.com/ipfs/kubo/blob/master/docs/libp2p-resource-management.md#how-does-the-resource-manager-resourcemgr-relate-to-the-connection-manager-connmgr `, rcm.System.Streams, highWater) } if rcm.System.StreamsInbound != rcmgr.Unlimited && int64(rcm.System.StreamsInbound) <= highWater { @@ -480,6 +483,7 @@ resource manager System.Streams (%d) must be bigger than ConnMgr.HighWater (%d) return fmt.Errorf(` Unable to initialize libp2p due to conflicting resource manager limit configuration. resource manager System.StreamsInbound (%d) must be bigger than ConnMgr.HighWater (%d) +See: https://github.com/ipfs/kubo/blob/master/docs/libp2p-resource-management.md#how-does-the-resource-manager-resourcemgr-relate-to-the-connection-manager-connmgr `, rcm.System.StreamsInbound, highWater) } return nil diff --git a/core/node/libp2p/rcmgr_defaults.go b/core/node/libp2p/rcmgr_defaults.go index 5faaf7979..4f02c6b4a 100644 --- a/core/node/libp2p/rcmgr_defaults.go +++ b/core/node/libp2p/rcmgr_defaults.go @@ -129,7 +129,9 @@ func createDefaultLimitConfig(cfg config.SwarmConfig) (limitConfig rcmgr.Concret } // Scale System.StreamsInbound as well, but use the existing ratio of StreamsInbound to ConnsInbound - partialLimits.System.StreamsInbound = rcmgr.LimitVal(maxInboundConns * int64(partialLimits.System.StreamsInbound) / int64(partialLimits.System.ConnsInbound)) + if partialLimits.System.StreamsInbound != rcmgr.Unlimited { + partialLimits.System.StreamsInbound = rcmgr.LimitVal(maxInboundConns * int64(partialLimits.System.StreamsInbound) / int64(partialLimits.System.ConnsInbound)) + } partialLimits.System.ConnsInbound = rcmgr.LimitVal(maxInboundConns) } From 383065397deafc7031aaa9ddad69c450400c3e2b Mon Sep 17 00:00:00 2001 From: Jorropo Date: Wed, 15 Mar 2023 17:36:08 +0100 Subject: [PATCH 17/26] test: add test for presarvation of unlimited configs for inbound systems --- test/cli/rcmgr_test.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/cli/rcmgr_test.go b/test/cli/rcmgr_test.go index 9d76a7dd8..3dbceb5d7 100644 --- a/test/cli/rcmgr_test.go +++ b/test/cli/rcmgr_test.go @@ -127,6 +127,22 @@ func TestRcmgr(t *testing.T) { }) }) + t.Run("smoke test unlimited System inbounds", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + node.UpdateUserSuppliedResourceManagerOverrides(func(overrides *rcmgr.PartialLimitConfig) { + overrides.System.StreamsInbound = rcmgr.Unlimited + overrides.System.ConnsInbound = rcmgr.Unlimited + }) + node.StartDaemon() + + res := node.RunIPFS("swarm", "resources", "--enc=json") + limits := unmarshalLimits(t, res.Stdout.Bytes()) + + assert.Equal(t, rcmgr.Unlimited, limits.System.ConnsInbound) + assert.Equal(t, rcmgr.Unlimited, limits.System.StreamsInbound) + }) + t.Run("smoke test transient scope", func(t *testing.T) { t.Parallel() node := harness.NewT(t).NewNode().Init() From af79d841dcd1429980d20a5427949f1afea8b11f Mon Sep 17 00:00:00 2001 From: Jorropo Date: Wed, 15 Mar 2023 17:40:12 +0100 Subject: [PATCH 18/26] fix: future proof with > rcmgr.DefaultLimit for new enum rcmgr values --- core/node/libp2p/rcmgr.go | 8 ++++---- core/node/libp2p/rcmgr_defaults.go | 4 ++-- test/cli/rcmgr_test.go | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/core/node/libp2p/rcmgr.go b/core/node/libp2p/rcmgr.go index 3e1624531..c61f8dcfe 100644 --- a/core/node/libp2p/rcmgr.go +++ b/core/node/libp2p/rcmgr.go @@ -454,7 +454,7 @@ func ensureConnMgrMakeSenseVsResourceMgr(concreteLimits rcmgr.ConcreteLimitConfi rcm := concreteLimits.ToPartialLimitConfig() highWater := cfg.ConnMgr.HighWater.WithDefault(config.DefaultConnMgrHighWater) - if rcm.System.Conns != rcmgr.Unlimited && int64(rcm.System.Conns) <= highWater { + if (rcm.System.Conns > rcmgr.DefaultLimit || rcm.System.Conns == rcmgr.BlockAllLimit) && int64(rcm.System.Conns) <= highWater { // nolint return fmt.Errorf(` Unable to initialize libp2p due to conflicting resource manager limit configuration. @@ -462,7 +462,7 @@ resource manager System.Conns (%d) must be bigger than ConnMgr.HighWater (%d) See: https://github.com/ipfs/kubo/blob/master/docs/libp2p-resource-management.md#how-does-the-resource-manager-resourcemgr-relate-to-the-connection-manager-connmgr `, rcm.System.Conns, highWater) } - if rcm.System.ConnsInbound != rcmgr.Unlimited && int64(rcm.System.ConnsInbound) <= highWater { + if (rcm.System.ConnsInbound > rcmgr.DefaultLimit || rcm.System.ConnsInbound == rcmgr.BlockAllLimit) && int64(rcm.System.ConnsInbound) <= highWater { // nolint return fmt.Errorf(` Unable to initialize libp2p due to conflicting resource manager limit configuration. @@ -470,7 +470,7 @@ resource manager System.ConnsInbound (%d) must be bigger than ConnMgr.HighWater See: https://github.com/ipfs/kubo/blob/master/docs/libp2p-resource-management.md#how-does-the-resource-manager-resourcemgr-relate-to-the-connection-manager-connmgr `, rcm.System.ConnsInbound, highWater) } - if rcm.System.Streams != rcmgr.Unlimited && int64(rcm.System.Streams) <= highWater { + if rcm.System.Streams > rcmgr.DefaultLimit || rcm.System.Streams == rcmgr.BlockAllLimit && int64(rcm.System.Streams) <= highWater { // nolint return fmt.Errorf(` Unable to initialize libp2p due to conflicting resource manager limit configuration. @@ -478,7 +478,7 @@ resource manager System.Streams (%d) must be bigger than ConnMgr.HighWater (%d) See: https://github.com/ipfs/kubo/blob/master/docs/libp2p-resource-management.md#how-does-the-resource-manager-resourcemgr-relate-to-the-connection-manager-connmgr `, rcm.System.Streams, highWater) } - if rcm.System.StreamsInbound != rcmgr.Unlimited && int64(rcm.System.StreamsInbound) <= highWater { + if (rcm.System.StreamsInbound > rcmgr.DefaultLimit || rcm.System.StreamsInbound == rcmgr.BlockAllLimit) && int64(rcm.System.StreamsInbound) <= highWater { // nolint return fmt.Errorf(` Unable to initialize libp2p due to conflicting resource manager limit configuration. diff --git a/core/node/libp2p/rcmgr_defaults.go b/core/node/libp2p/rcmgr_defaults.go index 4f02c6b4a..7a0e5a282 100644 --- a/core/node/libp2p/rcmgr_defaults.go +++ b/core/node/libp2p/rcmgr_defaults.go @@ -118,7 +118,7 @@ func createDefaultLimitConfig(cfg config.SwarmConfig) (limitConfig rcmgr.Concret // There are ways to break this, but this should catch most problems already. // We might improve this in the future. // See: https://github.com/ipfs/kubo/issues/9545 - if partialLimits.System.ConnsInbound != rcmgr.Unlimited && cfg.ConnMgr.Type.WithDefault(config.DefaultConnMgrType) != "none" { + if partialLimits.System.ConnsInbound > rcmgr.DefaultLimit && cfg.ConnMgr.Type.WithDefault(config.DefaultConnMgrType) != "none" { maxInboundConns := int64(partialLimits.System.ConnsInbound) if connmgrHighWaterTimesTwo := cfg.ConnMgr.HighWater.WithDefault(config.DefaultConnMgrHighWater) * 2; maxInboundConns < connmgrHighWaterTimesTwo { maxInboundConns = connmgrHighWaterTimesTwo @@ -129,7 +129,7 @@ func createDefaultLimitConfig(cfg config.SwarmConfig) (limitConfig rcmgr.Concret } // Scale System.StreamsInbound as well, but use the existing ratio of StreamsInbound to ConnsInbound - if partialLimits.System.StreamsInbound != rcmgr.Unlimited { + if partialLimits.System.StreamsInbound > rcmgr.DefaultLimit { partialLimits.System.StreamsInbound = rcmgr.LimitVal(maxInboundConns * int64(partialLimits.System.StreamsInbound) / int64(partialLimits.System.ConnsInbound)) } partialLimits.System.ConnsInbound = rcmgr.LimitVal(maxInboundConns) diff --git a/test/cli/rcmgr_test.go b/test/cli/rcmgr_test.go index 3dbceb5d7..50ea26979 100644 --- a/test/cli/rcmgr_test.go +++ b/test/cli/rcmgr_test.go @@ -81,10 +81,10 @@ func TestRcmgr(t *testing.T) { require.Equal(t, 0, res.ExitCode()) limits := unmarshalLimits(t, res.Stdout.Bytes()) - if limits.System.ConnsInbound != rcmgr.Unlimited { + if limits.System.ConnsInbound > rcmgr.DefaultLimit { assert.GreaterOrEqual(t, limits.System.ConnsInbound, 800) } - if limits.System.StreamsInbound != rcmgr.Unlimited { + if limits.System.StreamsInbound > rcmgr.DefaultLimit { assert.GreaterOrEqual(t, limits.System.StreamsInbound, 800) } }) From 3c35a0cdea9a9029fe4740f9f609fa99d5965d92 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Thu, 16 Mar 2023 17:21:35 +0100 Subject: [PATCH 19/26] chore: bump go-libipfs@v0.6.2 --- docs/examples/kubo-as-a-library/go.mod | 2 +- docs/examples/kubo-as-a-library/go.sum | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/examples/kubo-as-a-library/go.mod b/docs/examples/kubo-as-a-library/go.mod index 1c19dd3eb..d96fa795d 100644 --- a/docs/examples/kubo-as-a-library/go.mod +++ b/docs/examples/kubo-as-a-library/go.mod @@ -7,7 +7,7 @@ go 1.18 replace github.com/ipfs/kubo => ./../../.. require ( - github.com/ipfs/go-libipfs v0.6.1 + github.com/ipfs/go-libipfs v0.6.2 github.com/ipfs/interface-go-ipfs-core v0.11.0 github.com/ipfs/kubo v0.0.0-00010101000000-000000000000 github.com/libp2p/go-libp2p v0.26.2 diff --git a/docs/examples/kubo-as-a-library/go.sum b/docs/examples/kubo-as-a-library/go.sum index c927b58d9..3a08f464a 100644 --- a/docs/examples/kubo-as-a-library/go.sum +++ b/docs/examples/kubo-as-a-library/go.sum @@ -569,8 +569,8 @@ github.com/ipfs/go-ipld-legacy v0.1.1 h1:BvD8PEuqwBHLTKqlGFTHSwrwFOMkVESEvwIYwR2 github.com/ipfs/go-ipld-legacy v0.1.1/go.mod h1:8AyKFCjgRPsQFf15ZQgDB8Din4DML/fOmKZkkFkrIEg= github.com/ipfs/go-ipns v0.3.0 h1:ai791nTgVo+zTuq2bLvEGmWP1M0A6kGTXUsgv/Yq67A= github.com/ipfs/go-ipns v0.3.0/go.mod h1:3cLT2rbvgPZGkHJoPO1YMJeh6LtkxopCkKFcio/wE24= -github.com/ipfs/go-libipfs v0.6.1 h1:OSO9cm1H3r4OXfP0MP1Q5UhTnhd2fByGl6CVYyz/Rhk= -github.com/ipfs/go-libipfs v0.6.1/go.mod h1:FmhKgxMOQA572TK5DA3MZ5GL44ZqsMHIrkgK4gLn4A8= +github.com/ipfs/go-libipfs v0.6.2 h1:QUf3kS3RrCjgtE0QW2d18PFFfOLeEt24Ft892ipLzRI= +github.com/ipfs/go-libipfs v0.6.2/go.mod h1:FmhKgxMOQA572TK5DA3MZ5GL44ZqsMHIrkgK4gLn4A8= github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM= github.com/ipfs/go-log v1.0.2/go.mod h1:1MNjMxe0u6xvJZgeqbJ8vdo2TKaGwZ1a0Bpza+sr2Sk= github.com/ipfs/go-log v1.0.3/go.mod h1:OsLySYkwIbiSUR/yBTdv1qPtcE4FW3WPWk/ewz9Ru+A= diff --git a/go.mod b/go.mod index 673dd3793..4fce33b56 100644 --- a/go.mod +++ b/go.mod @@ -45,7 +45,7 @@ require ( github.com/ipfs/go-ipld-git v0.1.1 github.com/ipfs/go-ipld-legacy v0.1.1 github.com/ipfs/go-ipns v0.3.0 - github.com/ipfs/go-libipfs v0.6.1 + github.com/ipfs/go-libipfs v0.6.2 github.com/ipfs/go-log v1.0.5 github.com/ipfs/go-log/v2 v2.5.1 github.com/ipfs/go-merkledag v0.9.0 diff --git a/go.sum b/go.sum index 16329d6ad..100875344 100644 --- a/go.sum +++ b/go.sum @@ -591,8 +591,8 @@ github.com/ipfs/go-ipld-legacy v0.1.1 h1:BvD8PEuqwBHLTKqlGFTHSwrwFOMkVESEvwIYwR2 github.com/ipfs/go-ipld-legacy v0.1.1/go.mod h1:8AyKFCjgRPsQFf15ZQgDB8Din4DML/fOmKZkkFkrIEg= github.com/ipfs/go-ipns v0.3.0 h1:ai791nTgVo+zTuq2bLvEGmWP1M0A6kGTXUsgv/Yq67A= github.com/ipfs/go-ipns v0.3.0/go.mod h1:3cLT2rbvgPZGkHJoPO1YMJeh6LtkxopCkKFcio/wE24= -github.com/ipfs/go-libipfs v0.6.1 h1:OSO9cm1H3r4OXfP0MP1Q5UhTnhd2fByGl6CVYyz/Rhk= -github.com/ipfs/go-libipfs v0.6.1/go.mod h1:FmhKgxMOQA572TK5DA3MZ5GL44ZqsMHIrkgK4gLn4A8= +github.com/ipfs/go-libipfs v0.6.2 h1:QUf3kS3RrCjgtE0QW2d18PFFfOLeEt24Ft892ipLzRI= +github.com/ipfs/go-libipfs v0.6.2/go.mod h1:FmhKgxMOQA572TK5DA3MZ5GL44ZqsMHIrkgK4gLn4A8= github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM= github.com/ipfs/go-log v1.0.2/go.mod h1:1MNjMxe0u6xvJZgeqbJ8vdo2TKaGwZ1a0Bpza+sr2Sk= github.com/ipfs/go-log v1.0.3/go.mod h1:OsLySYkwIbiSUR/yBTdv1qPtcE4FW3WPWk/ewz9Ru+A= From 010e22b508153672a777d180e6cdc16f59090b21 Mon Sep 17 00:00:00 2001 From: Gus Eggert Date: Thu, 16 Mar 2023 08:41:50 -0400 Subject: [PATCH 20/26] feat: add heap allocs to 'ipfs diag profile' --- core/commands/profile.go | 4 +++- profile/profile.go | 10 ++++++++++ profile/profile_test.go | 6 ++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/core/commands/profile.go b/core/commands/profile.go index 3875cfdcb..d25711dac 100644 --- a/core/commands/profile.go +++ b/core/commands/profile.go @@ -49,7 +49,8 @@ The output file includes: - A list of running goroutines. - A CPU profile. -- A heap profile. +- A heap inuse profile. +- A heap allocation profile. - A mutex profile. - A block profile. - Your copy of go-ipfs. @@ -79,6 +80,7 @@ However, it could reveal: profile.CollectorGoroutinesPprof, profile.CollectorVersion, profile.CollectorHeap, + profile.CollectorAllocs, profile.CollectorBin, profile.CollectorCPU, profile.CollectorMutex, diff --git a/profile/profile.go b/profile/profile.go index d15d51251..b9ad86d2f 100644 --- a/profile/profile.go +++ b/profile/profile.go @@ -22,6 +22,7 @@ const ( CollectorGoroutinesPprof = "goroutines-pprof" CollectorVersion = "version" CollectorHeap = "heap" + CollectorAllocs = "allocs" CollectorBin = "bin" CollectorCPU = "cpu" CollectorMutex = "mutex" @@ -71,6 +72,11 @@ var collectors = map[string]collector{ collectFunc: heapProfile, enabledFunc: func(opts Options) bool { return true }, }, + CollectorAllocs: { + outputFile: "allocs.pprof", + collectFunc: allocsProfile, + enabledFunc: func(opts Options) bool { return true }, + }, CollectorBin: { outputFile: "ipfs", isExecutable: true, @@ -197,6 +203,10 @@ func heapProfile(ctx context.Context, _ Options, w io.Writer) error { return pprof.Lookup("heap").WriteTo(w, 0) } +func allocsProfile(ctx context.Context, _ Options, w io.Writer) error { + return pprof.Lookup("allocs").WriteTo(w, 0) +} + func versionInfo(ctx context.Context, _ Options, w io.Writer) error { return json.NewEncoder(w).Encode(version.GetVersionInfo()) } diff --git a/profile/profile_test.go b/profile/profile_test.go index 8da00d018..a2fe0b51d 100644 --- a/profile/profile_test.go +++ b/profile/profile_test.go @@ -17,6 +17,7 @@ func TestProfiler(t *testing.T) { CollectorGoroutinesPprof, CollectorVersion, CollectorHeap, + CollectorAllocs, CollectorBin, CollectorCPU, CollectorMutex, @@ -43,6 +44,7 @@ func TestProfiler(t *testing.T) { "goroutines.pprof", "version.json", "heap.pprof", + "allocs.pprof", "ipfs", "cpu.pprof", "mutex.pprof", @@ -63,6 +65,7 @@ func TestProfiler(t *testing.T) { "goroutines.pprof", "version.json", "heap.pprof", + "allocs.pprof", "ipfs.exe", "cpu.pprof", "mutex.pprof", @@ -81,6 +84,7 @@ func TestProfiler(t *testing.T) { "goroutines.pprof", "version.json", "heap.pprof", + "allocs.pprof", "ipfs", }, }, @@ -96,6 +100,7 @@ func TestProfiler(t *testing.T) { "goroutines.pprof", "version.json", "heap.pprof", + "allocs.pprof", "ipfs", "cpu.pprof", "block.pprof", @@ -114,6 +119,7 @@ func TestProfiler(t *testing.T) { "goroutines.pprof", "version.json", "heap.pprof", + "allocs.pprof", "ipfs", "cpu.pprof", "mutex.pprof", From 6cdc3c8a144f95d422563930f81c5d7a30a4523a Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Fri, 17 Mar 2023 15:14:10 +0100 Subject: [PATCH 21/26] fix: apply API.HTTPHeaders to /webui redirect --- core/corehttp/redirect.go | 15 +++++++++++++-- test/cli/gateway_test.go | 17 +++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/core/corehttp/redirect.go b/core/corehttp/redirect.go index bcd536d23..4b98963e2 100644 --- a/core/corehttp/redirect.go +++ b/core/corehttp/redirect.go @@ -8,8 +8,14 @@ import ( ) func RedirectOption(path string, redirect string) ServeOption { - handler := &redirectHandler{redirect} return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) { + cfg, err := n.Repo.Config() + if err != nil { + return nil, err + } + + handler := &redirectHandler{redirect, cfg.API.HTTPHeaders} + if len(path) > 0 { mux.Handle("/"+path+"/", handler) } else { @@ -20,9 +26,14 @@ func RedirectOption(path string, redirect string) ServeOption { } type redirectHandler struct { - path string + path string + headers map[string][]string } func (i *redirectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + for k, v := range i.headers { + w.Header()[k] = v + } + http.Redirect(w, r, i.path, http.StatusFound) } diff --git a/test/cli/gateway_test.go b/test/cli/gateway_test.go index a585925a7..d2a90b04a 100644 --- a/test/cli/gateway_test.go +++ b/test/cli/gateway_test.go @@ -219,6 +219,23 @@ func TestGateway(t *testing.T) { assert.Contains(t, []int{302, 301}, resp.StatusCode) }) + t.Run("GET /webui/ returns user-specified headers", func(t *testing.T) { + t.Parallel() + + header := "Access-Control-Allow-Origin" + values := []string{"http://localhost:3000", "https://webui.ipfs.io"} + + node := harness.NewT(t).NewNode().Init() + node.UpdateConfig(func(cfg *config.Config) { + cfg.API.HTTPHeaders = map[string][]string{header: values} + }) + node.StartDaemon() + + resp := node.APIClient().DisableRedirects().Get("/webui/") + assert.Equal(t, resp.Headers.Values(header), values) + assert.Contains(t, []int{302, 301}, resp.StatusCode) + }) + t.Run("GET /logs returns logs", func(t *testing.T) { t.Parallel() apiClient := node.APIClient() From a22db797b9794d71147459395b32dad6b1e19125 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Fri, 17 Mar 2023 15:29:39 +0100 Subject: [PATCH 22/26] fix: canonicalize user defined headers --- core/corehttp/gateway_writable.go | 2 +- core/corehttp/redirect.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/corehttp/gateway_writable.go b/core/corehttp/gateway_writable.go index 89a2973ac..34cd8438e 100644 --- a/core/corehttp/gateway_writable.go +++ b/core/corehttp/gateway_writable.go @@ -30,7 +30,7 @@ type writableGatewayHandler struct { func (i *writableGatewayHandler) addUserHeaders(w http.ResponseWriter) { for k, v := range i.config.Headers { - w.Header()[k] = v + w.Header()[http.CanonicalHeaderKey(k)] = v } } diff --git a/core/corehttp/redirect.go b/core/corehttp/redirect.go index 4b98963e2..106355d24 100644 --- a/core/corehttp/redirect.go +++ b/core/corehttp/redirect.go @@ -32,7 +32,7 @@ type redirectHandler struct { func (i *redirectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { for k, v := range i.headers { - w.Header()[k] = v + w.Header()[http.CanonicalHeaderKey(k)] = v } http.Redirect(w, r, i.path, http.StatusFound) From 7ea6e321fe0ccffdd76f39c203435ab0254f4205 Mon Sep 17 00:00:00 2001 From: Piotr Galar Date: Mon, 20 Mar 2023 09:47:59 +0100 Subject: [PATCH 23/26] chore: update go-libp2p to v0.26.3 (#9737) --- docs/examples/kubo-as-a-library/go.mod | 2 +- docs/examples/kubo-as-a-library/go.sum | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/examples/kubo-as-a-library/go.mod b/docs/examples/kubo-as-a-library/go.mod index d96fa795d..5453ae1d5 100644 --- a/docs/examples/kubo-as-a-library/go.mod +++ b/docs/examples/kubo-as-a-library/go.mod @@ -10,7 +10,7 @@ require ( github.com/ipfs/go-libipfs v0.6.2 github.com/ipfs/interface-go-ipfs-core v0.11.0 github.com/ipfs/kubo v0.0.0-00010101000000-000000000000 - github.com/libp2p/go-libp2p v0.26.2 + github.com/libp2p/go-libp2p v0.26.3 github.com/multiformats/go-multiaddr v0.8.0 ) diff --git a/docs/examples/kubo-as-a-library/go.sum b/docs/examples/kubo-as-a-library/go.sum index 3a08f464a..6f7e7f253 100644 --- a/docs/examples/kubo-as-a-library/go.sum +++ b/docs/examples/kubo-as-a-library/go.sum @@ -730,8 +730,8 @@ github.com/libp2p/go-libp2p v0.7.0/go.mod h1:hZJf8txWeCduQRDC/WSqBGMxaTHCOYHt2xS github.com/libp2p/go-libp2p v0.7.4/go.mod h1:oXsBlTLF1q7pxr+9w6lqzS1ILpyHsaBPniVO7zIHGMw= github.com/libp2p/go-libp2p v0.8.1/go.mod h1:QRNH9pwdbEBpx5DTJYg+qxcVaDMAz3Ee/qDKwXujH5o= github.com/libp2p/go-libp2p v0.14.3/go.mod h1:d12V4PdKbpL0T1/gsUNN8DfgMuRPDX8bS2QxCZlwRH0= -github.com/libp2p/go-libp2p v0.26.2 h1:eHEoW/696FP7/6DxOvcrKfTD6Bi0DExxiMSZUJxswA0= -github.com/libp2p/go-libp2p v0.26.2/go.mod h1:x75BN32YbwuY0Awm2Uix4d4KOz+/4piInkp4Wr3yOo8= +github.com/libp2p/go-libp2p v0.26.3 h1:6g/psubqwdaBqNNoidbRKSTBEYgaOuKBhHl8Q5tO+PM= +github.com/libp2p/go-libp2p v0.26.3/go.mod h1:x75BN32YbwuY0Awm2Uix4d4KOz+/4piInkp4Wr3yOo8= github.com/libp2p/go-libp2p-asn-util v0.3.0 h1:gMDcMyYiZKkocGXDQ5nsUQyquC9+H+iLEQHwOCZ7s8s= github.com/libp2p/go-libp2p-asn-util v0.3.0/go.mod h1:B1mcOrKUE35Xq/ASTmQ4tN3LNzVVaMNmq2NACuqyB9w= github.com/libp2p/go-libp2p-autonat v0.1.0/go.mod h1:1tLf2yXxiE/oKGtDwPYWTSYG3PtvYlJmg7NeVtPRqH8= diff --git a/go.mod b/go.mod index 4fce33b56..f7237b46a 100644 --- a/go.mod +++ b/go.mod @@ -67,7 +67,7 @@ require ( github.com/jbenet/go-temp-err-catcher v0.1.0 github.com/jbenet/goprocess v0.1.4 github.com/libp2p/go-doh-resolver v0.4.0 - github.com/libp2p/go-libp2p v0.26.2 + github.com/libp2p/go-libp2p v0.26.3 github.com/libp2p/go-libp2p-http v0.4.0 github.com/libp2p/go-libp2p-kad-dht v0.21.1 github.com/libp2p/go-libp2p-kbucket v0.5.0 diff --git a/go.sum b/go.sum index 100875344..27e101e1c 100644 --- a/go.sum +++ b/go.sum @@ -760,8 +760,8 @@ github.com/libp2p/go-libp2p v0.7.0/go.mod h1:hZJf8txWeCduQRDC/WSqBGMxaTHCOYHt2xS github.com/libp2p/go-libp2p v0.7.4/go.mod h1:oXsBlTLF1q7pxr+9w6lqzS1ILpyHsaBPniVO7zIHGMw= github.com/libp2p/go-libp2p v0.8.1/go.mod h1:QRNH9pwdbEBpx5DTJYg+qxcVaDMAz3Ee/qDKwXujH5o= github.com/libp2p/go-libp2p v0.14.3/go.mod h1:d12V4PdKbpL0T1/gsUNN8DfgMuRPDX8bS2QxCZlwRH0= -github.com/libp2p/go-libp2p v0.26.2 h1:eHEoW/696FP7/6DxOvcrKfTD6Bi0DExxiMSZUJxswA0= -github.com/libp2p/go-libp2p v0.26.2/go.mod h1:x75BN32YbwuY0Awm2Uix4d4KOz+/4piInkp4Wr3yOo8= +github.com/libp2p/go-libp2p v0.26.3 h1:6g/psubqwdaBqNNoidbRKSTBEYgaOuKBhHl8Q5tO+PM= +github.com/libp2p/go-libp2p v0.26.3/go.mod h1:x75BN32YbwuY0Awm2Uix4d4KOz+/4piInkp4Wr3yOo8= github.com/libp2p/go-libp2p-asn-util v0.2.0 h1:rg3+Os8jbnO5DxkC7K/Utdi+DkY3q/d1/1q+8WeNAsw= github.com/libp2p/go-libp2p-asn-util v0.2.0/go.mod h1:WoaWxbHKBymSN41hWSq/lGKJEca7TNm58+gGJi2WsLI= github.com/libp2p/go-libp2p-autonat v0.1.0/go.mod h1:1tLf2yXxiE/oKGtDwPYWTSYG3PtvYlJmg7NeVtPRqH8= From 754264fc08e86ecf777cbaeeee5580b120f84c6c Mon Sep 17 00:00:00 2001 From: Piotr Galar Date: Mon, 20 Mar 2023 11:45:02 +0100 Subject: [PATCH 24/26] Merge pull request #9707 from ipfs/changelog-0.19 docs: 0.19 changelog --- docs/changelogs/v0.19.md | 48 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/docs/changelogs/v0.19.md b/docs/changelogs/v0.19.md index 12331cae7..67dc57af1 100644 --- a/docs/changelogs/v0.19.md +++ b/docs/changelogs/v0.19.md @@ -7,6 +7,10 @@ - [Overview](#overview) - [🔦 Highlights](#-highlights) - [Improving the libp2p resource management integration](#improving-the-libp2p-resource-management-integration) + - [PubSub message caching improvements](#pubsub-message-caching-improvements) + - [Gateways](#gateways) + - [Signed IPNS Record response format](#signed-ipns-record-response-format) + - [Example fetch and inspect IPNS record](#example-fetch-and-inspect-ipns-record) - [Addition of "autoclient" router type](#addition-of-autoclient-router-type) - [Deprecation of the `ipfs pubsub` commands and matching HTTP endpoints](#deprecation-of-the-ipfs-pubsub-commands-and-matching-http-endpoints) - [📝 Changelog](#-changelog) @@ -24,6 +28,50 @@ and [0.18.1](https://github.com/ipfs/kubo/blob/master/docs/changelogs/v0.18.md#i - Note: we don't expect most users to need these capablities, but they are there if so. 1. [Doc updates](https://github.com/ipfs/kubo/blob/master/docs/libp2p-resource-management.md). +#### PubSub message caching improvements + +The PubSub message cache will now [prune messages after TTL is exhausted](https://github.com/ipfs/kubo/blob/master/docs/config.md#pubsubseenmessagesttl), [either based on the last time a message was seen or the first time it was seen](https://github.com/ipfs/kubo/blob/master/docs/config.md#pubsubseenmessagesstrategy). + +#### Gateways + +##### Signed IPNS Record response format + +This release implements [IPIP-351](https://github.com/ipfs/specs/pull/351) and +adds Gateway support for returning signed (verifiable) `ipns-record` (0x0300) +when `/ipns/{libp2p-key}` is requested with either +`Accept: application/vnd.ipfs.ipns-record` HTTP header +or `?format=ipns-record` URL query parameter. + + +The Gateway in Kubo already supported [trustless, verifiable retrieval](https://docs.ipfs.tech/reference/http/gateway/#trustless-verifiable-retrieval) of immutable `/ipfs/` namespace. +With `?format=ipns-record`, light HTTP clients are now able to get the same level of verifiability for IPNS websites. + +Tooling is limited at the moment, but we are working on [go-libipfs](https://github.com/ipfs/go-libipfs/) examples that illustrate the verifiable HTTP client pattern. + +##### Example: fetch IPNS record over HTTP and inspect it with `ipfs name inspect --verify` + +```console +$ FILE_CID=$(echo "Hello IPFS" | ipfs add --cid-version 1 -q) +$ IPNS_KEY=$(ipfs key gen test) +$ ipfs name publish /ipfs/$FILE_CID --key=test --ttl=30m +Published to k51q..dvf1: /ipfs/bafk..z244 +$ curl "http://127.0.0.1:8080/ipns/$IPNS_KEY?format=ipns-record" > signed.ipns-record +$ ipfs name inspect --verify $IPNS_KEY < signed.ipns-record +Value: "/ipfs/bafk..." +Validity Type: "EOL" +Validity: 2023-03-09T23:13:34.032977468Z +Sequence: 0 +TTL: 1800000000000 +PublicKey: "" +Signature V1: "m..." +Signature V2: "m..." +Data: {...} + +Validation results: + Valid: true + PublicKey: 12D3... +``` + #### Addition of "autoclient" router type A new routing type "autoclient" has been added. This mode is similar to "auto", in that it is a hybrid of content routers (including Kademlia and HTTP routers), but it does not run a DHT server. This is similar to the difference between "dhtclient" and "dht" router types. From 822106134af70eb56101e123aaf85e0b59b5f3be Mon Sep 17 00:00:00 2001 From: galargh Date: Mon, 20 Mar 2023 11:15:40 +0000 Subject: [PATCH 25/26] chore: update version --- version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.go b/version.go index 71912f891..18bbf9a2d 100644 --- a/version.go +++ b/version.go @@ -11,7 +11,7 @@ import ( var CurrentCommit string // CurrentVersionNumber is the current application's version literal -const CurrentVersionNumber = "0.19.0-rc1" +const CurrentVersionNumber = "0.19.0" const ApiVersion = "/kubo/" + CurrentVersionNumber + "/" //nolint From 13865920d835ea104be4d803eb2453de0fa42f11 Mon Sep 17 00:00:00 2001 From: galargh Date: Mon, 20 Mar 2023 12:33:11 +0100 Subject: [PATCH 26/26] docs: update changelog --- docs/changelogs/v0.19.md | 467 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 465 insertions(+), 2 deletions(-) diff --git a/docs/changelogs/v0.19.md b/docs/changelogs/v0.19.md index 67dc57af1..4663d1323 100644 --- a/docs/changelogs/v0.19.md +++ b/docs/changelogs/v0.19.md @@ -23,10 +23,10 @@ #### Improving the libp2p resource management integration There are further followups up on libp2p resource manager improvements in Kubo [0.18.0](https://github.com/ipfs/kubo/blob/master/docs/changelogs/v0.18.md#improving-libp2p-resource-management-integration-1) and [0.18.1](https://github.com/ipfs/kubo/blob/master/docs/changelogs/v0.18.md#improving-libp2p-resource-management-integration): -1. `ipfs swarm limits` and `ipfs swarm stats` have been replaced by `ipfs swarm resources` to provide a single/combined view for limits and their current usage in a more intuitive ordering. +1. `ipfs swarm limits` and `ipfs swarm stats` have been replaced by `ipfs swarm resources` to provide a single/combined view for limits and their current usage in a more intuitive ordering. 1. Removal of `Swarm.ResourceMgr.Limits` config. Instead [the power user can specify limits in a .json file that are fed directly to go-libp2p](https://github.com/ipfs/kubo/blob/master/docs/libp2p-resource-management.md#user-supplied-override-limits). This allows the power user to take advantage of the [new resource manager types introduced in go-libp2p 0.25](https://github.com/libp2p/go-libp2p/blob/master/CHANGELOG.md#new-resource-manager-types-) including "use default", "unlimited", "block all". - Note: we don't expect most users to need these capablities, but they are there if so. -1. [Doc updates](https://github.com/ipfs/kubo/blob/master/docs/libp2p-resource-management.md). +1. [Doc updates](https://github.com/ipfs/kubo/blob/master/docs/libp2p-resource-management.md). #### PubSub message caching improvements @@ -85,4 +85,467 @@ For more information and rational see [#9717](https://github.com/ipfs/kubo/issue ### 📝 Changelog +
Full Changelog + +- github.com/ipfs/kubo: + - chore: update version + - docs: 0.19 changelog ([ipfs/kubo#9707](https://github.com/ipfs/kubo/pull/9707)) + - fix: canonicalize user defined headers + - fix: apply API.HTTPHeaders to /webui redirect + - feat: add heap allocs to 'ipfs diag profile' + - fix: future proof with > rcmgr.DefaultLimit for new enum rcmgr values + - test: add test for presarvation of unlimited configs for inbound systems + - fix: preserve Unlimited StreamsInbound in connmgr reconciliation + - test: fix flaky rcmgr test + - chore: deprecate the pubsub api + - test: port peering test from sharness to Go + - test: use `T.TempDir` to create temporary test directory + - fix: --verify forgets the verified key + - test: name --verify forgets the verified key + - feat: add "autoclient" routing type + - test: parallelize more of rcmgr Go tests + - test: port legacy DHT tests to Go + - fix: t0116-gateway-cache.sh ([ipfs/kubo#9696](https://github.com/ipfs/kubo/pull/9696)) + - docs: add bifrost to early testers ([ipfs/kubo#9699](https://github.com/ipfs/kubo/pull/9699)) + - fix: typo in documentation for install path + - chore: update version + - feat: Reduce RM code footprint + - Doc updates/additions + - ci: replace junit html generation with gh action + - test: port rcmgr sharness tests to Go + - test(gateway): use deterministic CAR fixtures ([ipfs/kubo#9657](https://github.com/ipfs/kubo/pull/9657)) + - feat(gateway): error handling improvements (500, 502, 504) (#9660) ([ipfs/kubo#9660](https://github.com/ipfs/kubo/pull/9660)) + - docs: be clear about swarm.addrfilters (#9661) ([ipfs/kubo#9661](https://github.com/ipfs/kubo/pull/9661)) + - chore: update go-libp2p to v0.26 (#9656) ([ipfs/kubo#9656](https://github.com/ipfs/kubo/pull/9656)) + - feat(pinning): connect some missing go context (#9557) ([ipfs/kubo#9557](https://github.com/ipfs/kubo/pull/9557)) + - fix(gateway): return HTTP 500 on ErrResolveFailed (#9589) ([ipfs/kubo#9589](https://github.com/ipfs/kubo/pull/9589)) + - docs: bulk spelling edits (#9544) ([ipfs/kubo#9544](https://github.com/ipfs/kubo/pull/9544)) + - docs: "remote" errors from resource manager (#9653) ([ipfs/kubo#9653](https://github.com/ipfs/kubo/pull/9653)) + - test: remove gateway tests migrated to go-libipfs + - fix: update rcmgr for go-libp2p v0.25 + - chore: update go-libp2p to v0.25.1 + - docs(0.18.1): guide users to clean up limits (#9644) ([ipfs/kubo#9644](https://github.com/ipfs/kubo/pull/9644)) + - feat: add NewOptionalInteger function + - fix: dereference int64 pointer in OptionalInteger.String() (#9640) ([ipfs/kubo#9640](https://github.com/ipfs/kubo/pull/9640)) + - fix: restore wire format for /api/v0/routing/get|put (#9639) ([ipfs/kubo#9639](https://github.com/ipfs/kubo/pull/9639)) + - refactor(gw): move Host (DNSLink and subdomain) handling to go-libipfs (#9624) ([ipfs/kubo#9624](https://github.com/ipfs/kubo/pull/9624)) + - refactor: new go-libipfs/gateway API, deprecate Gateway.Writable (#9616) ([ipfs/kubo#9616](https://github.com/ipfs/kubo/pull/9616)) + - Create Changelog: v0.19 ([ipfs/kubo#9617](https://github.com/ipfs/kubo/pull/9617)) + - refactor: use gateway from go-libipfs (#9588) ([ipfs/kubo#9588](https://github.com/ipfs/kubo/pull/9588)) + - Merge Release: v0.18.1 ([ipfs/kubo#9613](https://github.com/ipfs/kubo/pull/9613)) + - Add overview section + - Adjust inbound connection limits depending on memory. + - feat: ipfs-webui 2.22.0 + - chore: bump go-libipfs remove go-bitswap + - docs: DefaultResourceMgrMinInboundConns + - feat(gateway): IPNS record response format (IPIP-351) (#9399) ([ipfs/kubo#9399](https://github.com/ipfs/kubo/pull/9399)) + - fix(ipns): honour --ttl flag in 'ipfs name publish' (#9471) ([ipfs/kubo#9471](https://github.com/ipfs/kubo/pull/9471)) + - feat: Pubsub.SeenMessagesStrategy (#9543) ([ipfs/kubo#9543](https://github.com/ipfs/kubo/pull/9543)) + - chore: bump go-libipfs to replace go-block-format + - Merge Kubo: v0.18 ([ipfs/kubo#9581](https://github.com/ipfs/kubo/pull/9581)) + - fix: clarity: no user supplied rcmgr limits of 0 (#9563) ([ipfs/kubo#9563](https://github.com/ipfs/kubo/pull/9563)) + - fix(gateway): undesired conversions to dag-json and friends (#9566) ([ipfs/kubo#9566](https://github.com/ipfs/kubo/pull/9566)) + - fix: ensure connmgr is smaller then autoscalled ressource limits + - fix: typo in ensureConnMgrMakeSenseVsResourcesMgr + - docs: clarify browser descriptions for webtransport + - fix: update saxon download path + - fix: refuse to start if connmgr is smaller than ressource limits and not using none connmgr + - fix: User-Agent sent to HTTP routers + - test: port gateway sharness tests to Go tests + - fix: do not download saxon in parallel + - docs: improve docs/README (#9539) ([ipfs/kubo#9539](https://github.com/ipfs/kubo/pull/9539)) + - test: port CircleCI to GH Actions and improve sharness reporting (#9355) ([ipfs/kubo#9355](https://github.com/ipfs/kubo/pull/9355)) + - chore: migrate from go-ipfs-files to go-libipfs/files (#9535) ([ipfs/kubo#9535](https://github.com/ipfs/kubo/pull/9535)) + - fix: stats dht command when Routing.Type=auto (#9538) ([ipfs/kubo#9538](https://github.com/ipfs/kubo/pull/9538)) + - fix: hint people to changing from RSA peer ids + - fix(gateway): JSON when Accept is a list + - fix(test): retry flaky t0125-twonode.sh + - docs: fix Router config Godoc (#9528) ([ipfs/kubo#9528](https://github.com/ipfs/kubo/pull/9528)) + - fix(ci): flaky sharness test + - docs(config): ProviderSearchDelay (#9526) ([ipfs/kubo#9526](https://github.com/ipfs/kubo/pull/9526)) + - docs: clarify debug environment variables + - fix: disable provide over HTTP with Routing.Type=auto (#9511) ([ipfs/kubo#9511](https://github.com/ipfs/kubo/pull/9511)) + - fix(test): stabilize flaky provider tests + - feat: port pins CLI test + - Removing QRI from early tester ([ipfs/kubo#9503](https://github.com/ipfs/kubo/pull/9503)) + - Update Version (dev): v0.18 ([ipfs/kubo#9500](https://github.com/ipfs/kubo/pull/9500)) +- github.com/ipfs/go-bitfield (v1.0.0 -> v1.1.0): + - Merge pull request from GHSA-2h6c-j3gf-xp9r + - sync: update CI config files (#3) ([ipfs/go-bitfield#3](https://github.com/ipfs/go-bitfield/pull/3)) +- github.com/ipfs/go-block-format (v0.0.3 -> v0.1.1): + - chore: release v0.1.1 + - docs: fix wrong copy paste in docs + - chore: release v0.1.0 + - refactor: deprecate and add stub types to go-libipfs/blocks + - sync: update CI config files (#34) ([ipfs/go-block-format#34](https://github.com/ipfs/go-block-format/pull/34)) + - remove Makefile ([ipfs/go-block-format#31](https://github.com/ipfs/go-block-format/pull/31)) +- github.com/ipfs/go-ipfs-files (v0.0.8 -> v0.3.0): + - ([ipfs/go-ipfs-files#59](https://github.com/ipfs/go-ipfs-files/pull/59)) + - docs: add moved noticed [ci skip] + - Release v0.2.0 + - fix: error when TAR has files outside of root (#56) ([ipfs/go-ipfs-files#56](https://github.com/ipfs/go-ipfs-files/pull/56)) + - sync: update CI config files ([ipfs/go-ipfs-files#55](https://github.com/ipfs/go-ipfs-files/pull/55)) + - chore(Directory): add DirIterator API restriction: iterate only once + - Release v0.1.1 + - fix: add dragonfly build option for filewriter flags + - fix: add freebsd build option for filewriter flags + - Release v0.1.0 + - docs: fix community CONTRIBUTING.md link (#45) ([ipfs/go-ipfs-files#45](https://github.com/ipfs/go-ipfs-files/pull/45)) + - chore(filewriter): cleanup writes (#43) ([ipfs/go-ipfs-files#43](https://github.com/ipfs/go-ipfs-files/pull/43)) + - sync: update CI config files (#44) ([ipfs/go-ipfs-files#44](https://github.com/ipfs/go-ipfs-files/pull/44)) + - sync: update CI config files ([ipfs/go-ipfs-files#40](https://github.com/ipfs/go-ipfs-files/pull/40)) + - fix: manually parse the content disposition to preserve directories ([ipfs/go-ipfs-files#42](https://github.com/ipfs/go-ipfs-files/pull/42)) + - fix: round timestamps down by truncating them to seconds ([ipfs/go-ipfs-files#41](https://github.com/ipfs/go-ipfs-files/pull/41)) + - sync: update CI config files ([ipfs/go-ipfs-files#34](https://github.com/ipfs/go-ipfs-files/pull/34)) + - Fix test failure on Windows caused by nil `sys` in mock `FileInfo` ([ipfs/go-ipfs-files#39](https://github.com/ipfs/go-ipfs-files/pull/39)) + - fix staticcheck ([ipfs/go-ipfs-files#35](https://github.com/ipfs/go-ipfs-files/pull/35)) + - fix linters ([ipfs/go-ipfs-files#33](https://github.com/ipfs/go-ipfs-files/pull/33)) +- github.com/ipfs/go-ipfs-pinner (v0.2.1 -> v0.3.0): + - chore: release v0.3.0 (#27) ([ipfs/go-ipfs-pinner#27](https://github.com/ipfs/go-ipfs-pinner/pull/27)) + - feat!: add and connect missing context, remove RemovePinWithMode (#23) ([ipfs/go-ipfs-pinner#23](https://github.com/ipfs/go-ipfs-pinner/pull/23)) + - sync: update CI config files ([ipfs/go-ipfs-pinner#16](https://github.com/ipfs/go-ipfs-pinner/pull/16)) +- github.com/ipfs/go-ipfs-pq (v0.0.2 -> v0.0.3): + - chore: release v0.0.3 + - fix: enable early GC + - sync: update CI config files (#10) ([ipfs/go-ipfs-pq#10](https://github.com/ipfs/go-ipfs-pq/pull/10)) + - sync: update CI config files ([ipfs/go-ipfs-pq#8](https://github.com/ipfs/go-ipfs-pq/pull/8)) + - remove Makefile ([ipfs/go-ipfs-pq#7](https://github.com/ipfs/go-ipfs-pq/pull/7)) +- github.com/ipfs/go-libipfs (v0.2.0 -> v0.6.2): + - chore: release 0.6.2 (#211) ([ipfs/go-libipfs#211](https://github.com/ipfs/go-libipfs/pull/211)) + - fix(gateway): 500 on panic, recover on WithHostname + - refactor: use assert in remaining gateway tests + - chore: release 0.6.1 + - feat: support HTTP 429 with Retry-After (#194) ([ipfs/go-libipfs#194](https://github.com/ipfs/go-libipfs/pull/194)) + - docs: fix typo in README.md + - fix(gateway): return 500 for all /ip[nf]s/id failures + - chore: make gocritic happier + - feat(gateway): improved error handling, support for 502 and 504 ([ipfs/go-libipfs#182](https://github.com/ipfs/go-libipfs/pull/182)) + - feat: add content path in request context (#184) ([ipfs/go-libipfs#184](https://github.com/ipfs/go-libipfs/pull/184)) + - sync: update CI config files ([ipfs/go-libipfs#159](https://github.com/ipfs/go-libipfs/pull/159)) + - fix(gateway): return HTTP 500 on namesys.ErrResolveFailed (#150) ([ipfs/go-libipfs#150](https://github.com/ipfs/go-libipfs/pull/150)) + - docs(examples): add UnixFS file download over Bitswap (#143) ([ipfs/go-libipfs#143](https://github.com/ipfs/go-libipfs/pull/143)) + - bitswap/server/internal/decision: fix: remove unused private type + - chore: release v0.6.0 + - bitswap/server/internal/decision: add more non flaky tests + - bitswap/server/internal/decision: add filtering on CIDs - Ignore cids that are too big. - Kill connection for peers that are using inline CIDs. + - bitswap/server/internal/decision: rewrite ledger inversion + - docs(readme): various updates for clarity (#171) ([ipfs/go-libipfs#171](https://github.com/ipfs/go-libipfs/pull/171)) + - feat: metric for implicit index.html in dirs + - fix(gateway): ensure ipfs_http_gw_get_duration_seconds gets updated + - test(gateway): migrate Go tests from Kubo ([ipfs/go-libipfs#156](https://github.com/ipfs/go-libipfs/pull/156)) + - docs: fix link (#165) ([ipfs/go-libipfs#165](https://github.com/ipfs/go-libipfs/pull/165)) + - fix: GetIPNSRecord example gateway implementation (#158) ([ipfs/go-libipfs#158](https://github.com/ipfs/go-libipfs/pull/158)) + - chore: release v0.5.0 + - chore: update go-libp2p to v0.25.1 + - fix(gateway): display correct error with 500 (#160) ([ipfs/go-libipfs#160](https://github.com/ipfs/go-libipfs/pull/160)) + - fix: gateway car example dnslink + - feat(gateway): add TAR, IPNS Record, DAG-* histograms and spans (#155) ([ipfs/go-libipfs#155](https://github.com/ipfs/go-libipfs/pull/155)) + - feat(gateway): migrate subdomain and dnslink code (#153) ([ipfs/go-libipfs#153](https://github.com/ipfs/go-libipfs/pull/153)) + - docs: add example of gateway that proxies to ?format=raw (#151) ([ipfs/go-libipfs#151](https://github.com/ipfs/go-libipfs/pull/151)) + - docs: add example of gateway backed by CAR file (#147) ([ipfs/go-libipfs#147](https://github.com/ipfs/go-libipfs/pull/147)) + - undefined ([ipfs/go-libipfs#145](https://github.com/ipfs/go-libipfs/pull/145)) + - Extract Gateway Code From Kubo + ([ipfs/go-libipfs#65](https://github.com/ipfs/go-libipfs/pull/65)) + - Migrate go-bitswap ([ipfs/go-libipfs#63](https://github.com/ipfs/go-libipfs/pull/63)) + - Use `PUT` as method to insert provider records + - Migrate `go-block-format` ([ipfs/go-libipfs#58](https://github.com/ipfs/go-libipfs/pull/58)) + - chore: add codecov PR comment + - chore: add a logo and some basics in the README (#37) ([ipfs/go-libipfs#37](https://github.com/ipfs/go-libipfs/pull/37)) +- github.com/ipfs/go-namesys (v0.6.0 -> v0.7.0): + - chore: release 0.7.0 (#36) ([ipfs/go-namesys#36](https://github.com/ipfs/go-namesys/pull/36)) + - feat: use PublishOptions for publishing IPNS records (#35) ([ipfs/go-namesys#35](https://github.com/ipfs/go-namesys/pull/35)) +- github.com/ipfs/go-path (v0.3.0 -> v0.3.1): + - chore: release v0.3.1 (#67) ([ipfs/go-path#67](https://github.com/ipfs/go-path/pull/67)) + - feat: expose ErrInvalidPath and implement .Is function (#66) ([ipfs/go-path#66](https://github.com/ipfs/go-path/pull/66)) + - sync: update CI config files (#60) ([ipfs/go-path#60](https://github.com/ipfs/go-path/pull/60)) + - feat: add basic tracing ([ipfs/go-path#59](https://github.com/ipfs/go-path/pull/59)) +- github.com/ipfs/go-peertaskqueue (v0.8.0 -> v0.8.1): + - chore: release v0.8.1 + - feat: add PushTasksTruncated which only push a limited amount of tasks + - feat: add (*PeerTaskQueue).Clear which fully removes a peer + - sync: update CI config files (#26) ([ipfs/go-peertaskqueue#26](https://github.com/ipfs/go-peertaskqueue/pull/26)) +- github.com/ipfs/go-unixfs (v0.4.2 -> v0.4.4): + - chore: release v0.4.4 + - fix: correctly handle return errors + - fix: correctly handle errors in balancedbuilder's Layout + - test: fix tests after hamt issues fixes + - Merge pull request from GHSA-q264-w97q-q778 +- github.com/ipfs/go-unixfsnode (v1.5.1 -> v1.5.2): + - Merge pull request from GHSA-4gj3-6r43-3wfc +- github.com/ipfs/interface-go-ipfs-core (v0.8.2 -> v0.11.0): + - chore: release v0.11.0 + - test: basic routing interface test + - chore: release v0.10.0 (#102) ([ipfs/interface-go-ipfs-core#102](https://github.com/ipfs/interface-go-ipfs-core/pull/102)) + - feat: add RoutingAPI to CoreAPI + - chore: release 0.9.0 (#101) ([ipfs/interface-go-ipfs-core#101](https://github.com/ipfs/interface-go-ipfs-core/pull/101)) + - feat: add namesys publish options (#94) ([ipfs/interface-go-ipfs-core#94](https://github.com/ipfs/interface-go-ipfs-core/pull/94)) +- github.com/ipld/go-car (v0.4.0 -> v0.5.0): + - chore: bump version to 0.5.0 + - fix: remove use of ioutil + - run gofmt -s + - bump go.mod to Go 1.18 and run go fix + - bump go.mod to Go 1.18 and run go fix + - OpenReadWriteFile: add test + - blockstore: allow to pass a file to write in (#323) ([ipld/go-car#323](https://github.com/ipld/go-car/pull/323)) + - feat: add `car inspect` command to cmd pkg (#320) ([ipld/go-car#320](https://github.com/ipld/go-car/pull/320)) + - Separate `index.ReadFrom` tests + - Only read index codec during inspection + - Upgrade to the latest `go-car/v2` + - Empty identity CID should be indexed when options are set +- github.com/libp2p/go-libp2p (v0.24.2 -> v0.26.3): + - Release v0.26.3 (#2197) ([libp2p/go-libp2p#2197](https://github.com/libp2p/go-libp2p/pull/2197)) + - retract v0.26.1, release v0.26.2 (#2153) ([libp2p/go-libp2p#2153](https://github.com/libp2p/go-libp2p/pull/2153)) + - rcmgr: fix JSON marshalling of ResourceManagerStat peer map (#2156) ([libp2p/go-libp2p#2156](https://github.com/libp2p/go-libp2p/pull/2156)) + - release v0.26.1 ([libp2p/go-libp2p#2146](https://github.com/libp2p/go-libp2p/pull/2146)) + - release v0.26.0 (#2133) ([libp2p/go-libp2p#2133](https://github.com/libp2p/go-libp2p/pull/2133)) + - identify: add more detailed metrics (#2126) ([libp2p/go-libp2p#2126](https://github.com/libp2p/go-libp2p/pull/2126)) + - autorelay: refactor relay finder and start autorelay after identify (#2120) ([libp2p/go-libp2p#2120](https://github.com/libp2p/go-libp2p/pull/2120)) + - don't use the time value from the time.Ticker channel (#2127) ([libp2p/go-libp2p#2127](https://github.com/libp2p/go-libp2p/pull/2127)) + - Wrap conn with metrics (#2131) ([libp2p/go-libp2p#2131](https://github.com/libp2p/go-libp2p/pull/2131)) + - chore: update changelog for 0.26.0 (#2132) ([libp2p/go-libp2p#2132](https://github.com/libp2p/go-libp2p/pull/2132)) + - circuitv2: Update proto files to proto3 (#2121) ([libp2p/go-libp2p#2121](https://github.com/libp2p/go-libp2p/pull/2121)) + - swarm: remove parallel tests from swarm tests (#2130) ([libp2p/go-libp2p#2130](https://github.com/libp2p/go-libp2p/pull/2130)) + - circuitv2: add a relay option to disable limits (#2125) ([libp2p/go-libp2p#2125](https://github.com/libp2p/go-libp2p/pull/2125)) + - quic: fix stalled virtual listener (#2122) ([libp2p/go-libp2p#2122](https://github.com/libp2p/go-libp2p/pull/2122)) + - swarm: add early muxer selection to swarm metrics (#2119) ([libp2p/go-libp2p#2119](https://github.com/libp2p/go-libp2p/pull/2119)) + - metrics: add options to disable metrics and to set Prometheus registerer (#2116) ([libp2p/go-libp2p#2116](https://github.com/libp2p/go-libp2p/pull/2116)) + - swarm: add ip_version to metrics (#2114) ([libp2p/go-libp2p#2114](https://github.com/libp2p/go-libp2p/pull/2114)) + - Revert mistaken "Bump timeout" + - Bump timeout + - remove all circuit v1 related code (#2107) ([libp2p/go-libp2p#2107](https://github.com/libp2p/go-libp2p/pull/2107)) + - quic: don't send detailed error messages when closing connections (#2112) ([libp2p/go-libp2p#2112](https://github.com/libp2p/go-libp2p/pull/2112)) + - metrics: add no alloc metrics for eventbus, swarm, identify (#2108) ([libp2p/go-libp2p#2108](https://github.com/libp2p/go-libp2p/pull/2108)) + - chore: fix typo in Changelog (#2111) ([libp2p/go-libp2p#2111](https://github.com/libp2p/go-libp2p/pull/2111)) + - chore: update changelog (#2109) ([libp2p/go-libp2p#2109](https://github.com/libp2p/go-libp2p/pull/2109)) + - chore: unify dashboard location (#2110) ([libp2p/go-libp2p#2110](https://github.com/libp2p/go-libp2p/pull/2110)) + - autonat: add metrics (#2086) ([libp2p/go-libp2p#2086](https://github.com/libp2p/go-libp2p/pull/2086)) + - relaymanager: do not start new relay if one already exists (#2093) ([libp2p/go-libp2p#2093](https://github.com/libp2p/go-libp2p/pull/2093)) + - autonat: don't emit reachability changed events on address change (#2092) ([libp2p/go-libp2p#2092](https://github.com/libp2p/go-libp2p/pull/2092)) + - chore: modify changelog entries (#2101) ([libp2p/go-libp2p#2101](https://github.com/libp2p/go-libp2p/pull/2101)) + - Introduce a changelog (#2084) ([libp2p/go-libp2p#2084](https://github.com/libp2p/go-libp2p/pull/2084)) + - use atomic.Int32 and atomic.Int64 (#2096) ([libp2p/go-libp2p#2096](https://github.com/libp2p/go-libp2p/pull/2096)) + - change atomic.Value to atomic.Pointer (#2088) ([libp2p/go-libp2p#2088](https://github.com/libp2p/go-libp2p/pull/2088)) + - use atomic.Bool instead of int32 operations (#2089) ([libp2p/go-libp2p#2089](https://github.com/libp2p/go-libp2p/pull/2089)) + - sync: update CI config files (#2073) ([libp2p/go-libp2p#2073](https://github.com/libp2p/go-libp2p/pull/2073)) + - chore: update examples to v0.25.1 (#2080) ([libp2p/go-libp2p#2080](https://github.com/libp2p/go-libp2p/pull/2080)) + - v0.25.1 (#2082) ([libp2p/go-libp2p#2082](https://github.com/libp2p/go-libp2p/pull/2082)) + - Start host in mocknet (#2078) ([libp2p/go-libp2p#2078](https://github.com/libp2p/go-libp2p/pull/2078)) + - Release v0.25.0 (#2077) ([libp2p/go-libp2p#2077](https://github.com/libp2p/go-libp2p/pull/2077)) + - identify: add some basic metrics (#2069) ([libp2p/go-libp2p#2069](https://github.com/libp2p/go-libp2p/pull/2069)) + - p2p/test/quic: use contexts with a timeout for Connect calls (#2070) ([libp2p/go-libp2p#2070](https://github.com/libp2p/go-libp2p/pull/2070)) + - feat!: rcmgr: Change LimitConfig to use LimitVal type (#2000) ([libp2p/go-libp2p#2000](https://github.com/libp2p/go-libp2p/pull/2000)) + - identify: refactor sending of Identify pushes (#1984) ([libp2p/go-libp2p#1984](https://github.com/libp2p/go-libp2p/pull/1984)) + - Update interop to match spec (#2049) ([libp2p/go-libp2p#2049](https://github.com/libp2p/go-libp2p/pull/2049)) + - chore: git-ignore various flavors of qlog files (#2064) ([libp2p/go-libp2p#2064](https://github.com/libp2p/go-libp2p/pull/2064)) + - rcmgr: add libp2p prefix to all metrics (#2063) ([libp2p/go-libp2p#2063](https://github.com/libp2p/go-libp2p/pull/2063)) + - websocket: Replace gorilla websocket transport with nhooyr websocket transport (#1982) ([libp2p/go-libp2p#1982](https://github.com/libp2p/go-libp2p/pull/1982)) + - rcmgr: Use prometheus SDK for rcmgr metrics (#2044) ([libp2p/go-libp2p#2044](https://github.com/libp2p/go-libp2p/pull/2044)) + - autorelay: Split libp2p.EnableAutoRelay into 2 functions (#2022) ([libp2p/go-libp2p#2022](https://github.com/libp2p/go-libp2p/pull/2022)) + - set names for eventbus event subscriptions (#2057) ([libp2p/go-libp2p#2057](https://github.com/libp2p/go-libp2p/pull/2057)) + - Test cleanup (#2053) ([libp2p/go-libp2p#2053](https://github.com/libp2p/go-libp2p/pull/2053)) + - metrics: use a single slice pool for all metrics tracer (#2054) ([libp2p/go-libp2p#2054](https://github.com/libp2p/go-libp2p/pull/2054)) + - eventbus: add metrics (#2038) ([libp2p/go-libp2p#2038](https://github.com/libp2p/go-libp2p/pull/2038)) + - quic: disable sending of Version Negotiation packets (#2015) ([libp2p/go-libp2p#2015](https://github.com/libp2p/go-libp2p/pull/2015)) + - p2p/test: fix flaky notification test (#2051) ([libp2p/go-libp2p#2051](https://github.com/libp2p/go-libp2p/pull/2051)) + - quic, tcp: only register Prometheus counters when metrics are enabled ([libp2p/go-libp2p#1971](https://github.com/libp2p/go-libp2p/pull/1971)) + - p2p/test: add test for EvtLocalAddressesUpdated event (#2016) ([libp2p/go-libp2p#2016](https://github.com/libp2p/go-libp2p/pull/2016)) + - quic / webtransport: extend test to test dialing draft-29 and v1 (#1957) ([libp2p/go-libp2p#1957](https://github.com/libp2p/go-libp2p/pull/1957)) + - holepunch: fix flaky by not remove holepunch protocol handler (#1948) ([libp2p/go-libp2p#1948](https://github.com/libp2p/go-libp2p/pull/1948)) + - use quic-go and webtransport-go from quic-go organization (#2040) ([libp2p/go-libp2p#2040](https://github.com/libp2p/go-libp2p/pull/2040)) + - Migrate to test-plan composite action (#2039) ([libp2p/go-libp2p#2039](https://github.com/libp2p/go-libp2p/pull/2039)) + - chore: remove license files from the eventbus package (#2042) ([libp2p/go-libp2p#2042](https://github.com/libp2p/go-libp2p/pull/2042)) + - rcmgr: *: Always close connscope (#2037) ([libp2p/go-libp2p#2037](https://github.com/libp2p/go-libp2p/pull/2037)) + - chore: remove textual roadmap in favor for Starmap (#2036) ([libp2p/go-libp2p#2036](https://github.com/libp2p/go-libp2p/pull/2036)) + - swarm metrics: fix datasource for dashboard (#2024) ([libp2p/go-libp2p#2024](https://github.com/libp2p/go-libp2p/pull/2024)) + - consistently use protocol.ID instead of strings (#2004) ([libp2p/go-libp2p#2004](https://github.com/libp2p/go-libp2p/pull/2004)) + - swarm: add a basic metrics tracer (#1973) ([libp2p/go-libp2p#1973](https://github.com/libp2p/go-libp2p/pull/1973)) + - Expose muxer ids (#2012) ([libp2p/go-libp2p#2012](https://github.com/libp2p/go-libp2p/pull/2012)) + - Clean addresses with peer id before adding to addrbook (#2007) ([libp2p/go-libp2p#2007](https://github.com/libp2p/go-libp2p/pull/2007)) + - feat: ci test-plans: Parse test timeout parameter for interop test (#2014) ([libp2p/go-libp2p#2014](https://github.com/libp2p/go-libp2p/pull/2014)) + - Export resource manager errors (#2008) ([libp2p/go-libp2p#2008](https://github.com/libp2p/go-libp2p/pull/2008)) + - peerstore: make it possible to use an empty peer ID (#2006) ([libp2p/go-libp2p#2006](https://github.com/libp2p/go-libp2p/pull/2006)) + - Add ci flakiness score to readme (#2002) ([libp2p/go-libp2p#2002](https://github.com/libp2p/go-libp2p/pull/2002)) + - rcmgr: fix: Ignore zero values when marshalling Limits. (#1998) ([libp2p/go-libp2p#1998](https://github.com/libp2p/go-libp2p/pull/1998)) + - CI: Fast multidimensional Interop tests (#1991) ([libp2p/go-libp2p#1991](https://github.com/libp2p/go-libp2p/pull/1991)) + - feat: add some users to the readme (#1981) ([libp2p/go-libp2p#1981](https://github.com/libp2p/go-libp2p/pull/1981)) + - ci: run go generate as part of the go-check workflow (#1986) ([libp2p/go-libp2p#1986](https://github.com/libp2p/go-libp2p/pull/1986)) + - switch to Google's Protobuf library, make protobufs compile with go generate ([libp2p/go-libp2p#1979](https://github.com/libp2p/go-libp2p/pull/1979)) + - circuitv2: correctly set the transport in the ConnectionState (#1972) ([libp2p/go-libp2p#1972](https://github.com/libp2p/go-libp2p/pull/1972)) + - roadmap: remove optimizations of the TCP-based handshake (#1959) ([libp2p/go-libp2p#1959](https://github.com/libp2p/go-libp2p/pull/1959)) + - identify: remove support for Identify Delta ([libp2p/go-libp2p#1975](https://github.com/libp2p/go-libp2p/pull/1975)) + - core: remove introspection package (#1978) ([libp2p/go-libp2p#1978](https://github.com/libp2p/go-libp2p/pull/1978)) + - identify: remove old code targeting Go 1.17 (#1964) ([libp2p/go-libp2p#1964](https://github.com/libp2p/go-libp2p/pull/1964)) + - add WebTransport to the list of default transports (#1915) ([libp2p/go-libp2p#1915](https://github.com/libp2p/go-libp2p/pull/1915)) + - core/crypto: drop all OpenSSL code paths (#1953) ([libp2p/go-libp2p#1953](https://github.com/libp2p/go-libp2p/pull/1953)) + - chore: use generic LRU cache (#1980) ([libp2p/go-libp2p#1980](https://github.com/libp2p/go-libp2p/pull/1980)) +- github.com/libp2p/go-libp2p-kad-dht (v0.20.0 -> v0.21.1): + - chore: bump to v0.21.1 (#821) ([libp2p/go-libp2p-kad-dht#821](https://github.com/libp2p/go-libp2p-kad-dht/pull/821)) + - feat: send FIND_NODE request to peers on routing table refresh (#810) ([libp2p/go-libp2p-kad-dht#810](https://github.com/libp2p/go-libp2p-kad-dht/pull/810)) + - chore: release v0.21. + - chore: Update to go libp2p v0.25 ([libp2p/go-libp2p-kad-dht#815](https://github.com/libp2p/go-libp2p-kad-dht/pull/815)) +- github.com/libp2p/go-libp2p-pubsub (v0.8.3 -> v0.9.0): + - chore: update to go-libp2p v0.25 (#517) ([libp2p/go-libp2p-pubsub#517](https://github.com/libp2p/go-libp2p-pubsub/pull/517)) +- github.com/libp2p/go-libp2p-routing-helpers (v0.6.0 -> v0.6.1): + - chore: release v0.6.1 + - fix: cancel parallel routers +- github.com/libp2p/go-msgio (v0.2.0 -> v0.3.0): + - release v0.3.0 (#39) ([libp2p/go-msgio#39](https://github.com/libp2p/go-msgio/pull/39)) + - switch from deprecated gogo to google.golang.org/protobuf ([libp2p/go-msgio#38](https://github.com/libp2p/go-msgio/pull/38)) + - sync: update CI config files (#36) ([libp2p/go-msgio#36](https://github.com/libp2p/go-msgio/pull/36)) +- github.com/lucas-clemente/quic-go (v0.31.1 -> v0.29.1): + - http3: fix double close of chan when using DontCloseRequestStream +- github.com/multiformats/go-multistream (v0.3.3 -> v0.4.1): + - release v0.4.1 ([multiformats/go-multistream#101](https://github.com/multiformats/go-multistream/pull/101)) + - Fix errors Is checking ([multiformats/go-multistream#100](https://github.com/multiformats/go-multistream/pull/100)) + - release v0.4.0 (#93) ([multiformats/go-multistream#93](https://github.com/multiformats/go-multistream/pull/93)) + - switch to Go's native fuzzing (#96) ([multiformats/go-multistream#96](https://github.com/multiformats/go-multistream/pull/96)) + - Add not supported protocols to returned errors (#97) ([multiformats/go-multistream#97](https://github.com/multiformats/go-multistream/pull/97)) + - Make MultistreamMuxer and Client APIs generic (#95) ([multiformats/go-multistream#95](https://github.com/multiformats/go-multistream/pull/95)) + - remove MultistreamMuxer.NegotiateLazy (#92) ([multiformats/go-multistream#92](https://github.com/multiformats/go-multistream/pull/92)) + - sync: update CI config files (#91) ([multiformats/go-multistream#91](https://github.com/multiformats/go-multistream/pull/91)) +- github.com/warpfork/go-wish (v0.0.0-20200122115046-b9ea61034e4a -> v0.0.0-20220906213052-39a1cc7a02d0): + - Update readme with deprecation info +- github.com/whyrusleeping/cbor-gen (v0.0.0-20221220214510-0333c149dec0 -> v0.0.0-20230126041949-52956bd4c9aa): + - add setter to allow reuse of cborreader struct + - fix typo + - allow fields to be ignored ([whyrusleeping/cbor-gen#79](https://github.com/whyrusleeping/cbor-gen/pull/79)) + +
+ ### 👨‍👩‍👧‍👦 Contributors + +| Contributor | Commits | Lines ± | Files Changed | +|-------------|---------|---------|---------------| +| Dirk McCormick | 128 | +16757/-7211 | 387 | +| Henrique Dias | 69 | +7599/-10016 | 316 | +| hannahhoward | 88 | +8503/-4397 | 271 | +| Jeromy Johnson | 244 | +6544/-4034 | 774 | +| Marten Seemann | 64 | +4870/-5628 | 266 | +| Steven Allen | 296 | +4769/-3517 | 972 | +| Brian Tiger Chow | 250 | +5520/-2579 | 435 | +| Jorropo | 64 | +4237/-3548 | 302 | +| Sukun | 18 | +4327/-1093 | 132 | +| Marco Munizaga | 35 | +2809/-1294 | 94 | +| Gus Eggert | 20 | +2523/-1476 | 99 | +| Adin Schmahmann | 15 | +683/-2625 | 69 | +| Marcin Rataj | 73 | +2348/-882 | 133 | +| whyrusleeping | 12 | +1683/-1338 | 23 | +| Jeromy | 99 | +1754/-1181 | 453 | +| Juan Batiz-Benet | 69 | +1182/-678 | 149 | +| Lars Gierth | 31 | +827/-358 | 92 | +| Paul Wolneykien | 2 | +670/-338 | 9 | +| Laurent Senta | 16 | +806/-134 | 53 | +| Henry | 19 | +438/-372 | 36 | +| Michael Muré | 8 | +400/-387 | 19 | +| Łukasz Magiera | 56 | +413/-354 | 117 | +| Jakub Sztandera | 40 | +413/-251 | 100 | +| Justin Johnson | 2 | +479/-165 | 5 | +| Piotr Galar | 7 | +227/-378 | 24 | +| Kevin Atkinson | 11 | +252/-232 | 49 | +| web3-bot | 17 | +236/-240 | 59 | +| Petar Maymounkov | 2 | +348/-84 | 11 | +| Hector Sanjuan | 38 | +206/-223 | 85 | +| Antonio Navarro Perez | 9 | +259/-95 | 17 | +| keks | 22 | +233/-118 | 24 | +| Ho-Sheng Hsiao | 3 | +170/-170 | 30 | +| Lucas Molas | 6 | +266/-54 | 16 | +| Mildred Ki'Lya | 4 | +280/-35 | 7 | +| Steve Loeppky | 5 | +147/-156 | 9 | +| rht | 14 | +97/-188 | 20 | +| Prithvi Shahi | 6 | +89/-193 | 11 | +| Ian Davis | 6 | +198/-75 | 11 | +| taylor | 1 | +180/-89 | 8 | +| ᴍᴀᴛᴛ ʙᴇʟʟ | 14 | +158/-104 | 18 | +| Chris Boddy | 6 | +190/-45 | 8 | +| Rod Vagg | 3 | +203/-28 | 15 | +| Masih H. Derkani | 8 | +165/-61 | 16 | +| Kevin Wallace | 4 | +194/-27 | 7 | +| Mohsin Zaidi | 1 | +179/-41 | 5 | +| ElPaisano | 1 | +110/-110 | 22 | +| Simon Zhu | 6 | +177/-32 | 8 | +| galargh | 9 | +80/-120 | 14 | +| Tomasz Zdybał | 1 | +180/-1 | 4 | +| dgrisham | 3 | +176/-2 | 4 | +| Michael Avila | 3 | +116/-59 | 8 | +| Raúl Kripalani | 2 | +85/-77 | 34 | +| Dr Ian Preston | 11 | +101/-48 | 11 | +| JP Hastings-Spital | 1 | +145/-0 | 2 | +| George Antoniadis | 6 | +59/-58 | 43 | +| Kevin Neaton | 2 | +97/-16 | 4 | +| Adrian Lanzafame | 6 | +81/-25 | 7 | +| Dennis Trautwein | 3 | +89/-9 | 5 | +| mathew-cf | 2 | +82/-9 | 5 | +| tg | 1 | +41/-33 | 1 | +| Eng Zer Jun | 1 | +15/-54 | 5 | +| zramsay | 4 | +15/-53 | 12 | +| muXxer | 1 | +28/-33 | 4 | +| Thomas Eizinger | 1 | +24/-37 | 4 | +| Remco Bloemen | 2 | +28/-18 | 3 | +| Manuel Alonso | 1 | +36/-9 | 1 | +| vyzo | 4 | +26/-12 | 13 | +| Djalil Dreamski | 3 | +27/-9 | 3 | +| Thomas Gardner | 2 | +32/-3 | 4 | +| Jan Winkelmann | 2 | +23/-12 | 8 | +| Artem Andreenko | 1 | +16/-19 | 1 | +| James Stanley | 1 | +34/-0 | 1 | +| Brendan McMillion | 1 | +10/-17 | 3 | +| Jack Loughran | 1 | +22/-0 | 3 | +| Peter Wu | 2 | +12/-9 | 2 | +| Gowtham G | 4 | +14/-7 | 4 | +| Tor Arne Vestbø | 3 | +19/-1 | 3 | +| Cory Schwartz | 1 | +8/-12 | 5 | +| Peter Rabbitson | 1 | +15/-4 | 1 | +| David Dias | 1 | +9/-9 | 1 | +| Will Scott | 1 | +13/-4 | 2 | +| Eric Myhre | 1 | +15/-2 | 1 | +| Stephen Whitmore | 1 | +8/-8 | 1 | +| Rafael Ramalho | 5 | +11/-5 | 5 | +| Christian Couder | 1 | +14/-2 | 1 | +| W. Trevor King | 2 | +9/-6 | 3 | +| Steven Vandevelde | 1 | +11/-3 | 1 | +| Knut Ahlers | 3 | +9/-5 | 3 | +| Bob Potter | 1 | +3/-10 | 1 | +| Russell Dempsey | 4 | +8/-4 | 4 | +| Diogo Silva | 4 | +8/-4 | 4 | +| Dave Justice | 1 | +8/-4 | 1 | +| Andy Leap | 2 | +2/-10 | 2 | +| divingpetrel | 1 | +7/-4 | 2 | +| Iaroslav Gridin | 1 | +9/-2 | 1 | +| Dominic Della Valle | 3 | +5/-5 | 3 | +| Vijayee Kulkaa | 1 | +3/-6 | 1 | +| Friedel Ziegelmayer | 3 | +6/-3 | 3 | +| Stephen Solka | 1 | +1/-7 | 1 | +| Richard Littauer | 3 | +4/-4 | 3 | +| Franky W | 2 | +4/-4 | 2 | +| Dimitris Apostolou | 2 | +4/-4 | 3 | +| Adrian Ulrich | 1 | +8/-0 | 1 | +| Masashi Salvador Mitsuzawa | 1 | +5/-1 | 1 | +| Gabe | 1 | +3/-3 | 1 | +| zuuluuz | 1 | +4/-1 | 1 | +| myml | 1 | +5/-0 | 1 | +| swedneck | 1 | +3/-1 | 1 | +| Wayback Archiver | 1 | +2/-2 | 1 | +| Vladimir Ivanov | 1 | +2/-2 | 1 | +| Péter Szilágyi | 1 | +2/-2 | 1 | +| Karthik Bala | 1 | +2/-2 | 1 | +| Etienne Laurin | 1 | +1/-3 | 1 | +| Shotaro Yamada | 1 | +2/-1 | 1 | +| Robert Carlsen | 1 | +2/-1 | 1 | +| Oli Evans | 1 | +2/-1 | 1 | +| Dan McQuillan | 1 | +2/-1 | 1 | +| susarlanikhilesh | 1 | +1/-1 | 1 | +| mateon1 | 1 | +1/-1 | 1 | +| kpcyrd | 1 | +1/-1 | 1 | +| bbenshoof | 1 | +1/-1 | 1 | +| ZenGround0 | 1 | +1/-1 | 1 | +| Will Hawkins | 1 | +1/-1 | 1 | +| Tommi Virtanen | 1 | +1/-1 | 1 | +| Seungbae Yu | 1 | +1/-1 | 1 | +| Riishab Joshi | 1 | +1/-1 | 1 | +| Kubo Mage | 1 | +1/-1 | 1 | +| Ivan | 1 | +1/-1 | 1 | +| Guillaume Renault | 1 | +1/-1 | 1 | +| Anjor Kanekar | 1 | +1/-1 | 1 | +| Andrew Chin | 1 | +1/-1 | 1 | +| Abdul Rauf | 1 | +1/-1 | 1 | +| makeworld | 1 | +1/-0 | 1 |