gitlab.com/flarenetwork/coreth@v0.1.1/plugin/evm/block_verification.go (about) 1 // (c) 2019-2020, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package evm 5 6 import ( 7 "fmt" 8 "math/big" 9 10 "github.com/ethereum/go-ethereum/common" 11 "github.com/ethereum/go-ethereum/trie" 12 coreth "gitlab.com/flarenetwork/coreth/chain" 13 "gitlab.com/flarenetwork/coreth/core/types" 14 "gitlab.com/flarenetwork/coreth/params" 15 ) 16 17 var ( 18 phase0BlockValidator = blockValidatorPhase0{} 19 apricotPhase0MinGasPrice = big.NewInt(params.LaunchMinGasPrice) 20 phase1BlockValidator = blockValidatorPhase1{} 21 apricotPhase1MinGasPrice = big.NewInt(params.ApricotPhase1MinGasPrice) 22 phase3BlockValidator = blockValidatorPhase3{} 23 ) 24 25 type BlockValidator interface { 26 SyntacticVerify(b *Block) error 27 } 28 29 type blockValidatorPhase0 struct { 30 extDataHashes map[common.Hash]common.Hash 31 } 32 33 func (v blockValidatorPhase0) SyntacticVerify(b *Block) error { 34 if b == nil || b.ethBlock == nil { 35 return errInvalidBlock 36 } 37 38 blockHash := b.ethBlock.Hash() 39 if v.extDataHashes != nil { 40 extData := b.ethBlock.ExtData() 41 extDataHash := types.CalcExtDataHash(extData) 42 // If there is no extra data, check that there is no extra data in the hash map either to ensure we do not 43 // have a block that is unexpectedly missing extra data. 44 expectedExtDataHash, ok := v.extDataHashes[blockHash] 45 if len(extData) == 0 { 46 if ok { 47 return fmt.Errorf("found block with unexpected missing extra data (%s, %d), expected extra data hash: %s", blockHash, b.Height(), expectedExtDataHash) 48 } 49 } else { 50 // If there is extra data, check to make sure that the extra data hash matches the expected extra data hash for this 51 // block 52 if extDataHash != expectedExtDataHash { 53 return fmt.Errorf("extra data hash in block (%s, %d): %s, did not match the expected extra data hash: %s", blockHash, b.Height(), extDataHash, expectedExtDataHash) 54 } 55 } 56 } 57 58 // Skip verification of the genesis block since it 59 // should already be marked as accepted 60 if blockHash == b.vm.genesisHash { 61 return nil 62 } 63 64 // Perform block and header sanity checks 65 ethHeader := b.ethBlock.Header() 66 if ethHeader.Number == nil || !ethHeader.Number.IsUint64() { 67 return errInvalidBlock 68 } 69 if ethHeader.Difficulty == nil || !ethHeader.Difficulty.IsUint64() || 70 ethHeader.Difficulty.Uint64() != 1 { 71 return fmt.Errorf( 72 "expected difficulty to be 1 but got %v: %w", 73 ethHeader.Difficulty, errInvalidDifficulty, 74 ) 75 } 76 if ethHeader.Nonce.Uint64() != 0 { 77 return fmt.Errorf( 78 "expected nonce to be 0 but got %d: %w", 79 ethHeader.Nonce.Uint64(), errInvalidNonce, 80 ) 81 } 82 if ethHeader.MixDigest != (common.Hash{}) { 83 return fmt.Errorf( 84 "expected MixDigest to be empty but got %x: %w", 85 ethHeader.MixDigest, errInvalidMixDigest, 86 ) 87 } 88 if ethHeader.ExtDataHash != (common.Hash{}) { 89 return fmt.Errorf( 90 "expected ExtDataHash to be empty but got %x: %w", 91 ethHeader.ExtDataHash, errInvalidExtDataHash, 92 ) 93 } 94 headerExtraDataSize := uint64(len(ethHeader.Extra)) 95 if headerExtraDataSize > params.MaximumExtraDataSize { 96 return fmt.Errorf( 97 "expected header ExtraData to be <= %d but got %d: %w", 98 params.MaximumExtraDataSize, headerExtraDataSize, errHeaderExtraDataTooBig, 99 ) 100 } 101 if b.ethBlock.Version() != 0 { 102 return fmt.Errorf( 103 "expected block version to be 0 but got %d: %w", 104 b.ethBlock.Version(), errInvalidBlockVersion, 105 ) 106 } 107 108 // Check that the tx hash in the header matches the body 109 txsHash := types.DeriveSha(b.ethBlock.Transactions(), new(trie.Trie)) 110 if txsHash != ethHeader.TxHash { 111 return errTxHashMismatch 112 } 113 // Check that the uncle hash in the header matches the body 114 uncleHash := types.CalcUncleHash(b.ethBlock.Uncles()) 115 if uncleHash != ethHeader.UncleHash { 116 return errUncleHashMismatch 117 } 118 // Coinbase must be zero on C-Chain 119 if b.ethBlock.Coinbase() != coreth.BlackholeAddr { 120 return errInvalidBlock 121 } 122 // Block must not have any uncles 123 if len(b.ethBlock.Uncles()) > 0 { 124 return errUnclesUnsupported 125 } 126 // Block must not be empty 127 // 128 // Note: extractAtomicTx also asserts a maximum size 129 atomicTx, err := b.vm.extractAtomicTx(b.ethBlock) 130 if err != nil { 131 return err 132 } 133 txs := b.ethBlock.Transactions() 134 if len(txs) == 0 && atomicTx == nil { 135 return errEmptyBlock 136 } 137 138 // Make sure that all the txs have the correct fee set. 139 for _, tx := range txs { 140 if tx.GasPrice().Cmp(apricotPhase0MinGasPrice) < 0 { 141 return fmt.Errorf("block contains tx %s with gas price too low (%d < %d)", tx.Hash(), tx.GasPrice(), params.LaunchMinGasPrice) 142 } 143 } 144 145 // Make sure the block isn't too far in the future 146 blockTimestamp := b.ethBlock.Time() 147 if maxBlockTime := uint64(b.vm.clock.Time().Add(maxFutureBlockTime).Unix()); blockTimestamp > maxBlockTime { 148 return fmt.Errorf("block timestamp is too far in the future: %d > allowed %d", blockTimestamp, maxBlockTime) 149 } 150 return nil 151 } 152 153 type blockValidatorPhase1 struct{} 154 155 func (blockValidatorPhase1) SyntacticVerify(b *Block) error { 156 if b == nil || b.ethBlock == nil { 157 return errInvalidBlock 158 } 159 160 // Skip verification of the genesis block since it 161 // should already be marked as accepted 162 if b.ethBlock.Hash() == b.vm.genesisHash { 163 return nil 164 } 165 166 // Perform block and header sanity checks 167 ethHeader := b.ethBlock.Header() 168 if ethHeader.Number == nil || !ethHeader.Number.IsUint64() { 169 return errInvalidBlock 170 } 171 if ethHeader.Difficulty == nil || !ethHeader.Difficulty.IsUint64() || 172 ethHeader.Difficulty.Uint64() != 1 { 173 return fmt.Errorf( 174 "expected difficulty to be 1 but got %v: %w", 175 ethHeader.Difficulty, errInvalidDifficulty, 176 ) 177 } 178 if ethHeader.Nonce.Uint64() != 0 { 179 return fmt.Errorf( 180 "expected nonce to be 0 but got %d: %w", 181 ethHeader.Nonce.Uint64(), errInvalidNonce, 182 ) 183 } 184 if ethHeader.GasLimit != params.ApricotPhase1GasLimit { 185 return fmt.Errorf( 186 "expected gas limit to be %d in apricot phase 1 but got %d", 187 params.ApricotPhase1GasLimit, ethHeader.GasLimit, 188 ) 189 } 190 if ethHeader.MixDigest != (common.Hash{}) { 191 return fmt.Errorf( 192 "expected MixDigest to be empty but got %x: %w", 193 ethHeader.MixDigest, errInvalidMixDigest, 194 ) 195 } 196 if hash := types.CalcExtDataHash(b.ethBlock.ExtData()); ethHeader.ExtDataHash != hash { 197 return fmt.Errorf("extra data hash mismatch: have %x, want %x", ethHeader.ExtDataHash, hash) 198 } 199 headerExtraDataSize := uint64(len(ethHeader.Extra)) 200 if headerExtraDataSize > 0 { 201 return fmt.Errorf( 202 "expected header ExtraData to be <= 0 but got %d: %w", 203 headerExtraDataSize, errHeaderExtraDataTooBig, 204 ) 205 } 206 if b.ethBlock.Version() != 0 { 207 return fmt.Errorf( 208 "expected block version to be 0 but got %d: %w", 209 b.ethBlock.Version(), errInvalidBlockVersion, 210 ) 211 } 212 213 // Check that the tx hash in the header matches the body 214 txsHash := types.DeriveSha(b.ethBlock.Transactions(), new(trie.Trie)) 215 if txsHash != ethHeader.TxHash { 216 return errTxHashMismatch 217 } 218 // Check that the uncle hash in the header matches the body 219 uncleHash := types.CalcUncleHash(b.ethBlock.Uncles()) 220 if uncleHash != ethHeader.UncleHash { 221 return errUncleHashMismatch 222 } 223 // Coinbase must be zero on C-Chain 224 if b.ethBlock.Coinbase() != coreth.BlackholeAddr { 225 return errInvalidBlock 226 } 227 // Block must not have any uncles 228 if len(b.ethBlock.Uncles()) > 0 { 229 return errUnclesUnsupported 230 } 231 // Block must not be empty 232 // 233 // Note: extractAtomicTx also asserts a maximum size 234 atomicTx, err := b.vm.extractAtomicTx(b.ethBlock) 235 if err != nil { 236 return err 237 } 238 txs := b.ethBlock.Transactions() 239 if len(txs) == 0 && atomicTx == nil { 240 return errEmptyBlock 241 } 242 243 // Make sure that all the txs have the correct fee set. 244 for _, tx := range txs { 245 if tx.GasPrice().Cmp(apricotPhase1MinGasPrice) < 0 { 246 return fmt.Errorf("block contains tx %s with gas price too low (%d < %d)", tx.Hash(), tx.GasPrice(), params.ApricotPhase1MinGasPrice) 247 } 248 } 249 250 // Make sure the block isn't too far in the future 251 blockTimestamp := b.ethBlock.Time() 252 if maxBlockTime := uint64(b.vm.clock.Time().Add(maxFutureBlockTime).Unix()); blockTimestamp > maxBlockTime { 253 return fmt.Errorf("block timestamp is too far in the future: %d > allowed %d", blockTimestamp, maxBlockTime) 254 } 255 return nil 256 } 257 258 type blockValidatorPhase3 struct{} 259 260 func (blockValidatorPhase3) SyntacticVerify(b *Block) error { 261 if b == nil || b.ethBlock == nil { 262 return errInvalidBlock 263 } 264 265 // Skip verification of the genesis block since it 266 // should already be marked as accepted 267 if b.ethBlock.Hash() == b.vm.genesisHash { 268 return nil 269 } 270 271 // Perform block and header sanity checks 272 ethHeader := b.ethBlock.Header() 273 if ethHeader.Number == nil || !ethHeader.Number.IsUint64() { 274 return errInvalidBlock 275 } 276 if ethHeader.Difficulty == nil || !ethHeader.Difficulty.IsUint64() || 277 ethHeader.Difficulty.Uint64() != 1 { 278 return fmt.Errorf( 279 "expected difficulty to be 1 but got %v: %w", 280 ethHeader.Difficulty, errInvalidDifficulty, 281 ) 282 } 283 if ethHeader.Nonce.Uint64() != 0 { 284 return fmt.Errorf( 285 "expected nonce to be 0 but got %d: %w", 286 ethHeader.Nonce.Uint64(), errInvalidNonce, 287 ) 288 } 289 if ethHeader.GasLimit != params.ApricotPhase1GasLimit { 290 return fmt.Errorf( 291 "expected gas limit to be %d in apricot phase 1 but got %d", 292 params.ApricotPhase1GasLimit, ethHeader.GasLimit, 293 ) 294 } 295 if ethHeader.MixDigest != (common.Hash{}) { 296 return fmt.Errorf( 297 "expected MixDigest to be empty but got %x: %w", 298 ethHeader.MixDigest, errInvalidMixDigest, 299 ) 300 } 301 if hash := types.CalcExtDataHash(b.ethBlock.ExtData()); ethHeader.ExtDataHash != hash { 302 return fmt.Errorf("extra data hash mismatch: have %x, want %x", ethHeader.ExtDataHash, hash) 303 } 304 if headerExtraDataSize := len(ethHeader.Extra); headerExtraDataSize != params.ApricotPhase3ExtraDataSize { 305 return fmt.Errorf( 306 "expected header ExtraData to be %d but got %d: %w", 307 params.ApricotPhase3ExtraDataSize, headerExtraDataSize, errHeaderExtraDataTooBig, 308 ) 309 } 310 if ethHeader.BaseFee == nil { 311 return fmt.Errorf("base fee is not set with apricot phase 3 enabled for block: %s", ethHeader.Hash()) 312 } 313 if bfLen := ethHeader.BaseFee.BitLen(); bfLen > 256 { 314 return fmt.Errorf("too large base fee: bitlen %d", bfLen) 315 } 316 if b.ethBlock.Version() != 0 { 317 return fmt.Errorf( 318 "expected block version to be 0 but got %d: %w", 319 b.ethBlock.Version(), errInvalidBlockVersion, 320 ) 321 } 322 323 // Check that the tx hash in the header matches the body 324 txsHash := types.DeriveSha(b.ethBlock.Transactions(), new(trie.Trie)) 325 if txsHash != ethHeader.TxHash { 326 return errTxHashMismatch 327 } 328 // Check that the uncle hash in the header matches the body 329 uncleHash := types.CalcUncleHash(b.ethBlock.Uncles()) 330 if uncleHash != ethHeader.UncleHash { 331 return errUncleHashMismatch 332 } 333 // Coinbase must be zero on C-Chain 334 if b.ethBlock.Coinbase() != coreth.BlackholeAddr { 335 return errInvalidBlock 336 } 337 // Block must not have any uncles 338 if len(b.ethBlock.Uncles()) > 0 { 339 return errUnclesUnsupported 340 } 341 // Block must not be empty 342 // 343 // Note: extractAtomicTx also asserts a maximum size 344 atomicTx, err := b.vm.extractAtomicTx(b.ethBlock) 345 if err != nil { 346 return err 347 } 348 txs := b.ethBlock.Transactions() 349 if len(txs) == 0 && atomicTx == nil { 350 return errEmptyBlock 351 } 352 353 // Make sure the block isn't too far in the future 354 blockTimestamp := b.ethBlock.Time() 355 if maxBlockTime := uint64(b.vm.clock.Time().Add(maxFutureBlockTime).Unix()); blockTimestamp > maxBlockTime { 356 return fmt.Errorf("block timestamp is too far in the future: %d > allowed %d", blockTimestamp, maxBlockTime) 357 } 358 return nil 359 }