Skip to content

Commit

Permalink
处理区块分叉流程下VerifyHeader的逻辑
Browse files Browse the repository at this point in the history
  • Loading branch information
niuxiaojie81 committed Feb 4, 2024
1 parent dd962b1 commit 98964ae
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 12 deletions.
37 changes: 30 additions & 7 deletions consensus/cbft/cbft.go
Original file line number Diff line number Diff line change
Expand Up @@ -648,10 +648,13 @@ func (cbft *Cbft) VerifyHeader(chain consensus.ChainReader, header *types.Header
}

parent := chain.GetHeader(header.ParentHash, number-1)
// 当 parentBlock 出现分叉,且 cbft 还未同步该分叉区块(只有旧的分叉块高的区块)
// 那么此时 cbft 无法查询到该分叉区块,在这种正常流程下为了避免出现 unknown ancestor。增加了 forked ancestor
forked := false
if parent == nil {
var parentBlock *types.Block
if async {
parentBlock = cbft.GetBlockWithLock(header.ParentHash, number-1)
parentBlock, forked = cbft.GetBlockWithLock(header.ParentHash, number-1)
} else {
parentBlock = cbft.GetBlockWithoutLock(header.ParentHash, number-1)
}
Expand All @@ -663,6 +666,10 @@ func (cbft *Cbft) VerifyHeader(chain consensus.ChainReader, header *types.Header
// Find it again from the blockChain
p := chain.GetHeader(header.ParentHash, number-1)
if p == nil {
if forked {
cbft.log.Warn("VerifyHeader, forked ancestor", "blockNumber", number, "blockHash", header.Hash(), "parentHash", header.ParentHash)
return consensus.ErrForkedAncestor
}
cbft.log.Warn("VerifyHeader, unknown ancestor", "blockNumber", number, "blockHash", header.Hash(), "parentHash", header.ParentHash)
return consensus.ErrUnknownAncestor
}
Expand Down Expand Up @@ -743,11 +750,16 @@ func (cbft *Cbft) VerifyHeaders(chain consensus.ChainReader, headers []*types.He
}

func (cbft *Cbft) verifyHeaderWorker(chain consensus.ChainReader, headers []*types.Header, index int) error {
var parent *types.Header
var (
parent *types.Header
parentBlock *types.Block
forked = false
)

if index == 0 {
parent = chain.GetHeader(headers[0].ParentHash, headers[0].Number.Uint64()-1)
if parent == nil {
parentBlock := cbft.GetBlockWithLock(headers[0].ParentHash, headers[0].Number.Uint64()-1)
parentBlock, forked = cbft.GetBlockWithLock(headers[0].ParentHash, headers[0].Number.Uint64()-1)
if parentBlock != nil {
parent = parentBlock.Header()
}
Expand All @@ -761,6 +773,10 @@ func (cbft *Cbft) verifyHeaderWorker(chain consensus.ChainReader, headers []*typ
}

if parent == nil {
if forked {
cbft.log.Warn("VerifyHeaderWorker, forked ancestor", "blockNumber", headers[index].Number.Uint64(), "blockHash", headers[index].Hash(), "parentHash", headers[index].ParentHash)
return consensus.ErrForkedAncestor
}
cbft.log.Warn("VerifyHeaderWorker, unknown ancestor", "blockNumber", headers[index].Number.Uint64(), "blockHash", headers[index].Hash(), "parentHash", headers[index].ParentHash)
return consensus.ErrUnknownAncestor
}
Expand Down Expand Up @@ -1336,22 +1352,29 @@ func (cbft *Cbft) GetBlock(hash common.Hash, number uint64) *types.Block {
}

// GetBlockWithLock synchronously obtains blocks according to the specified number and hash.
func (cbft *Cbft) GetBlockWithLock(hash common.Hash, number uint64) *types.Block {
result := make(chan *types.Block, 1)
func (cbft *Cbft) GetBlockWithLock(hash common.Hash, number uint64) (*types.Block, bool) {
type result struct {
block *types.Block
forked bool
}
resultCh := make(chan result, 1)

cbft.asyncCallCh <- func() {
block, _ := cbft.blockTree.FindBlockAndQC(hash, number)
var forked bool
if block == nil {
if eb := cbft.state.FindBlock(hash, number); eb != nil {
block = eb
} else {
cbft.log.Debug("Get block failed", "hash", hash, "number", number)
_, _, forked = cbft.blockTree.IsForked(hash, number)
}
}
result <- block
resultCh <- result{block: block, forked: forked}
}

return <-result
ret := <-resultCh
return ret.block, ret.forked
}

// GetBlockWithoutLock returns the block corresponding to the specified number and hash.
Expand Down
4 changes: 4 additions & 0 deletions consensus/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ var (
// that is unknown.
ErrUnknownAncestor = errors.New("unknown ancestor")

// ErrForkedAncestor is returned when validating a block requires an ancestor
// that is forked.
ErrForkedAncestor = errors.New("forked ancestor")

// ErrPrunedAncestor is returned when validating a block requires an ancestor
// that is known, but the state of which is not available.
ErrPrunedAncestor = errors.New("pruned ancestor")
Expand Down
11 changes: 8 additions & 3 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import (
"sync/atomic"
"time"

lru "github.com/hashicorp/golang-lru"

"github.com/PlatONnetwork/PlatON-Go/common"
"github.com/PlatONnetwork/PlatON-Go/common/mclock"
"github.com/PlatONnetwork/PlatON-Go/common/prque"
Expand All @@ -43,7 +45,6 @@ import (
"github.com/PlatONnetwork/PlatON-Go/metrics"
"github.com/PlatONnetwork/PlatON-Go/params"
"github.com/PlatONnetwork/PlatON-Go/trie"
lru "github.com/hashicorp/golang-lru"
)

var (
Expand Down Expand Up @@ -1563,19 +1564,23 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
// Some other error(except ErrKnownBlock) occurred, abort.
// ErrKnownBlock is allowed here since some known blocks
// still need re-execution to generate snapshots that are missing
case err != nil && !errors.Is(err, ErrKnownBlock):
case err != nil && !errors.Is(err, ErrKnownBlock) && !errors.Is(err, consensus.ErrForkedAncestor):
bc.futureBlocks.Remove(block.Hash())
stats.ignored += len(it.chain)
bc.reportBlock(block, nil, err)
return it.index, err
}
// No validation errors for the first block (or chain prefix skipped)
for ; block != nil && (err == nil || errors.Is(err, ErrKnownBlock)); block, err = it.next() {
for ; block != nil && (err == nil || errors.Is(err, ErrKnownBlock) || errors.Is(err, consensus.ErrForkedAncestor)); block, err = it.next() {
// If the chain is terminating, stop processing blocks
if bc.insertStopped() {
log.Debug("Abort during block processing")
break
}
if errors.Is(err, consensus.ErrForkedAncestor) {
return it.index, nil
}

start := time.Now()
err = bc.engine.InsertChain(block)
if err != nil {
Expand Down
10 changes: 8 additions & 2 deletions eth/fetcher/block_fetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -717,19 +717,25 @@ func (f *BlockFetcher) insert(peer string, block *types.Block) {
case consensus.ErrFutureBlock:
// Weird future block, don't fail, but neither propagate

case consensus.ErrForkedAncestor:
log.Warn("Forked parent of propagated block", "peer", peer, "number", block.Number(), "hash", hash, "parent", block.ParentHash())
return

case consensus.ErrUnknownAncestor:
// If the parent's unknown, abort insertion
log.Warn("Unknown parent of propagated block", "peer", peer, "number", block.Number(), "hash", hash, "parent", block.ParentHash())
f.dropPeer(peer)
return

default:
// Something went very wrong, drop the peer
log.Debug("Propagated block verification failed", "peer", peer, "number", block.Number(), "hash", hash, "err", err)
log.Warn("Propagated block verification failed", "peer", peer, "number", block.Number(), "hash", hash, "err", err)
f.dropPeer(peer)
return
}
// Run the actual import and log any issues
if _, err := f.insertChain(types.Blocks{block}); err != nil {
log.Debug("Propagated block import failed", "peer", peer, "number", block.Number(), "hash", hash, "err", err)
log.Warn("Propagated block import failed", "peer", peer, "number", block.Number(), "hash", hash, "err", err)
return
}
// If import succeeded, broadcast the block
Expand Down

0 comments on commit 98964ae

Please sign in to comment.