master
go 244 lines 9.64 KB
Raw
1 package node
2
3 import (
4 "context"
5 "errors"
6 "io"
7 "time"
8
9 "github.com/dustin/go-humanize"
10 "github.com/ipfs/boxo/bitswap"
11 "github.com/ipfs/boxo/bitswap/client"
12 "github.com/ipfs/boxo/bitswap/network"
13 bsnet "github.com/ipfs/boxo/bitswap/network/bsnet"
14 "github.com/ipfs/boxo/bitswap/network/httpnet"
15 blockstore "github.com/ipfs/boxo/blockstore"
16 exchange "github.com/ipfs/boxo/exchange"
17 rpqm "github.com/ipfs/boxo/routing/providerquerymanager"
18 "github.com/ipfs/go-cid"
19 ipld "github.com/ipfs/go-ipld-format"
20 version "github.com/ipfs/kubo"
21 "github.com/ipfs/kubo/config"
22 "github.com/libp2p/go-libp2p/core/host"
23 peer "github.com/libp2p/go-libp2p/core/peer"
24 "github.com/libp2p/go-libp2p/core/routing"
25 "go.uber.org/fx"
26
27 blocks "github.com/ipfs/go-block-format"
28 "github.com/ipfs/kubo/core/node/helpers"
29 "github.com/ipfs/kubo/core/shutdown"
30 )
31
32 // Docs: https://github.com/ipfs/kubo/blob/master/docs/config.md#internalbitswap
33 const (
34 DefaultEngineBlockstoreWorkerCount = 128
35 DefaultTaskWorkerCount = 8
36 DefaultEngineTaskWorkerCount = 8
37 DefaultMaxOutstandingBytesPerPeer = 1 << 20
38 DefaultProviderSearchDelay = 1000 * time.Millisecond
39 DefaultMaxProviders = 10 // matching BitswapClientDefaultMaxProviders from https://github.com/ipfs/boxo/blob/v0.29.1/bitswap/internal/defaults/defaults.go#L15
40 DefaultWantHaveReplaceSize = 1024
41 )
42
43 type bitswapOptionsOut struct {
44 fx.Out
45
46 BitswapOpts []bitswap.Option `group:"bitswap-options,flatten"`
47 }
48
49 // BitswapOptions creates configuration options for Bitswap from the config file
50 // and whether to provide data.
51 func BitswapOptions(cfg *config.Config) any {
52 return func() bitswapOptionsOut {
53 var internalBsCfg config.InternalBitswap
54 if cfg.Internal.Bitswap != nil {
55 internalBsCfg = *cfg.Internal.Bitswap
56 }
57
58 opts := []bitswap.Option{
59 bitswap.ProviderSearchDelay(internalBsCfg.ProviderSearchDelay.WithDefault(DefaultProviderSearchDelay)), // See https://github.com/ipfs/go-ipfs/issues/8807 for rationale
60 bitswap.EngineBlockstoreWorkerCount(int(internalBsCfg.EngineBlockstoreWorkerCount.WithDefault(DefaultEngineBlockstoreWorkerCount))),
61 bitswap.TaskWorkerCount(int(internalBsCfg.TaskWorkerCount.WithDefault(DefaultTaskWorkerCount))),
62 bitswap.EngineTaskWorkerCount(int(internalBsCfg.EngineTaskWorkerCount.WithDefault(DefaultEngineTaskWorkerCount))),
63 bitswap.MaxOutstandingBytesPerPeer(int(internalBsCfg.MaxOutstandingBytesPerPeer.WithDefault(DefaultMaxOutstandingBytesPerPeer))),
64 bitswap.WithWantHaveReplaceSize(int(internalBsCfg.WantHaveReplaceSize.WithDefault(DefaultWantHaveReplaceSize))),
65 }
66
67 return bitswapOptionsOut{BitswapOpts: opts}
68 }
69 }
70
71 type bitswapIn struct {
72 fx.In
73
74 Mctx helpers.MetricsCtx
75 Cfg *config.Config
76 Host host.Host
77 Discovery routing.ContentDiscovery
78 Bs blockstore.GCBlockstore
79 BitswapOpts []bitswap.Option `group:"bitswap-options"`
80 }
81
82 // Bitswap creates the BitSwap server/client instance.
83 // If Bitswap.ServerEnabled is false, the node will act only as a client
84 // using an empty blockstore to prevent serving blocks to other peers.
85 func Bitswap(serverEnabled, libp2pEnabled, httpEnabled bool) any {
86 return func(in bitswapIn, lc fx.Lifecycle) (*bitswap.Bitswap, error) {
87 var bitswapNetworks, bitswapLibp2p network.BitSwapNetwork
88 var bitswapBlockstore blockstore.Blockstore = in.Bs
89
90 connEvtMgr := network.NewConnectEventManager()
91
92 libp2pEnabled := in.Cfg.Bitswap.Libp2pEnabled.WithDefault(config.DefaultBitswapLibp2pEnabled)
93 if libp2pEnabled {
94 bitswapLibp2p = bsnet.NewFromIpfsHost(
95 in.Host,
96 bsnet.WithConnectEventManager(connEvtMgr),
97 )
98 }
99
100 if httpEnabled {
101 httpCfg := in.Cfg.HTTPRetrieval
102 maxBlockSize, err := humanize.ParseBytes(httpCfg.MaxBlockSize.WithDefault(config.DefaultHTTPRetrievalMaxBlockSize))
103 if err != nil {
104 return nil, err
105 }
106 logger.Infof("HTTP Retrieval enabled: Allowlist: %t. Denylist: %t",
107 httpCfg.Allowlist != nil,
108 httpCfg.Denylist != nil,
109 )
110
111 bitswapHTTP := httpnet.New(in.Host,
112 httpnet.WithHTTPWorkers(int(httpCfg.NumWorkers.WithDefault(config.DefaultHTTPRetrievalNumWorkers))),
113 httpnet.WithAllowlist(httpCfg.Allowlist),
114 httpnet.WithDenylist(httpCfg.Denylist),
115 httpnet.WithInsecureSkipVerify(httpCfg.TLSInsecureSkipVerify.WithDefault(config.DefaultHTTPRetrievalTLSInsecureSkipVerify)),
116 httpnet.WithMaxBlockSize(int64(maxBlockSize)),
117 httpnet.WithUserAgent(version.GetUserAgentVersion()),
118 httpnet.WithMetricsLabelsForEndpoints(httpCfg.Allowlist),
119 httpnet.WithConnectEventManager(connEvtMgr),
120 )
121 bitswapNetworks = network.New(in.Host.Peerstore(), bitswapLibp2p, bitswapHTTP)
122 } else if libp2pEnabled {
123 bitswapNetworks = bitswapLibp2p
124 } else {
125 return nil, errors.New("invalid configuration: Bitswap.Libp2pEnabled and HTTPRetrieval.Enabled are both disabled, unable to initialize Bitswap")
126 }
127
128 // Kubo uses own, customized ProviderQueryManager
129 in.BitswapOpts = append(in.BitswapOpts, bitswap.WithClientOption(client.WithDefaultProviderQueryManager(false)))
130 var maxProviders int = DefaultMaxProviders
131
132 var bcDisposition string
133 if in.Cfg.Internal.Bitswap != nil {
134 maxProviders = int(in.Cfg.Internal.Bitswap.ProviderSearchMaxResults.WithDefault(DefaultMaxProviders))
135 if in.Cfg.Internal.Bitswap.BroadcastControl != nil {
136 bcCfg := in.Cfg.Internal.Bitswap.BroadcastControl
137 bcEnable := bcCfg.Enable.WithDefault(config.DefaultBroadcastControlEnable)
138 in.BitswapOpts = append(in.BitswapOpts, bitswap.WithClientOption(client.BroadcastControlEnable(bcEnable)))
139 if bcEnable {
140 bcDisposition = "enabled"
141 bcMaxPeers := int(bcCfg.MaxPeers.WithDefault(config.DefaultBroadcastControlMaxPeers))
142 in.BitswapOpts = append(in.BitswapOpts, bitswap.WithClientOption(client.BroadcastControlMaxPeers(bcMaxPeers)))
143
144 bcLocalPeers := bcCfg.LocalPeers.WithDefault(config.DefaultBroadcastControlLocalPeers)
145 in.BitswapOpts = append(in.BitswapOpts, bitswap.WithClientOption(client.BroadcastControlLocalPeers(bcLocalPeers)))
146
147 bcPeeredPeers := bcCfg.PeeredPeers.WithDefault(config.DefaultBroadcastControlPeeredPeers)
148 in.BitswapOpts = append(in.BitswapOpts, bitswap.WithClientOption(client.BroadcastControlPeeredPeers(bcPeeredPeers)))
149
150 bcMaxRandomPeers := int(bcCfg.MaxRandomPeers.WithDefault(config.DefaultBroadcastControlMaxRandomPeers))
151 in.BitswapOpts = append(in.BitswapOpts, bitswap.WithClientOption(client.BroadcastControlMaxRandomPeers(bcMaxRandomPeers)))
152
153 bcSendToPendingPeers := bcCfg.SendToPendingPeers.WithDefault(config.DefaultBroadcastControlSendToPendingPeers)
154 in.BitswapOpts = append(in.BitswapOpts, bitswap.WithClientOption(client.BroadcastControlSendToPendingPeers(bcSendToPendingPeers)))
155 } else {
156 bcDisposition = "disabled"
157 }
158 }
159 }
160
161 // If broadcast control is not configured, then configure with defaults.
162 if bcDisposition == "" {
163 in.BitswapOpts = append(in.BitswapOpts, bitswap.WithClientOption(client.BroadcastControlEnable(config.DefaultBroadcastControlEnable)))
164 if config.DefaultBroadcastControlEnable {
165 bcDisposition = "enabled"
166 in.BitswapOpts = append(in.BitswapOpts, bitswap.WithClientOption(client.BroadcastControlMaxPeers(config.DefaultBroadcastControlMaxPeers)))
167 in.BitswapOpts = append(in.BitswapOpts, bitswap.WithClientOption(client.BroadcastControlLocalPeers(config.DefaultBroadcastControlLocalPeers)))
168 in.BitswapOpts = append(in.BitswapOpts, bitswap.WithClientOption(client.BroadcastControlPeeredPeers(config.DefaultBroadcastControlPeeredPeers)))
169 in.BitswapOpts = append(in.BitswapOpts, bitswap.WithClientOption(client.BroadcastControlMaxRandomPeers(config.DefaultBroadcastControlMaxRandomPeers)))
170 in.BitswapOpts = append(in.BitswapOpts, bitswap.WithClientOption(client.BroadcastControlSendToPendingPeers(config.DefaultBroadcastControlSendToPendingPeers)))
171 } else {
172 bcDisposition = "enabled"
173 }
174 }
175 logger.Infof("bitswap client broadcast control %s", bcDisposition)
176
177 ignoredPeerIDs := make([]peer.ID, 0, len(in.Cfg.Routing.IgnoreProviders))
178 for _, str := range in.Cfg.Routing.IgnoreProviders {
179 pid, err := peer.Decode(str)
180 if err != nil {
181 return nil, err
182 }
183 ignoredPeerIDs = append(ignoredPeerIDs, pid)
184 }
185 providerQueryMgr, err := rpqm.New(bitswapNetworks,
186 in.Discovery,
187 rpqm.WithMaxProviders(maxProviders),
188 rpqm.WithIgnoreProviders(ignoredPeerIDs...),
189 )
190 if err != nil {
191 return nil, err
192 }
193
194 // Explicitly enable/disable server
195 in.BitswapOpts = append(in.BitswapOpts, bitswap.WithServerEnabled(serverEnabled))
196
197 bs := bitswap.New(helpers.LifecycleCtx(in.Mctx, lc), bitswapNetworks, providerQueryMgr, bitswapBlockstore, in.BitswapOpts...)
198
199 lc.Append(fx.Hook{
200 OnStop: func(ctx context.Context) error {
201 return shutdown.CloseWithCtx(ctx, "bitswap", bs.Close)
202 },
203 })
204 return bs, nil
205 }
206 }
207
208 // OnlineExchange creates new LibP2P backed block exchange.
209 // Returns a no-op exchange if Bitswap is disabled.
210 func OnlineExchange(isBitswapActive bool) any {
211 return func(in *bitswap.Bitswap, lc fx.Lifecycle) exchange.Interface {
212 if !isBitswapActive {
213 return &noopExchange{closer: in}
214 }
215 lc.Append(fx.Hook{
216 OnStop: func(ctx context.Context) error {
217 return shutdown.CloseWithCtx(ctx, "bitswap-exchange", in.Close)
218 },
219 })
220 return in
221 }
222 }
223
224 type noopExchange struct {
225 closer io.Closer
226 }
227
228 func (e *noopExchange) GetBlock(ctx context.Context, c cid.Cid) (blocks.Block, error) {
229 return nil, ipld.ErrNotFound{Cid: c}
230 }
231
232 func (e *noopExchange) GetBlocks(ctx context.Context, cids []cid.Cid) (<-chan blocks.Block, error) {
233 ch := make(chan blocks.Block)
234 close(ch)
235 return ch, nil
236 }
237
238 func (e *noopExchange) NotifyNewBlocks(ctx context.Context, blocks ...blocks.Block) error {
239 return nil
240 }
241
242 func (e *noopExchange) Close() error {
243 return e.closer.Close()
244 }