github.com/ebakus/go-ebakus@v1.0.5-0.20200520105415-dbccef9ec421/tests/block_test_util.go (about)

     1  // Copyright 2019 The ebakus/go-ebakus Authors
     2  // This file is part of the ebakus/go-ebakus library.
     3  //
     4  // The ebakus/go-ebakus 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 ebakus/go-ebakus 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 ebakus/go-ebakus library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  // Package tests implements execution of Ebakus 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/ebakus/go-ebakus/common"
    28  	"github.com/ebakus/go-ebakus/common/hexutil"
    29  	"github.com/ebakus/go-ebakus/common/math"
    30  	"github.com/ebakus/go-ebakus/consensus"
    31  	"github.com/ebakus/go-ebakus/consensus/ethash"
    32  	"github.com/ebakus/go-ebakus/core"
    33  	"github.com/ebakus/go-ebakus/core/rawdb"
    34  	"github.com/ebakus/go-ebakus/core/state"
    35  	"github.com/ebakus/go-ebakus/core/types"
    36  	"github.com/ebakus/go-ebakus/core/vm"
    37  	"github.com/ebakus/go-ebakus/params"
    38  	"github.com/ebakus/go-ebakus/rlp"
    39  )
    40  
    41  // A BlockTest checks handling of entire blocks.
    42  type BlockTest struct {
    43  	json btJSON
    44  }
    45  
    46  // UnmarshalJSON implements json.Unmarshaler interface.
    47  func (t *BlockTest) UnmarshalJSON(in []byte) error {
    48  	return json.Unmarshal(in, &t.json)
    49  }
    50  
    51  type btJSON struct {
    52  	Blocks     []btBlock             `json:"blocks"`
    53  	Genesis    btHeader              `json:"genesisBlockHeader"`
    54  	Pre        core.GenesisAlloc     `json:"pre"`
    55  	Post       core.GenesisAlloc     `json:"postState"`
    56  	BestBlock  common.UnprefixedHash `json:"lastblockhash"`
    57  	Network    string                `json:"network"`
    58  	SealEngine string                `json:"sealEngine"`
    59  }
    60  
    61  type btBlock struct {
    62  	BlockHeader  *btHeader
    63  	Rlp          string
    64  	UncleHeaders []*btHeader
    65  }
    66  
    67  //go:generate gencodec -type btHeader -field-override btHeaderMarshaling -out gen_btheader.go
    68  
    69  type btHeader struct {
    70  	Bloom            types.Bloom
    71  	Coinbase         common.Address
    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        uint64
    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.HexOrDecimal64
    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 := rawdb.NewMemoryDatabase()
   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  	var engine consensus.Engine
   114  	if t.json.SealEngine == "NoProof" {
   115  		engine = ethash.NewFaker()
   116  	} else {
   117  		engine = ethash.NewShared()
   118  	}
   119  	chain, err := core.NewBlockChain(db, &core.CacheConfig{TrieCleanLimit: 0}, config, engine, vm.Config{}, nil)
   120  	if err != nil {
   121  		return err
   122  	}
   123  	defer chain.Stop()
   124  
   125  	validBlocks, err := t.insertBlocks(chain)
   126  	if err != nil {
   127  		return err
   128  	}
   129  	cmlast := chain.CurrentBlock().Hash()
   130  	if common.Hash(t.json.BestBlock) != cmlast {
   131  		return fmt.Errorf("last block hash validation mismatch: want: %x, have: %x", t.json.BestBlock, cmlast)
   132  	}
   133  	newDB, err := chain.State()
   134  	if err != nil {
   135  		return err
   136  	}
   137  	if err = t.validatePostState(newDB); err != nil {
   138  		return fmt.Errorf("post state validation failed: %v", err)
   139  	}
   140  	return t.validateImportedHeaders(chain, validBlocks)
   141  }
   142  
   143  func (t *BlockTest) genesis(config *params.ChainConfig) *core.Genesis {
   144  	return &core.Genesis{
   145  		Config:     config,
   146  		Timestamp:  t.json.Genesis.Timestamp,
   147  		ParentHash: t.json.Genesis.ParentHash,
   148  		ExtraData:  t.json.Genesis.ExtraData,
   149  		GasLimit:   t.json.Genesis.GasLimit,
   150  		GasUsed:    t.json.Genesis.GasUsed,
   151  		Difficulty: t.json.Genesis.Difficulty,
   152  		Coinbase:   t.json.Genesis.Coinbase,
   153  		Alloc:      t.json.Pre,
   154  	}
   155  }
   156  
   157  /* See https://github.com/ethereum/tests/wiki/Blockchain-Tests-II
   158  
   159     Whether a block is valid or not is a bit subtle, it's defined by presence of
   160     blockHeader, transactions and uncleHeaders fields. If they are missing, the block is
   161     invalid and we must verify that we do not accept it.
   162  
   163     Since some tests mix valid and invalid blocks we need to check this for every block.
   164  
   165     If a block is invalid it does not necessarily fail the test, if it's invalidness is
   166     expected we are expected to ignore it and continue processing and then validate the
   167     post state.
   168  */
   169  func (t *BlockTest) insertBlocks(blockchain *core.BlockChain) ([]btBlock, error) {
   170  	validBlocks := make([]btBlock, 0)
   171  	// insert the test blocks, which will execute all transactions
   172  	for _, b := range t.json.Blocks {
   173  		cb, err := b.decode()
   174  		if err != nil {
   175  			if b.BlockHeader == nil {
   176  				continue // OK - block is supposed to be invalid, continue with next block
   177  			} else {
   178  				return nil, fmt.Errorf("Block RLP decoding failed when expected to succeed: %v", err)
   179  			}
   180  		}
   181  		// RLP decoding worked, try to insert into chain:
   182  		blocks := types.Blocks{cb}
   183  		i, err := blockchain.InsertChain(blocks)
   184  		if err != nil {
   185  			if b.BlockHeader == nil {
   186  				continue // OK - block is supposed to be invalid, continue with next block
   187  			} else {
   188  				return nil, fmt.Errorf("Block #%v insertion into chain failed: %v", blocks[i].Number(), err)
   189  			}
   190  		}
   191  		if b.BlockHeader == nil {
   192  			return nil, fmt.Errorf("Block insertion should have failed")
   193  		}
   194  
   195  		// validate RLP decoding by checking all values against test file JSON
   196  		if err = validateHeader(b.BlockHeader, cb.Header()); err != nil {
   197  			return nil, fmt.Errorf("Deserialised block header validation failed: %v", err)
   198  		}
   199  		validBlocks = append(validBlocks, b)
   200  	}
   201  	return validBlocks, nil
   202  }
   203  
   204  func validateHeader(h *btHeader, h2 *types.Header) error {
   205  	if h.Bloom != h2.Bloom {
   206  		return fmt.Errorf("Bloom: want: %x have: %x", h.Bloom, h2.Bloom)
   207  	}
   208  	if h.Coinbase != h2.Coinbase {
   209  		return fmt.Errorf("Coinbase: want: %x have: %x", h.Coinbase, h2.Coinbase)
   210  	}
   211  	if h.Number.Cmp(h2.Number) != 0 {
   212  		return fmt.Errorf("Number: want: %v have: %v", h.Number, h2.Number)
   213  	}
   214  	if h.ParentHash != h2.ParentHash {
   215  		return fmt.Errorf("Parent hash: want: %x have: %x", h.ParentHash, h2.ParentHash)
   216  	}
   217  	if h.ReceiptTrie != h2.ReceiptHash {
   218  		return fmt.Errorf("Receipt hash: want: %x have: %x", h.ReceiptTrie, h2.ReceiptHash)
   219  	}
   220  	if h.TransactionsTrie != h2.TxHash {
   221  		return fmt.Errorf("Tx hash: want: %x have: %x", h.TransactionsTrie, h2.TxHash)
   222  	}
   223  	if h.StateRoot != h2.Root {
   224  		return fmt.Errorf("State hash: want: %x have: %x", h.StateRoot, h2.Root)
   225  	}
   226  	if h.Difficulty.Cmp(h2.Difficulty) != 0 {
   227  		return fmt.Errorf("Difficulty: want: %v have: %v", h.Difficulty, h2.Difficulty)
   228  	}
   229  	if h.GasLimit != h2.GasLimit {
   230  		return fmt.Errorf("GasLimit: want: %d have: %d", h.GasLimit, h2.GasLimit)
   231  	}
   232  	if h.GasUsed != h2.GasUsed {
   233  		return fmt.Errorf("GasUsed: want: %d have: %d", h.GasUsed, h2.GasUsed)
   234  	}
   235  	if h.Timestamp != h2.Time {
   236  		return fmt.Errorf("Timestamp: want: %v have: %v", h.Timestamp, h2.Time)
   237  	}
   238  	return nil
   239  }
   240  
   241  func (t *BlockTest) validatePostState(statedb *state.StateDB) error {
   242  	// validate post state accounts in test file against what we have in state db
   243  	for addr, acct := range t.json.Post {
   244  		// address is indirectly verified by the other fields, as it's the db key
   245  		code2 := statedb.GetCode(addr)
   246  		balance2 := statedb.GetBalance(addr)
   247  		nonce2 := statedb.GetNonce(addr)
   248  		if !bytes.Equal(code2, acct.Code) {
   249  			return fmt.Errorf("account code mismatch for addr: %s want: %v have: %s", addr, acct.Code, hex.EncodeToString(code2))
   250  		}
   251  		if balance2.Cmp(acct.Balance) != 0 {
   252  			return fmt.Errorf("account balance mismatch for addr: %s, want: %d, have: %d", addr, acct.Balance, balance2)
   253  		}
   254  		if nonce2 != acct.Nonce {
   255  			return fmt.Errorf("account nonce mismatch for addr: %s want: %d have: %d", addr, acct.Nonce, nonce2)
   256  		}
   257  	}
   258  	return nil
   259  }
   260  
   261  func (t *BlockTest) validateImportedHeaders(cm *core.BlockChain, validBlocks []btBlock) error {
   262  	// to get constant lookup when verifying block headers by hash (some tests have many blocks)
   263  	bmap := make(map[common.Hash]btBlock, len(t.json.Blocks))
   264  	for _, b := range validBlocks {
   265  		bmap[b.BlockHeader.Hash] = b
   266  	}
   267  	// iterate over blocks backwards from HEAD and validate imported
   268  	// headers vs test file. some tests have reorgs, and we import
   269  	// block-by-block, so we can only validate imported headers after
   270  	// all blocks have been processed by BlockChain, as they may not
   271  	// be part of the longest chain until last block is imported.
   272  	for b := cm.CurrentBlock(); b != nil && b.NumberU64() != 0; b = cm.GetBlockByHash(b.Header().ParentHash) {
   273  		if err := validateHeader(bmap[b.Hash()].BlockHeader, b.Header()); err != nil {
   274  			return fmt.Errorf("Imported block header validation failed: %v", err)
   275  		}
   276  	}
   277  	return nil
   278  }
   279  
   280  func (bb *btBlock) decode() (*types.Block, error) {
   281  	data, err := hexutil.Decode(bb.Rlp)
   282  	if err != nil {
   283  		return nil, err
   284  	}
   285  	var b types.Block
   286  	err = rlp.DecodeBytes(data, &b)
   287  	return &b, err
   288  }