github.com/MetalBlockchain/subnet-evm@v0.4.9/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  
    12  	"github.com/MetalBlockchain/subnet-evm/core/types"
    13  	"github.com/MetalBlockchain/subnet-evm/params"
    14  	"github.com/MetalBlockchain/subnet-evm/trie"
    15  )
    16  
    17  var legacyMinGasPrice = big.NewInt(params.MinGasPrice)
    18  
    19  type BlockValidator interface {
    20  	SyntacticVerify(b *Block, rules params.Rules) error
    21  }
    22  
    23  type blockValidator struct{}
    24  
    25  func NewBlockValidator() BlockValidator {
    26  	return &blockValidator{}
    27  }
    28  
    29  func (v blockValidator) SyntacticVerify(b *Block, rules params.Rules) error {
    30  	if b == nil || b.ethBlock == nil {
    31  		return errInvalidBlock
    32  	}
    33  
    34  	// Skip verification of the genesis block since it
    35  	// should already be marked as accepted
    36  	if b.ethBlock.Hash() == b.vm.genesisHash {
    37  		return nil
    38  	}
    39  
    40  	// Perform block and header sanity checks
    41  	ethHeader := b.ethBlock.Header()
    42  	if ethHeader.Number == nil || !ethHeader.Number.IsUint64() {
    43  		return errInvalidBlock
    44  	}
    45  	if ethHeader.Difficulty == nil || !ethHeader.Difficulty.IsUint64() ||
    46  		ethHeader.Difficulty.Uint64() != 1 {
    47  		return fmt.Errorf("invalid difficulty: %d", ethHeader.Difficulty)
    48  	}
    49  	if ethHeader.Nonce.Uint64() != 0 {
    50  		return fmt.Errorf(
    51  			"expected nonce to be 0 but got %d: %w",
    52  			ethHeader.Nonce.Uint64(), errInvalidNonce,
    53  		)
    54  	}
    55  
    56  	if ethHeader.MixDigest != (common.Hash{}) {
    57  		return fmt.Errorf("invalid mix digest: %v", ethHeader.MixDigest)
    58  	}
    59  
    60  	if rules.IsSubnetEVM {
    61  		expectedExtraDataSize := params.ExtraDataSize
    62  		if headerExtraDataSize := len(ethHeader.Extra); headerExtraDataSize != expectedExtraDataSize {
    63  			return fmt.Errorf(
    64  				"expected header ExtraData to be %d but got %d",
    65  				expectedExtraDataSize, headerExtraDataSize,
    66  			)
    67  		}
    68  	} else {
    69  		headerExtraDataSize := uint64(len(ethHeader.Extra))
    70  		if headerExtraDataSize > params.MaximumExtraDataSize {
    71  			return fmt.Errorf(
    72  				"expected header ExtraData to be <= %d but got %d",
    73  				params.MaximumExtraDataSize, headerExtraDataSize,
    74  			)
    75  		}
    76  	}
    77  
    78  	if rules.IsSubnetEVM {
    79  		if ethHeader.BaseFee == nil {
    80  			return errNilBaseFeeSubnetEVM
    81  		}
    82  		if bfLen := ethHeader.BaseFee.BitLen(); bfLen > 256 {
    83  			return fmt.Errorf("too large base fee: bitlen %d", bfLen)
    84  		}
    85  	}
    86  
    87  	// Check that the tx hash in the header matches the body
    88  	txsHash := types.DeriveSha(b.ethBlock.Transactions(), new(trie.Trie))
    89  	if txsHash != ethHeader.TxHash {
    90  		return fmt.Errorf("invalid txs hash %v does not match calculated txs hash %v", ethHeader.TxHash, txsHash)
    91  	}
    92  	// Check that the uncle hash in the header matches the body
    93  	uncleHash := types.CalcUncleHash(b.ethBlock.Uncles())
    94  	if uncleHash != ethHeader.UncleHash {
    95  		return fmt.Errorf("invalid uncle hash %v does not match calculated uncle hash %v", ethHeader.UncleHash, uncleHash)
    96  	}
    97  
    98  	// Block must not have any uncles
    99  	if len(b.ethBlock.Uncles()) > 0 {
   100  		return errUnclesUnsupported
   101  	}
   102  	// Block must not be empty
   103  	txs := b.ethBlock.Transactions()
   104  	if len(txs) == 0 {
   105  		return errEmptyBlock
   106  	}
   107  
   108  	if !rules.IsSubnetEVM {
   109  		// Make sure that all the txs have the correct fee set.
   110  		for _, tx := range txs {
   111  			if tx.GasPrice().Cmp(legacyMinGasPrice) < 0 {
   112  				return fmt.Errorf("block contains tx %s with gas price too low (%d < %d)", tx.Hash(), tx.GasPrice(), legacyMinGasPrice)
   113  			}
   114  		}
   115  	}
   116  
   117  	// Make sure the block isn't too far in the future
   118  	blockTimestamp := b.ethBlock.Time()
   119  	if maxBlockTime := uint64(b.vm.clock.Time().Add(maxFutureBlockTime).Unix()); blockTimestamp > maxBlockTime {
   120  		return fmt.Errorf("block timestamp is too far in the future: %d > allowed %d", blockTimestamp, maxBlockTime)
   121  	}
   122  
   123  	if rules.IsSubnetEVM {
   124  		switch {
   125  		// Make sure BlockGasCost is not nil
   126  		// NOTE: ethHeader.BlockGasCost correctness is checked in header verification
   127  		case ethHeader.BlockGasCost == nil:
   128  			return errNilBlockGasCostSubnetEVM
   129  		case !ethHeader.BlockGasCost.IsUint64():
   130  			return fmt.Errorf("too large blockGasCost: %d", ethHeader.BlockGasCost)
   131  		}
   132  	}
   133  	return nil
   134  }