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 }