bmgr: Remove block manager chain state.

This removes the block manager chain state in favor of using the
blockchain.BlockChain instance now that it is safe for concurrency.
This commit is contained in:
Dave Collins 2016-04-02 02:12:38 -05:00
parent 2cfc6478ce
commit 2274d36333
4 changed files with 26 additions and 104 deletions

View file

@ -135,31 +135,6 @@ type headerNode struct {
hash *chainhash.Hash
}
// chainState tracks the state of the best chain as blocks are inserted. This
// is done because btcchain is currently not safe for concurrent access and the
// block manager is typically quite busy processing block and inventory.
// Therefore, requesting this information from chain through the block manager
// would not be anywhere near as efficient as simply updating it as each block
// is inserted and protecting it with a mutex.
type chainState struct {
sync.Mutex
newestHash *chainhash.Hash
newestHeight int32
pastMedianTime time.Time
pastMedianTimeErr error
}
// Best returns the block hash and height known for the tip of the best known
// chain.
//
// This function is safe for concurrent access.
func (c *chainState) Best() (*chainhash.Hash, int32) {
c.Lock()
defer c.Unlock()
return c.newestHash, c.newestHeight
}
// blockManager provides a concurrency safe block manager for handling all
// incoming blocks.
type blockManager struct {
@ -176,7 +151,6 @@ type blockManager struct {
processingReqs bool
syncPeer *serverPeer
msgChan chan interface{}
chainState chainState
wg sync.WaitGroup
quit chan struct{}
@ -203,20 +177,6 @@ func (b *blockManager) resetHeaderState(newestHash *chainhash.Hash, newestHeight
}
}
// updateChainState updates the chain state associated with the block manager.
// This allows fast access to chain information since btcchain is currently not
// safe for concurrent access and the block manager is typically quite busy
// processing block and inventory.
func (b *blockManager) updateChainState(newestHash *chainhash.Hash, newestHeight int32) {
b.chainState.Lock()
defer b.chainState.Unlock()
b.chainState.newestHash = newestHash
b.chainState.newestHeight = newestHeight
b.chainState.pastMedianTime = b.chain.BestSnapshot().MedianTime
b.chainState.pastMedianTimeErr = nil
}
// findNextHeaderCheckpoint returns the next checkpoint after the passed height.
// It returns nil when there is not one either because the height is already
// later than the final checkpoint or some other reason such as disabled
@ -635,14 +595,9 @@ func (b *blockManager) handleBlockMsg(bmsg *blockMsg) {
// update the chain state.
b.progressLogger.LogBlockHeight(bmsg.block)
// Query the chain for the latest best block since the block
// that was processed could be on a side chain or have caused
// a reorg.
best := b.chain.BestSnapshot()
b.updateChainState(best.Hash, best.Height)
// Update this peer's latest block height, for future
// potential sync node candidacy.
best := b.chain.BestSnapshot()
heightUpdate = best.Height
blkHashUpdate = best.Hash
@ -1126,12 +1081,6 @@ out:
}
}
// Query the chain for the latest best block
// since the block that was processed could be
// on a side chain or have caused a reorg.
best := b.chain.BestSnapshot()
b.updateChainState(best.Hash, best.Height)
// Allow any clients performing long polling via the
// getblocktemplate RPC to be notified when the new block causes
// their old block template to become stale.
@ -1419,10 +1368,6 @@ func newBlockManager(s *server, indexManager blockchain.IndexManager) (*blockMan
bmgrLog.Info("Checkpoints are disabled")
}
// Initialize the chain state now that the initial block node index has
// been generated.
bm.updateChainState(best.Hash, best.Height)
return &bm, nil
}

View file

