github.com/n1ghtfa1l/go-vnt@v0.6.4-alpha.6/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 VNT 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/vntchain/go-vnt/common"
    28  	"github.com/vntchain/go-vnt/common/hexutil"
    29  	"github.com/vntchain/go-vnt/common/math"
    30  	"github.com/vntchain/go-vnt/consensus/mock"
    31  	"github.com/vntchain/go-vnt/core"
    32  	"github.com/vntchain/go-vnt/core/state"
    33  	"github.com/vntchain/go-vnt/core/types"
    34  	"github.com/vntchain/go-vnt/core/vm"
    35  	"github.com/vntchain/go-vnt/params"
    36  	"github.com/vntchain/go-vnt/rlp"
    37  	"github.com/vntchain/go-vnt/vntdb"
    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  }
    63  
    64  //go:generate gencodec -type btHeader -field-override btHeaderMarshaling -out gen_btheader.go
    65  
    66  type btHeader struct {
    67  	Bloom            types.Bloom
    68  	Coinbase         common.Address
    69  	Number           *big.Int
    70  	Hash             common.Hash
    71  	ParentHash       common.Hash
    72  	ReceiptTrie      common.Hash
    73  	StateRoot        common.Hash
    74  	TransactionsTrie common.Hash
    75  	ExtraData        []byte
    76  	Difficulty       *big.Int
    77  	GasLimit         uint64
    78  	GasUsed          uint64
    79  	Timestamp        *big.Int
    80  }
    81  
    82  type btHeaderMarshaling struct {
    83  	ExtraData  hexutil.Bytes
    84  	Number     *math.HexOrDecimal256
    85  	Difficulty *math.HexOrDecimal256
    86  	GasLimit   math.HexOrDecimal64
    87  	GasUsed    math.HexOrDecimal64
    88  	Timestamp  *math.HexOrDecimal256
    89  }
    90  
    91  func (t *BlockTest) Run() error {
    92  	config, ok := Forks[t.json.Network]
    93  	if !ok {
    94  		return UnsupportedForkError{t.json.Network}
    95  	}
    96  
    97  	// import pre accounts & construct test genesis block & state root
    98  	db := vntdb.NewMemDatabase()
    99  	gblock, err := t.genesis(config).Commit(db)
   100  	if err != nil {
   101  		return err
   102  	}
   103  	if gblock.Hash() != t.json.Genesis.Hash {
   104  		return fmt.Errorf("genesis block hash doesn't match test: computed=%x, test=%x", gblock.Hash().Bytes()[:6], t.json.Genesis.Hash[:6])
   105  	}
   106  	if gblock.Root() != t.json.Genesis.StateRoot {
   107  		return fmt.Errorf("genesis block state root does not match test: computed=%x, test=%x", gblock.Root().Bytes()[:6], t.json.Genesis.StateRoot[:6])
   108  	}
   109  
   110  	chain, err := core.NewBlockChain(db, nil, config, mock.NewMock(), vm.Config{})
   111  	if err != nil {
   112  		return err
   113  	}
   114  	defer chain.Stop()
   115  
   116  	validBlocks, err := t.insertBlocks(chain)
   117  	if err != nil {
   118  		return err
   119  	}
   120  	cmlast := chain.CurrentBlock().Hash()
   121  	if common.Hash(t.json.BestBlock) != cmlast {
   122  		return fmt.Errorf("last block hash validation mismatch: want: %x, have: %x", t.json.BestBlock, cmlast)
   123  	}
   124  	newDB, err := chain.State()
   125  	if err != nil {
   126  		return err
   127  	}
   128  	if err = t.validatePostState(newDB); err != nil {
   129  		return fmt.Errorf("post state validation failed: %v", err)
   130  	}
   131  	return t.validateImportedHeaders(chain, validBlocks)
   132  }
   133  
   134  func (t *BlockTest) genesis(config *params.ChainConfig) *core.Genesis {
   135  	return &core.Genesis{
   136  		Config:     config,
   137  		Timestamp:  t.json.Genesis.Timestamp.Uint64(),
   138  		ParentHash: t.json.Genesis.ParentHash,
   139  		ExtraData:  t.json.Genesis.ExtraData,
   140  		GasLimit:   t.json.Genesis.GasLimit,
   141  		GasUsed:    t.json.Genesis.GasUsed,
   142  		Difficulty: t.json.Genesis.Difficulty,
   143  		Coinbase:   t.json.Genesis.Coinbase,
   144  		Alloc:      t.json.Pre,
   145  	}
   146  }
   147  
   148  func (t *BlockTest) insertBlocks(blockchain *core.BlockChain) ([]btBlock, error) {
   149  	validBlocks := make([]btBlock, 0)
   150  	// insert the test blocks, which will execute all transactions
   151  	for _, b := range t.json.Blocks {
   152  		cb, err := b.decode()
   153  		if err != nil {
   154  			if b.BlockHeader == nil {
   155  				continue // OK - block is supposed to be invalid, continue with next block
   156  			} else {
   157  				return nil, fmt.Errorf("Block RLP decoding failed when expected to succeed: %v", err)
   158  			}
   159  		}
   160  		// RLP decoding worked, try to insert into chain:
   161  		blocks := types.Blocks{cb}
   162  		i, err := blockchain.InsertChain(blocks)
   163  		if err != nil {
   164  			if b.BlockHeader == nil {
   165  				continue // OK - block is supposed to be invalid, continue with next block
   166  			} else {
   167  				return nil, fmt.Errorf("Block #%v insertion into chain failed: %v", blocks[i].Number(), err)
   168  			}
   169  		}
   170  		if b.BlockHeader == nil {
   171  			return nil, fmt.Errorf("Block insertion should have failed")
   172  		}
   173  
   174  		// validate RLP decoding by checking all values against test file JSON
   175  		if err = validateHeader(b.BlockHeader, cb.Header()); err != nil {
   176  			return nil, fmt.Errorf("Deserialised block header validation failed: %v", err)
   177  		}
   178  		validBlocks = append(validBlocks, b)
   179  	}
   180  	return validBlocks, nil
   181  }
   182  
   183  func validateHeader(h *btHeader, h2 *types.Header) error {
   184  	if h.Bloom != h2.Bloom {
   185  		return fmt.Errorf("Bloom: want: %x have: %x", h.Bloom, h2.Bloom)
   186  	}
   187  	if h.Coinbase != h2.Coinbase {
   188  		return fmt.Errorf("Coinbase: want: %x have: %x", h.Coinbase, h2.Coinbase)
   189  	}
   190  	if h.Number.Cmp(h2.Number) != 0 {
   191  		return fmt.Errorf("Number: want: %v have: %v", h.Number, h2.Number)
   192  	}
   193  	if h.ParentHash != h2.ParentHash {
   194  		return fmt.Errorf("Parent hash: want: %x have: %x", h.ParentHash, h2.ParentHash)
   195  	}
   196  	if h.ReceiptTrie != h2.ReceiptHash {
   197  		return fmt.Errorf("Receipt hash: want: %x have: %x", h.ReceiptTrie, h2.ReceiptHash)
   198  	}
   199  	if h.TransactionsTrie != h2.TxHash {
   200  		return fmt.Errorf("Tx hash: want: %x have: %x", h.TransactionsTrie, h2.TxHash)
   201  	}
   202  	if h.StateRoot != h2.Root {
   203  		return fmt.Errorf("State hash: want: %x have: %x", h.StateRoot, h2.Root)
   204  	}
   205  	if !bytes.Equal(h.ExtraData, h2.Extra) {
   206  		return fmt.Errorf("Extra data: want: %x have: %x", h.ExtraData, h2.Extra)
   207  	}
   208  	if h.Difficulty.Cmp(h2.Difficulty) != 0 {
   209  		return fmt.Errorf("Difficulty: want: %v have: %v", h.Difficulty, h2.Difficulty)
   210  	}
   211  	if h.GasLimit != h2.GasLimit {
   212  		return fmt.Errorf("GasLimit: want: %d have: %d", h.GasLimit, h2.GasLimit)
   213  	}
   214  	if h.GasUsed != h2.GasUsed {
   215  		return fmt.Errorf("GasUsed: want: %d have: %d", h.GasUsed, h2.GasUsed)
   216  	}
   217  	if h.Timestamp.Cmp(h2.Time) != 0 {
   218  		return fmt.Errorf("Timestamp: want: %v have: %v", h.Timestamp, h2.Time)
   219  	}
   220  	return nil
   221  }
   222  
   223  func (t *BlockTest) validatePostState(statedb *state.StateDB) error {
   224  	// validate post state accounts in test file against what we have in state db
   225  	for addr, acct := range t.json.Post {
   226  		// address is indirectly verified by the other fields, as it's the db key
   227  		code2 := statedb.GetCode(addr)
   228  		balance2 := statedb.GetBalance(addr)
   229  		nonce2 := statedb.GetNonce(addr)
   230  		if !bytes.Equal(code2, acct.Code) {
   231  			return fmt.Errorf("account code mismatch for addr: %s want: %v have: %s", addr, acct.Code, hex.EncodeToString(code2))
   232  		}
   233  		if balance2.Cmp(acct.Balance) != 0 {
   234  			return fmt.Errorf("account balance mismatch for addr: %s, want: %d, have: %d", addr, acct.Balance, balance2)
   235  		}
   236  		if nonce2 != acct.Nonce {
   237  			return fmt.Errorf("account nonce mismatch for addr: %s want: %d have: %d", addr, acct.Nonce, nonce2)
   238  		}
   239  	}
   240  	return nil
   241  }
   242  
   243  func (t *BlockTest) validateImportedHeaders(cm *core.BlockChain, validBlocks []btBlock) error {
   244  	// to get constant lookup when verifying block headers by hash (some tests have many blocks)
   245  	bmap := make(map[common.Hash]btBlock, len(t.json.Blocks))
   246  	for _, b := range validBlocks {
   247  		bmap[b.BlockHeader.Hash] = b
   248  	}
   249  	// iterate over blocks backwards from HEAD and validate imported
   250  	// headers vs test file. some tests have reorgs, and we import
   251  	// block-by-block, so we can only validate imported headers after
   252  	// all blocks have been processed by BlockChain, as they may not
   253  	// be part of the longest chain until last block is imported.
   254  	for b := cm.CurrentBlock(); b != nil && b.NumberU64() != 0; b = cm.GetBlockByHash(b.Header().ParentHash) {
   255  		if err := validateHeader(bmap[b.Hash()].BlockHeader, b.Header()); err != nil {
   256  			return fmt.Errorf("Imported block header validation failed: %v", err)
   257  		}
   258  	}
   259  	return nil
   260  }
   261  
   262  func (bb *btBlock) decode() (*types.Block, error) {
   263  	data, err := hexutil.Decode(bb.Rlp)
   264  	if err != nil {
   265  		return nil, err
   266  	}
   267  	var b types.Block
   268  	err = rlp.DecodeBytes(data, &b)
   269  	return &b, err
   270  }