Skip to content

Commit 47ca690

Browse files
author
Gustav Simonsson
committed
tests: use lastblockhash field to validate reorgs and block headers
1 parent 075815e commit 47ca690

File tree

2 files changed

+58
-48
lines changed

2 files changed

+58
-48
lines changed

cmd/geth/blocktestcmd.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -111,25 +111,27 @@ func runOneBlockTest(ctx *cli.Context, test *tests.BlockTest) (*eth.Ethereum, er
111111
if err != nil {
112112
return nil, err
113113
}
114-
// if err := ethereum.Start(); err != nil {
115-
// return nil, err
116-
// }
117114

118115
// import the genesis block
119116
ethereum.ResetWithGenesisBlock(test.Genesis)
120117

121118
// import pre accounts
122-
statedb, err := test.InsertPreState(ethereum)
119+
_, err = test.InsertPreState(ethereum)
123120
if err != nil {
124121
return ethereum, fmt.Errorf("InsertPreState: %v", err)
125122
}
126123

127-
if err := test.TryBlocksInsert(ethereum.ChainManager()); err != nil {
124+
cm := ethereum.ChainManager()
125+
126+
validBlocks, err := test.TryBlocksInsert(cm)
127+
if err != nil {
128128
return ethereum, fmt.Errorf("Block Test load error: %v", err)
129129
}
130130

131-
if err := test.ValidatePostState(statedb); err != nil {
131+
newDB := cm.State()
132+
if err := test.ValidatePostState(newDB); err != nil {
132133
return ethereum, fmt.Errorf("post state validation failed: %v", err)
133134
}
134-
return ethereum, nil
135+
136+
return ethereum, test.ValidateImportedHeaders(cm, validBlocks)
135137
}

tests/block_test_util.go

Lines changed: 49 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,18 @@ import (
4444
type BlockTest struct {
4545
Genesis *types.Block
4646

47-
Json *btJSON
48-
preAccounts map[string]btAccount
49-
postAccounts map[string]btAccount
47+
Json *btJSON
48+
preAccounts map[string]btAccount
49+
postAccounts map[string]btAccount
50+
lastblockhash string
5051
}
5152

5253
type btJSON struct {
5354
Blocks []btBlock
5455
GenesisBlockHeader btHeader
5556
Pre map[string]btAccount
5657
PostState map[string]btAccount
58+
Lastblockhash string
5759
}
5860

5961
type btBlock struct {
@@ -77,6 +79,7 @@ type btHeader struct {
7779
MixHash string
7880
Nonce string
7981
Number string
82+
Hash string
8083
ParentHash string
8184
ReceiptTrie string
8285
SeedHash string
@@ -178,16 +181,24 @@ func runBlockTest(test *BlockTest) error {
178181
return fmt.Errorf("InsertPreState: %v", err)
179182
}
180183

181-
err = test.TryBlocksInsert(ethereum.ChainManager())
184+
cm := ethereum.ChainManager()
185+
validBlocks, err := test.TryBlocksInsert(cm)
182186
if err != nil {
183187
return err
184188
}
185189

186-
newDB := ethereum.ChainManager().State()
190+
lastblockhash := common.HexToHash(test.lastblockhash)
191+
cmlast := cm.LastBlockHash()
192+
if lastblockhash != cmlast {
193+
return fmt.Errorf("lastblockhash validation mismatch: want: %x, have: %x", lastblockhash, cmlast)
194+
}
195+
196+
newDB := cm.State()
187197
if err = test.ValidatePostState(newDB); err != nil {
188198
return fmt.Errorf("post state validation failed: %v", err)
189199
}
190-
return nil
200+
201+
return test.ValidateImportedHeaders(cm, validBlocks)
191202
}
192203

193204
func (test *BlockTest) makeEthConfig() *eth.Config {
@@ -265,16 +276,16 @@ func (t *BlockTest) InsertPreState(ethereum *eth.Ethereum) (*state.StateDB, erro
265276
expected we are expected to ignore it and continue processing and then validate the
266277
post state.
267278
*/
268-
func (t *BlockTest) TryBlocksInsert(chainManager *core.ChainManager) error {
269-
blockNums := make(map[string]bool)
279+
func (t *BlockTest) TryBlocksInsert(chainManager *core.ChainManager) ([]btBlock, error) {
280+
validBlocks := make([]btBlock, 0)
270281
// insert the test blocks, which will execute all transactions
271282
for _, b := range t.Json.Blocks {
272283
cb, err := mustConvertBlock(b)
273284
if err != nil {
274285
if b.BlockHeader == nil {
275286
continue // OK - block is supposed to be invalid, continue with next block
276287
} else {
277-
return fmt.Errorf("Block RLP decoding failed when expected to succeed: %v", err)
288+
return nil, fmt.Errorf("Block RLP decoding failed when expected to succeed: %v", err)
278289
}
279290
}
280291
// RLP decoding worked, try to insert into chain:
@@ -283,47 +294,23 @@ func (t *BlockTest) TryBlocksInsert(chainManager *core.ChainManager) error {
283294
if b.BlockHeader == nil {
284295
continue // OK - block is supposed to be invalid, continue with next block
285296
} else {
286-
return fmt.Errorf("Block insertion into chain failed: %v", err)
297+
return nil, fmt.Errorf("Block insertion into chain failed: %v", err)
287298
}
288299
}
289300
if b.BlockHeader == nil {
290-
return fmt.Errorf("Block insertion should have failed")
301+
return nil, fmt.Errorf("Block insertion should have failed")
291302
}
292303

293304
// validate RLP decoding by checking all values against test file JSON
294-
if err = t.validateBlockHeader(b.BlockHeader, cb.Header()); err != nil {
295-
return fmt.Errorf("Deserialised block header validation failed: %v", err)
296-
}
297-
298-
// validate the imported header against test file JSON
299-
300-
/*
301-
TODO: currently test files do not contain information on what
302-
reorg is expected other than possibly the post state (which may
303-
or may not depend on a specific chain).
304-
305-
discussed with winswega and it was agreed to add this information
306-
to the test files explicitly.
307-
308-
meanwhile we skip header validation on blocks with the same block
309-
number as a prior block, since this test code cannot know what
310-
blocks are in the longest chain without making use of the very
311-
protocol rules the tests verify or rely on the correctness of the
312-
code that is being tested.
313-
314-
*/
315-
if !blockNums[b.BlockHeader.Number] {
316-
importedBlock := chainManager.CurrentBlock()
317-
if err = t.validateBlockHeader(b.BlockHeader, importedBlock.Header()); err != nil {
318-
return fmt.Errorf("Imported block header validation failed: %v", err)
319-
}
320-
blockNums[b.BlockHeader.Number] = true
305+
if err = validateHeader(b.BlockHeader, cb.Header()); err != nil {
306+
return nil, fmt.Errorf("Deserialised block header validation failed: %v", err)
321307
}
308+
validBlocks = append(validBlocks, b)
322309
}
323-
return nil
310+
return validBlocks, nil
324311
}
325312

326-
func (s *BlockTest) validateBlockHeader(h *btHeader, h2 *types.Header) error {
313+
func validateHeader(h *btHeader, h2 *types.Header) error {
327314
expectedBloom := mustConvertBytes(h.Bloom)
328315
if !bytes.Equal(expectedBloom, h2.Bloom.Bytes()) {
329316
return fmt.Errorf("Bloom: want: %x have: %x", expectedBloom, h2.Bloom.Bytes())
@@ -439,6 +426,27 @@ func (t *BlockTest) ValidatePostState(statedb *state.StateDB) error {
439426
return nil
440427
}
441428

429+
func (test *BlockTest) ValidateImportedHeaders(cm *core.ChainManager, validBlocks []btBlock) error {
430+
// to get constant lookup when verifying block headers by hash (some tests have many blocks)
431+
bmap := make(map[string]btBlock, len(test.Json.Blocks))
432+
for _, b := range validBlocks {
433+
bmap[b.BlockHeader.Hash] = b
434+
}
435+
436+
// iterate over blocks backwards from HEAD and validate imported
437+
// headers vs test file. some tests have reorgs, and we import
438+
// block-by-block, so we can only validate imported headers after
439+
// all blocks have been processed by ChainManager, as they may not
440+
// be part of the longest chain until last block is imported.
441+
for b := cm.CurrentBlock(); b != nil && b.NumberU64() != 0; b = cm.GetBlock(b.Header().ParentHash) {
442+
bHash := common.Bytes2Hex(b.Hash().Bytes()) // hex without 0x prefix
443+
if err := validateHeader(bmap[bHash].BlockHeader, b.Header()); err != nil {
444+
return fmt.Errorf("Imported block header validation failed: %v", err)
445+
}
446+
}
447+
return nil
448+
}
449+
442450
func convertBlockTests(in map[string]*btJSON) (map[string]*BlockTest, error) {
443451
out := make(map[string]*BlockTest)
444452
for name, test := range in {
@@ -461,7 +469,7 @@ func convertBlockTest(in *btJSON) (out *BlockTest, err error) {
461469
err = fmt.Errorf("%v\n%s", recovered, buf)
462470
}
463471
}()
464-
out = &BlockTest{preAccounts: in.Pre, postAccounts: in.PostState, Json: in}
472+
out = &BlockTest{preAccounts: in.Pre, postAccounts: in.PostState, Json: in, lastblockhash: in.Lastblockhash}
465473
out.Genesis = mustConvertGenesis(in.GenesisBlockHeader)
466474
return out, err
467475
}

0 commit comments

Comments
 (0)