core/blockchain: rework import of pruned canon blocks and canon-prepended sidechains

This commit is contained in:
Martin Holst Swende 2019-02-21 10:11:49 +01:00
parent 92420baba8
commit 2d64d3389f
No known key found for this signature in database
GPG key ID: 683B438C05A5DDF0
2 changed files with 28 additions and 24 deletions

View file

@ -1093,16 +1093,10 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
}
// Remove already known canon-blocks
var (
start = 0
block, prev *types.Block
)
for start, block = range chain {
if !bc.IsCanon(block.Hash(), block.NumberU64()) {
break
}
}
// Do a sanity check that the provided chain is actually ordered and linked
for i := start + 1; i < len(chain); i++ {
for i := 1; i < len(chain); i++ {
block = chain[i]
prev = chain[i-1]
if block.NumberU64() != prev.NumberU64()+1 || block.ParentHash() != prev.Hash() {
@ -1117,7 +1111,7 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
// Pre-checks passed, start the full block imports
bc.wg.Add(1)
bc.chainmu.Lock()
n, events, logs, err := bc.insertChain(chain[start:], true)
n, events, logs, err := bc.insertChain(chain, true)
bc.chainmu.Unlock()
bc.wg.Done()
@ -1165,6 +1159,22 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
it := newInsertIterator(chain, results, bc.Validator())
block, err := it.next()
// Left-trim all the known blocks
if err == ErrKnownBlock {
// First block (and state) is known
// 1. We did a roll-back, and should now do a re-import
// 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() {
stats.ignored++
block, err = it.next()
}
// Falls through to the block import
}
switch {
// First block is pruned, insert as sidechain and reorg only if TD grows enough
case err == consensus.ErrPrunedAncestor:
@ -1184,20 +1194,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
// If there are any still remaining, mark as ignored
return it.index, events, coalescedLogs, err
// First block (and state) is known
// 1. We did a roll-back, and should now do a re-import
// 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.
case err == ErrKnownBlock:
// Skip all known blocks that behind us
current := bc.CurrentBlock().NumberU64()
for block != nil && err == ErrKnownBlock && current >= block.NumberU64() {
stats.ignored++
block, err = it.next()
}
// Falls through to the block import
// Some other error occurred, abort
case err != nil:
stats.ignored += len(it.chain)
@ -1323,6 +1319,10 @@ func (bc *BlockChain) insertSidechain(block *types.Block, it *insertIterator) (i
for ; block != nil && (err == consensus.ErrPrunedAncestor); block, err = it.next() {
// Check the canonical state root for that number
if number := block.NumberU64(); current.NumberU64() >= number {
if canonical := bc.GetBlockByNumber(number); canonical != nil && canonical.Hash() == block.Hash() {
// Not a sidechain block, this is a re-import of a canon block which has it's state pruned
continue
}
if canonical := bc.GetBlockByNumber(number); canonical != nil && canonical.Root() == block.Root() {
// This is most likely a shadow-state attack. When a fork is imported into the
// database, and it eventually reaches a block height which is not pruned, we

View file

@ -1580,14 +1580,15 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
// canon(pruned), side, side...
// Generate fork chain, make it longer than canon
parent := blocks[lastPrunedIndex+blocksBetweenCommonAncestorAndPruneblock]
parentIndex := lastPrunedIndex + blocksBetweenCommonAncestorAndPruneblock
parent := blocks[parentIndex]
fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 2*triesInMemory, func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{2})
})
// Prepend the parent(s)
var sidechain []*types.Block
for i := numCanonBlocksInSidechain; i > 0; i-- {
sidechain = append(sidechain, blocks[len(blocks)-i])
sidechain = append(sidechain, blocks[parentIndex+1-i])
}
sidechain = append(sidechain, fork...)
_, err = chain.InsertChain(sidechain)
@ -1610,6 +1611,9 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
// [ Cn, Cn+1, Cc, Sn+3 ... Sm]
// ^ ^ ^ pruned
func TestPrunedImportSide(t *testing.T) {
//glogger := log.NewGlogHandler(log.StreamHandler(os.Stdout, log.TerminalFormat(false)))
//glogger.Verbosity(3)
//log.Root().SetHandler(log.Handler(glogger))
testSideImport(t, 3, 3)
testSideImport(t, 3, -3)
testSideImport(t, 10, 0)