-
Notifications
You must be signed in to change notification settings - Fork 218
/
block_verification.go
134 lines (115 loc) · 3.91 KB
/
block_verification.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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package evm
import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ava-labs/subnet-evm/params"
"github.com/ava-labs/subnet-evm/trie"
)
var legacyMinGasPrice = big.NewInt(params.MinGasPrice)
type BlockValidator interface {
SyntacticVerify(b *Block, rules params.Rules) error
}
type blockValidator struct{}
func NewBlockValidator() BlockValidator {
return &blockValidator{}
}
func (v blockValidator) SyntacticVerify(b *Block, rules params.Rules) error {
if b == nil || b.ethBlock == nil {
return errInvalidBlock
}
// Skip verification of the genesis block since it
// should already be marked as accepted
if b.ethBlock.Hash() == b.vm.genesisHash {
return nil
}
// Perform block and header sanity checks
ethHeader := b.ethBlock.Header()
if ethHeader.Number == nil || !ethHeader.Number.IsUint64() {
return errInvalidBlock
}
if ethHeader.Difficulty == nil || !ethHeader.Difficulty.IsUint64() ||
ethHeader.Difficulty.Uint64() != 1 {
return fmt.Errorf("invalid difficulty: %d", ethHeader.Difficulty)
}
if ethHeader.Nonce.Uint64() != 0 {
return fmt.Errorf(
"expected nonce to be 0 but got %d: %w",
ethHeader.Nonce.Uint64(), errInvalidNonce,
)
}
if ethHeader.MixDigest != (common.Hash{}) {
return fmt.Errorf("invalid mix digest: %v", ethHeader.MixDigest)
}
if rules.IsSubnetEVM {
expectedExtraDataSize := params.ExtraDataSize
if headerExtraDataSize := len(ethHeader.Extra); headerExtraDataSize != expectedExtraDataSize {
return fmt.Errorf(
"expected header ExtraData to be %d but got %d",
expectedExtraDataSize, headerExtraDataSize,
)
}
} else {
headerExtraDataSize := uint64(len(ethHeader.Extra))
if headerExtraDataSize > params.MaximumExtraDataSize {
return fmt.Errorf(
"expected header ExtraData to be <= %d but got %d",
params.MaximumExtraDataSize, headerExtraDataSize,
)
}
}
if rules.IsSubnetEVM {
if ethHeader.BaseFee == nil {
return errNilBaseFeeSubnetEVM
}
if bfLen := ethHeader.BaseFee.BitLen(); bfLen > 256 {
return fmt.Errorf("too large base fee: bitlen %d", bfLen)
}
}
// Check that the tx hash in the header matches the body
txsHash := types.DeriveSha(b.ethBlock.Transactions(), new(trie.Trie))
if txsHash != ethHeader.TxHash {
return fmt.Errorf("invalid txs hash %v does not match calculated txs hash %v", ethHeader.TxHash, txsHash)
}
// Check that the uncle hash in the header matches the body
uncleHash := types.CalcUncleHash(b.ethBlock.Uncles())
if uncleHash != ethHeader.UncleHash {
return fmt.Errorf("invalid uncle hash %v does not match calculated uncle hash %v", ethHeader.UncleHash, uncleHash)
}
// Block must not have any uncles
if len(b.ethBlock.Uncles()) > 0 {
return errUnclesUnsupported
}
// Block must not be empty
txs := b.ethBlock.Transactions()
if len(txs) == 0 {
return errEmptyBlock
}
if !rules.IsSubnetEVM {
// Make sure that all the txs have the correct fee set.
for _, tx := range txs {
if tx.GasPrice().Cmp(legacyMinGasPrice) < 0 {
return fmt.Errorf("block contains tx %s with gas price too low (%d < %d)", tx.Hash(), tx.GasPrice(), legacyMinGasPrice)
}
}
}
// Make sure the block isn't too far in the future
blockTimestamp := b.ethBlock.Time()
if maxBlockTime := uint64(b.vm.clock.Time().Add(maxFutureBlockTime).Unix()); blockTimestamp > maxBlockTime {
return fmt.Errorf("block timestamp is too far in the future: %d > allowed %d", blockTimestamp, maxBlockTime)
}
if rules.IsSubnetEVM {
switch {
// Make sure BlockGasCost is not nil
// NOTE: ethHeader.BlockGasCost correctness is checked in header verification
case ethHeader.BlockGasCost == nil:
return errNilBlockGasCostSubnetEVM
case !ethHeader.BlockGasCost.IsUint64():
return fmt.Errorf("too large blockGasCost: %d", ethHeader.BlockGasCost)
}
}
return nil
}