mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-18 18:02:24 +00:00
Merge f10832685c into ae9f97221a
This commit is contained in:
commit
3c519a44f3
6 changed files with 155 additions and 20 deletions
83
common/chainstats/chainstats.go
Normal file
83
common/chainstats/chainstats.go
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
// Copyright 2018 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package chainstats implements some chain utilities for sync-free blockchain info lookup
|
||||
|
||||
package chainstats
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
type Chainstats struct {
|
||||
currentBlockNumber uint64
|
||||
currentFastBlockNumber uint64
|
||||
currentTd atomic.Value
|
||||
}
|
||||
|
||||
func NewChainstats() *Chainstats {
|
||||
stats := &Chainstats{}
|
||||
stats.currentTd.Store(big.NewInt(0))
|
||||
return stats
|
||||
}
|
||||
|
||||
// GetNumber returns the latest block number
|
||||
func (stats *Chainstats) GetNumber() uint64 {
|
||||
return stats.currentBlockNumber
|
||||
}
|
||||
|
||||
// Update is a convenience method to set all values
|
||||
func (stats *Chainstats) Update(currentBlock, currentFastBlock *types.Block, totalDifficulty *big.Int) {
|
||||
stats.SetNumber(currentBlock.NumberU64())
|
||||
stats.SetFastNumber(currentFastBlock.NumberU64())
|
||||
stats.SetTotalDifficulty(totalDifficulty)
|
||||
|
||||
}
|
||||
|
||||
// GetNumbers convenience-method to get all values
|
||||
func (stats *Chainstats) Get() (uint64, uint64, *big.Int) {
|
||||
return stats.currentBlockNumber,
|
||||
stats.currentFastBlockNumber,
|
||||
new(big.Int).Set(stats.currentTd.Load().(*big.Int))
|
||||
}
|
||||
|
||||
// SetNumber stores latest block number
|
||||
func (stats *Chainstats) SetNumber(number uint64) {
|
||||
atomic.StoreUint64(&stats.currentBlockNumber, number)
|
||||
}
|
||||
|
||||
// GetFastNumber return latest fast block number
|
||||
func (stats *Chainstats) GetFastNumber() uint64 {
|
||||
return stats.currentFastBlockNumber
|
||||
}
|
||||
|
||||
// SetFastNumber stores latest fast block number
|
||||
func (stats *Chainstats) SetFastNumber(number uint64) {
|
||||
atomic.StoreUint64(&stats.currentFastBlockNumber, number)
|
||||
}
|
||||
|
||||
// GetTotalDifficulty return latest total difficulty
|
||||
func (stats *Chainstats) GetTotalDifficulty() *big.Int {
|
||||
return new(big.Int).Set(stats.currentTd.Load().(*big.Int))
|
||||
}
|
||||
|
||||
// SetTotalDifficulty sets latest total difficulty
|
||||
func (stats *Chainstats) SetTotalDifficulty(newTd *big.Int) {
|
||||
stats.currentTd.Store(newTd)
|
||||
}
|
||||
|
|
@ -28,6 +28,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/chainstats"
|
||||
"github.com/ethereum/go-ethereum/common/mclock"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
|
|
@ -127,7 +128,8 @@ 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
|
||||
chainStats *chainstats.Chainstats // Mutex-free lookups of chain stats
|
||||
}
|
||||
|
||||
// NewBlockChain returns a fully initialised block chain using information
|
||||
|
|
@ -160,6 +162,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
|
|||
engine: engine,
|
||||
vmConfig: vmConfig,
|
||||
badBlocks: badBlocks,
|
||||
chainStats: chainstats.NewChainstats(),
|
||||
}
|
||||
bc.SetValidator(NewBlockValidator(chainConfig, bc, engine))
|
||||
bc.SetProcessor(NewStateProcessor(chainConfig, bc, engine))
|
||||
|
|
@ -248,6 +251,8 @@ func (bc *BlockChain) loadLastState() error {
|
|||
blockTd := bc.GetTd(bc.currentBlock.Hash(), bc.currentBlock.NumberU64())
|
||||
fastTd := bc.GetTd(bc.currentFastBlock.Hash(), bc.currentFastBlock.NumberU64())
|
||||
|
||||
bc.chainStats.Update(bc.currentBlock, bc.currentFastBlock, blockTd)
|
||||
|
||||
log.Info("Loaded most recent local header", "number", currentHeader.Number, "hash", currentHeader.Hash(), "td", headerTd)
|
||||
log.Info("Loaded most recent local full block", "number", bc.currentBlock.Number(), "hash", bc.currentBlock.Hash(), "td", blockTd)
|
||||
log.Info("Loaded most recent local fast block", "number", bc.currentFastBlock.Number(), "hash", bc.currentFastBlock.Hash(), "td", fastTd)
|
||||
|
|
@ -305,6 +310,9 @@ func (bc *BlockChain) SetHead(head uint64) error {
|
|||
if err := WriteHeadFastBlockHash(bc.db, bc.currentFastBlock.Hash()); err != nil {
|
||||
log.Crit("Failed to reset head fast block", "err", err)
|
||||
}
|
||||
|
||||
bc.chainStats.Update(bc.currentBlock, bc.currentFastBlock, bc.GetTd(bc.currentBlock.Hash(), bc.currentBlock.NumberU64()))
|
||||
|
||||
return bc.loadLastState()
|
||||
}
|
||||
|
||||
|
|
@ -322,6 +330,8 @@ func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error {
|
|||
// If all checks out, manually set the head block
|
||||
bc.mu.Lock()
|
||||
bc.currentBlock = block
|
||||
bc.chainStats.SetNumber(block.Number().Uint64())
|
||||
bc.chainStats.SetTotalDifficulty(bc.GetTd(block.Hash(), block.NumberU64()))
|
||||
bc.mu.Unlock()
|
||||
|
||||
log.Info("Committed new head block", "number", block.Number(), "hash", hash)
|
||||
|
|
@ -421,6 +431,8 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {
|
|||
bc.hc.SetCurrentHeader(bc.genesisBlock.Header())
|
||||
bc.currentFastBlock = bc.genesisBlock
|
||||
|
||||
bc.chainStats.Update(bc.currentBlock, bc.currentFastBlock, bc.genesisBlock.Difficulty())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -499,6 +511,7 @@ func (bc *BlockChain) insert(block *types.Block) {
|
|||
}
|
||||
bc.currentFastBlock = block
|
||||
}
|
||||
bc.chainStats.Update(bc.currentBlock, bc.currentFastBlock, bc.GetTd(block.Hash(), block.NumberU64()))
|
||||
}
|
||||
|
||||
// Genesis retrieves the chain's genesis block.
|
||||
|
|
@ -693,6 +706,24 @@ func (bc *BlockChain) procFutureBlocks() {
|
|||
}
|
||||
}
|
||||
|
||||
// Stats returns the chainstats which can be queried non-blocking for info about difficulty and numbers
|
||||
// These are provided on a best-effort, and it's theoretically possible that two consecutive calls to
|
||||
// number and difficulty return number for X and difficulty for Y, if the stats is updated between the calls
|
||||
func (bc *BlockChain) Stats() *chainstats.Chainstats {
|
||||
return bc.chainStats
|
||||
}
|
||||
func (bc *BlockChain) CurrentNumber() uint64 {
|
||||
return bc.chainStats.GetNumber()
|
||||
}
|
||||
|
||||
func (bc *BlockChain) CurrentFastNumber() uint64 {
|
||||
return bc.chainStats.GetFastNumber()
|
||||
}
|
||||
|
||||
func (bc *BlockChain) CurrentTD() *big.Int {
|
||||
return bc.chainStats.GetTotalDifficulty()
|
||||
}
|
||||
|
||||
// WriteStatus status of write
|
||||
type WriteStatus byte
|
||||
|
||||
|
|
@ -724,6 +755,7 @@ func (bc *BlockChain) Rollback(chain []common.Hash) {
|
|||
WriteHeadBlockHash(bc.db, bc.currentBlock.Hash())
|
||||
}
|
||||
}
|
||||
bc.chainStats.Update(bc.currentBlock, bc.currentFastBlock, bc.GetTd(bc.currentBlock.Hash(), bc.currentBlock.NumberU64()))
|
||||
}
|
||||
|
||||
// SetReceiptsData computes all the non-consensus fields of the receipts
|
||||
|
|
@ -835,6 +867,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
|||
log.Crit("Failed to update head fast block hash", "err", err)
|
||||
}
|
||||
bc.currentFastBlock = head
|
||||
bc.chainStats.SetFastNumber(bc.currentFastBlock.NumberU64())
|
||||
}
|
||||
}
|
||||
bc.mu.Unlock()
|
||||
|
|
|
|||
|
|
@ -193,6 +193,15 @@ type BlockChain interface {
|
|||
|
||||
// InsertReceiptChain inserts a batch of receipts into the local chain.
|
||||
InsertReceiptChain(types.Blocks, []types.Receipts) (int, error)
|
||||
|
||||
// CurrentNumber retrieves number of the current head block
|
||||
CurrentNumber() uint64
|
||||
|
||||
// CurrentNumber retrieves number of the current head fast block
|
||||
CurrentFastNumber() uint64
|
||||
|
||||
// CurrentTD retrives the total difficulty of the current head block
|
||||
CurrentTD() *big.Int
|
||||
}
|
||||
|
||||
// New creates a new downloader to fetch hashes and blocks from remote peers.
|
||||
|
|
@ -583,9 +592,9 @@ func (d *Downloader) findAncestor(p *peerConnection, height uint64) (uint64, err
|
|||
floor, ceil := int64(-1), d.lightchain.CurrentHeader().Number.Uint64()
|
||||
|
||||
if d.mode == FullSync {
|
||||
ceil = d.blockchain.CurrentBlock().NumberU64()
|
||||
ceil = d.blockchain.CurrentNumber()
|
||||
} else if d.mode == FastSync {
|
||||
ceil = d.blockchain.CurrentFastBlock().NumberU64()
|
||||
ceil = d.blockchain.CurrentFastNumber()
|
||||
}
|
||||
if ceil >= MaxForkAncestry {
|
||||
floor = int64(ceil - MaxForkAncestry)
|
||||
|
|
@ -1156,16 +1165,16 @@ func (d *Downloader) processHeaders(origin uint64, pivot uint64, td *big.Int) er
|
|||
for i, header := range rollback {
|
||||
hashes[i] = header.Hash()
|
||||
}
|
||||
lastHeader, lastFastBlock, lastBlock := d.lightchain.CurrentHeader().Number, common.Big0, common.Big0
|
||||
lastHeader, lastFastBlock, lastBlock := d.lightchain.CurrentHeader().Number, uint64(0), uint64(0)
|
||||
if d.mode != LightSync {
|
||||
lastFastBlock = d.blockchain.CurrentFastBlock().Number()
|
||||
lastBlock = d.blockchain.CurrentBlock().Number()
|
||||
lastFastBlock = d.blockchain.CurrentFastNumber()
|
||||
lastBlock = d.blockchain.CurrentNumber()
|
||||
}
|
||||
d.lightchain.Rollback(hashes)
|
||||
curFastBlock, curBlock := common.Big0, common.Big0
|
||||
curFastBlock, curBlock := uint64(0), uint64(0)
|
||||
if d.mode != LightSync {
|
||||
curFastBlock = d.blockchain.CurrentFastBlock().Number()
|
||||
curBlock = d.blockchain.CurrentBlock().Number()
|
||||
curFastBlock = d.blockchain.CurrentFastNumber()
|
||||
curBlock = d.blockchain.CurrentNumber()
|
||||
}
|
||||
log.Warn("Rolled back headers", "count", len(hashes),
|
||||
"header", fmt.Sprintf("%d->%d", lastHeader, d.lightchain.CurrentHeader().Number),
|
||||
|
|
@ -1205,8 +1214,7 @@ func (d *Downloader) processHeaders(origin uint64, pivot uint64, td *big.Int) er
|
|||
// L: Request new headers up from 11 (R's TD was higher, it must have something)
|
||||
// R: Nothing to give
|
||||
if d.mode != LightSync {
|
||||
head := d.blockchain.CurrentBlock()
|
||||
if !gotHeaders && td.Cmp(d.blockchain.GetTd(head.Hash(), head.NumberU64())) > 0 {
|
||||
if !gotHeaders && td.Cmp(d.blockchain.CurrentTD()) > 0 {
|
||||
return errStallingPeer
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -282,6 +282,18 @@ func (dl *downloadTester) CurrentFastBlock() *types.Block {
|
|||
}
|
||||
return dl.genesis
|
||||
}
|
||||
func (dl *downloadTester) CurrentNumber() uint64 {
|
||||
return dl.CurrentBlock().NumberU64()
|
||||
}
|
||||
|
||||
func (dl *downloadTester) CurrentFastNumber() uint64 {
|
||||
return dl.CurrentFastBlock().NumberU64()
|
||||
}
|
||||
|
||||
func (dl *downloadTester) CurrentTD() *big.Int {
|
||||
cur := dl.CurrentBlock()
|
||||
return dl.GetTd(cur.Hash(), cur.NumberU64())
|
||||
}
|
||||
|
||||
// FastSyncCommitHead manually sets the head block to a given hash.
|
||||
func (dl *downloadTester) FastSyncCommitHead(hash common.Hash) error {
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, ne
|
|||
quitSync: make(chan struct{}),
|
||||
}
|
||||
// Figure out whether to allow fast sync or not
|
||||
if mode == downloader.FastSync && blockchain.CurrentBlock().NumberU64() > 0 {
|
||||
if mode == downloader.FastSync && blockchain.Stats().GetNumber() > 0 {
|
||||
log.Warn("Blockchain not empty, fast sync disabled")
|
||||
mode = downloader.FullSync
|
||||
}
|
||||
|
|
@ -165,7 +165,7 @@ func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, ne
|
|||
return engine.VerifyHeader(blockchain, header, true)
|
||||
}
|
||||
heighter := func() uint64 {
|
||||
return blockchain.CurrentBlock().NumberU64()
|
||||
return blockchain.Stats().GetNumber()
|
||||
}
|
||||
inserter := func(blocks types.Blocks) (int, error) {
|
||||
// If fast sync is running, deny importing weird blocks
|
||||
|
|
@ -647,8 +647,8 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
|||
// Schedule a sync if above ours. Note, this will not fire a sync for a gap of
|
||||
// a singe block (as the true TD is below the propagated block), however this
|
||||
// scenario should easily be covered by the fetcher.
|
||||
currentBlock := pm.blockchain.CurrentBlock()
|
||||
if trueTD.Cmp(pm.blockchain.GetTd(currentBlock.Hash(), currentBlock.NumberU64())) > 0 {
|
||||
currentTd := pm.blockchain.Stats().GetTotalDifficulty()
|
||||
if trueTD.Cmp(currentTd) > 0 {
|
||||
go pm.synchronise(p)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -167,8 +167,7 @@ func (pm *ProtocolManager) synchronise(peer *peer) {
|
|||
return
|
||||
}
|
||||
// Make sure the peer's TD is higher than our own
|
||||
currentBlock := pm.blockchain.CurrentBlock()
|
||||
td := pm.blockchain.GetTd(currentBlock.Hash(), currentBlock.NumberU64())
|
||||
currentNumber, currentFastNumber, td := pm.blockchain.Stats().Get()
|
||||
|
||||
pHead, pTd := peer.Head()
|
||||
if pTd.Cmp(td) <= 0 {
|
||||
|
|
@ -179,7 +178,7 @@ func (pm *ProtocolManager) synchronise(peer *peer) {
|
|||
if atomic.LoadUint32(&pm.fastSync) == 1 {
|
||||
// Fast sync was explicitly requested, and explicitly granted
|
||||
mode = downloader.FastSync
|
||||
} else if currentBlock.NumberU64() == 0 && pm.blockchain.CurrentFastBlock().NumberU64() > 0 {
|
||||
} else if currentNumber == 0 && currentFastNumber > 0 {
|
||||
// The database seems empty as the current block is the genesis. Yet the fast
|
||||
// block is ahead, so fast sync was enabled for this node at a certain point.
|
||||
// The only scenario where this can happen is if the user manually (or via a
|
||||
|
|
@ -197,13 +196,13 @@ func (pm *ProtocolManager) synchronise(peer *peer) {
|
|||
atomic.StoreUint32(&pm.fastSync, 0)
|
||||
}
|
||||
atomic.StoreUint32(&pm.acceptTxs, 1) // Mark initial sync done
|
||||
if head := pm.blockchain.CurrentBlock(); head.NumberU64() > 0 {
|
||||
if pm.blockchain.Stats().GetNumber() > 0 {
|
||||
// We've completed a sync cycle, notify all peers of new state. This path is
|
||||
// essential in star-topology networks where a gateway node needs to notify
|
||||
// all its out-of-date peers of the availability of a new block. This failure
|
||||
// scenario will most often crop up in private and hackathon networks with
|
||||
// degenerate connectivity, but it should be healthy for the mainnet too to
|
||||
// more reliably update peers or the local TD state.
|
||||
go pm.BroadcastBlock(head, false)
|
||||
go pm.BroadcastBlock(pm.blockchain.CurrentBlock(), false)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue