core: insert knowns blocks

This commit is contained in:
rjl493456442 2019-04-10 16:40:13 +08:00
parent fa569d5b2b
commit 76cdb8afa7
2 changed files with 137 additions and 134 deletions

View file

@ -883,10 +883,10 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
var lastWrite uint64
// WriteBlockWithoutState writes only the block and its metadata to the database,
// writeBlockWithoutState writes only the block and its metadata to the database,
// but does not write any state. This is used to construct competing side forks
// up to the point where they exceed the canonical total difficulty.
func (bc *BlockChain) WriteBlockWithoutState(block *types.Block, td *big.Int) (err error) {
func (bc *BlockChain) writeBlockWithoutState(block *types.Block, td *big.Int) (err error) {
bc.wg.Add(1)
defer bc.wg.Done()
@ -898,6 +898,26 @@ func (bc *BlockChain) WriteBlockWithoutState(block *types.Block, td *big.Int) (e
return nil
}
// writeKnownBlock updates the head block flag with a known block
// and introduces chain reorg if necessary.
func (bc *BlockChain) writeKnownBlock(block *types.Block) error {
bc.wg.Add(1)
defer bc.wg.Done()
current := bc.CurrentBlock()
if block.ParentHash() != current.Hash() {
if err := bc.reorg(current, block); err != nil {
return err
}
}
// Write the positional metadata for transaction/receipt lookups.
// Preimages here is empty, ignore it.
rawdb.WriteTxLookupEntries(bc.db, block)
bc.insert(block)
return nil
}
// WriteBlockWithState writes the block and all associated state to the database.
func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.Receipt, state *state.StateDB) (status WriteStatus, err error) {
bc.chainmu.Lock()
@ -1139,19 +1159,36 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
// 2. The block is stored as a sidechain, and is lying about it's stateroot, and passes a stateroot
// from the canonical chain, which has not been verified.
// Skip all known blocks that are behind us
current := bc.CurrentBlock().NumberU64()
for block != nil && err == ErrKnownBlock && current >= block.NumberU64() {
var (
current = bc.CurrentBlock()
localTd = bc.GetTd(current.Hash(), current.NumberU64())
externTd = bc.GetTd(block.ParentHash(), block.NumberU64()-1) // The first block can't be nil
)
externTd = new(big.Int).Add(externTd, block.Difficulty())
for block != nil && err == ErrKnownBlock && localTd.Cmp(externTd) >= 0 {
stats.ignored++
block, err = it.next()
}
// First block is still known block, the only scenario here is:
// We did a roll-back, and we want to re-import a batch of known blocks while a part
// of known blocks are higher than current head block.
if err == ErrKnownBlock {
block, err = bc.insertKnownChain(block, it)
if bc.CurrentBlock().NumberU64() != current {
lastCanon = bc.CurrentBlock()
if block != nil {
externTd = new(big.Int).Add(externTd, block.Difficulty())
}
}
// The remaining blocks are still known blocks, the only scenario here is:
// During the fast sync, the pivot point is already submitted but rollback
// happens. Then node resets the head full block to a lower height via `rollback`
// and leaves a few known blocks in the database.
//
// When node runs a fast sync again, it can re-import a batch of known blocks via
// `insertChain` while a part of them have higher total difficulty than current
// head full block(new pivot point).
if err == ErrKnownBlock {
for block != nil && err == ErrKnownBlock {
if err := bc.writeKnownBlock(block); err != nil {
return it.index, nil, nil, err
}
lastCanon = block
block, err = it.next()
}
}
// Falls through to the block import
@ -1323,37 +1360,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
return it.index, events, coalescedLogs, err
}
// insertKnownChain inserts a batch of known blocks which are higher than current
// head block.
func (bc *BlockChain) insertKnownChain(block *types.Block, it *insertIterator) (*types.Block, error) {
var (
externTd *big.Int
knownBlocks []*types.Block
err = ErrKnownBlock
current = bc.CurrentBlock()
localTd = bc.GetTd(current.Hash(), current.NumberU64())
)
for ; block != nil && (err == ErrKnownBlock); block, err = it.next() {
if externTd == nil {
externTd = bc.GetTd(block.ParentHash(), block.NumberU64()-1)
}
externTd = new(big.Int).Add(externTd, block.Difficulty())
// Short circuit if the known block cannot be imported as a new
// canonical block.
if block.ParentHash() != current.Hash() || externTd.Cmp(localTd) <= 0 {
break
}
knownBlocks = append(knownBlocks, block)
localTd, current = new(big.Int).Add(localTd, block.Difficulty()), block
}
if len(knownBlocks) > 0 {
bc.insert(knownBlocks[len(knownBlocks)-1])
}
return block, err
}
// insertSideChain is called when an import batch hits upon a pruned ancestor
// error, which happens when a sidechain with a sufficiently old fork-block is
// found.
@ -1401,7 +1407,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
if !bc.HasBlock(block.Hash(), block.NumberU64()) {
start := time.Now()
if err := bc.WriteBlockWithoutState(block, externTd); err != nil {
if err := bc.writeBlockWithoutState(block, externTd); err != nil {
return it.index, nil, nil, err
}
log.Debug("Injected sidechain block", "number", block.Number(), "hash", block.Hash(),

View file

@ -1702,116 +1702,113 @@ func TestPrunedImportSide(t *testing.T) {
}
func TestInsertKnownHeaders(t *testing.T) { testInsertKnownChainData(t, "headers") }
func TestInsertKnownReceiptChain(t *testing.T) { testInsertKnownChainData(t, "receiptChain") }
func TestInsertKnownReceiptChain(t *testing.T) { testInsertKnownChainData(t, "receipts") }
func TestInsertKnownBlocks(t *testing.T) { testInsertKnownChainData(t, "blocks") }
func testInsertKnownChainData(t *testing.T, typ string) {
// Generate the original common chain segment and the two competing forks
engine := ethash.NewFaker()
db := rawdb.NewMemoryDatabase()
genesis := new(Genesis).MustCommit(db)
blocks, receipts := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
blocks2, receipts2 := GenerateChain(params.TestChainConfig, blocks[len(blocks)-1], engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
blocks, receipts := GenerateChain(params.TestChainConfig, genesis, engine, db, 32, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
blocks2, receipts2 := GenerateChain(params.TestChainConfig, blocks[len(blocks)-1], engine, db, 32, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
blocks3, receipts3 := GenerateChain(params.TestChainConfig, blocks[len(blocks)-1], engine, db, 33, func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{1})
b.OffsetTime(-9) // A higher difficulty
})
// Import the shared chain and the original canonical one
diskdb := rawdb.NewMemoryDatabase()
new(Genesis).MustCommit(diskdb)
chaindb := rawdb.NewMemoryDatabase()
new(Genesis).MustCommit(chaindb)
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
chain, err := NewBlockChain(chaindb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
if err != nil {
t.Fatalf("failed to create tester chain: %v", err)
}
var (
inserter func(blocks []*types.Block, receipts []types.Receipts) error
asserter func(t *testing.T, block *types.Block)
)
headers, headers2 := make([]*types.Header, 0, len(blocks)), make([]*types.Header, 0, len(blocks2))
for _, block := range blocks {
headers = append(headers, block.Header())
}
for _, block := range blocks2 {
headers2 = append(headers2, block.Header())
}
if typ == "headers" {
inserter = func(blocks []*types.Block, receipts []types.Receipts) error {
headers := make([]*types.Header, 0, len(blocks))
for _, block := range blocks {
headers = append(headers, block.Header())
}
_, err := chain.InsertHeaderChain(headers, 1)
return err
}
asserter = func(t *testing.T, block *types.Block) {
if chain.CurrentHeader().Hash() != block.Hash() {
t.Fatalf("current head header mismatch, have %v, want %v", chain.CurrentHeader().Hash().Hex(), block.Hash().Hex())
}
}
} else if typ == "receipts" {
inserter = func(blocks []*types.Block, receipts []types.Receipts) error {
headers := make([]*types.Header, 0, len(blocks))
for _, block := range blocks {
headers = append(headers, block.Header())
}
_, err := chain.InsertHeaderChain(headers, 1)
if err != nil {
return err
}
_, err = chain.InsertReceiptChain(blocks, receipts)
return err
}
asserter = func(t *testing.T, block *types.Block) {
if chain.CurrentFastBlock().Hash() != block.Hash() {
t.Fatalf("current head fast block mismatch, have %v, want %v", chain.CurrentFastBlock().Hash().Hex(), block.Hash().Hex())
}
}
} else {
inserter = func(blocks []*types.Block, receipts []types.Receipts) error {
_, err := chain.InsertChain(blocks)
return err
}
asserter = func(t *testing.T, block *types.Block) {
if chain.CurrentBlock().Hash() != block.Hash() {
t.Fatalf("current head block mismatch, have %v, want %v", chain.CurrentBlock().Hash().Hex(), block.Hash().Hex())
}
}
}
if err := inserter(blocks, receipts); err != nil {
t.Fatalf("failed to insert chain data: %v", err)
}
// Reimport the chain data again. All the imported
// chain data are regarded "known" data.
if err := inserter(blocks, receipts); err != nil {
t.Fatalf("failed to insert chain data: %v", err)
}
asserter(t, blocks[len(blocks)-1])
// Import a long canonical chain with some known data as prefix.
var rollback []common.Hash
for i := len(blocks) / 2; i < len(blocks); i++ {
rollback = append(rollback, blocks[i].Hash())
}
if typ == "blocks" {
if _, err := chain.InsertChain(blocks); err != nil {
t.Fatalf("failed to insert chain: %v", err)
}
// The imported blocks are all known blocks and not higher than
// head block.
if _, err := chain.InsertChain(blocks); err != nil {
t.Fatalf("failed to insert known chain: %v", err)
}
// A part of imported blocks are known blocks. Besides a part of
// known blocks are higher than head blocks.
chain.Rollback(rollback)
if _, err := chain.InsertChain(append(blocks, blocks2...)); err != nil {
t.Fatalf("failed to insert chain with known block as prefix: %v", err)
}
if chain.CurrentBlock().Hash() != blocks2[len(blocks2)-1].Hash() {
t.Fatalf("failed to insert chain with known block as prefix, want head block %v, have head block %v",
blocks2[len(blocks2)-1].Hash().Hex(), chain.CurrentBlock().Hash().Hex())
}
} else if typ == "headers" {
headers, headers2 := make([]*types.Header, 0, len(blocks)), make([]*types.Header, 0, len(blocks2))
for _, block := range blocks {
headers = append(headers, block.Header())
}
for _, block := range blocks2 {
headers2 = append(headers2, block.Header())
}
if _, err := chain.InsertHeaderChain(headers, 1); err != nil {
t.Fatalf("failed to insert header chain: %v", err)
}
// The imported headers are all known headers and not higher than
// head block.
if _, err := chain.InsertHeaderChain(headers, 1); err != nil {
t.Fatalf("failed to insert known header chain: %v", err)
}
// A part of imported headers are known headers. Besides a part of
// known headers are higher than head headers.
chain.Rollback(rollback)
if _, err := chain.InsertHeaderChain(append(headers, headers2...), 1); err != nil {
t.Fatalf("failed to insert header chain with known headers as prefix: %v", err)
}
if chain.CurrentHeader().Hash() != headers2[len(headers2)-1].Hash() {
t.Fatalf("failed to insert header chain with known header as prefix, want head header %v, have head header %v",
headers2[len(headers2)-1].Hash().Hex(), chain.CurrentBlock().Hash().Hex())
}
} else {
headers, headers2 := make([]*types.Header, 0, len(blocks)), make([]*types.Header, 0, len(blocks2))
for _, block := range blocks {
headers = append(headers, block.Header())
}
for _, block := range blocks2 {
headers2 = append(headers2, block.Header())
}
if _, err := chain.InsertHeaderChain(headers, 1); err != nil {
t.Fatalf("failed to insert header chain: %v", err)
}
if _, err := chain.InsertReceiptChain(blocks, receipts); err != nil {
t.Fatalf("failed to insert receipt chain: %v", err)
}
// The imported receipt chain are all known and not higher than
// head fast block.
if _, err := chain.InsertReceiptChain(blocks, receipts); err != nil {
t.Fatalf("failed to insert receipt chain: %v", err)
}
// A part of imported headers are known headers. Besides a part of
// known headers are higher than head headers.
chain.Rollback(rollback)
if _, err := chain.InsertHeaderChain(append(headers, headers2...), 1); err != nil {
t.Fatalf("failed to insert header chain with known headers as prefix: %v", err)
}
if _, err := chain.InsertReceiptChain(append(blocks, blocks2...), append(receipts, receipts2...)); err != nil {
t.Fatalf("failed to insert header chain with known headers as prefix: %v", err)
}
if chain.CurrentFastBlock().Hash() != blocks2[len(blocks2)-1].Hash() {
t.Fatalf("failed to insert header chain with known header as prefix, want head header %v, have head header %v",
blocks2[len(blocks2)-1].Hash().Hex(), chain.CurrentFastBlock().Hash().Hex())
}
chain.Rollback(rollback)
if err := inserter(append(blocks, blocks2...), append(receipts, receipts2...)); err != nil {
t.Fatalf("failed to insert chain data: %v", err)
}
asserter(t, blocks2[len(blocks2)-1])
// Import a heavier forked chain with some known data as prefix.
if err := inserter(append(blocks, blocks3...), append(receipts, receipts3...)); err != nil {
t.Fatalf("failed to insert chain data: %v", err)
}
asserter(t, blocks3[len(blocks3)-1])
}
// Benchmarks large blocks with value transfers to non-existing accounts