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 }