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  }