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