all: protect self-mined block during reorg (#17656)
This commit is contained in:
committed by
Péter Szilágyi
parent
f89dce0126
commit
d6254f827b
@ -31,6 +31,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common/mclock"
|
||||
"github.com/ethereum/go-ethereum/common/prque"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/consensus/clique"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
@ -128,13 +129,14 @@ type BlockChain struct {
|
||||
validator Validator // block and state validator interface
|
||||
vmConfig vm.Config
|
||||
|
||||
badBlocks *lru.Cache // Bad block cache
|
||||
badBlocks *lru.Cache // Bad block cache
|
||||
isLocalFn func(common.Address) bool // Function used to determine whether the block author is a local miner account.
|
||||
}
|
||||
|
||||
// NewBlockChain returns a fully initialised block chain using information
|
||||
// available in the database. It initialises the default Ethereum Validator and
|
||||
// Processor.
|
||||
func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config) (*BlockChain, error) {
|
||||
func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config, isLocalFn func(common.Address) bool) (*BlockChain, error) {
|
||||
if cacheConfig == nil {
|
||||
cacheConfig = &CacheConfig{
|
||||
TrieNodeLimit: 256 * 1024 * 1024,
|
||||
@ -154,6 +156,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
|
||||
triegc: prque.New(nil),
|
||||
stateCache: state.NewDatabase(db),
|
||||
quit: make(chan struct{}),
|
||||
isLocalFn: isLocalFn,
|
||||
bodyCache: bodyCache,
|
||||
bodyRLPCache: bodyRLPCache,
|
||||
blockCache: blockCache,
|
||||
@ -967,8 +970,45 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
|
||||
reorg := externTd.Cmp(localTd) > 0
|
||||
currentBlock = bc.CurrentBlock()
|
||||
if !reorg && externTd.Cmp(localTd) == 0 {
|
||||
// Split same-difficulty blocks by number, then at random
|
||||
reorg = block.NumberU64() < currentBlock.NumberU64() || (block.NumberU64() == currentBlock.NumberU64() && mrand.Float64() < 0.5)
|
||||
// Split same-difficulty blocks by number, then preferentially select
|
||||
// the block generated by the local miner as the canonical block.
|
||||
if block.NumberU64() < currentBlock.NumberU64() {
|
||||
reorg = true
|
||||
} else if block.NumberU64() == currentBlock.NumberU64() {
|
||||
if _, ok := bc.engine.(*clique.Clique); ok {
|
||||
// The reason we need to disable the self-reorg preserving for clique
|
||||
// is it can be probable to introduce a deadlock.
|
||||
//
|
||||
// e.g. If there are 7 available signers
|
||||
//
|
||||
// r1 A
|
||||
// r2 B
|
||||
// r3 C
|
||||
// r4 D
|
||||
// r5 A [X] F G
|
||||
// r6 [X]
|
||||
//
|
||||
// In the round5, the inturn signer E is offline, so the worst case
|
||||
// is A, F and G sign the block of round5 and reject the block of opponents
|
||||
// and in the round6, the last available signer B is offline, the whole
|
||||
// network is stuck.
|
||||
reorg = mrand.Float64() < 0.5
|
||||
} else {
|
||||
currentAuthor, err := bc.engine.Author(currentBlock.Header())
|
||||
if err != nil {
|
||||
return NonStatTy, err
|
||||
}
|
||||
blockAuthor, err := bc.engine.Author(block.Header())
|
||||
if err != nil {
|
||||
return NonStatTy, err
|
||||
}
|
||||
var currentLocal, blockLocal bool
|
||||
if bc.isLocalFn != nil {
|
||||
currentLocal, blockLocal = bc.isLocalFn(currentAuthor), bc.isLocalFn(blockAuthor)
|
||||
}
|
||||
reorg = !currentLocal && (blockLocal || mrand.Float64() < 0.5)
|
||||
}
|
||||
}
|
||||
}
|
||||
if reorg {
|
||||
// Reorganise the chain if the parent is not the head block
|
||||
|
Reference in New Issue
Block a user