github.com/MetalBlockchain/subnet-evm@v0.4.9/sync/handlers/code_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  	"context"
     8  	"time"
     9  
    10  	"github.com/MetalBlockchain/metalgo/codec"
    11  	"github.com/MetalBlockchain/metalgo/ids"
    12  
    13  	"github.com/MetalBlockchain/subnet-evm/core/rawdb"
    14  	"github.com/MetalBlockchain/subnet-evm/ethdb"
    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  // CodeRequestHandler is a peer.RequestHandler for message.CodeRequest
    22  // serving requested contract code bytes
    23  type CodeRequestHandler struct {
    24  	codeReader ethdb.KeyValueReader
    25  	codec      codec.Manager
    26  	stats      stats.CodeRequestHandlerStats
    27  }
    28  
    29  func NewCodeRequestHandler(codeReader ethdb.KeyValueReader, codec codec.Manager, stats stats.CodeRequestHandlerStats) *CodeRequestHandler {
    30  	handler := &CodeRequestHandler{
    31  		codeReader: codeReader,
    32  		codec:      codec,
    33  		stats:      stats,
    34  	}
    35  	return handler
    36  }
    37  
    38  // OnCodeRequest handles request to retrieve contract code by its hash in message.CodeRequest
    39  // Never returns error
    40  // Returns nothing if code hash is not found
    41  // Expects returned errors to be treated as FATAL
    42  // Assumes ctx is active
    43  func (n *CodeRequestHandler) OnCodeRequest(_ context.Context, nodeID ids.NodeID, requestID uint32, codeRequest message.CodeRequest) ([]byte, error) {
    44  	startTime := time.Now()
    45  	n.stats.IncCodeRequest()
    46  
    47  	// always report code read time metric
    48  	defer func() {
    49  		n.stats.UpdateCodeReadTime(time.Since(startTime))
    50  	}()
    51  
    52  	if len(codeRequest.Hashes) > message.MaxCodeHashesPerRequest {
    53  		n.stats.IncTooManyHashesRequested()
    54  		log.Debug("too many hashes requested, dropping request", "nodeID", nodeID, "requestID", requestID, "numHashes", len(codeRequest.Hashes))
    55  		return nil, nil
    56  	}
    57  	if !isUnique(codeRequest.Hashes) {
    58  		n.stats.IncDuplicateHashesRequested()
    59  		log.Debug("duplicate code hashes requested, dropping request", "nodeID", nodeID, "requestID", requestID)
    60  		return nil, nil
    61  	}
    62  
    63  	codeBytes := make([][]byte, len(codeRequest.Hashes))
    64  	totalBytes := 0
    65  	for i, hash := range codeRequest.Hashes {
    66  		codeBytes[i] = rawdb.ReadCode(n.codeReader, hash)
    67  		if len(codeBytes[i]) == 0 {
    68  			n.stats.IncMissingCodeHash()
    69  			log.Debug("requested code not found, dropping request", "nodeID", nodeID, "requestID", requestID, "hash", hash)
    70  			return nil, nil
    71  		}
    72  		totalBytes += len(codeBytes[i])
    73  	}
    74  
    75  	codeResponse := message.CodeResponse{Data: codeBytes}
    76  	responseBytes, err := n.codec.Marshal(message.Version, codeResponse)
    77  	if err != nil {
    78  		log.Warn("could not marshal CodeResponse, dropping request", "nodeID", nodeID, "requestID", requestID, "request", codeRequest, "err", err)
    79  		return nil, nil
    80  	}
    81  	n.stats.UpdateCodeBytesReturned(uint32(totalBytes))
    82  	return responseBytes, nil
    83  }
    84  
    85  func isUnique(hashes []common.Hash) bool {
    86  	seen := make(map[common.Hash]struct{})
    87  	for _, hash := range hashes {
    88  		if _, found := seen[hash]; found {
    89  			return false
    90  		}
    91  		seen[hash] = struct{}{}
    92  	}
    93  	return true
    94  }