Skip to content

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

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
9 changes: 9 additions & 0 deletions config/bitswap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package config

// Bitswap holds Bitswap configuration options
type Bitswap 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"`
}
2 changes: 2 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ type Config struct {
Version Version

Internal Internal // experimental/unstable options

Bitswap Bitswap `json:",omitempty"`
}

const (
Expand Down
4 changes: 2 additions & 2 deletions core/commands/stat_provide.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ This interface is not stable and may change from release to release.
wtr := tabwriter.NewWriter(w, 1, 2, 1, ' ', 0)
defer wtr.Flush()

fmt.Fprintf(wtr, "TotalProvides:\t%s\n", humanNumber(s.TotalProvides))
fmt.Fprintf(wtr, "AvgProvideDuration:\t%s\n", humanDuration(s.AvgProvideDuration))
fmt.Fprintf(wtr, "TotalReprovides:\t%s\n", humanNumber(s.TotalReprovides))
fmt.Fprintf(wtr, "AvgReprovideDuration:\t%s\n", humanDuration(s.AvgReprovideDuration))
fmt.Fprintf(wtr, "LastReprovideDuration:\t%s\n", humanDuration(s.LastReprovideDuration))
if !s.LastRun.IsZero() {
fmt.Fprintf(wtr, "LastRun:\t%s\n", humanTime(s.LastRun))
Expand Down
51 changes: 45 additions & 6 deletions core/node/bitswap.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package node

import (
"context"
"io"
"time"

"github.com/ipfs/boxo/bitswap"
Expand All @@ -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"
)

Expand Down Expand Up @@ -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),
Expand All @@ -93,10 +100,16 @@ func Bitswap(provide bool) interface{} {
return nil, err
}
in.BitswapOpts = append(in.BitswapOpts, bitswap.WithClientOption(client.WithDefaultProviderQueryManager(false)))
in.BitswapOpts = append(in.BitswapOpts, bitswap.WithServerEnabled(true))
provider = pqm

} else {
provider = nil
// When server is disabled, use an empty blockstore to prevent serving blocks
blockstoree = blockstore.NewBlockstore(datastore.NewMapDatastore())
in.BitswapOpts = append(in.BitswapOpts, bitswap.WithServerEnabled(false))
}
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...)
Copy link
Member

@lidel lidel Apr 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am afraid this does not actually disable bitswap server, it only makes sure the server will always return "i dont have this blocks" because empty datastore is used.

The problem with this approach is that such node will still announce itself as bitswap server – you can see it by making ipfs idProtocols section will still have:

"/ipfs/bitswap",
"/ipfs/bitswap/1.0.0",
"/ipfs/bitswap/1.1.0",
"/ipfs/bitswap/1.2.0",

This is a problem because such node will still receive bitswap queries ans asks for random CIDs from other peers, and will have to respond to them, effectively wasting resources.

Desired behavior

What we want for Bitswap.ServerEnabled=false is to completely "stop acting as bitswap server" and that means removing it from libp2p identify response (ipfs id), so other peers won't even talk to us over bitswap on their own (and only when we connect to them, as a client).

There are two ways of doing it – not sure which one will be easier, it is fine for you to investugate before making a decision:

  • (A) When server is disabled, do not initialize entire bitswap (which is wrapper for both client+server), and initialize only the client
  • (B) Initialize noop server with emty blockstore, but make sure we dont announce ourselves as /ipfs/bitswap* server to other peers.
    • If client-only approach is too complex to execute in Kubo codebase, it is sensible to keep your solution with noop server, and just finding a way to remove /ipfs/bitswap* from libp2p identify response (which can be inspected via ipfs id <peerid> or vole libp2p identify)
    • This may require changes to https://github.com/ipfs/boxo – perhaps adding a new configuration option for Bitswap server that disables annoucing /ipfs/bitswap to libp2p host

I'd say see if (A) is easy to do, but if you find yourself being blocked by a bigger refactor (e.g. all the commands that expect bitswap server to be always present), consider (B) instead, as it would have smaller code surface, and also be easier to maintain.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I investigated both the suggested solutions , but I got stuck in trying to prevent the node to communicate /ipfs/bitswap* as supported protocols: the identify protocol is segregated inside the go-libp2p library and I cannot manipulate its internals. I have found a way to remove the handler associated to the /ipfs/bitswap* protocol in the Host's multiplexer (Mux). This way, the protocol is effectively removed from the list of supported protocols, but it results in the client not being able to send messages anymore (I suspect it is due to this boxo function). What if we register a stream handler for that just immediately shutsdown? I mean replacing this handler with an empty method that just closes the stream. This way maybe the node will stop responding completely to other nodes bitswap requests.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gystemd thank you for looking into this.

I wonder if we could do this earlier bitswap/network/bsnet – I'm thinking Start instead of handleNewStream:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you for you time and sorry for the confusion, but after a deeper investigation I found out that the StreamHandler seems to be mandatory. It is responsible for deliverying packets to the client. When running tests, I had crashes due to failed invocations of the handler. I added a proposal option in Boxo ipfs/boxo#911 to disable the server with related tests. Could you see if this is an acceptable solution that fit the needs?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pushed the new tests for Kubo, which are failing due to the reliance of a modified version of Boxo.


