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