@ -125,7 +125,7 @@ func (m *CPUMiner) submitBlock(block *btcutil.Block) bool {
// detected and all work on the stale block is halted to start work on
// a new block, but the check only happens periodically, so it is
// possible a block was found and submitted in between.
latestHash, _ := m.server.blockManager.chainState.Best()
latestHash := m.server.blockManager.chain.BestSnapshot().Hash
msgBlock := block.MsgBlock()
if !msgBlock.Header.PrevBlock.IsEqual(latestHash) {
minrLog.Debugf("Block submitted via CPU miner with previous "+
@ -213,8 +213,8 @@ func (m *CPUMiner) solveBlock(msgBlock *wire.MsgBlock, blockHeight int32,
// The current block is stale if the best block
// has changed.
bestHash, _ := m.server.blockManager.chainState.Best()
if !header.PrevBlock.IsEqual(bestHash) {
best := m.server.blockManager.chain.BestSnapshot()
if !header.PrevBlock.IsEqual(best.Hash) {
return false
}
@ -292,7 +292,7 @@ out:
// this would otherwise end up building a new block template on
// a block that is in the process of becoming stale.
m.submitBlockLock.Lock()
_, curHeight := m.server.blockManager.chainState.Best()
curHeight := m.server.blockManager.chain.BestSnapshot().Height
if curHeight != 0 && !m.server.blockManager.IsCurrent() {
m.submitBlockLock.Unlock()
time.Sleep(time.Second)
@ -559,7 +559,7 @@ func (m *CPUMiner) GenerateNBlocks(n uint32) ([]*chainhash.Hash, error) {
// be changing and this would otherwise end up building a new block
// template on a block that is in the process of becoming stale.
m.submitBlockLock.Lock()
_, curHeight := m.server.blockManager.chainState.Best()
curHeight := m.server.blockManager.chain.BestSnapshot().Height
// Choose a payment address at random.
rand.Seed(time.Now().UnixNano())

View file

@ -280,26 +280,14 @@ func logSkippedDeps(tx *btcutil.Tx, deps map[chainhash.Hash]*txPrioItem) {
// on the end of the current best chain. In particular, it is one second after
// the median timestamp of the last several blocks per the chain consensus
// rules.
func minimumMedianTime(chainState *chainState) (time.Time, error) {
chainState.Lock()
defer chainState.Unlock()
if chainState.pastMedianTimeErr != nil {
return time.Time{}, chainState.pastMedianTimeErr
}
return chainState.pastMedianTime.Add(time.Second), nil
func minimumMedianTime(chainState *blockchain.BestState) time.Time {
return chainState.MedianTime.Add(time.Second)
}
// medianAdjustedTime returns the current time adjusted to ensure it is at least
// one second after the median timestamp of the last several blocks per the
// chain consensus rules.
func medianAdjustedTime(chainState *chainState, timeSource blockchain.MedianTimeSource) (time.Time, error) {
chainState.Lock()
defer chainState.Unlock()
if chainState.pastMedianTimeErr != nil {
return time.Time{}, chainState.pastMedianTimeErr
}
func medianAdjustedTime(chainState *blockchain.BestState, timeSource blockchain.MedianTimeSource) time.Time {
// The timestamp for the block must not be before the median timestamp
// of the last several blocks. Thus, choose the maximum between the
// current time and one second after the past median time. The current
@ -307,12 +295,12 @@ func medianAdjustedTime(chainState *chainState, timeSource blockchain.MedianTime
// block timestamp does not supported a precision greater than one
// second.
newTimestamp := timeSource.AdjustedTime()
minTimestamp := chainState.pastMedianTime.Add(time.Second)
minTimestamp := minimumMedianTime(chainState)
if newTimestamp.Before(minTimestamp) {
newTimestamp = minTimestamp
}
return newTimestamp, nil
return newTimestamp
}
// NewBlockTemplate returns a new block template that is ready to be solved
@ -381,13 +369,11 @@ func NewBlockTemplate(policy *mining.Policy, server *server, payToAddress btcuti
var txSource mining.TxSource = server.txMemPool
blockManager := server.blockManager
timeSource := server.timeSource
chainState := &blockManager.chainState
// Extend the most recently known best block.
chainState.Lock()
prevHash := chainState.newestHash
nextBlockHeight := chainState.newestHeight + 1
chainState.Unlock()
best := blockManager.chain.BestSnapshot()
prevHash := best.Hash
nextBlockHeight := best.Height + 1
// Create a standard coinbase transaction paying to the provided
// address. NOTE: The coinbase value will be updated to include the
@ -701,10 +687,7 @@ mempoolLoop:
// Calculate the required difficulty for the block. The timestamp
// is potentially adjusted to ensure it comes after the median time of
// the last several blocks per the chain consensus rules.
ts, err := medianAdjustedTime(chainState, timeSource)
if err != nil {
return nil, err
}
ts := medianAdjustedTime(best, timeSource)
reqDifficulty, err := blockManager.chain.CalcNextRequiredDifficulty(ts)
if err != nil {
return nil, err
@ -759,11 +742,8 @@ func UpdateBlockTime(msgBlock *wire.MsgBlock, bManager *blockManager) error {
// The new timestamp is potentially adjusted to ensure it comes after
// the median time of the last several blocks per the chain consensus
// rules.
newTimestamp, err := medianAdjustedTime(&bManager.chainState,
bManager.server.timeSource)
if err != nil {
return err
}
best := bManager.chain.BestSnapshot()
newTimestamp := medianAdjustedTime(best, bManager.server.timeSource)
msgBlock.Header.Timestamp = newTimestamp
// If running on a network that requires recalculating the difficulty,

View file

@ -1363,7 +1363,7 @@ func (state *gbtWorkState) updateBlockTemplate(s *rpcServer, useCoinbaseValue bo
// generated.
var msgBlock *wire.MsgBlock
var targetDifficulty string
latestHash, _ := s.server.blockManager.chainState.Best()
latestHash := s.server.blockManager.chain.BestSnapshot().Hash
template := state.template
if template == nil || state.prevHash == nil ||
!state.prevHash.IsEqual(latestHash) ||
@ -1402,12 +1402,8 @@ func (state *gbtWorkState) updateBlockTemplate(s *rpcServer, useCoinbaseValue bo
// Find the minimum allowed timestamp for the block based on the
// median timestamp of the last several blocks per the chain
// consensus rules.
chainState := &s.server.blockManager.chainState
minTimestamp, err := minimumMedianTime(chainState)
if err != nil {
context := "Failed to get minimum median time"
return internalRPCError(err.Error(), context)
}
best := s.server.blockManager.chain.BestSnapshot()
minTimestamp := minimumMedianTime(best)
// Update work state to ensure another block template isn't
// generated until needed.
@ -1760,7 +1756,7 @@ func handleGetBlockTemplateRequest(s *rpcServer, request *btcjson.TemplateReques
}
// No point in generating or accepting work before the chain is synced.
_, currentHeight := s.server.blockManager.chainState.Best()
currentHeight := s.server.blockManager.chain.BestSnapshot().Height
if currentHeight != 0 && !s.server.blockManager.IsCurrent() {
return nil, &btcjson.RPCError{
Code: btcjson.ErrRPCClientInInitialDownload,
@ -1923,7 +1919,7 @@ func handleGetBlockTemplateProposal(s *rpcServer, request *btcjson.TemplateReque
block := btcutil.NewBlock(&msgBlock)
// Ensure the block is building from the expected previous block.
expectedPrevHash, _ := s.server.blockManager.chainState.Best()
expectedPrevHash := s.server.blockManager.chain.BestSnapshot().Hash
prevHash := &block.MsgBlock().Header.PrevBlock
if expectedPrevHash == nil || !expectedPrevHash.IsEqual(prevHash) {
return "bad-prevblk", nil
@ -2540,7 +2536,8 @@ func handleGetWorkRequest(s *rpcServer) (interface{}, error) {
// and it has been at least one minute since the last template was
// generated.
lastTxUpdate := s.server.txMemPool.LastUpdated()
latestHash, latestHeight := s.server.blockManager.chainState.Best()
best := s.server.blockManager.chain.BestSnapshot()
latestHash, latestHeight := best.Hash, best.Height
msgBlock := state.msgBlock
if msgBlock == nil || state.prevHash == nil ||
!state.prevHash.IsEqual(latestHash) ||
@ -2769,7 +2766,7 @@ func handleGetWorkSubmission(s *rpcServer, hexData string) (interface{}, error)
return false, nil
}
latestHash, _ := s.server.blockManager.chainState.Best()
latestHash := s.server.blockManager.chain.BestSnapshot().Hash
if !msgBlock.Header.PrevBlock.IsEqual(latestHash) {
rpcsLog.Debugf("Block submitted via getwork with previous "+
"block %s is stale", msgBlock.Header.PrevBlock)
@ -2821,7 +2818,7 @@ func handleGetWork(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (in
}
// No point in generating or accepting work before the chain is synced.
_, currentHeight := s.server.blockManager.chainState.Best()
currentHeight := s.server.blockManager.chain.BestSnapshot().Height
if currentHeight != 0 && !s.server.blockManager.IsCurrent() {
return nil, &btcjson.RPCError{
Code: btcjson.ErrRPCClientInInitialDownload,