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