From 2274d3633329dfcf098fdf141ea69c8bd01a5aa2 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Sat, 2 Apr 2016 02:12:38 -0500 Subject: [PATCH] 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. --- blockmanager.go | 57 +------------------------------------------------ cpuminer.go | 10 ++++----- mining.go | 42 ++++++++++-------------------------- rpcserver.go | 21 ++++++++---------- 4 files changed, 26 insertions(+), 104 deletions(-) diff --git a/blockmanager.go b/blockmanager.go index 61761c30..0e051eee 100644 --- a/blockmanager.go +++ b/blockmanager.go @@ -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 } diff --git a/cpuminer.go b/cpuminer.go index e70af34b..ad0eb55a 100644 --- a/cpuminer.go +++ b/cpuminer.go @@ -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()) diff --git a/mining.go b/mining.go index aa8c7702..cd621003 100644 --- a/mining.go +++ b/mining.go @@ -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, diff --git a/rpcserver.go b/rpcserver.go index c9e8a4fc..9f638e18 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -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,