github.com/luckypickle/go-ethereum-vet@v1.14.2/tests/block_test_util.go (about)

     1  // Copyright 2015 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  // Package tests implements execution of Ethereum JSON tests.
    18  package tests
    19  
    20  import (
    21  	"bytes"
    22  	"encoding/hex"
    23  	"encoding/json"
    24  	"fmt"
    25  	"math/big"
    26  
    27  	"github.com/luckypickle/go-ethereum-vet/common"
    28  	"github.com/luckypickle/go-ethereum-vet/common/hexutil"
    29  	"github.com/luckypickle/go-ethereum-vet/common/math"
    30  	"github.com/luckypickle/go-ethereum-vet/consensus/ethash"
    31  	"github.com/luckypickle/go-ethereum-vet/core"
    32  	"github.com/luckypickle/go-ethereum-vet/core/state"
    33  	"github.com/luckypickle/go-ethereum-vet/core/types"
    34  	"github.com/luckypickle/go-ethereum-vet/core/vm"
    35  	"github.com/luckypickle/go-ethereum-vet/ethdb"
    36  	"github.com/luckypickle/go-ethereum-vet/params"
    37  	"github.com/luckypickle/go-ethereum-vet/rlp"
    38  )
    39  
    40  // A BlockTest checks handling of entire blocks.
    41  type BlockTest struct {
    42  	json btJSON
    43  }
    44  
    45  // UnmarshalJSON implements json.Unmarshaler interface.
    46  func (t *BlockTest) UnmarshalJSON(in []byte) error {
    47  	return json.Unmarshal(in, &t.json)
    48  }
    49  
    50  type btJSON struct {
    51  	Blocks    []btBlock             `json:"blocks"`
    52  	Genesis   btHeader              `json:"genesisBlockHeader"`
    53  	Pre       core.GenesisAlloc     `json:"pre"`
    54  	Post      core.GenesisAlloc     `json:"postState"`
    55  	BestBlock common.UnprefixedHash `json:"lastblockhash"`
    56  	Network   string                `json:"network"`
    57  }
    58  
    59  type btBlock struct {
    60  	BlockHeader  *btHeader
    61  	Rlp          string
    62  	UncleHeaders []*btHeader
    63  }
    64  
    65  //go:generate gencodec -type btHeader -field-override btHeaderMarshaling -out gen_btheader.go
    66  
    67  type btHeader struct {
    68  	Bloom            types.Bloom
    69  	Coinbase         common.Address
    70  	MixHash          common.Hash
    71  	Nonce            types.BlockNonce
    72  	Number           *big.Int
    73  	Hash             common.Hash
    74  	ParentHash       common.Hash
    75  	ReceiptTrie      common.Hash
    76  	StateRoot        common.Hash
    77  	TransactionsTrie common.Hash
    78  	UncleHash        common.Hash
    79  	ExtraData        []byte
    80  	Difficulty       *big.Int
    81  	GasLimit         uint64
    82  	GasUsed          uint64
    83  	Timestamp        *big.Int
    84  }
    85  
    86  type btHeaderMarshaling struct {
    87  	ExtraData  hexutil.Bytes
    88  	Number     *math.HexOrDecimal256
    89  	Difficulty *math.HexOrDecimal256
    90  	GasLimit   math.HexOrDecimal64
    91  	GasUsed    math.HexOrDecimal64
    92  	Timestamp  *math.HexOrDecimal256
    93  }
    94  
    95  func (t *BlockTest) Run() error {
    96  	config, ok := Forks[t.json.Network]
    97  	if !ok {
    98  		return UnsupportedForkError{t.json.Network}
    99  	}
   100  
   101  	// import pre accounts & construct test genesis block & state root
   102  	db := ethdb.NewMemDatabase()
   103  	gblock, err := t.genesis(config).Commit(db)
   104  	if err != nil {
   105  		return err
   106  	}
   107  	if gblock.Hash() != t.json.Genesis.Hash {
   108  		return fmt.Errorf("genesis block hash doesn't match test: computed=%x, test=%x", gblock.Hash().Bytes()[:6], t.json.Genesis.Hash[:6])
   109  	}
   110  	if gblock.Root() != t.json.Genesis.StateRoot {
   111  		return fmt.Errorf("genesis block state root does not match test: computed=%x, test=%x", gblock.Root().Bytes()[:6], t.json.Genesis.StateRoot[:6])
   112  	}
   113  
   114  	chain, err := core.NewBlockChain(db, nil, config, ethash.NewShared(), vm.Config{})
   115  	if err != nil {
   116  		return err
   117  	}
   118  	defer chain.Stop()
   119  
   120  	validBlocks, err := t.insertBlocks(chain)
   121  	if err != nil {
   122  		return err
   123  	}
   124  	cmlast := chain.CurrentBlock().Hash()
   125  	if common.Hash(t.json.BestBlock) != cmlast {
   126  		return fmt.Errorf("last block hash validation mismatch: want: %x, have: %x", t.json.BestBlock, cmlast)
   127  	}
   128  	newDB, err := chain.State()
   129  	if err != nil {
   130  		return err
   131  	}
   132  	if err = t.validatePostState(newDB); err != nil {
   133  		return fmt.Errorf("post state validation failed: %v", err)
   134  	}
   135  	return t.validateImportedHeaders(chain, validBlocks)
   136  }
   137  
   138  func (t *BlockTest) genesis(config *params.ChainConfig) *core.Genesis {
   139  	return &core.Genesis{
   140  		Config:     config,
   141  		Nonce:      t.json.Genesis.Nonce.Uint64(),
   142  		Timestamp:  t.json.Genesis.Timestamp.Uint64(),
   143  		ParentHash: t.json.Genesis.ParentHash,
   144  		ExtraData:  t.json.Genesis.ExtraData,
   145  		GasLimit:   t.json.Genesis.GasLimit,
   146  		GasUsed:    t.json.Genesis.GasUsed,
   147  		Difficulty: t.json.Genesis.Difficulty,
   148  		Mixhash:    t.json.Genesis.MixHash,
   149  		Coinbase:   t.json.Genesis.Coinbase,
   150  		Alloc:      t.json.Pre,
   151  	}
   152  }
   153  
   154  /*
   155  See https://github.com/ethereum/tests/wiki/Blockchain-Tests-II
   156  
   157  	Whether a block is valid or not is a bit subtle, it's defined by presence of
   158  	blockHeader, transactions and uncleHeaders fields. If they are missing, the block is
   159  	invalid and we must verify that we do not accept it.
   160  
   161  	Since some tests mix valid and invalid blocks we need to check this for every block.
   162  
   163  	If a block is invalid it does not necessarily fail the test, if it's invalidness is
   164  	expected we are expected to ignore it and continue processing and then validate the
   165  	post state.
   166  */
   167  func (t *BlockTest) insertBlocks(blockchain *core.BlockChain) ([]btBlock, error) {
   168  	validBlocks := make([]btBlock, 0)
   169  	// insert the test blocks, which will execute all transactions
   170  	for _, b := range t.json.Blocks {
   171  		cb, err := b.decode()
   172  		if err != nil {
   173  			if b.BlockHeader == nil {
   174  				continue // OK - block is supposed to be invalid, continue with next block
   175  			} else {
   176  				return nil, fmt.Errorf("Block RLP decoding failed when expected to succeed: %v", err)
   177  			}
   178  		}
   179  		// RLP decoding worked, try to insert into chain:
   180  		blocks := types.Blocks{cb}
   181  		i, err := blockchain.InsertChain(blocks)
   182  		if err != nil {
   183  			if b.BlockHeader == nil {
   184  				continue // OK - block is supposed to be invalid, continue with next block
   185  			} else {
   186  				return nil, fmt.Errorf("Block #%v insertion into chain failed: %v", blocks[i].Number(), err)
   187  			}
   188  		}
   189  		if b.BlockHeader == nil {
   190  			return nil, fmt.Errorf("Block insertion should have failed")
   191  		}
   192  
   193  		// validate RLP decoding by checking all values against test file JSON
   194  		if err = validateHeader(b.BlockHeader, cb.Header()); err != nil {
   195  			return nil, fmt.Errorf("Deserialised block header validation failed: %v", err)
   196  		}
   197  		validBlocks = append(validBlocks, b)
   198  	}
   199  	return validBlocks, nil
   200  }
   201  
   202  func validateHeader(h *btHeader, h2 *types.Header) error {
   203  	if h.Bloom != h2.Bloom {
   204  		return fmt.Errorf("Bloom: want: %x have: %x", h.Bloom, h2.Bloom)
   205  	}
   206  	if h.Coinbase != h2.Coinbase {
   207  		return fmt.Errorf("Coinbase: want: %x have: %x", h.Coinbase, h2.Coinbase)
   208  	}
   209  	if h.MixHash != h2.MixDigest {
   210  		return fmt.Errorf("MixHash: want: %x have: %x", h.MixHash, h2.MixDigest)
   211  	}
   212  	if h.Nonce != h2.Nonce {
   213  		return fmt.Errorf("Nonce: want: %x have: %x", h.Nonce, h2.Nonce)
   214  	}
   215  	if h.Number.Cmp(h2.Number) != 0 {
   216  		return fmt.Errorf("Number: want: %v have: %v", h.Number, h2.Number)
   217  	}
   218  	if h.ParentHash != h2.ParentHash {
   219  		return fmt.Errorf("Parent hash: want: %x have: %x", h.ParentHash, h2.ParentHash)
   220  	}
   221  	if h.ReceiptTrie != h2.ReceiptHash {
   222  		return fmt.Errorf("Receipt hash: want: %x have: %x", h.ReceiptTrie, h2.ReceiptHash)
   223  	}
   224  	if h.TransactionsTrie != h2.TxHash {
   225  		return fmt.Errorf("Tx hash: want: %x have: %x", h.TransactionsTrie, h2.TxHash)
   226  	}
   227  	if h.StateRoot != h2.Root {
   228  		return fmt.Errorf("State hash: want: %x have: %x", h.StateRoot, h2.Root)
   229  	}
   230  	if h.UncleHash != h2.UncleHash {
   231  		return fmt.Errorf("Uncle hash: want: %x have: %x", h.UncleHash, h2.UncleHash)
   232  	}
   233  	if !bytes.Equal(h.ExtraData, h2.Extra) {
   234  		return fmt.Errorf("Extra data: want: %x have: %x", h.ExtraData, h2.Extra)
   235  	}
   236  	if h.Difficulty.Cmp(h2.Difficulty) != 0 {
   237  		return fmt.Errorf("Difficulty: want: %v have: %v", h.Difficulty, h2.Difficulty)
   238  	}
   239  	if h.GasLimit != h2.GasLimit {
   240  		return fmt.Errorf("GasLimit: want: %d have: %d", h.GasLimit, h2.GasLimit)
   241  	}
   242  	if h.GasUsed != h2.GasUsed {
   243  		return fmt.Errorf("GasUsed: want: %d have: %d", h.GasUsed, h2.GasUsed)
   244  	}
   245  	if h.Timestamp.Cmp(h2.Time) != 0 {
   246  		return fmt.Errorf("Timestamp: want: %v have: %v", h.Timestamp, h2.Time)
   247  	}
   248  	return nil
   249  }
   250  
   251  func (t *BlockTest) validatePostState(statedb *state.StateDB) error {
   252  	// validate post state accounts in test file against what we have in state db
   253  	for addr, acct := range t.json.Post {
   254  		// address is indirectly verified by the other fields, as it's the db key
   255  		code2 := statedb.GetCode(addr)
   256  		balance2 := statedb.GetBalance(addr)
   257  		nonce2 := statedb.GetNonce(addr)
   258  		if !bytes.Equal(code2, acct.Code) {
   259  			return fmt.Errorf("account code mismatch for addr: %s want: %v have: %s", addr, acct.Code, hex.EncodeToString(code2))
   260  		}
   261  		if balance2.Cmp(acct.Balance) != 0 {
   262  			return fmt.Errorf("account balance mismatch for addr: %s, want: %d, have: %d", addr, acct.Balance, balance2)
   263  		}
   264  		if nonce2 != acct.Nonce {
   265  			return fmt.Errorf("account nonce mismatch for addr: %s want: %d have: %d", addr, acct.Nonce, nonce2)
   266  		}
   267  	}
   268  	return nil
   269  }
   270  
   271  func (t *BlockTest) validateImportedHeaders(cm *core.BlockChain, validBlocks []btBlock) error {
   272  	// to get constant lookup when verifying block headers by hash (some tests have many blocks)
   273  	bmap := make(map[common.Hash]btBlock, len(t.json.Blocks))
   274  	for _, b := range validBlocks {
   275  		bmap[b.BlockHeader.Hash] = b
   276  	}
   277  	// iterate over blocks backwards from HEAD and validate imported
   278  	// headers vs test file. some tests have reorgs, and we import
   279  	// block-by-block, so we can only validate imported headers after
   280  	// all blocks have been processed by BlockChain, as they may not
   281  	// be part of the longest chain until last block is imported.
   282  	for b := cm.CurrentBlock(); b != nil && b.NumberU64() != 0; b = cm.GetBlockByHash(b.Header().ParentHash) {
   283  		if err := validateHeader(bmap[b.Hash()].BlockHeader, b.Header()); err != nil {
   284  			return fmt.Errorf("Imported block header validation failed: %v", err)
   285  		}
   286  	}
   287  	return nil
   288  }
   289  
   290  func (bb *btBlock) decode() (*types.Block, error) {
   291  	data, err := hexutil.Decode(bb.Rlp)
   292  	if err != nil {
   293  		return nil, err
   294  	}
   295  	var b types.Block
   296  	err = rlp.DecodeBytes(data, &b)
   297  	return &b, err
   298  }