github.com/MetalBlockchain/subnet-evm@v0.4.9/sync/handlers/block_request.go (about)

     1  // (c) 2021-2022, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package handlers
     5  
     6  import (
     7  	"bytes"
     8  	"context"
     9  	"time"
    10  
    11  	"github.com/MetalBlockchain/metalgo/codec"
    12  	"github.com/MetalBlockchain/metalgo/ids"
    13  
    14  	"github.com/MetalBlockchain/subnet-evm/peer"
    15  	"github.com/MetalBlockchain/subnet-evm/plugin/evm/message"
    16  	"github.com/MetalBlockchain/subnet-evm/sync/handlers/stats"
    17  	"github.com/ethereum/go-ethereum/common"
    18  	"github.com/ethereum/go-ethereum/log"
    19  )
    20  
    21  // parentLimit specifies how many parents to retrieve and send given a starting hash
    22  // This value overrides any specified limit in blockRequest.Parents if it is greater than this value
    23  const parentLimit = uint16(64)
    24  
    25  // BlockRequestHandler is a peer.RequestHandler for message.BlockRequest
    26  // serving requested blocks starting at specified hash
    27  type BlockRequestHandler struct {
    28  	stats         stats.BlockRequestHandlerStats
    29  	network       peer.Network
    30  	blockProvider BlockProvider
    31  	codec         codec.Manager
    32  }
    33  
    34  func NewBlockRequestHandler(blockProvider BlockProvider, codec codec.Manager, handlerStats stats.BlockRequestHandlerStats) *BlockRequestHandler {
    35  	return &BlockRequestHandler{
    36  		blockProvider: blockProvider,
    37  		codec:         codec,
    38  		stats:         handlerStats,
    39  	}
    40  }
    41  
    42  // OnBlockRequest handles incoming message.BlockRequest, returning blocks as requested
    43  // Never returns error
    44  // Expects returned errors to be treated as FATAL
    45  // Returns empty response or subset of requested blocks if ctx expires during fetch
    46  // Assumes ctx is active
    47  func (b *BlockRequestHandler) OnBlockRequest(ctx context.Context, nodeID ids.NodeID, requestID uint32, blockRequest message.BlockRequest) ([]byte, error) {
    48  	startTime := time.Now()
    49  	b.stats.IncBlockRequest()
    50  
    51  	// override given Parents limit if it is greater than parentLimit
    52  	parents := blockRequest.Parents
    53  	if parents > parentLimit {
    54  		parents = parentLimit
    55  	}
    56  	blocks := make([][]byte, 0, parents)
    57  
    58  	// ensure metrics are captured properly on all return paths
    59  	defer func() {
    60  		b.stats.UpdateBlockRequestProcessingTime(time.Since(startTime))
    61  		b.stats.UpdateBlocksReturned(uint16(len(blocks)))
    62  	}()
    63  
    64  	hash := blockRequest.Hash
    65  	height := blockRequest.Height
    66  	for i := 0; i < int(parents); i++ {
    67  		// we return whatever we have until ctx errors, limit is exceeded, or we reach the genesis block
    68  		// this will happen either when the ctx is cancelled or we hit the ctx deadline
    69  		if ctx.Err() != nil {
    70  			break
    71  		}
    72  
    73  		if (hash == common.Hash{}) {
    74  			break
    75  		}
    76  
    77  		block := b.blockProvider.GetBlock(hash, height)
    78  		if block == nil {
    79  			b.stats.IncMissingBlockHash()
    80  			break
    81  		}
    82  
    83  		buf := new(bytes.Buffer)
    84  		if err := block.EncodeRLP(buf); err != nil {
    85  			log.Warn("failed to RLP encode block", "hash", block.Hash(), "height", block.NumberU64(), "err", err)
    86  			return nil, nil
    87  		}
    88  
    89  		blocks = append(blocks, buf.Bytes())
    90  		hash = block.ParentHash()
    91  		height--
    92  	}
    93  
    94  	if len(blocks) == 0 {
    95  		// drop this request
    96  		log.Debug("no requested blocks found, dropping request", "nodeID", nodeID, "requestID", requestID, "hash", blockRequest.Hash, "parents", blockRequest.Parents)
    97  		return nil, nil
    98  	}
    99  
   100  	response := message.BlockResponse{
   101  		Blocks: blocks,
   102  	}
   103  	responseBytes, err := b.codec.Marshal(message.Version, response)
   104  	if err != nil {
   105  		log.Warn("failed to marshal BlockResponse, dropping request", "nodeID", nodeID, "requestID", requestID, "hash", blockRequest.Hash, "parents", blockRequest.Parents, "blocksLen", len(response.Blocks), "err", err)
   106  		return nil, nil
   107  	}
   108  
   109  	return responseBytes, nil
   110  }