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