mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
core/blockchain: rework import of pruned canon blocks and canon-prepended sidechains
This commit is contained in:
parent
92420baba8
commit
2d64d3389f
2 changed files with 28 additions and 24 deletions
|
|
@ -1093,16 +1093,10 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||||
}
|
}
|
||||||
// Remove already known canon-blocks
|
// Remove already known canon-blocks
|
||||||
var (
|
var (
|
||||||
start = 0
|
|
||||||
block, prev *types.Block
|
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
|
// 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]
|
block = chain[i]
|
||||||
prev = chain[i-1]
|
prev = chain[i-1]
|
||||||
if block.NumberU64() != prev.NumberU64()+1 || block.ParentHash() != prev.Hash() {
|
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
|
// Pre-checks passed, start the full block imports
|
||||||
bc.wg.Add(1)
|
bc.wg.Add(1)
|
||||||
bc.chainmu.Lock()
|
bc.chainmu.Lock()
|
||||||
n, events, logs, err := bc.insertChain(chain[start:], true)
|
n, events, logs, err := bc.insertChain(chain, true)
|
||||||
bc.chainmu.Unlock()
|
bc.chainmu.Unlock()
|
||||||
bc.wg.Done()
|
bc.wg.Done()
|
||||||
|
|
||||||
|
|
@ -1165,6 +1159,22 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
||||||
it := newInsertIterator(chain, results, bc.Validator())
|
it := newInsertIterator(chain, results, bc.Validator())
|
||||||
|
|
||||||
block, err := it.next()
|
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 {
|
switch {
|
||||||
// First block is pruned, insert as sidechain and reorg only if TD grows enough
|
// First block is pruned, insert as sidechain and reorg only if TD grows enough
|
||||||
case err == consensus.ErrPrunedAncestor:
|
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
|
// If there are any still remaining, mark as ignored
|
||||||
return it.index, events, coalescedLogs, err
|
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
|
// Some other error occurred, abort
|
||||||
case err != nil:
|
case err != nil:
|
||||||
stats.ignored += len(it.chain)
|
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() {
|
for ; block != nil && (err == consensus.ErrPrunedAncestor); block, err = it.next() {
|
||||||
// Check the canonical state root for that number
|
// Check the canonical state root for that number
|
||||||
if number := block.NumberU64(); current.NumberU64() >= 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() {
|
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
|
// 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
|
// database, and it eventually reaches a block height which is not pruned, we
|
||||||
|
|
|
||||||
|
|
@ -1580,14 +1580,15 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
|
||||||
// canon(pruned), side, side...
|
// canon(pruned), side, side...
|
||||||
|
|
||||||
// Generate fork chain, make it longer than canon
|
// 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) {
|
fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 2*triesInMemory, func(i int, b *BlockGen) {
|
||||||
b.SetCoinbase(common.Address{2})
|
b.SetCoinbase(common.Address{2})
|
||||||
})
|
})
|
||||||
// Prepend the parent(s)
|
// Prepend the parent(s)
|
||||||
var sidechain []*types.Block
|
var sidechain []*types.Block
|
||||||
for i := numCanonBlocksInSidechain; i > 0; i-- {
|
for i := numCanonBlocksInSidechain; i > 0; i-- {
|
||||||
sidechain = append(sidechain, blocks[len(blocks)-i])
|
sidechain = append(sidechain, blocks[parentIndex+1-i])
|
||||||
}
|
}
|
||||||
sidechain = append(sidechain, fork...)
|
sidechain = append(sidechain, fork...)
|
||||||
_, err = chain.InsertChain(sidechain)
|
_, err = chain.InsertChain(sidechain)
|
||||||
|
|
@ -1610,6 +1611,9 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
|
||||||
// [ Cn, Cn+1, Cc, Sn+3 ... Sm]
|
// [ Cn, Cn+1, Cc, Sn+3 ... Sm]
|
||||||
// ^ ^ ^ pruned
|
// ^ ^ ^ pruned
|
||||||
func TestPrunedImportSide(t *testing.T) {
|
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, 3, -3)
|
testSideImport(t, 3, -3)
|
||||||
testSideImport(t, 10, 0)
|
testSideImport(t, 10, 0)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue