diff --git a/core/blockchain.go b/core/blockchain.go index 2f12ca62b..fe961e0c4 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -31,7 +31,6 @@ 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" @@ -129,14 +128,14 @@ type BlockChain struct { validator Validator // block and state validator interface vmConfig vm.Config - badBlocks *lru.Cache // Bad block cache - isLocalFn func(common.Address) bool // Function used to determine whether the block author is a local miner account. + badBlocks *lru.Cache // Bad block cache + shouldPreserve func(*types.Block) bool // Function used to determine whether should preserve the given block. } // 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, isLocalFn func(common.Address) bool) (*BlockChain, error) { +func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config, shouldPreserve func(block *types.Block) bool) (*BlockChain, error) { if cacheConfig == nil { cacheConfig = &CacheConfig{ TrieNodeLimit: 256 * 1024 * 1024, @@ -150,20 +149,20 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par badBlocks, _ := lru.New(badBlockLimit) bc := &BlockChain{ - chainConfig: chainConfig, - cacheConfig: cacheConfig, - db: db, - triegc: prque.New(nil), - stateCache: state.NewDatabase(db), - quit: make(chan struct{}), - isLocalFn: isLocalFn, - bodyCache: bodyCache, - bodyRLPCache: bodyRLPCache, - blockCache: blockCache, - futureBlocks: futureBlocks, - engine: engine, - vmConfig: vmConfig, - badBlocks: badBlocks, + chainConfig: chainConfig, + cacheConfig: cacheConfig, + db: db, + triegc: prque.New(nil), + stateCache: state.NewDatabase(db), + quit: make(chan struct{}), + shouldPreserve: shouldPreserve, + bodyCache: bodyCache, + bodyRLPCache: bodyRLPCache, + blockCache: blockCache, + futureBlocks: futureBlocks, + engine: engine, + vmConfig: vmConfig, + badBlocks: badBlocks, } bc.SetValidator(NewBlockValidator(chainConfig, bc, engine)) bc.SetProcessor(NewStateProcessor(chainConfig, bc, engine)) @@ -975,39 +974,11 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types. 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) + var currentPreserve, blockPreserve bool + if bc.shouldPreserve != nil { + currentPreserve, blockPreserve = bc.shouldPreserve(currentBlock), bc.shouldPreserve(block) } + reorg = !currentPreserve && (blockPreserve || mrand.Float64() < 0.5) } } if reorg { diff --git a/eth/backend.go b/eth/backend.go index 90d185ed4..ca0b13eed 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -156,7 +156,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { } cacheConfig = &core.CacheConfig{Disabled: config.NoPruning, TrieNodeLimit: config.TrieCache, TrieTimeLimit: config.TrieTimeout} ) - eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, eth.chainConfig, eth.engine, vmConfig, eth.isMinerAccount) + eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, eth.chainConfig, eth.engine, vmConfig, eth.shouldPreserve) if err != nil { return nil, err } @@ -334,30 +334,60 @@ func (s *Ethereum) Etherbase() (eb common.Address, err error) { return common.Address{}, fmt.Errorf("etherbase must be explicitly specified") } -// isMinerAccount checks whether the specified address is a miner account. +// isLocalBlock checks whether the specified block is mined +// by local miner accounts. // -// This function is used during block chain reorg checking to determine -// whether a block is mined by local accounts. We regard two types of -// accounts as local account: etherbase and accounts specified via -// `txpool.locals` flag. -func (s *Ethereum) isMinerAccount(addr common.Address) bool { +// We regard two types of accounts as local miner account: etherbase +// and accounts specified via `txpool.locals` flag. +func (s *Ethereum) isLocalBlock(block *types.Block) bool { + author, err := s.engine.Author(block.Header()) + if err != nil { + log.Warn("Failed to retrieve block author", "number", block.NumberU64(), "hash", block.Hash(), "err", err) + return false + } // Check whether the given address is etherbase. s.lock.RLock() etherbase := s.etherbase s.lock.RUnlock() - if addr == etherbase { + if author == etherbase { return true } // Check whether the given address is specified by `txpool.local` // CLI flag. for _, account := range s.config.TxPool.Locals { - if account == addr { + if account == author { return true } } return false } +// shouldPreserve checks whether we should preserve the given block +// during the chain reorg depending on whether the author of block +// is a local account. +func (s *Ethereum) shouldPreserve(block *types.Block) bool { + // 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. + if _, ok := s.engine.(*clique.Clique); ok { + return false + } + return s.isLocalBlock(block) +} + // SetEtherbase sets the mining reward address. func (s *Ethereum) SetEtherbase(etherbase common.Address) { s.lock.Lock()