From c33cc382b3561ca91871111933f81653bfd8532f Mon Sep 17 00:00:00 2001 From: Péter Szilágyi Date: Mon, 21 Sep 2015 15:36:29 +0300 Subject: core: support inserting pure header chains --- core/blockchain_test.go | 587 +++++++++++++++++++++++++++++------------------- 1 file changed, 352 insertions(+), 235 deletions(-) (limited to 'core/blockchain_test.go') diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 13971ccba..4d0f13ef1 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -64,44 +64,58 @@ func theBlockChain(db ethdb.Database, t *testing.T) *BlockChain { } // Test fork of length N starting from block i -func testFork(t *testing.T, bman *BlockProcessor, i, N int, f func(td1, td2 *big.Int)) { - // switch databases to process the new chain - db, err := ethdb.NewMemDatabase() - if err != nil { - t.Fatal("Failed to create db:", err) - } - // copy old chain up to i into new db with deterministic canonical - bman2, err := newCanonical(i, db) +func testFork(t *testing.T, processor *BlockProcessor, i, n int, full bool, comparator func(td1, td2 *big.Int)) { + // Copy old chain up to #i into a new db + db, processor2, err := newCanonical(i, full) if err != nil { t.Fatal("could not make new canonical in testFork", err) } - // assert the bmans have the same block at i - bi1 := bman.bc.GetBlockByNumber(uint64(i)).Hash() - bi2 := bman2.bc.GetBlockByNumber(uint64(i)).Hash() - if bi1 != bi2 { - fmt.Printf("%+v\n%+v\n\n", bi1, bi2) - t.Fatal("chains do not have the same hash at height", i) + // Assert the chains have the same header/block at #i + var hash1, hash2 common.Hash + if full { + hash1 = processor.bc.GetBlockByNumber(uint64(i)).Hash() + hash2 = processor2.bc.GetBlockByNumber(uint64(i)).Hash() + } else { + hash1 = processor.bc.GetHeaderByNumber(uint64(i)).Hash() + hash2 = processor2.bc.GetHeaderByNumber(uint64(i)).Hash() } - bman2.bc.SetProcessor(bman2) - - // extend the fork - parent := bman2.bc.CurrentBlock() - chainB := makeChain(parent, N, db, forkSeed) - _, err = bman2.bc.InsertChain(chainB) - if err != nil { - t.Fatal("Insert chain error for fork:", err) + if hash1 != hash2 { + t.Errorf("chain content mismatch at %d: have hash %v, want hash %v", i, hash2, hash1) } - - tdpre := bman.bc.Td() - // Test the fork's blocks on the original chain - td, err := testChain(chainB, bman) - if err != nil { - t.Fatal("expected chainB not to give errors:", err) + // Extend the newly created chain + var ( + blockChainB []*types.Block + headerChainB []*types.Header + ) + if full { + blockChainB = makeBlockChain(processor2.bc.CurrentBlock(), n, db, forkSeed) + if _, err := processor2.bc.InsertChain(blockChainB); err != nil { + t.Fatalf("failed to insert forking chain: %v", err) + } + } else { + headerChainB = makeHeaderChain(processor2.bc.CurrentHeader(), n, db, forkSeed) + if _, err := processor2.bc.InsertHeaderChain(headerChainB, true); err != nil { + t.Fatalf("failed to insert forking chain: %v", err) + } } - // Compare difficulties - f(tdpre, td) + // Sanity check that the forked chain can be imported into the original + var tdPre, tdPost *big.Int - // Loop over parents making sure reconstruction is done properly + if full { + tdPre = processor.bc.GetTd(processor.bc.CurrentBlock().Hash()) + if err := testBlockChainImport(blockChainB, processor); err != nil { + t.Fatalf("failed to import forked block chain: %v", err) + } + tdPost = processor.bc.GetTd(blockChainB[len(blockChainB)-1].Hash()) + } else { + tdPre = processor.bc.GetTd(processor.bc.CurrentHeader().Hash()) + if err := testHeaderChainImport(headerChainB, processor); err != nil { + t.Fatalf("failed to import forked header chain: %v", err) + } + tdPost = processor.bc.GetTd(headerChainB[len(headerChainB)-1].Hash()) + } + // Compare the total difficulties of the chains + comparator(tdPre, tdPost) } func printChain(bc *BlockChain) { @@ -111,22 +125,41 @@ func printChain(bc *BlockChain) { } } -// process blocks against a chain -func testChain(chainB types.Blocks, bman *BlockProcessor) (*big.Int, error) { - for _, block := range chainB { - _, _, err := bman.bc.processor.Process(block) - if err != nil { +// testBlockChainImport tries to process a chain of blocks, writing them into +// the database if successful. +func testBlockChainImport(chain []*types.Block, processor *BlockProcessor) error { + for _, block := range chain { + // Try and process the block + if _, _, err := processor.Process(block); err != nil { if IsKnownBlockErr(err) { continue } - return nil, err + return err } - bman.bc.mu.Lock() - WriteTd(bman.bc.chainDb, block.Hash(), new(big.Int).Add(block.Difficulty(), bman.bc.GetTd(block.ParentHash()))) - WriteBlock(bman.bc.chainDb, block) - bman.bc.mu.Unlock() + // Manually insert the block into the database, but don't reorganize (allows subsequent testing) + processor.bc.mu.Lock() + WriteTd(processor.chainDb, block.Hash(), new(big.Int).Add(block.Difficulty(), processor.bc.GetTd(block.ParentHash()))) + WriteBlock(processor.chainDb, block) + processor.bc.mu.Unlock() } - return bman.bc.GetTd(chainB[len(chainB)-1].Hash()), nil + return nil +} + +// testHeaderChainImport tries to process a chain of header, writing them into +// the database if successful. +func testHeaderChainImport(chain []*types.Header, processor *BlockProcessor) error { + for _, header := range chain { + // Try and validate the header + if err := processor.ValidateHeader(header, false, false); err != nil { + return err + } + // Manually insert the header into the database, but don't reorganize (allows subsequent testing) + processor.bc.mu.Lock() + WriteTd(processor.chainDb, header.Hash(), new(big.Int).Add(header.Difficulty, processor.bc.GetTd(header.ParentHash))) + WriteHeader(processor.chainDb, header) + processor.bc.mu.Unlock() + } + return nil } func loadChain(fn string, t *testing.T) (types.Blocks, error) { @@ -154,139 +187,147 @@ func insertChain(done chan bool, blockchain *BlockChain, chain types.Blocks, t * } func TestLastBlock(t *testing.T) { - db, err := ethdb.NewMemDatabase() - if err != nil { - t.Fatal("Failed to create db:", err) - } + db, _ := ethdb.NewMemDatabase() + bchain := theBlockChain(db, t) - block := makeChain(bchain.CurrentBlock(), 1, db, 0)[0] + block := makeBlockChain(bchain.CurrentBlock(), 1, db, 0)[0] bchain.insert(block) if block.Hash() != GetHeadBlockHash(db) { t.Errorf("Write/Get HeadBlockHash failed") } } -func TestExtendCanonical(t *testing.T) { - CanonicalLength := 5 - db, err := ethdb.NewMemDatabase() - if err != nil { - t.Fatal("Failed to create db:", err) - } - // make first chain starting from genesis - bman, err := newCanonical(CanonicalLength, db) +// Tests that given a starting canonical chain of a given size, it can be extended +// with various length chains. +func TestExtendCanonicalHeaders(t *testing.T) { testExtendCanonical(t, false) } +func TestExtendCanonicalBlocks(t *testing.T) { testExtendCanonical(t, true) } + +func testExtendCanonical(t *testing.T, full bool) { + length := 5 + + // Make first chain starting from genesis + _, processor, err := newCanonical(length, full) if err != nil { - t.Fatal("Could not make new canonical chain:", err) + t.Fatalf("failed to make new canonical chain: %v", err) } - f := func(td1, td2 *big.Int) { + // Define the difficulty comparator + better := func(td1, td2 *big.Int) { if td2.Cmp(td1) <= 0 { - t.Error("expected chainB to have higher difficulty. Got", td2, "expected more than", td1) + t.Errorf("total difficulty mismatch: have %v, expected more than %v", td2, td1) } } - // Start fork from current height (CanonicalLength) - testFork(t, bman, CanonicalLength, 1, f) - testFork(t, bman, CanonicalLength, 2, f) - testFork(t, bman, CanonicalLength, 5, f) - testFork(t, bman, CanonicalLength, 10, f) + // Start fork from current height + testFork(t, processor, length, 1, full, better) + testFork(t, processor, length, 2, full, better) + testFork(t, processor, length, 5, full, better) + testFork(t, processor, length, 10, full, better) } -func TestShorterFork(t *testing.T) { - db, err := ethdb.NewMemDatabase() - if err != nil { - t.Fatal("Failed to create db:", err) - } - // make first chain starting from genesis - bman, err := newCanonical(10, db) +// Tests that given a starting canonical chain of a given size, creating shorter +// forks do not take canonical ownership. +func TestShorterForkHeaders(t *testing.T) { testShorterFork(t, false) } +func TestShorterForkBlocks(t *testing.T) { testShorterFork(t, true) } + +func testShorterFork(t *testing.T, full bool) { + length := 10 + + // Make first chain starting from genesis + _, processor, err := newCanonical(length, full) if err != nil { - t.Fatal("Could not make new canonical chain:", err) + t.Fatalf("failed to make new canonical chain: %v", err) } - f := func(td1, td2 *big.Int) { + // Define the difficulty comparator + worse := func(td1, td2 *big.Int) { if td2.Cmp(td1) >= 0 { - t.Error("expected chainB to have lower difficulty. Got", td2, "expected less than", td1) + t.Errorf("total difficulty mismatch: have %v, expected less than %v", td2, td1) } } - // Sum of numbers must be less than 10 - // for this to be a shorter fork - testFork(t, bman, 0, 3, f) - testFork(t, bman, 0, 7, f) - testFork(t, bman, 1, 1, f) - testFork(t, bman, 1, 7, f) - testFork(t, bman, 5, 3, f) - testFork(t, bman, 5, 4, f) + // Sum of numbers must be less than `length` for this to be a shorter fork + testFork(t, processor, 0, 3, full, worse) + testFork(t, processor, 0, 7, full, worse) + testFork(t, processor, 1, 1, full, worse) + testFork(t, processor, 1, 7, full, worse) + testFork(t, processor, 5, 3, full, worse) + testFork(t, processor, 5, 4, full, worse) } -func TestLongerFork(t *testing.T) { - db, err := ethdb.NewMemDatabase() - if err != nil { - t.Fatal("Failed to create db:", err) - } - // make first chain starting from genesis - bman, err := newCanonical(10, db) +// Tests that given a starting canonical chain of a given size, creating longer +// forks do take canonical ownership. +func TestLongerForkHeaders(t *testing.T) { testLongerFork(t, false) } +func TestLongerForkBlocks(t *testing.T) { testLongerFork(t, true) } + +func testLongerFork(t *testing.T, full bool) { + length := 10 + + // Make first chain starting from genesis + _, processor, err := newCanonical(length, full) if err != nil { - t.Fatal("Could not make new canonical chain:", err) + t.Fatalf("failed to make new canonical chain: %v", err) } - f := func(td1, td2 *big.Int) { + // Define the difficulty comparator + better := func(td1, td2 *big.Int) { if td2.Cmp(td1) <= 0 { - t.Error("expected chainB to have higher difficulty. Got", td2, "expected more than", td1) + t.Errorf("total difficulty mismatch: have %v, expected more than %v", td2, td1) } } - // Sum of numbers must be greater than 10 - // for this to be a longer fork - testFork(t, bman, 0, 11, f) - testFork(t, bman, 0, 15, f) - testFork(t, bman, 1, 10, f) - testFork(t, bman, 1, 12, f) - testFork(t, bman, 5, 6, f) - testFork(t, bman, 5, 8, f) + // Sum of numbers must be greater than `length` for this to be a longer fork + testFork(t, processor, 0, 11, full, better) + testFork(t, processor, 0, 15, full, better) + testFork(t, processor, 1, 10, full, better) + testFork(t, processor, 1, 12, full, better) + testFork(t, processor, 5, 6, full, better) + testFork(t, processor, 5, 8, full, better) } -func TestEqualFork(t *testing.T) { - db, err := ethdb.NewMemDatabase() - if err != nil { - t.Fatal("Failed to create db:", err) - } - bman, err := newCanonical(10, db) +// Tests that given a starting canonical chain of a given size, creating equal +// forks do take canonical ownership. +func TestEqualForkHeaders(t *testing.T) { testEqualFork(t, false) } +func TestEqualForkBlocks(t *testing.T) { testEqualFork(t, true) } + +func testEqualFork(t *testing.T, full bool) { + length := 10 + + // Make first chain starting from genesis + _, processor, err := newCanonical(length, full) if err != nil { - t.Fatal("Could not make new canonical chain:", err) + t.Fatalf("failed to make new canonical chain: %v", err) } - f := func(td1, td2 *big.Int) { + // Define the difficulty comparator + equal := func(td1, td2 *big.Int) { if td2.Cmp(td1) != 0 { - t.Error("expected chainB to have equal difficulty. Got", td2, "expected ", td1) + t.Errorf("total difficulty mismatch: have %v, want %v", td2, td1) } } - // Sum of numbers must be equal to 10 - // for this to be an equal fork - testFork(t, bman, 0, 10, f) - testFork(t, bman, 1, 9, f) - testFork(t, bman, 2, 8, f) - testFork(t, bman, 5, 5, f) - testFork(t, bman, 6, 4, f) - testFork(t, bman, 9, 1, f) + // Sum of numbers must be equal to `length` for this to be an equal fork + testFork(t, processor, 0, 10, full, equal) + testFork(t, processor, 1, 9, full, equal) + testFork(t, processor, 2, 8, full, equal) + testFork(t, processor, 5, 5, full, equal) + testFork(t, processor, 6, 4, full, equal) + testFork(t, processor, 9, 1, full, equal) } -func TestBrokenChain(t *testing.T) { - db, err := ethdb.NewMemDatabase() - if err != nil { - t.Fatal("Failed to create db:", err) - } - bman, err := newCanonical(10, db) - if err != nil { - t.Fatal("Could not make new canonical chain:", err) - } - db2, err := ethdb.NewMemDatabase() - if err != nil { - t.Fatal("Failed to create db:", err) - } - bman2, err := newCanonical(10, db2) +// Tests that chains missing links do not get accepted by the processor. +func TestBrokenHeaderChain(t *testing.T) { testBrokenChain(t, false) } +func TestBrokenBlockChain(t *testing.T) { testBrokenChain(t, true) } + +func testBrokenChain(t *testing.T, full bool) { + // Make chain starting from genesis + db, processor, err := newCanonical(10, full) if err != nil { - t.Fatal("Could not make new canonical chain:", err) + t.Fatalf("failed to make new canonical chain: %v", err) } - bman2.bc.SetProcessor(bman2) - parent := bman2.bc.CurrentBlock() - chainB := makeChain(parent, 5, db2, forkSeed) - chainB = chainB[1:] - _, err = testChain(chainB, bman) - if err == nil { - t.Error("expected broken chain to return error") + // Create a forked chain, and try to insert with a missing link + if full { + chain := makeBlockChain(processor.bc.CurrentBlock(), 5, db, forkSeed)[1:] + if err := testBlockChainImport(chain, processor); err == nil { + t.Errorf("broken block chain not reported") + } + } else { + chain := makeHeaderChain(processor.bc.CurrentHeader(), 5, db, forkSeed)[1:] + if err := testHeaderChainImport(chain, processor); err == nil { + t.Errorf("broken header chain not reported") + } } } @@ -376,7 +417,16 @@ type bproc struct{} func (bproc) Process(*types.Block) (vm.Logs, types.Receipts, error) { return nil, nil, nil } -func makeChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Block { +func makeHeaderChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Header { + blocks := makeBlockChainWithDiff(genesis, d, seed) + headers := make([]*types.Header, len(blocks)) + for i, block := range blocks { + headers[i] = block.Header() + } + return headers +} + +func makeBlockChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Block { var chain []*types.Block for i, difficulty := range d { header := &types.Header{ @@ -410,142 +460,209 @@ func chm(genesis *types.Block, db ethdb.Database) *BlockChain { return bc } -func TestReorgLongest(t *testing.T) { - db, _ := ethdb.NewMemDatabase() +// Tests that reorganizing a long difficult chain after a short easy one +// overwrites the canonical numbers and links in the database. +func TestReorgLongHeaders(t *testing.T) { testReorgLong(t, false) } +func TestReorgLongBlocks(t *testing.T) { testReorgLong(t, true) } - genesis, err := WriteTestNetGenesisBlock(db, 0) - if err != nil { - t.Error(err) - t.FailNow() - } - bc := chm(genesis, db) +func testReorgLong(t *testing.T, full bool) { + testReorg(t, []int{1, 2, 4}, []int{1, 2, 3, 4}, 10, full) +} - chain1 := makeChainWithDiff(genesis, []int{1, 2, 4}, 10) - chain2 := makeChainWithDiff(genesis, []int{1, 2, 3, 4}, 11) +// Tests that reorganizing a short difficult chain after a long easy one +// overwrites the canonical numbers and links in the database. +func TestReorgShortHeaders(t *testing.T) { testReorgShort(t, false) } +func TestReorgShortBlocks(t *testing.T) { testReorgShort(t, true) } - bc.InsertChain(chain1) - bc.InsertChain(chain2) +func testReorgShort(t *testing.T, full bool) { + testReorg(t, []int{1, 2, 3, 4}, []int{1, 10}, 11, full) +} - prev := bc.CurrentBlock() - for block := bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - 1); block.NumberU64() != 0; prev, block = block, bc.GetBlockByNumber(block.NumberU64()-1) { - if prev.ParentHash() != block.Hash() { - t.Errorf("parent hash mismatch %x - %x", prev.ParentHash(), block.Hash()) +func testReorg(t *testing.T, first, second []int, td int64, full bool) { + // Create a pristine block chain + db, _ := ethdb.NewMemDatabase() + genesis, _ := WriteTestNetGenesisBlock(db, 0) + bc := chm(genesis, db) + + // Insert an easy and a difficult chain afterwards + if full { + bc.InsertChain(makeBlockChainWithDiff(genesis, first, 11)) + bc.InsertChain(makeBlockChainWithDiff(genesis, second, 22)) + } else { + bc.InsertHeaderChain(makeHeaderChainWithDiff(genesis, first, 11), false) + bc.InsertHeaderChain(makeHeaderChainWithDiff(genesis, second, 22), false) + } + // Check that the chain is valid number and link wise + if full { + prev := bc.CurrentBlock() + for block := bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - 1); block.NumberU64() != 0; prev, block = block, bc.GetBlockByNumber(block.NumberU64()-1) { + if prev.ParentHash() != block.Hash() { + t.Errorf("parent block hash mismatch: have %x, want %x", prev.ParentHash(), block.Hash()) + } + } + } else { + prev := bc.CurrentHeader() + for header := bc.GetHeaderByNumber(bc.CurrentHeader().Number.Uint64() - 1); header.Number.Uint64() != 0; prev, header = header, bc.GetHeaderByNumber(header.Number.Uint64()-1) { + if prev.ParentHash != header.Hash() { + t.Errorf("parent header hash mismatch: have %x, want %x", prev.ParentHash, header.Hash()) + } + } + } + // Make sure the chain total difficulty is the correct one + want := new(big.Int).Add(genesis.Difficulty(), big.NewInt(td)) + if full { + if have := bc.GetTd(bc.CurrentBlock().Hash()); have.Cmp(want) != 0 { + t.Errorf("total difficulty mismatch: have %v, want %v", have, want) + } + } else { + if have := bc.GetTd(bc.CurrentHeader().Hash()); have.Cmp(want) != 0 { + t.Errorf("total difficulty mismatch: have %v, want %v", have, want) } } } -func TestBadHashes(t *testing.T) { +// Tests that the insertion functions detect banned hashes. +func TestBadHeaderHashes(t *testing.T) { testBadHashes(t, false) } +func TestBadBlockHashes(t *testing.T) { testBadHashes(t, true) } + +func testBadHashes(t *testing.T, full bool) { + // Create a pristine block chain db, _ := ethdb.NewMemDatabase() - genesis, err := WriteTestNetGenesisBlock(db, 0) - if err != nil { - t.Error(err) - t.FailNow() - } + genesis, _ := WriteTestNetGenesisBlock(db, 0) bc := chm(genesis, db) - chain := makeChainWithDiff(genesis, []int{1, 2, 4}, 10) - BadHashes[chain[2].Header().Hash()] = true - - _, err = bc.InsertChain(chain) + // Create a chain, ban a hash and try to import + var err error + if full { + blocks := makeBlockChainWithDiff(genesis, []int{1, 2, 4}, 10) + BadHashes[blocks[2].Header().Hash()] = true + _, err = bc.InsertChain(blocks) + } else { + headers := makeHeaderChainWithDiff(genesis, []int{1, 2, 4}, 10) + BadHashes[headers[2].Hash()] = true + _, err = bc.InsertHeaderChain(headers, true) + } if !IsBadHashError(err) { t.Errorf("error mismatch: want: BadHashError, have: %v", err) } } -func TestReorgBadHashes(t *testing.T) { +// Tests that bad hashes are detected on boot, and the chan rolled back to a +// good state prior to the bad hash. +func TestReorgBadHeaderHashes(t *testing.T) { testReorgBadHashes(t, false) } +func TestReorgBadBlockHashes(t *testing.T) { testReorgBadHashes(t, true) } + +func testReorgBadHashes(t *testing.T, full bool) { + // Create a pristine block chain db, _ := ethdb.NewMemDatabase() - genesis, err := WriteTestNetGenesisBlock(db, 0) - if err != nil { - t.Error(err) - t.FailNow() - } + genesis, _ := WriteTestNetGenesisBlock(db, 0) bc := chm(genesis, db) - chain := makeChainWithDiff(genesis, []int{1, 2, 3, 4}, 11) - bc.InsertChain(chain) - - if chain[3].Header().Hash() != bc.LastBlockHash() { - t.Errorf("last block hash mismatch: want: %x, have: %x", chain[3].Header().Hash(), bc.LastBlockHash()) - } - - // NewChainManager should check BadHashes when loading it db - BadHashes[chain[3].Header().Hash()] = true - - var eventMux event.TypeMux - ncm, err := NewBlockChain(db, FakePow{}, &eventMux) - if err != nil { - t.Errorf("NewChainManager err: %s", err) - } - - // check it set head to (valid) parent of bad hash block - if chain[2].Header().Hash() != ncm.LastBlockHash() { - t.Errorf("last block hash mismatch: want: %x, have: %x", chain[2].Header().Hash(), ncm.LastBlockHash()) - } + // Create a chain, import and ban aferwards + headers := makeHeaderChainWithDiff(genesis, []int{1, 2, 3, 4}, 10) + blocks := makeBlockChainWithDiff(genesis, []int{1, 2, 3, 4}, 10) - if chain[2].Header().GasLimit.Cmp(ncm.GasLimit()) != 0 { - t.Errorf("current block gasLimit mismatch: want: %x, have: %x", chain[2].Header().GasLimit, ncm.GasLimit()) + if full { + if _, err := bc.InsertChain(blocks); err != nil { + t.Fatalf("failed to import blocks: %v", err) + } + if bc.CurrentBlock().Hash() != blocks[3].Hash() { + t.Errorf("last block hash mismatch: have: %x, want %x", bc.CurrentBlock().Hash(), blocks[3].Header().Hash()) + } + BadHashes[blocks[3].Header().Hash()] = true + defer func() { delete(BadHashes, blocks[3].Header().Hash()) }() + } else { + if _, err := bc.InsertHeaderChain(headers, true); err != nil { + t.Fatalf("failed to import headers: %v", err) + } + if bc.CurrentHeader().Hash() != headers[3].Hash() { + t.Errorf("last header hash mismatch: have: %x, want %x", bc.CurrentHeader().Hash(), headers[3].Hash()) + } + BadHashes[headers[3].Hash()] = true + defer func() { delete(BadHashes, headers[3].Hash()) }() } -} - -func TestReorgShortest(t *testing.T) { - db, _ := ethdb.NewMemDatabase() - genesis, err := WriteTestNetGenesisBlock(db, 0) + // Create a new chain manager and check it rolled back the state + ncm, err := NewBlockChain(db, FakePow{}, new(event.TypeMux)) if err != nil { - t.Error(err) - t.FailNow() + t.Fatalf("failed to create new chain manager: %v", err) } - bc := chm(genesis, db) - - chain1 := makeChainWithDiff(genesis, []int{1, 2, 3, 4}, 10) - chain2 := makeChainWithDiff(genesis, []int{1, 10}, 11) - - bc.InsertChain(chain1) - bc.InsertChain(chain2) - - prev := bc.CurrentBlock() - for block := bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - 1); block.NumberU64() != 0; prev, block = block, bc.GetBlockByNumber(block.NumberU64()-1) { - if prev.ParentHash() != block.Hash() { - t.Errorf("parent hash mismatch %x - %x", prev.ParentHash(), block.Hash()) + if full { + if ncm.CurrentBlock().Hash() != blocks[2].Header().Hash() { + t.Errorf("last block hash mismatch: have: %x, want %x", ncm.CurrentBlock().Hash(), blocks[2].Header().Hash()) + } + if blocks[2].Header().GasLimit.Cmp(ncm.GasLimit()) != 0 { + t.Errorf("last block gasLimit mismatch: have: %x, want %x", ncm.GasLimit(), blocks[2].Header().GasLimit) + } + } else { + if ncm.CurrentHeader().Hash() != genesis.Hash() { + t.Errorf("last header hash mismatch: have: %x, want %x", ncm.CurrentHeader().Hash(), genesis.Hash()) } } } -func TestInsertNonceError(t *testing.T) { +// Tests chain insertions in the face of one entity containing an invalid nonce. +func TestHeadersInsertNonceError(t *testing.T) { testInsertNonceError(t, false) } +func TestBlocksInsertNonceError(t *testing.T) { testInsertNonceError(t, true) } + +func testInsertNonceError(t *testing.T, full bool) { for i := 1; i < 25 && !t.Failed(); i++ { - db, _ := ethdb.NewMemDatabase() - genesis, err := WriteTestNetGenesisBlock(db, 0) + // Create a pristine chain and database + db, processor, err := newCanonical(0, full) if err != nil { - t.Error(err) - t.FailNow() + t.Fatalf("failed to create pristine chain: %v", err) } - bc := chm(genesis, db) - bc.processor = NewBlockProcessor(db, bc.pow, bc, bc.eventMux) - blocks := makeChain(bc.currentBlock, i, db, 0) + bc := processor.bc + + // Create and insert a chain with a failing nonce + var ( + failAt int + failRes int + failNum uint64 + failHash common.Hash + ) + if full { + blocks := makeBlockChain(processor.bc.CurrentBlock(), i, db, 0) + + failAt = rand.Int() % len(blocks) + failNum = blocks[failAt].NumberU64() + failHash = blocks[failAt].Hash() + + processor.bc.pow = failPow{failNum} + failRes, err = processor.bc.InsertChain(blocks) + } else { + headers := makeHeaderChain(processor.bc.CurrentHeader(), i, db, 0) - fail := rand.Int() % len(blocks) - failblock := blocks[fail] - bc.pow = failPow{failblock.NumberU64()} - n, err := bc.InsertChain(blocks) + failAt = rand.Int() % len(headers) + failNum = headers[failAt].Number.Uint64() + failHash = headers[failAt].Hash() + processor.bc.pow = failPow{failNum} + failRes, err = processor.bc.InsertHeaderChain(headers, true) + } // Check that the returned error indicates the nonce failure. - if n != fail { - t.Errorf("(i=%d) wrong failed block index: got %d, want %d", i, n, fail) + if failRes != failAt { + t.Errorf("test %d: failure index mismatch: have %d, want %d", i, failRes, failAt) } if !IsBlockNonceErr(err) { - t.Fatalf("(i=%d) got %q, want a nonce error", i, err) + t.Fatalf("test %d: error mismatch: have %v, want nonce error", i, err) } nerr := err.(*BlockNonceErr) - if nerr.Number.Cmp(failblock.Number()) != 0 { - t.Errorf("(i=%d) wrong block number in error, got %v, want %v", i, nerr.Number, failblock.Number()) + if nerr.Number.Uint64() != failNum { + t.Errorf("test %d: number mismatch: have %v, want %v", i, nerr.Number, failNum) } - if nerr.Hash != failblock.Hash() { - t.Errorf("(i=%d) wrong block hash in error, got %v, want %v", i, nerr.Hash, failblock.Hash()) + if nerr.Hash != failHash { + t.Errorf("test %d: hash mismatch: have %x, want %x", i, nerr.Hash[:4], failHash[:4]) } - // Check that all no blocks after the failing block have been inserted. - for _, block := range blocks[fail:] { - if bc.HasBlock(block.Hash()) { - t.Errorf("(i=%d) invalid block %d present in chain", i, block.NumberU64()) + for j := 0; j < i-failAt; j++ { + if full { + if block := bc.GetBlockByNumber(failNum + uint64(j)); block != nil { + t.Errorf("test %d: invalid block in chain: %v", i, block) + } + } else { + if header := bc.GetHeaderByNumber(failNum + uint64(j)); header != nil { + t.Errorf("test %d: invalid header in chain: %v", i, header) + } } } } -- cgit