github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/tests/block_test_util.go (about)

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