lc.Append(fx.Hook{
OnStop: func(ctx context.Context) error {
Expand All @@ -108,8 +121,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()
Expand Down Expand Up @@ -144,3 +161,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
}

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()
}
7 changes: 4 additions & 3 deletions core/node/groups.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,13 +335,14 @@ func Online(bcfg *BuildCfg, cfg *config.Config, userResourceOverrides rcmgr.Part
recordLifetime = d
}

/* don't provide from bitswap when the strategic provider service is active */
shouldBitswapProvide := !cfg.Experimental.StrategicProviding
isBitswapEnabled := cfg.Bitswap.Enabled.WithDefault(true) && cfg.Bitswap.ServerEnabled.WithDefault(true)
// Don't provide from bitswap when the strategic provider service is active
shouldBitswapProvide := isBitswapEnabled && !cfg.Experimental.StrategicProviding

return fx.Options(
fx.Provide(BitswapOptions(cfg)),
fx.Provide(Bitswap(shouldBitswapProvide)),
fx.Provide(OnlineExchange()),
fx.Provide(OnlineExchange(cfg.Bitswap.Enabled.WithDefault(true))),
// Replace our Exchange with a Providing exchange!
fx.Decorate(ProvidingExchange(shouldBitswapProvide)),
fx.Provide(DNSResolver),
Expand Down
3 changes: 2 additions & 1 deletion docs/changelogs/v0.35.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,6 @@ The WebUI, accessible at http://127.0.0.1:5001/webui/, now includes support for
- update `ipfs-webui` to [v4.7.0](https://github.com/ipfs/ipfs-webui/releases/tag/v4.7.0)

### 📝 Changelog

- Completely disable Bitswap functionality through the `Bitswap.Enabled` flag
- Control server behavior separately with `Bitswap.ServerEnabled`, allowing nodes to operate in client-only mode
### 👨‍👩‍👧‍👦 Contributors
42 changes: 21 additions & 21 deletions docs/examples/kubo-as-a-library/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ go 1.24
replace github.com/ipfs/kubo => ./../../..

require (
github.com/ipfs/boxo v0.29.2-0.20250409154342-bbaf2e146dfb
github.com/ipfs/boxo v0.29.2-0.20250430060235-64141fd10dbd
github.com/ipfs/kubo v0.0.0-00010101000000-000000000000
github.com/libp2p/go-libp2p v0.41.1
github.com/multiformats/go-multiaddr v0.15.0
Expand Down Expand Up @@ -124,8 +124,8 @@ require (
github.com/libp2p/go-doh-resolver v0.5.0 // indirect
github.com/libp2p/go-flow-metrics v0.2.0 // indirect
github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect
github.com/libp2p/go-libp2p-kad-dht v0.30.2 // indirect
github.com/libp2p/go-libp2p-kbucket v0.6.5 // indirect
github.com/libp2p/go-libp2p-kad-dht v0.32.0 // indirect
github.com/libp2p/go-libp2p-kbucket v0.7.0 // indirect
github.com/libp2p/go-libp2p-pubsub v0.13.0 // indirect
github.com/libp2p/go-libp2p-pubsub-router v0.6.0 // indirect
github.com/libp2p/go-libp2p-record v0.3.1 // indirect
Expand All @@ -139,7 +139,7 @@ require (
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mholt/acmez/v3 v3.0.0 // indirect
github.com/miekg/dns v1.1.63 // indirect
github.com/miekg/dns v1.1.65 // indirect
github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect
github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect
github.com/minio/sha256-simd v1.0.1 // indirect
Expand Down Expand Up @@ -181,10 +181,10 @@ require (
github.com/pion/webrtc/v4 v4.0.10 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/polydawn/refmt v0.89.0 // indirect
github.com/prometheus/client_golang v1.21.1 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/prometheus/client_golang v1.22.0 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.63.0 // indirect
github.com/prometheus/procfs v0.16.1 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.50.1 // indirect
github.com/quic-go/webtransport-go v0.8.1-0.20241018022711-4ac2c9250e66 // indirect
Expand All @@ -205,15 +205,15 @@ require (
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect
go.opentelemetry.io/otel v1.34.0 // indirect
go.opentelemetry.io/otel v1.35.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0 // indirect
go.opentelemetry.io/otel/exporters/zipkin v1.31.0 // indirect
go.opentelemetry.io/otel/metric v1.34.0 // indirect
go.opentelemetry.io/otel/metric v1.35.0 // indirect
go.opentelemetry.io/otel/sdk v1.31.0 // indirect
go.opentelemetry.io/otel/trace v1.34.0 // indirect
go.opentelemetry.io/otel/trace v1.35.0 // indirect
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/dig v1.18.0 // indirect
Expand All @@ -223,20 +223,20 @@ require (
go.uber.org/zap v1.27.0 // indirect
go.uber.org/zap/exp v0.3.0 // indirect
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
golang.org/x/crypto v0.35.0 // indirect
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa // indirect
golang.org/x/mod v0.23.0 // indirect
golang.org/x/net v0.35.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/text v0.22.0 // indirect
golang.org/x/tools v0.30.0 // indirect
golang.org/x/crypto v0.37.0 // indirect
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
golang.org/x/mod v0.24.0 // indirect
golang.org/x/net v0.39.0 // indirect
golang.org/x/sync v0.13.0 // indirect
golang.org/x/sys v0.32.0 // indirect
golang.org/x/text v0.24.0 // indirect
golang.org/x/tools v0.32.0 // indirect
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
gonum.org/v1/gonum v0.15.1 // indirect
gonum.org/v1/gonum v0.16.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 // indirect
google.golang.org/grpc v1.67.1 // indirect
google.golang.org/protobuf v1.36.5 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
lukechampine.com/blake3 v1.4.0 // indirect
)
Loading