tests: use lastblockhash field to validate reorgs and block headers
This commit is contained in:
parent
075815e5ff
commit
47ca6904b3
@ -111,25 +111,27 @@ func runOneBlockTest(ctx *cli.Context, test *tests.BlockTest) (*eth.Ethereum, er
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// if err := ethereum.Start(); err != nil {
|
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
|
|
||||||
// import the genesis block
|
// import the genesis block
|
||||||
ethereum.ResetWithGenesisBlock(test.Genesis)
|
ethereum.ResetWithGenesisBlock(test.Genesis)
|
||||||
|
|
||||||
// import pre accounts
|
// import pre accounts
|
||||||
statedb, err := test.InsertPreState(ethereum)
|
_, err = test.InsertPreState(ethereum)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ethereum, fmt.Errorf("InsertPreState: %v", err)
|
return ethereum, fmt.Errorf("InsertPreState: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := test.TryBlocksInsert(ethereum.ChainManager()); err != nil {
|
cm := ethereum.ChainManager()
|
||||||
|
|
||||||
|
validBlocks, err := test.TryBlocksInsert(cm)
|
||||||
|
if err != nil {
|
||||||
return ethereum, fmt.Errorf("Block Test load error: %v", err)
|
return ethereum, fmt.Errorf("Block Test load error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := test.ValidatePostState(statedb); err != nil {
|
newDB := cm.State()
|
||||||
|
if err := test.ValidatePostState(newDB); err != nil {
|
||||||
return ethereum, fmt.Errorf("post state validation failed: %v", err)
|
return ethereum, fmt.Errorf("post state validation failed: %v", err)
|
||||||
}
|
}
|
||||||
return ethereum, nil
|
|
||||||
|
return ethereum, test.ValidateImportedHeaders(cm, validBlocks)
|
||||||
}
|
}
|
||||||
|
@ -44,9 +44,10 @@ import (
|
|||||||
type BlockTest struct {
|
type BlockTest struct {
|
||||||
Genesis *types.Block
|
Genesis *types.Block
|
||||||
|
|
||||||
Json *btJSON
|
Json *btJSON
|
||||||
preAccounts map[string]btAccount
|
preAccounts map[string]btAccount
|
||||||
postAccounts map[string]btAccount
|
postAccounts map[string]btAccount
|
||||||
|
lastblockhash string
|
||||||
}
|
}
|
||||||
|
|
||||||
type btJSON struct {
|
type btJSON struct {
|
||||||
@ -54,6 +55,7 @@ type btJSON struct {
|
|||||||
GenesisBlockHeader btHeader
|
GenesisBlockHeader btHeader
|
||||||
Pre map[string]btAccount
|
Pre map[string]btAccount
|
||||||
PostState map[string]btAccount
|
PostState map[string]btAccount
|
||||||
|
Lastblockhash string
|
||||||
}
|
}
|
||||||
|
|
||||||
type btBlock struct {
|
type btBlock struct {
|
||||||
@ -77,6 +79,7 @@ type btHeader struct {
|
|||||||
MixHash string
|
MixHash string
|
||||||
Nonce string
|
Nonce string
|
||||||
Number string
|
Number string
|
||||||
|
Hash string
|
||||||
ParentHash string
|
ParentHash string
|
||||||
ReceiptTrie string
|
ReceiptTrie string
|
||||||
SeedHash string
|
SeedHash string
|
||||||
@ -178,16 +181,24 @@ func runBlockTest(test *BlockTest) error {
|
|||||||
return fmt.Errorf("InsertPreState: %v", err)
|
return fmt.Errorf("InsertPreState: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = test.TryBlocksInsert(ethereum.ChainManager())
|
cm := ethereum.ChainManager()
|
||||||
|
validBlocks, err := test.TryBlocksInsert(cm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
newDB := ethereum.ChainManager().State()
|
lastblockhash := common.HexToHash(test.lastblockhash)
|
||||||
|
cmlast := cm.LastBlockHash()
|
||||||
|
if lastblockhash != cmlast {
|
||||||
|
return fmt.Errorf("lastblockhash validation mismatch: want: %x, have: %x", lastblockhash, cmlast)
|
||||||
|
}
|
||||||
|
|
||||||
|
newDB := cm.State()
|
||||||
if err = test.ValidatePostState(newDB); err != nil {
|
if err = test.ValidatePostState(newDB); err != nil {
|
||||||
return fmt.Errorf("post state validation failed: %v", err)
|
return fmt.Errorf("post state validation failed: %v", err)
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
|
return test.ValidateImportedHeaders(cm, validBlocks)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (test *BlockTest) makeEthConfig() *eth.Config {
|
func (test *BlockTest) makeEthConfig() *eth.Config {
|
||||||
@ -265,8 +276,8 @@ func (t *BlockTest) InsertPreState(ethereum *eth.Ethereum) (*state.StateDB, erro
|
|||||||
expected we are expected to ignore it and continue processing and then validate the
|
expected we are expected to ignore it and continue processing and then validate the
|
||||||
post state.
|
post state.
|
||||||
*/
|
*/
|
||||||
func (t *BlockTest) TryBlocksInsert(chainManager *core.ChainManager) error {
|
func (t *BlockTest) TryBlocksInsert(chainManager *core.ChainManager) ([]btBlock, error) {
|
||||||
blockNums := make(map[string]bool)
|
validBlocks := make([]btBlock, 0)
|
||||||
// insert the test blocks, which will execute all transactions
|
// insert the test blocks, which will execute all transactions
|
||||||
for _, b := range t.Json.Blocks {
|
for _, b := range t.Json.Blocks {
|
||||||
cb, err := mustConvertBlock(b)
|
cb, err := mustConvertBlock(b)
|
||||||
@ -274,7 +285,7 @@ func (t *BlockTest) TryBlocksInsert(chainManager *core.ChainManager) error {
|
|||||||
if b.BlockHeader == nil {
|
if b.BlockHeader == nil {
|
||||||
continue // OK - block is supposed to be invalid, continue with next block
|
continue // OK - block is supposed to be invalid, continue with next block
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("Block RLP decoding failed when expected to succeed: %v", err)
|
return nil, fmt.Errorf("Block RLP decoding failed when expected to succeed: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// RLP decoding worked, try to insert into chain:
|
// RLP decoding worked, try to insert into chain:
|
||||||
@ -283,47 +294,23 @@ func (t *BlockTest) TryBlocksInsert(chainManager *core.ChainManager) error {
|
|||||||
if b.BlockHeader == nil {
|
if b.BlockHeader == nil {
|
||||||
continue // OK - block is supposed to be invalid, continue with next block
|
continue // OK - block is supposed to be invalid, continue with next block
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("Block insertion into chain failed: %v", err)
|
return nil, fmt.Errorf("Block insertion into chain failed: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if b.BlockHeader == nil {
|
if b.BlockHeader == nil {
|
||||||
return fmt.Errorf("Block insertion should have failed")
|
return nil, fmt.Errorf("Block insertion should have failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate RLP decoding by checking all values against test file JSON
|
// validate RLP decoding by checking all values against test file JSON
|
||||||
if err = t.validateBlockHeader(b.BlockHeader, cb.Header()); err != nil {
|
if err = validateHeader(b.BlockHeader, cb.Header()); err != nil {
|
||||||
return fmt.Errorf("Deserialised block header validation failed: %v", err)
|
return nil, fmt.Errorf("Deserialised block header validation failed: %v", err)
|
||||||
}
|
|
||||||
|
|
||||||
// validate the imported header against test file JSON
|
|
||||||
|
|
||||||
/*
|
|
||||||
TODO: currently test files do not contain information on what
|
|
||||||
reorg is expected other than possibly the post state (which may
|
|
||||||
or may not depend on a specific chain).
|
|
||||||
|
|
||||||
discussed with winswega and it was agreed to add this information
|
|
||||||
to the test files explicitly.
|
|
||||||
|
|
||||||
meanwhile we skip header validation on blocks with the same block
|
|
||||||
number as a prior block, since this test code cannot know what
|
|
||||||
blocks are in the longest chain without making use of the very
|
|
||||||
protocol rules the tests verify or rely on the correctness of the
|
|
||||||
code that is being tested.
|
|
||||||
|
|
||||||
*/
|
|
||||||
if !blockNums[b.BlockHeader.Number] {
|
|
||||||
importedBlock := chainManager.CurrentBlock()
|
|
||||||
if err = t.validateBlockHeader(b.BlockHeader, importedBlock.Header()); err != nil {
|
|
||||||
return fmt.Errorf("Imported block header validation failed: %v", err)
|
|
||||||
}
|
|
||||||
blockNums[b.BlockHeader.Number] = true
|
|
||||||
}
|
}
|
||||||
|
validBlocks = append(validBlocks, b)
|
||||||
}
|
}
|
||||||
return nil
|
return validBlocks, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BlockTest) validateBlockHeader(h *btHeader, h2 *types.Header) error {
|
func validateHeader(h *btHeader, h2 *types.Header) error {
|
||||||
expectedBloom := mustConvertBytes(h.Bloom)
|
expectedBloom := mustConvertBytes(h.Bloom)
|
||||||
if !bytes.Equal(expectedBloom, h2.Bloom.Bytes()) {
|
if !bytes.Equal(expectedBloom, h2.Bloom.Bytes()) {
|
||||||
return fmt.Errorf("Bloom: want: %x have: %x", expectedBloom, h2.Bloom.Bytes())
|
return fmt.Errorf("Bloom: want: %x have: %x", expectedBloom, h2.Bloom.Bytes())
|
||||||
@ -439,6 +426,27 @@ func (t *BlockTest) ValidatePostState(statedb *state.StateDB) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (test *BlockTest) ValidateImportedHeaders(cm *core.ChainManager, validBlocks []btBlock) error {
|
||||||
|
// to get constant lookup when verifying block headers by hash (some tests have many blocks)
|
||||||
|
bmap := make(map[string]btBlock, len(test.Json.Blocks))
|
||||||
|
for _, b := range validBlocks {
|
||||||
|
bmap[b.BlockHeader.Hash] = b
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterate over blocks backwards from HEAD and validate imported
|
||||||
|
// headers vs test file. some tests have reorgs, and we import
|
||||||
|
// block-by-block, so we can only validate imported headers after
|
||||||
|
// all blocks have been processed by ChainManager, as they may not
|
||||||
|
// be part of the longest chain until last block is imported.
|
||||||
|
for b := cm.CurrentBlock(); b != nil && b.NumberU64() != 0; b = cm.GetBlock(b.Header().ParentHash) {
|
||||||
|
bHash := common.Bytes2Hex(b.Hash().Bytes()) // hex without 0x prefix
|
||||||
|
if err := validateHeader(bmap[bHash].BlockHeader, b.Header()); err != nil {
|
||||||
|
return fmt.Errorf("Imported block header validation failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func convertBlockTests(in map[string]*btJSON) (map[string]*BlockTest, error) {
|
func convertBlockTests(in map[string]*btJSON) (map[string]*BlockTest, error) {
|
||||||
out := make(map[string]*BlockTest)
|
out := make(map[string]*BlockTest)
|
||||||
for name, test := range in {
|
for name, test := range in {
|
||||||
@ -461,7 +469,7 @@ func convertBlockTest(in *btJSON) (out *BlockTest, err error) {
|
|||||||
err = fmt.Errorf("%v\n%s", recovered, buf)
|
err = fmt.Errorf("%v\n%s", recovered, buf)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
out = &BlockTest{preAccounts: in.Pre, postAccounts: in.PostState, Json: in}
|
out = &BlockTest{preAccounts: in.Pre, postAccounts: in.PostState, Json: in, lastblockhash: in.Lastblockhash}
|
||||||
out.Genesis = mustConvertGenesis(in.GenesisBlockHeader)
|
out.Genesis = mustConvertGenesis(in.GenesisBlockHeader)
|
||||||
return out, err
|
return out, err
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user