github.com/klaytn/klaytn@v1.12.1/tests/block_test_util.go (about)

     1  // Modifications Copyright 2018 The klaytn Authors
     2  // Copyright 2015 The go-ethereum Authors
     3  // This file is part of the go-ethereum library.
     4  //
     5  // The go-ethereum library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Lesser General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // The go-ethereum library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU Lesser General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public License
    16  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  // This file is derived from tests/block_test_util.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package tests
    22  
    23  import (
    24  	"bytes"
    25  	"encoding/hex"
    26  	"encoding/json"
    27  	"fmt"
    28  	"math/big"
    29  
    30  	"github.com/klaytn/klaytn/blockchain"
    31  	"github.com/klaytn/klaytn/blockchain/state"
    32  	"github.com/klaytn/klaytn/blockchain/types"
    33  	"github.com/klaytn/klaytn/blockchain/vm"
    34  	"github.com/klaytn/klaytn/common"
    35  	"github.com/klaytn/klaytn/common/hexutil"
    36  	"github.com/klaytn/klaytn/common/math"
    37  	"github.com/klaytn/klaytn/consensus/gxhash"
    38  	"github.com/klaytn/klaytn/params"
    39  	"github.com/klaytn/klaytn/rlp"
    40  	"github.com/klaytn/klaytn/storage/database"
    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       blockchain.GenesisAlloc `json:"pre"`
    57  	Post      blockchain.GenesisAlloc `json:"postState"`
    58  	BestBlock common.UnprefixedHash   `json:"lastblockhash"`
    59  	Network   string                  `json:"networks"`
    60  }
    61  
    62  type btBlock struct {
    63  	BlockHeader *btHeader
    64  	Rlp         string
    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  	Number           *big.Int
    72  	Hash             common.Hash
    73  	ParentHash       common.Hash
    74  	ReceiptTrie      common.Hash
    75  	StateRoot        common.Hash
    76  	TransactionsTrie common.Hash
    77  	ExtraData        []byte
    78  	BlockScore       *big.Int
    79  	GasUsed          uint64
    80  	Timestamp        *big.Int
    81  }
    82  
    83  type btHeaderMarshaling struct {
    84  	ExtraData  hexutil.Bytes
    85  	Number     *math.HexOrDecimal256
    86  	BlockScore *math.HexOrDecimal256
    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 := database.NewMemoryDBManager()
    99  	gblock, err := t.genesis(config).Commit(common.Hash{}, 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  	// TODO-Klaytn: Replace gxhash with istanbul
   111  	chain, err := blockchain.NewBlockChain(db, nil, config, gxhash.NewShared(), vm.Config{})
   112  	if err != nil {
   113  		return err
   114  	}
   115  	defer chain.Stop()
   116  
   117  	validBlocks, err := t.insertBlocks(chain)
   118  	if err != nil {
   119  		return err
   120  	}
   121  	cmlast := chain.CurrentBlock().Hash()
   122  	if common.Hash(t.json.BestBlock) != cmlast {
   123  		return fmt.Errorf("last block hash validation mismatch: want: %x, have: %x", t.json.BestBlock, cmlast)
   124  	}
   125  	newDB, err := chain.State()
   126  	if err != nil {
   127  		return err
   128  	}
   129  	if err = t.validatePostState(newDB); err != nil {
   130  		return fmt.Errorf("post state validation failed: %v", err)
   131  	}
   132  	return t.validateImportedHeaders(chain, validBlocks)
   133  }
   134  
   135  func (t *BlockTest) genesis(config *params.ChainConfig) *blockchain.Genesis {
   136  	return &blockchain.Genesis{
   137  		Config:     config,
   138  		Timestamp:  t.json.Genesis.Timestamp.Uint64(),
   139  		ParentHash: t.json.Genesis.ParentHash,
   140  		ExtraData:  t.json.Genesis.ExtraData,
   141  		GasUsed:    t.json.Genesis.GasUsed,
   142  		BlockScore: t.json.Genesis.BlockScore,
   143  		Alloc:      t.json.Pre,
   144  	}
   145  }
   146  
   147  /* See https://github.com/ethereum/tests/wiki/Blockchain-Tests-II
   148  
   149     Whether a block is valid or not is a bit subtle, it's defined by presence of
   150     blockHeader and transactions fields. If they are missing, the block is
   151     invalid and we must verify that we do not accept it.
   152  
   153     Since some tests mix valid and invalid blocks we need to check this for every block.
   154  
   155     If a block is invalid it does not necessarily fail the test, if it's invalidness is
   156     expected we are expected to ignore it and continue processing and then validate the
   157     post state.
   158  */
   159  func (t *BlockTest) insertBlocks(blockchain *blockchain.BlockChain) ([]btBlock, error) {
   160  	validBlocks := make([]btBlock, 0)
   161  	// insert the test blocks, which will execute all transactions
   162  	for _, b := range t.json.Blocks {
   163  		cb, err := b.decode()
   164  		if err != nil {
   165  			if b.BlockHeader == nil {
   166  				continue // OK - block is supposed to be invalid, continue with next block
   167  			} else {
   168  				return nil, fmt.Errorf("Block RLP decoding failed when expected to succeed: %v", err)
   169  			}
   170  		}
   171  		// RLP decoding worked, try to insert into chain:
   172  		blocks := types.Blocks{cb}
   173  		i, err := blockchain.InsertChain(blocks)
   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 #%v insertion into chain failed: %v", blocks[i].Number(), err)
   179  			}
   180  		}
   181  		if b.BlockHeader == nil {
   182  			return nil, fmt.Errorf("Block insertion should have failed")
   183  		}
   184  
   185  		// validate RLP decoding by checking all values against test file JSON
   186  		if err = validateHeader(b.BlockHeader, cb.Header()); err != nil {
   187  			return nil, fmt.Errorf("Deserialised block header validation failed: %v", err)
   188  		}
   189  		validBlocks = append(validBlocks, b)
   190  	}
   191  	return validBlocks, nil
   192  }
   193  
   194  func validateHeader(h *btHeader, h2 *types.Header) error {
   195  	if h.Bloom != h2.Bloom {
   196  		return fmt.Errorf("Bloom: want: %x have: %x", h.Bloom, h2.Bloom)
   197  	}
   198  	if h.Number.Cmp(h2.Number) != 0 {
   199  		return fmt.Errorf("Number: want: %v have: %v", h.Number, h2.Number)
   200  	}
   201  	if h.ParentHash != h2.ParentHash {
   202  		return fmt.Errorf("Parent hash: want: %x have: %x", h.ParentHash, h2.ParentHash)
   203  	}
   204  	if h.ReceiptTrie != h2.ReceiptHash {
   205  		return fmt.Errorf("Receipt hash: want: %x have: %x", h.ReceiptTrie, h2.ReceiptHash)
   206  	}
   207  	if h.TransactionsTrie != h2.TxHash {
   208  		return fmt.Errorf("Tx hash: want: %x have: %x", h.TransactionsTrie, h2.TxHash)
   209  	}
   210  	if h.StateRoot != h2.Root {
   211  		return fmt.Errorf("State hash: want: %x have: %x", h.StateRoot, h2.Root)
   212  	}
   213  	if !bytes.Equal(h.ExtraData, h2.Extra) {
   214  		return fmt.Errorf("Extra data: want: %x have: %x", h.ExtraData, h2.Extra)
   215  	}
   216  	if h.BlockScore.Cmp(h2.BlockScore) != 0 {
   217  		return fmt.Errorf("BlockScore: want: %v have: %v", h.BlockScore, h2.BlockScore)
   218  	}
   219  	if h.GasUsed != h2.GasUsed {
   220  		return fmt.Errorf("GasUsed: want: %d have: %d", h.GasUsed, h2.GasUsed)
   221  	}
   222  	if h.Timestamp.Cmp(h2.Time) != 0 {
   223  		return fmt.Errorf("Timestamp: want: %v have: %v", h.Timestamp, h2.Time)
   224  	}
   225  	return nil
   226  }
   227  
   228  func (t *BlockTest) validatePostState(statedb *state.StateDB) error {
   229  	// validate post state accounts in test file against what we have in state db
   230  	for addr, acct := range t.json.Post {
   231  		// address is indirectly verified by the other fields, as it's the db key
   232  		code2 := statedb.GetCode(addr)
   233  		balance2 := statedb.GetBalance(addr)
   234  		nonce2 := statedb.GetNonce(addr)
   235  		if !bytes.Equal(code2, acct.Code) {
   236  			return fmt.Errorf("account code mismatch for addr: %s want: %v have: %s", addr, acct.Code, hex.EncodeToString(code2))
   237  		}
   238  		if balance2.Cmp(acct.Balance) != 0 {
   239  			return fmt.Errorf("account balance mismatch for addr: %s, want: %d, have: %d", addr, acct.Balance, balance2)
   240  		}
   241  		if nonce2 != acct.Nonce {
   242  			return fmt.Errorf("account nonce mismatch for addr: %s want: %d have: %d", addr, acct.Nonce, nonce2)
   243  		}
   244  	}
   245  	return nil
   246  }
   247  
   248  func (t *BlockTest) validateImportedHeaders(cm *blockchain.BlockChain, validBlocks []btBlock) error {
   249  	// to get constant lookup when verifying block headers by hash (some tests have many blocks)
   250  	bmap := make(map[common.Hash]btBlock, len(t.json.Blocks))
   251  	for _, b := range validBlocks {
   252  		bmap[b.BlockHeader.Hash] = b
   253  	}
   254  	// iterate over blocks backwards from HEAD and validate imported
   255  	// headers vs test file. some tests have reorgs, and we import
   256  	// block-by-block, so we can only validate imported headers after
   257  	// all blocks have been processed by BlockChain, as they may not
   258  	// be part of the longest chain until last block is imported.
   259  	for b := cm.CurrentBlock(); b != nil && b.NumberU64() != 0; b = cm.GetBlockByHash(b.Header().ParentHash) {
   260  		if err := validateHeader(bmap[b.Hash()].BlockHeader, b.Header()); err != nil {
   261  			return fmt.Errorf("Imported block header validation failed: %v", err)
   262  		}
   263  	}
   264  	return nil
   265  }
   266  
   267  func (bb *btBlock) decode() (*types.Block, error) {
   268  	data, err := hexutil.Decode(bb.Rlp)
   269  	if err != nil {
   270  		return nil, err
   271  	}
   272  	var b types.Block
   273  	err = rlp.DecodeBytes(data, &b)
   274  	return &b, err
   275  }