-
-
Notifications
You must be signed in to change notification settings - Fork 3.2k
feat(config): ability to disable Bitswap fully or just server #10782
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
14c7ccc
682b47b
a2c34bd
366b56e
bc4a386
411ed04
7c6b791
fd65781
6de6673
60bf94a
e812e3e
188c1b9
258fb25
e7f501e
9de888b
b11a65a
0473134
f71926f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| package config | ||
|
|
||
| // BitswapConfig holds Bitswap configuration options | ||
| type BitswapConfig struct { | ||
| // Enabled controls both client and server (enabled by default) | ||
| Enabled Flag `json:",omitempty"` | ||
| // ServerEnabled controls if the node responds to WANTs (depends on Enabled, enabled by default) | ||
| ServerEnabled Flag `json:",omitempty"` | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,7 @@ package node | |
|
|
||
| import ( | ||
| "context" | ||
| "io" | ||
| "time" | ||
|
|
||
| "github.com/ipfs/boxo/bitswap" | ||
|
|
@@ -12,12 +13,15 @@ import ( | |
| "github.com/ipfs/boxo/exchange/providing" | ||
| provider "github.com/ipfs/boxo/provider" | ||
| rpqm "github.com/ipfs/boxo/routing/providerquerymanager" | ||
| "github.com/ipfs/go-cid" | ||
| "github.com/ipfs/go-datastore" | ||
| "github.com/ipfs/kubo/config" | ||
| irouting "github.com/ipfs/kubo/routing" | ||
| "github.com/libp2p/go-libp2p/core/host" | ||
| "github.com/libp2p/go-libp2p/core/routing" | ||
| "go.uber.org/fx" | ||
|
|
||
| blocks "github.com/ipfs/go-block-format" | ||
| "github.com/ipfs/kubo/core/node/helpers" | ||
| ) | ||
|
|
||
|
|
@@ -72,18 +76,21 @@ type bitswapIn struct { | |
| } | ||
|
|
||
| // Bitswap creates the BitSwap server/client instance. | ||
| // Additional options to bitswap.New can be provided via the "bitswap-options" | ||
| // group. | ||
| // If Bitswap.ServerEnabled is false, the node will act only as a client | ||
| // using an empty blockstore to prevent serving blocks to other peers. | ||
| func Bitswap(provide bool) interface{} { | ||
| return func(in bitswapIn, lc fx.Lifecycle) (*bitswap.Bitswap, error) { | ||
| bitswapNetwork := bsnet.NewFromIpfsHost(in.Host) | ||
|
|
||
| var blockstoree blockstore.Blockstore = in.Bs | ||
| var provider routing.ContentDiscovery | ||
|
|
||
| if provide { | ||
|
|
||
| var maxProviders int = DefaultMaxProviders | ||
| if in.Cfg.Internal.Bitswap != nil { | ||
| maxProviders = int(in.Cfg.Internal.Bitswap.ProviderSearchMaxResults.WithDefault(DefaultMaxProviders)) | ||
| } | ||
|
|
||
| pqm, err := rpqm.New(bitswapNetwork, | ||
| in.Rt, | ||
| rpqm.WithMaxProviders(maxProviders), | ||
|
|
@@ -94,9 +101,13 @@ func Bitswap(provide bool) interface{} { | |
| } | ||
| in.BitswapOpts = append(in.BitswapOpts, bitswap.WithClientOption(client.WithDefaultProviderQueryManager(false))) | ||
| provider = pqm | ||
|
|
||
| } else { | ||
| provider = nil | ||
| // When server is disabled, use an empty blockstore to prevent serving blocks | ||
| blockstoree = blockstore.NewBlockstore(datastore.NewMapDatastore()) | ||
| } | ||
| bs := bitswap.New(helpers.LifecycleCtx(in.Mctx, lc), bitswapNetwork, provider, in.Bs, in.BitswapOpts...) | ||
|
|
||
| bs := bitswap.New(helpers.LifecycleCtx(in.Mctx, lc), bitswapNetwork, provider, blockstoree, in.BitswapOpts...) | ||
|
lidel marked this conversation as resolved.
|
||
|
|
||
| lc.Append(fx.Hook{ | ||
| OnStop: func(ctx context.Context) error { | ||
|
|
@@ -108,8 +119,12 @@ func Bitswap(provide bool) interface{} { | |
| } | ||
|
|
||
| // OnlineExchange creates new LibP2P backed block exchange. | ||
| func OnlineExchange() interface{} { | ||
| // Returns a no-op exchange if Bitswap is disabled. | ||
| func OnlineExchange(isBitswapActive bool) interface{} { | ||
| return func(in *bitswap.Bitswap, lc fx.Lifecycle) exchange.Interface { | ||
| if !isBitswapActive { | ||
| return &noopExchange{closer: in} | ||
| } | ||
| lc.Append(fx.Hook{ | ||
| OnStop: func(ctx context.Context) error { | ||
| return in.Close() | ||
|
|
@@ -144,3 +159,25 @@ func ProvidingExchange(provide bool) interface{} { | |
| return exch | ||
| } | ||
| } | ||
|
|
||
| type noopExchange struct { | ||
| closer io.Closer | ||
| } | ||
|
|
||
| func (e *noopExchange) GetBlock(ctx context.Context, c cid.Cid) (blocks.Block, error) { | ||
| return nil, nil | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tested latest commit 258fb25 and not returning error here seems to be why we have a panic when executing $ ipfs daemon
...
Daemon is ready
... ipfs cat for lon-local cid is executed from other terminal
2025-04-30T17:00:40.931+0200 ERROR cmds/http http/handler.go:96 a panic has occurred in the commands handler!
2025-04-30T17:00:40.931+0200 ERROR cmds/http http/handler.go:97 runtime error: invalid memory address or nil pointer dereference
2025-04-30T17:00:40.933+0200 ERROR cmds/http http/handler.go:98 stack trace:
goroutine 50865 [running]:
runtime/debug.Stack()
runtime/debug/stack.go:26 +0x5e
github.com/ipfs/go-ipfs-cmds/http.(*handler).ServeHTTP.func1()
github.com/ipfs/go-ipfs-cmds@v0.14.1/http/handler.go:98 +0xef
panic({0x2e5e7c0?, 0x4b6e100?})
runtime/panic.go:792 +0x132
github.com/ipfs/boxo/blockstore.(*idstore).Put(0xc0011d9400, {0x385dbf0, 0xc002d40060}, {0x0, 0x0})
github.com/ipfs/boxo@v0.30.0/blockstore/idstore.go:93 +0x27
github.com/ipfs/boxo/blockservice.getBlock({0x385dbf0, 0xc002d40060}, {{0xc0022d8fc0?, 0x1d?}}, {0x3871b68, 0xc00130a270}, 0xc001462740)
github.com/ipfs/boxo@v0.30.0/blockservice/blockservice.go:278 +0x242
github.com/ipfs/boxo/blockservice.(*Session).GetBlock(0xc002e559c0, {0x385dbf0, 0xc0004afe00}, {{0xc0022d8fc0?, 0x248f580?}})
github.com/ipfs/boxo@v0.30.0/blockservice/blockservice.go:462 +0x33c
github.com/ipfs/boxo/ipld/merkledag.(*sesGetter).Get(0xc002ed7940, {0x385dbf0, 0xc0004afe00}, {{0xc0022d8fc0?, 0xc001462788?}})
github.com/ipfs/boxo@v0.30.0/ipld/merkledag/merkledag.go:143 +0x2e
github.com/ipfs/boxo/ipld/merkledag.(*ComboService).Get(0xc002356300?, {0x385dbf0?, 0xc0004afe00?}, {{0xc0022d8fc0?, 0xc00074cdc0?}})
github.com/ipfs/boxo@v0.30.0/ipld/merkledag/rwservice.go:31 +0x2a
github.com/ipfs/kubo/core/coreapi.(*CoreAPI).ResolveNode(0xc002356300, {0x385dbf0, 0xc0004afdd0}, {0x385db48, 0xc00074cdc0})
github.com/ipfs/kubo/core/coreapi/path.go:31 +0x338
github.com/ipfs/kubo/core/coreapi.(*UnixfsAPI).Get(0xc000b48480, {0x385dc28, 0xc0042f09b0}, {0x385db48, 0xc00074cdc0})
github.com/ipfs/kubo/core/coreapi/unixfs.go:210 +0x318
github.com/ipfs/kubo/core/commands.cat({0x385dc28, 0xc0042f09b0}, {0x387bba0, 0xc000b48480}, {0xc002ed7840, 0x1, 0x0?}, 0x0, 0xffffffffffffffff)
github.com/ipfs/kubo/core/commands/cat.go:136 +0x17bSeems to be an easy fix, I'll push one soon.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix: b11a65a |
||
| } | ||
|
|
||
| func (e *noopExchange) GetBlocks(ctx context.Context, cids []cid.Cid) (<-chan blocks.Block, error) { | ||
| ch := make(chan blocks.Block) | ||
| close(ch) | ||
| return ch, nil | ||
| } | ||
|
|
||
| func (e *noopExchange) NotifyNewBlocks(ctx context.Context, blocks ...blocks.Block) error { | ||
| return nil | ||
| } | ||
|
|
||
| func (e *noopExchange) Close() error { | ||
| return e.closer.Close() | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need a test that confirms |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| package cli | ||
|
|
||
| import ( | ||
| "testing" | ||
| "time" | ||
|
|
||
| "github.com/ipfs/kubo/config" | ||
| "github.com/ipfs/kubo/test/cli/harness" | ||
| "github.com/ipfs/kubo/test/cli/testutils" | ||
| "github.com/stretchr/testify/assert" | ||
| ) | ||
|
|
||
| func TestBitswapConfig(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| // Create test data that will be shared between nodes | ||
| testData := testutils.RandomBytes(100) | ||
|
|
||
| t.Run("server enabled (default)", func(t *testing.T) { | ||
| t.Parallel() | ||
| h := harness.NewT(t) | ||
| provider := h.NewNode().Init().StartDaemon() | ||
| requester := h.NewNode().Init().StartDaemon() | ||
|
|
||
| hash := provider.IPFSAddStr(string(testData)) | ||
| requester.Connect(provider) | ||
|
|
||
| res := requester.IPFS("cat", hash) | ||
| assert.Equal(t, testData, res.Stdout.Bytes(), "retrieved data should match original") | ||
| }) | ||
|
|
||
| t.Run("server disabled", func(t *testing.T) { | ||
| t.Parallel() | ||
| h := harness.NewT(t) | ||
|
|
||
| provider := h.NewNode().Init() | ||
| provider.SetIPFSConfig("Bitswap.ServerEnabled", false) | ||
| provider = provider.StartDaemon() | ||
|
|
||
| requester := h.NewNode().Init().StartDaemon() | ||
|
|
||
| hash := provider.IPFSAddStr(string(testData)) | ||
| requester.Connect(provider) | ||
|
|
||
| // If the data was available, it would be retrieved immediately. | ||
| // Therefore, after the timeout, we can assume the data is not available | ||
| // i.e. the server is disabled | ||
| timeout := time.After(3 * time.Second) | ||
| dataChan := make(chan []byte) | ||
|
|
||
| go func() { | ||
| res := requester.RunIPFS("cat", hash) | ||
| dataChan <- res.Stdout.Bytes() | ||
| }() | ||
|
|
||
| select { | ||
| case data := <-dataChan: | ||
| assert.NotEqual(t, testData, data, "retrieved data should not match original") | ||
| case <-timeout: | ||
| t.Log("Test passed: operation timed out after 3 seconds as expected") | ||
| } | ||
| }) | ||
|
|
||
| t.Run("server disabled and client enabled", func(t *testing.T) { | ||
| t.Parallel() | ||
| h := harness.NewT(t) | ||
|
|
||
| provider := h.NewNode().Init() | ||
| provider.SetIPFSConfig("Bitswap.ServerEnabled", false) | ||
| provider = provider.StartDaemon() | ||
|
|
||
| requester := h.NewNode().Init().StartDaemon() | ||
| hash := requester.IPFSAddStr(string(testData)) | ||
|
|
||
| provider.Connect(requester) | ||
|
|
||
| // even when the server is disabled, the client should be able to retrieve data | ||
| res := provider.RunIPFS("cat", hash) | ||
| assert.Equal(t, testData, res.Stdout.Bytes(), "retrieved data should match original") | ||
| }) | ||
|
|
||
| t.Run("bitswap completely disabled", func(t *testing.T) { | ||
| t.Parallel() | ||
| h := harness.NewT(t) | ||
|
|
||
| requester := h.NewNode().Init() | ||
| requester.UpdateConfig(func(cfg *config.Config) { | ||
| cfg.Bitswap.Enabled = config.False | ||
| cfg.Bitswap.ServerEnabled = config.False | ||
| }) | ||
| requester.StartDaemon() | ||
|
|
||
| provider := h.NewNode().Init().StartDaemon() | ||
| hash := provider.IPFSAddStr(string(testData)) | ||
|
|
||
| requester.Connect(provider) | ||
| res := requester.RunIPFS("cat", hash) | ||
| assert.Equal(t, []uint8([]byte{}), res.Stdout.Bytes(), "cat should not return any data") | ||
|
|
||
| // Verify that basic operations still work with bitswap disabled | ||
| res = requester.IPFS("id") | ||
| assert.Equal(t, 0, res.ExitCode(), "basic IPFS operations should work") | ||
| res = requester.IPFS("bitswap", "stat") | ||
| assert.Equal(t, 0, res.ExitCode(), "bitswap stat should work even with bitswap disabled") | ||
| res = requester.IPFS("bitswap", "wantlist") | ||
| assert.Equal(t, 0, res.ExitCode(), "bitswap wantlist should work even with bitswap disabled") | ||
|
|
||
| // Verify local operations still work | ||
| hash_new := requester.IPFSAddStr(string("random")) | ||
| res = requester.IPFS("cat", hash_new) | ||
| assert.Equal(t, []uint8([]byte("random")), res.Stdout.Bytes(), "cat should return the added data") | ||
| }) | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.