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
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue