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:
parent
2cfc6478ce
commit
2274d36333
4 changed files with 26 additions and 104 deletions
|
@ -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
|
||||
}
|
||||
|
||||
|
|
10
cpuminer.go
10
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())
|
||||
|
|
42
mining.go
42
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,
|
||||
|
|
21
rpcserver.go
21
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,
|
||||
|
|
Loading…
Reference in a new issue