github.com/calmw/ethereum@v0.1.1/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  	"os"
    27  	"reflect"
    28  
    29  	"github.com/calmw/ethereum/common"
    30  	"github.com/calmw/ethereum/common/hexutil"
    31  	"github.com/calmw/ethereum/common/math"
    32  	"github.com/calmw/ethereum/consensus/beacon"
    33  	"github.com/calmw/ethereum/consensus/ethash"
    34  	"github.com/calmw/ethereum/core"
    35  	"github.com/calmw/ethereum/core/rawdb"
    36  	"github.com/calmw/ethereum/core/state"
    37  	"github.com/calmw/ethereum/core/types"
    38  	"github.com/calmw/ethereum/core/vm"
    39  	"github.com/calmw/ethereum/params"
    40  	"github.com/calmw/ethereum/rlp"
    41  )
    42  
    43  // A BlockTest checks handling of entire blocks.
    44  type BlockTest struct {
    45  	json btJSON
    46  }
    47  
    48  // UnmarshalJSON implements json.Unmarshaler interface.
    49  func (t *BlockTest) UnmarshalJSON(in []byte) error {
    50  	return json.Unmarshal(in, &t.json)
    51  }
    52  
    53  type btJSON struct {
    54  	Blocks     []btBlock             `json:"blocks"`
    55  	Genesis    btHeader              `json:"genesisBlockHeader"`
    56  	Pre        core.GenesisAlloc     `json:"pre"`
    57  	Post       core.GenesisAlloc     `json:"postState"`
    58  	BestBlock  common.UnprefixedHash `json:"lastblockhash"`
    59  	Network    string                `json:"network"`
    60  	SealEngine string                `json:"sealEngine"`
    61  }
    62  
    63  type btBlock struct {
    64  	BlockHeader     *btHeader
    65  	ExpectException string
    66  	Rlp             string
    67  	UncleHeaders    []*btHeader
    68  }
    69  
    70  //go:generate go run github.com/fjl/gencodec -type btHeader -field-override btHeaderMarshaling -out gen_btheader.go
    71  
    72  type btHeader struct {
    73  	Bloom            types.Bloom
    74  	Coinbase         common.Address
    75  	MixHash          common.Hash
    76  	Nonce            types.BlockNonce
    77  	Number           *big.Int
    78  	Hash             common.Hash
    79  	ParentHash       common.Hash
    80  	ReceiptTrie      common.Hash
    81  	StateRoot        common.Hash
    82  	TransactionsTrie common.Hash
    83  	UncleHash        common.Hash
    84  	ExtraData        []byte
    85  	Difficulty       *big.Int
    86  	GasLimit         uint64
    87  	GasUsed          uint64
    88  	Timestamp        uint64
    89  	BaseFeePerGas    *big.Int
    90  	WithdrawalsRoot  *common.Hash
    91  }
    92  
    93  type btHeaderMarshaling struct {
    94  	ExtraData     hexutil.Bytes
    95  	Number        *math.HexOrDecimal256
    96  	Difficulty    *math.HexOrDecimal256
    97  	GasLimit      math.HexOrDecimal64
    98  	GasUsed       math.HexOrDecimal64
    99  	Timestamp     math.HexOrDecimal64
   100  	BaseFeePerGas *math.HexOrDecimal256
   101  }
   102  
   103  func (t *BlockTest) Run(snapshotter bool) error {
   104  	config, ok := Forks[t.json.Network]
   105  	if !ok {
   106  		return UnsupportedForkError{t.json.Network}
   107  	}
   108  
   109  	// import pre accounts & construct test genesis block & state root
   110  	db := rawdb.NewMemoryDatabase()
   111  	gspec := t.genesis(config)
   112  	gblock := gspec.MustCommit(db)
   113  	if gblock.Hash() != t.json.Genesis.Hash {
   114  		return fmt.Errorf("genesis block hash doesn't match test: computed=%x, test=%x", gblock.Hash().Bytes()[:6], t.json.Genesis.Hash[:6])
   115  	}
   116  	if gblock.Root() != t.json.Genesis.StateRoot {
   117  		return fmt.Errorf("genesis block state root does not match test: computed=%x, test=%x", gblock.Root().Bytes()[:6], t.json.Genesis.StateRoot[:6])
   118  	}
   119  	// Wrap the original engine within the beacon-engine
   120  	engine := beacon.New(ethash.NewFaker())
   121  
   122  	cache := &core.CacheConfig{TrieCleanLimit: 0}
   123  	if snapshotter {
   124  		cache.SnapshotLimit = 1
   125  		cache.SnapshotWait = true
   126  	}
   127  	chain, err := core.NewBlockChain(db, cache, gspec, nil, engine, vm.Config{}, nil, nil)
   128  	if err != nil {
   129  		return err
   130  	}
   131  	defer chain.Stop()
   132  
   133  	validBlocks, err := t.insertBlocks(chain)
   134  	if err != nil {
   135  		return err
   136  	}
   137  	cmlast := chain.CurrentBlock().Hash()
   138  	if common.Hash(t.json.BestBlock) != cmlast {
   139  		return fmt.Errorf("last block hash validation mismatch: want: %x, have: %x", t.json.BestBlock, cmlast)
   140  	}
   141  	newDB, err := chain.State()
   142  	if err != nil {
   143  		return err
   144  	}
   145  	if err = t.validatePostState(newDB); err != nil {
   146  		return fmt.Errorf("post state validation failed: %v", err)
   147  	}
   148  	// Cross-check the snapshot-to-hash against the trie hash
   149  	if snapshotter {
   150  		if err := chain.Snapshots().Verify(chain.CurrentBlock().Root); err != nil {
   151  			return err
   152  		}
   153  	}
   154  	return t.validateImportedHeaders(chain, validBlocks)
   155  }
   156  
   157  func (t *BlockTest) genesis(config *params.ChainConfig) *core.Genesis {
   158  	return &core.Genesis{
   159  		Config:     config,
   160  		Nonce:      t.json.Genesis.Nonce.Uint64(),
   161  		Timestamp:  t.json.Genesis.Timestamp,
   162  		ParentHash: t.json.Genesis.ParentHash,
   163  		ExtraData:  t.json.Genesis.ExtraData,
   164  		GasLimit:   t.json.Genesis.GasLimit,
   165  		GasUsed:    t.json.Genesis.GasUsed,
   166  		Difficulty: t.json.Genesis.Difficulty,
   167  		Mixhash:    t.json.Genesis.MixHash,
   168  		Coinbase:   t.json.Genesis.Coinbase,
   169  		Alloc:      t.json.Pre,
   170  		BaseFee:    t.json.Genesis.BaseFeePerGas,
   171  	}
   172  }
   173  
   174  /*
   175  See https://github.com/ethereum/tests/wiki/Blockchain-Tests-II
   176  
   177  	Whether a block is valid or not is a bit subtle, it's defined by presence of
   178  	blockHeader, transactions and uncleHeaders fields. If they are missing, the block is
   179  	invalid and we must verify that we do not accept it.
   180  
   181  	Since some tests mix valid and invalid blocks we need to check this for every block.
   182  
   183  	If a block is invalid it does not necessarily fail the test, if it's invalidness is
   184  	expected we are expected to ignore it and continue processing and then validate the
   185  	post state.
   186  */
   187  func (t *BlockTest) insertBlocks(blockchain *core.BlockChain) ([]btBlock, error) {
   188  	validBlocks := make([]btBlock, 0)
   189  	// insert the test blocks, which will execute all transactions
   190  	for bi, b := range t.json.Blocks {
   191  		cb, err := b.decode()
   192  		if err != nil {
   193  			if b.BlockHeader == nil {
   194  				continue // OK - block is supposed to be invalid, continue with next block
   195  			} else {
   196  				return nil, fmt.Errorf("block RLP decoding failed when expected to succeed: %v", err)
   197  			}
   198  		}
   199  		// RLP decoding worked, try to insert into chain:
   200  		blocks := types.Blocks{cb}
   201  		i, err := blockchain.InsertChain(blocks)
   202  		if err != nil {
   203  			if b.BlockHeader == nil {
   204  				continue // OK - block is supposed to be invalid, continue with next block
   205  			} else {
   206  				return nil, fmt.Errorf("block #%v insertion into chain failed: %v", blocks[i].Number(), err)
   207  			}
   208  		}
   209  		if b.BlockHeader == nil {
   210  			if data, err := json.MarshalIndent(cb.Header(), "", "  "); err == nil {
   211  				fmt.Fprintf(os.Stderr, "block (index %d) insertion should have failed due to: %v:\n%v\n",
   212  					bi, b.ExpectException, string(data))
   213  			}
   214  			return nil, fmt.Errorf("block (index %d) insertion should have failed due to: %v",
   215  				bi, b.ExpectException)
   216  		}
   217  
   218  		// validate RLP decoding by checking all values against test file JSON
   219  		if err = validateHeader(b.BlockHeader, cb.Header()); err != nil {
   220  			return nil, fmt.Errorf("deserialised block header validation failed: %v", err)
   221  		}
   222  		validBlocks = append(validBlocks, b)
   223  	}
   224  	return validBlocks, nil
   225  }
   226  
   227  func validateHeader(h *btHeader, h2 *types.Header) error {
   228  	if h.Bloom != h2.Bloom {
   229  		return fmt.Errorf("bloom: want: %x have: %x", h.Bloom, h2.Bloom)
   230  	}
   231  	if h.Coinbase != h2.Coinbase {
   232  		return fmt.Errorf("coinbase: want: %x have: %x", h.Coinbase, h2.Coinbase)
   233  	}
   234  	if h.MixHash != h2.MixDigest {
   235  		return fmt.Errorf("MixHash: want: %x have: %x", h.MixHash, h2.MixDigest)
   236  	}
   237  	if h.Nonce != h2.Nonce {
   238  		return fmt.Errorf("nonce: want: %x have: %x", h.Nonce, h2.Nonce)
   239  	}
   240  	if h.Number.Cmp(h2.Number) != 0 {
   241  		return fmt.Errorf("number: want: %v have: %v", h.Number, h2.Number)
   242  	}
   243  	if h.ParentHash != h2.ParentHash {
   244  		return fmt.Errorf("parent hash: want: %x have: %x", h.ParentHash, h2.ParentHash)
   245  	}
   246  	if h.ReceiptTrie != h2.ReceiptHash {
   247  		return fmt.Errorf("receipt hash: want: %x have: %x", h.ReceiptTrie, h2.ReceiptHash)
   248  	}
   249  	if h.TransactionsTrie != h2.TxHash {
   250  		return fmt.Errorf("tx hash: want: %x have: %x", h.TransactionsTrie, h2.TxHash)
   251  	}
   252  	if h.StateRoot != h2.Root {
   253  		return fmt.Errorf("state hash: want: %x have: %x", h.StateRoot, h2.Root)
   254  	}
   255  	if h.UncleHash != h2.UncleHash {
   256  		return fmt.Errorf("uncle hash: want: %x have: %x", h.UncleHash, h2.UncleHash)
   257  	}
   258  	if !bytes.Equal(h.ExtraData, h2.Extra) {
   259  		return fmt.Errorf("extra data: want: %x have: %x", h.ExtraData, h2.Extra)
   260  	}
   261  	if h.Difficulty.Cmp(h2.Difficulty) != 0 {
   262  		return fmt.Errorf("difficulty: want: %v have: %v", h.Difficulty, h2.Difficulty)
   263  	}
   264  	if h.GasLimit != h2.GasLimit {
   265  		return fmt.Errorf("gasLimit: want: %d have: %d", h.GasLimit, h2.GasLimit)
   266  	}
   267  	if h.GasUsed != h2.GasUsed {
   268  		return fmt.Errorf("gasUsed: want: %d have: %d", h.GasUsed, h2.GasUsed)
   269  	}
   270  	if h.Timestamp != h2.Time {
   271  		return fmt.Errorf("timestamp: want: %v have: %v", h.Timestamp, h2.Time)
   272  	}
   273  	if !reflect.DeepEqual(h.BaseFeePerGas, h2.BaseFee) {
   274  		return fmt.Errorf("baseFeePerGas: want: %v have: %v", h.BaseFeePerGas, h2.BaseFee)
   275  	}
   276  	if !reflect.DeepEqual(h.WithdrawalsRoot, h2.WithdrawalsHash) {
   277  		return fmt.Errorf("withdrawalsRoot: want: %v have: %v", h.WithdrawalsRoot, h2.WithdrawalsHash)
   278  	}
   279  	return nil
   280  }
   281  
   282  func (t *BlockTest) validatePostState(statedb *state.StateDB) error {
   283  	// validate post state accounts in test file against what we have in state db
   284  	for addr, acct := range t.json.Post {
   285  		// address is indirectly verified by the other fields, as it's the db key
   286  		code2 := statedb.GetCode(addr)
   287  		balance2 := statedb.GetBalance(addr)
   288  		nonce2 := statedb.GetNonce(addr)
   289  		if !bytes.Equal(code2, acct.Code) {
   290  			return fmt.Errorf("account code mismatch for addr: %s want: %v have: %s", addr, acct.Code, hex.EncodeToString(code2))
   291  		}
   292  		if balance2.Cmp(acct.Balance) != 0 {
   293  			return fmt.Errorf("account balance mismatch for addr: %s, want: %d, have: %d", addr, acct.Balance, balance2)
   294  		}
   295  		if nonce2 != acct.Nonce {
   296  			return fmt.Errorf("account nonce mismatch for addr: %s want: %d have: %d", addr, acct.Nonce, nonce2)
   297  		}
   298  	}
   299  	return nil
   300  }
   301  
   302  func (t *BlockTest) validateImportedHeaders(cm *core.BlockChain, validBlocks []btBlock) error {
   303  	// to get constant lookup when verifying block headers by hash (some tests have many blocks)
   304  	bmap := make(map[common.Hash]btBlock, len(t.json.Blocks))
   305  	for _, b := range validBlocks {
   306  		bmap[b.BlockHeader.Hash] = b
   307  	}
   308  	// iterate over blocks backwards from HEAD and validate imported
   309  	// headers vs test file. some tests have reorgs, and we import
   310  	// block-by-block, so we can only validate imported headers after
   311  	// all blocks have been processed by BlockChain, as they may not
   312  	// be part of the longest chain until last block is imported.
   313  	for b := cm.CurrentBlock(); b != nil && b.Number.Uint64() != 0; b = cm.GetBlockByHash(b.ParentHash).Header() {
   314  		if err := validateHeader(bmap[b.Hash()].BlockHeader, b); err != nil {
   315  			return fmt.Errorf("imported block header validation failed: %v", err)
   316  		}
   317  	}
   318  	return nil
   319  }
   320  
   321  func (bb *btBlock) decode() (*types.Block, error) {
   322  	data, err := hexutil.Decode(bb.Rlp)
   323  	if err != nil {
   324  		return nil, err
   325  	}
   326  	var b types.Block
   327  	err = rlp.DecodeBytes(data, &b)
   328  	return &b, err
   329  }