github.com/onflow/flow-go@v0.33.17/network/p2p/blob/blob_service.go (about) 1 package blob 2 3 import ( 4 "context" 5 "errors" 6 "time" 7 8 "github.com/hashicorp/go-multierror" 9 "github.com/ipfs/boxo/bitswap" 10 bsmsg "github.com/ipfs/boxo/bitswap/message" 11 bsnet "github.com/ipfs/boxo/bitswap/network" 12 blocks "github.com/ipfs/go-block-format" 13 "github.com/ipfs/go-blockservice" 14 "github.com/ipfs/go-cid" 15 "github.com/ipfs/go-datastore" 16 blockstore "github.com/ipfs/go-ipfs-blockstore" 17 provider "github.com/ipfs/go-ipfs-provider" 18 "github.com/ipfs/go-ipfs-provider/simple" 19 "github.com/libp2p/go-libp2p/core/host" 20 "github.com/libp2p/go-libp2p/core/peer" 21 "github.com/libp2p/go-libp2p/core/protocol" 22 "github.com/libp2p/go-libp2p/core/routing" 23 "github.com/rs/zerolog" 24 "golang.org/x/time/rate" 25 26 "github.com/onflow/flow-go/model/flow" 27 "github.com/onflow/flow-go/module" 28 "github.com/onflow/flow-go/module/blobs" 29 "github.com/onflow/flow-go/module/component" 30 "github.com/onflow/flow-go/module/irrecoverable" 31 "github.com/onflow/flow-go/module/metrics" 32 "github.com/onflow/flow-go/network" 33 p2plogging "github.com/onflow/flow-go/network/p2p/logging" 34 "github.com/onflow/flow-go/utils/logging" 35 36 ipld "github.com/ipfs/go-ipld-format" 37 ) 38 39 type blobService struct { 40 prefix string 41 component.Component 42 blockService blockservice.BlockService 43 blockStore blockstore.Blockstore 44 reprovider provider.Reprovider 45 config *BlobServiceConfig 46 } 47 48 var _ network.BlobService = (*blobService)(nil) 49 var _ component.Component = (*blobService)(nil) 50 51 type BlobServiceConfig struct { 52 ReprovideInterval time.Duration // the interval at which the DHT provider entries are refreshed 53 BitswapOptions []bitswap.Option // options to pass to the Bitswap service 54 } 55 56 // WithReprovideInterval sets the interval at which DHT provider entries are refreshed 57 func WithReprovideInterval(d time.Duration) network.BlobServiceOption { 58 return func(bs network.BlobService) { 59 bs.(*blobService).config.ReprovideInterval = d 60 } 61 } 62 63 // WithBitswapOptions sets additional options for Bitswap exchange 64 func WithBitswapOptions(opts ...bitswap.Option) network.BlobServiceOption { 65 return func(bs network.BlobService) { 66 bs.(*blobService).config.BitswapOptions = opts 67 } 68 } 69 70 // WithHashOnRead sets whether or not the blobstore will rehash the blob data on read 71 // When set, calls to GetBlob will fail with an error if the hash of the data in storage does not 72 // match its CID 73 func WithHashOnRead(enabled bool) network.BlobServiceOption { 74 return func(bs network.BlobService) { 75 bs.(*blobService).blockStore.HashOnRead(enabled) 76 } 77 } 78 79 // WithRateLimit sets a rate limit on reads from the underlying datastore that allows up 80 // to r bytes per second and permits bursts of at most b bytes. Note that b should be 81 // set to at least the max blob size, otherwise blobs larger than b cannot be read from 82 // the blobstore. 83 func WithRateLimit(r float64, b int) network.BlobServiceOption { 84 return func(bs network.BlobService) { 85 blobService := bs.(*blobService) 86 blobService.blockStore = newRateLimitedBlockStore(blobService.blockStore, blobService.prefix, r, b) 87 } 88 } 89 90 // NewBlobService creates a new BlobService. 91 func NewBlobService( 92 host host.Host, 93 r routing.ContentRouting, 94 prefix string, 95 ds datastore.Batching, 96 metrics module.BitswapMetrics, 97 logger zerolog.Logger, 98 opts ...network.BlobServiceOption, 99 ) *blobService { 100 bsNetwork := bsnet.NewFromIpfsHost(host, r, bsnet.Prefix(protocol.ID(prefix))) 101 bs := &blobService{ 102 prefix: prefix, 103 config: &BlobServiceConfig{ 104 ReprovideInterval: 12 * time.Hour, 105 }, 106 blockStore: blockstore.NewBlockstore(ds), 107 } 108 109 for _, opt := range opts { 110 opt(bs) 111 } 112 113 cm := component.NewComponentManagerBuilder(). 114 AddWorker(func(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) { 115 btswp := bitswap.New(ctx, bsNetwork, bs.blockStore, bs.config.BitswapOptions...) 116 bs.blockService = blockservice.New(bs.blockStore, btswp) 117 118 ready() 119 120 ticker := time.NewTicker(15 * time.Second) 121 for { 122 select { 123 case <-ctx.Done(): 124 return 125 case <-ticker.C: 126 stat, err := btswp.Stat() 127 if err != nil { 128 logger.Err(err).Str("component", "blob_service").Str("prefix", prefix).Msg("failed to get bitswap stats") 129 continue 130 } 131 132 metrics.Peers(prefix, len(stat.Peers)) 133 metrics.Wantlist(prefix, len(stat.Wantlist)) 134 metrics.BlobsReceived(prefix, stat.BlocksReceived) 135 metrics.DataReceived(prefix, stat.DataReceived) 136 metrics.BlobsSent(prefix, stat.BlocksSent) 137 metrics.DataSent(prefix, stat.DataSent) 138 metrics.DupBlobsReceived(prefix, stat.DupBlksReceived) 139 metrics.DupDataReceived(prefix, stat.DupDataReceived) 140 metrics.MessagesReceived(prefix, stat.MessagesReceived) 141 } 142 } 143 }). 144 AddWorker(func(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) { 145 bs.reprovider = simple.NewReprovider(ctx, bs.config.ReprovideInterval, r, simple.NewBlockstoreProvider(bs.blockStore)) 146 147 ready() 148 149 bs.reprovider.Run() 150 }). 151 AddWorker(func(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) { 152 ready() 153 154 <-bs.Ready() // wait for variables to be initialized 155 <-ctx.Done() 156 157 var err *multierror.Error 158 159 err = multierror.Append(err, bs.reprovider.Close()) 160 err = multierror.Append(err, bs.blockService.Close()) 161 162 if err.ErrorOrNil() != nil { 163 ctx.Throw(err) 164 } 165 }). 166 Build() 167 168 bs.Component = cm 169 170 return bs 171 } 172 173 func (bs *blobService) TriggerReprovide(ctx context.Context) error { 174 return bs.reprovider.Trigger(ctx) 175 } 176 177 func (bs *blobService) GetBlob(ctx context.Context, c cid.Cid) (blobs.Blob, error) { 178 blob, err := bs.blockService.GetBlock(ctx, c) 179 if ipld.IsNotFound(err) { 180 return nil, network.ErrBlobNotFound 181 } 182 183 return blob, err 184 } 185 186 func (bs *blobService) GetBlobs(ctx context.Context, ks []cid.Cid) <-chan blobs.Blob { 187 return bs.blockService.GetBlocks(ctx, ks) 188 } 189 190 func (bs *blobService) AddBlob(ctx context.Context, b blobs.Blob) error { 191 return bs.blockService.AddBlock(ctx, b) 192 } 193 194 func (bs *blobService) AddBlobs(ctx context.Context, blobs []blobs.Blob) error { 195 return bs.blockService.AddBlocks(ctx, blobs) 196 } 197 198 func (bs *blobService) DeleteBlob(ctx context.Context, c cid.Cid) error { 199 return bs.blockService.DeleteBlock(ctx, c) 200 } 201 202 func (bs *blobService) GetSession(ctx context.Context) network.BlobGetter { 203 return &blobServiceSession{blockservice.NewSession(ctx, bs.blockService)} 204 } 205 206 type blobServiceSession struct { 207 session *blockservice.Session 208 } 209 210 var _ network.BlobGetter = (*blobServiceSession)(nil) 211 212 func (s *blobServiceSession) GetBlob(ctx context.Context, c cid.Cid) (blobs.Blob, error) { 213 return s.session.GetBlock(ctx, c) 214 } 215 216 func (s *blobServiceSession) GetBlobs(ctx context.Context, ks []cid.Cid) <-chan blobs.Blob { 217 return s.session.GetBlocks(ctx, ks) 218 } 219 220 type rateLimitedBlockStore struct { 221 blockstore.Blockstore 222 limiter *rate.Limiter 223 metrics module.RateLimitedBlockstoreMetrics 224 } 225 226 var rateLimitedError = errors.New("rate limited") 227 228 func newRateLimitedBlockStore(bs blockstore.Blockstore, prefix string, r float64, b int) *rateLimitedBlockStore { 229 return &rateLimitedBlockStore{ 230 Blockstore: bs, 231 limiter: rate.NewLimiter(rate.Limit(r), b), 232 metrics: metrics.NewRateLimitedBlockstoreCollector(prefix), 233 } 234 } 235 236 func (r *rateLimitedBlockStore) Get(ctx context.Context, c cid.Cid) (blocks.Block, error) { 237 size, err := r.Blockstore.GetSize(ctx, c) 238 if err != nil { 239 return nil, err 240 } 241 242 allowed := r.limiter.AllowN(time.Now(), size) 243 if !allowed { 244 return nil, rateLimitedError 245 } 246 247 r.metrics.BytesRead(size) 248 249 return r.Blockstore.Get(ctx, c) 250 } 251 252 // AuthorizedRequester returns a callback function used by bitswap to authorize block requests 253 // A request is authorized if the peer is 254 // * known by the identity provider 255 // * not ejected 256 // * an Access node 257 // * in the allowedNodes list (if non-empty) 258 func AuthorizedRequester( 259 allowedNodes map[flow.Identifier]bool, 260 identityProvider module.IdentityProvider, 261 logger zerolog.Logger, 262 ) func(peer.ID, cid.Cid) bool { 263 return func(peerID peer.ID, _ cid.Cid) bool { 264 lg := logger.With(). 265 Str("component", "blob_service"). 266 Str("peer_id", p2plogging.PeerId(peerID)). 267 Logger() 268 269 id, ok := identityProvider.ByPeerID(peerID) 270 271 if !ok { 272 lg.Warn(). 273 Bool(logging.KeySuspicious, true). 274 Msg("rejecting request from unknown peer") 275 return false 276 } 277 278 lg = lg.With(). 279 Str("peer_node_id", id.NodeID.String()). 280 Str("role", id.Role.String()). 281 Logger() 282 283 // TODO: when execution data verification is enabled, add verification nodes here 284 if (id.Role != flow.RoleExecution && id.Role != flow.RoleAccess) || id.Ejected { 285 lg.Warn(). 286 Bool(logging.KeySuspicious, true). 287 Msg("rejecting request from peer: unauthorized") 288 return false 289 } 290 291 // allow list is only for Access nodes 292 if id.Role == flow.RoleAccess && len(allowedNodes) > 0 && !allowedNodes[id.NodeID] { 293 // honest peers not on the allowed list have no way to know and will continue to request 294 // blobs. therefore, these requests do not indicate suspicious behavior 295 lg.Debug().Msg("rejecting request from peer: not in allowed list") 296 return false 297 } 298 299 lg.Debug().Msg("accepting request from peer") 300 return true 301 } 302 } 303 304 type Tracer struct { 305 logger zerolog.Logger 306 } 307 308 func NewTracer(logger zerolog.Logger) *Tracer { 309 return &Tracer{ 310 logger, 311 } 312 } 313 314 func (t *Tracer) logMsg(msg bsmsg.BitSwapMessage, s string) { 315 evt := t.logger.Debug() 316 317 wantlist := zerolog.Arr() 318 for _, entry := range msg.Wantlist() { 319 wantlist = wantlist.Interface(entry) 320 } 321 evt.Array("wantlist", wantlist) 322 323 blks := zerolog.Arr() 324 for _, blk := range msg.Blocks() { 325 blks = blks.Str(blk.Cid().String()) 326 } 327 evt.Array("blocks", blks) 328 329 haves := zerolog.Arr() 330 for _, have := range msg.Haves() { 331 haves = haves.Str(have.String()) 332 } 333 evt.Array("haves", haves) 334 335 dontHaves := zerolog.Arr() 336 for _, dontHave := range msg.DontHaves() { 337 dontHaves = dontHaves.Str(dontHave.String()) 338 } 339 evt.Array("dontHaves", dontHaves) 340 341 evt.Int32("pendingBytes", msg.PendingBytes()) 342 343 evt.Msg(s) 344 } 345 346 func (t *Tracer) MessageReceived(pid peer.ID, msg bsmsg.BitSwapMessage) { 347 if t.logger.Debug().Enabled() { 348 t.logMsg(msg, "bitswap message received") 349 } 350 } 351 352 func (t *Tracer) MessageSent(pid peer.ID, msg bsmsg.BitSwapMessage) { 353 if t.logger.Debug().Enabled() { 354 t.logMsg(msg, "bitswap message sent") 355 } 356 }