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
|
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
|
// blockManager provides a concurrency safe block manager for handling all
|
||||||
// incoming blocks.
|
// incoming blocks.
|
||||||
type blockManager struct {
|
type blockManager struct {
|
||||||
|
@ -176,7 +151,6 @@ type blockManager struct {
|
||||||
processingReqs bool
|
processingReqs bool
|
||||||
syncPeer *serverPeer
|
syncPeer *serverPeer
|
||||||
msgChan chan interface{}
|
msgChan chan interface{}
|
||||||
chainState chainState
|
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
quit chan struct{}
|
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.
|
// findNextHeaderCheckpoint returns the next checkpoint after the passed height.
|
||||||
// It returns nil when there is not one either because the height is already
|
// 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
|
// 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.
|
// update the chain state.
|
||||||
b.progressLogger.LogBlockHeight(bmsg.block)
|
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
|
// Update this peer's latest block height, for future
|
||||||
// potential sync node candidacy.
|
// potential sync node candidacy.
|
||||||
|
best := b.chain.BestSnapshot()
|
||||||
heightUpdate = best.Height
|
heightUpdate = best.Height
|
||||||
blkHashUpdate = best.Hash
|
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
|
// Allow any clients performing long polling via the
|
||||||
// getblocktemplate RPC to be notified when the new block causes
|
// getblocktemplate RPC to be notified when the new block causes
|
||||||
// their old block template to become stale.
|
// their old block template to become stale.
|
||||||
|
@ -1419,10 +1368,6 @@ func newBlockManager(s *server, indexManager blockchain.IndexManager) (*blockMan
|
||||||
bmgrLog.Info("Checkpoints are disabled")
|
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
|
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
|
// 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
|
// a new block, but the check only happens periodically, so it is
|
||||||
// possible a block was found and submitted in between.
|
// 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()
|
msgBlock := block.MsgBlock()
|
||||||
if !msgBlock.Header.PrevBlock.IsEqual(latestHash) {
|
if !msgBlock.Header.PrevBlock.IsEqual(latestHash) {
|
||||||
minrLog.Debugf("Block submitted via CPU miner with previous "+
|
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
|
// The current block is stale if the best block
|
||||||
// has changed.
|
// has changed.
|
||||||
bestHash, _ := m.server.blockManager.chainState.Best()
|
best := m.server.blockManager.chain.BestSnapshot()
|
||||||
if !header.PrevBlock.IsEqual(bestHash) {
|
if !header.PrevBlock.IsEqual(best.Hash) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,7 +292,7 @@ out:
|
||||||
// this would otherwise end up building a new block template on
|
// this would otherwise end up building a new block template on
|
||||||
// a block that is in the process of becoming stale.
|
// a block that is in the process of becoming stale.
|
||||||
m.submitBlockLock.Lock()
|
m.submitBlockLock.Lock()
|
||||||
_, curHeight := m.server.blockManager.chainState.Best()
|
curHeight := m.server.blockManager.chain.BestSnapshot().Height
|
||||||
if curHeight != 0 && !m.server.blockManager.IsCurrent() {
|
if curHeight != 0 && !m.server.blockManager.IsCurrent() {
|
||||||
m.submitBlockLock.Unlock()
|
m.submitBlockLock.Unlock()
|
||||||
time.Sleep(time.Second)
|
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
|
// be changing and this would otherwise end up building a new block
|
||||||
// template on a block that is in the process of becoming stale.
|
// template on a block that is in the process of becoming stale.
|
||||||
m.submitBlockLock.Lock()
|
m.submitBlockLock.Lock()
|
||||||
_, curHeight := m.server.blockManager.chainState.Best()
|
curHeight := m.server.blockManager.chain.BestSnapshot().Height
|
||||||
|
|
||||||
// Choose a payment address at random.
|
// Choose a payment address at random.
|
||||||
rand.Seed(time.Now().UnixNano())
|
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
|
// 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
|
// the median timestamp of the last several blocks per the chain consensus
|
||||||
// rules.
|
// rules.
|
||||||
func minimumMedianTime(chainState *chainState) (time.Time, error) {
|
func minimumMedianTime(chainState *blockchain.BestState) time.Time {
|
||||||
chainState.Lock()
|
return chainState.MedianTime.Add(time.Second)
|
||||||
defer chainState.Unlock()
|
|
||||||
if chainState.pastMedianTimeErr != nil {
|
|
||||||
return time.Time{}, chainState.pastMedianTimeErr
|
|
||||||
}
|
|
||||||
|
|
||||||
return chainState.pastMedianTime.Add(time.Second), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// medianAdjustedTime returns the current time adjusted to ensure it is at least
|
// 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
|
// one second after the median timestamp of the last several blocks per the
|
||||||
// chain consensus rules.
|
// chain consensus rules.
|
||||||
func medianAdjustedTime(chainState *chainState, timeSource blockchain.MedianTimeSource) (time.Time, error) {
|
func medianAdjustedTime(chainState *blockchain.BestState, timeSource blockchain.MedianTimeSource) time.Time {
|
||||||
chainState.Lock()
|
|
||||||
defer chainState.Unlock()
|
|
||||||
if chainState.pastMedianTimeErr != nil {
|
|
||||||
return time.Time{}, chainState.pastMedianTimeErr
|
|
||||||
}
|
|
||||||
|
|
||||||
// The timestamp for the block must not be before the median timestamp
|
// The timestamp for the block must not be before the median timestamp
|
||||||
// of the last several blocks. Thus, choose the maximum between the
|
// of the last several blocks. Thus, choose the maximum between the
|
||||||
// current time and one second after the past median time. The current
|
// 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
|
// block timestamp does not supported a precision greater than one
|
||||||
// second.
|
// second.
|
||||||
newTimestamp := timeSource.AdjustedTime()
|
newTimestamp := timeSource.AdjustedTime()
|
||||||
minTimestamp := chainState.pastMedianTime.Add(time.Second)
|
minTimestamp := minimumMedianTime(chainState)
|
||||||
if newTimestamp.Before(minTimestamp) {
|
if newTimestamp.Before(minTimestamp) {
|
||||||
newTimestamp = minTimestamp
|
newTimestamp = minTimestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
return newTimestamp, nil
|
return newTimestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBlockTemplate returns a new block template that is ready to be solved
|
// 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
|
var txSource mining.TxSource = server.txMemPool
|
||||||
blockManager := server.blockManager
|
blockManager := server.blockManager
|
||||||
timeSource := server.timeSource
|
timeSource := server.timeSource
|
||||||
chainState := &blockManager.chainState
|
|
||||||
|
|
||||||
// Extend the most recently known best block.
|
// Extend the most recently known best block.
|
||||||
chainState.Lock()
|
best := blockManager.chain.BestSnapshot()
|
||||||
prevHash := chainState.newestHash
|
prevHash := best.Hash
|
||||||
nextBlockHeight := chainState.newestHeight + 1
|
nextBlockHeight := best.Height + 1
|
||||||
chainState.Unlock()
|
|
||||||
|
|
||||||
// Create a standard coinbase transaction paying to the provided
|
// Create a standard coinbase transaction paying to the provided
|
||||||
// address. NOTE: The coinbase value will be updated to include the
|
// 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
|
// Calculate the required difficulty for the block. The timestamp
|
||||||
// is potentially adjusted to ensure it comes after the median time of
|
// is potentially adjusted to ensure it comes after the median time of
|
||||||
// the last several blocks per the chain consensus rules.
|
// the last several blocks per the chain consensus rules.
|
||||||
ts, err := medianAdjustedTime(chainState, timeSource)
|
ts := medianAdjustedTime(best, timeSource)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
reqDifficulty, err := blockManager.chain.CalcNextRequiredDifficulty(ts)
|
reqDifficulty, err := blockManager.chain.CalcNextRequiredDifficulty(ts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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 new timestamp is potentially adjusted to ensure it comes after
|
||||||
// the median time of the last several blocks per the chain consensus
|
// the median time of the last several blocks per the chain consensus
|
||||||
// rules.
|
// rules.
|
||||||
newTimestamp, err := medianAdjustedTime(&bManager.chainState,
|
best := bManager.chain.BestSnapshot()
|
||||||
bManager.server.timeSource)
|
newTimestamp := medianAdjustedTime(best, bManager.server.timeSource)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
msgBlock.Header.Timestamp = newTimestamp
|
msgBlock.Header.Timestamp = newTimestamp
|
||||||
|
|
||||||
// If running on a network that requires recalculating the difficulty,
|
// 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.
|
// generated.
|
||||||
var msgBlock *wire.MsgBlock
|
var msgBlock *wire.MsgBlock
|
||||||
var targetDifficulty string
|
var targetDifficulty string
|
||||||
latestHash, _ := s.server.blockManager.chainState.Best()
|
latestHash := s.server.blockManager.chain.BestSnapshot().Hash
|
||||||
template := state.template
|
template := state.template
|
||||||
if template == nil || state.prevHash == nil ||
|
if template == nil || state.prevHash == nil ||
|
||||||
!state.prevHash.IsEqual(latestHash) ||
|
!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
|
// Find the minimum allowed timestamp for the block based on the
|
||||||
// median timestamp of the last several blocks per the chain
|
// median timestamp of the last several blocks per the chain
|
||||||
// consensus rules.
|
// consensus rules.
|
||||||
chainState := &s.server.blockManager.chainState
|
best := s.server.blockManager.chain.BestSnapshot()
|
||||||
minTimestamp, err := minimumMedianTime(chainState)
|
minTimestamp := minimumMedianTime(best)
|
||||||
if err != nil {
|
|
||||||
context := "Failed to get minimum median time"
|
|
||||||
return internalRPCError(err.Error(), context)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update work state to ensure another block template isn't
|
// Update work state to ensure another block template isn't
|
||||||
// generated until needed.
|
// 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.
|
// 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() {
|
if currentHeight != 0 && !s.server.blockManager.IsCurrent() {
|
||||||
return nil, &btcjson.RPCError{
|
return nil, &btcjson.RPCError{
|
||||||
Code: btcjson.ErrRPCClientInInitialDownload,
|
Code: btcjson.ErrRPCClientInInitialDownload,
|
||||||
|
@ -1923,7 +1919,7 @@ func handleGetBlockTemplateProposal(s *rpcServer, request *btcjson.TemplateReque
|
||||||
block := btcutil.NewBlock(&msgBlock)
|
block := btcutil.NewBlock(&msgBlock)
|
||||||
|
|
||||||
// Ensure the block is building from the expected previous block.
|
// 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
|
prevHash := &block.MsgBlock().Header.PrevBlock
|
||||||
if expectedPrevHash == nil || !expectedPrevHash.IsEqual(prevHash) {
|
if expectedPrevHash == nil || !expectedPrevHash.IsEqual(prevHash) {
|
||||||
return "bad-prevblk", nil
|
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
|
// and it has been at least one minute since the last template was
|
||||||
// generated.
|
// generated.
|
||||||
lastTxUpdate := s.server.txMemPool.LastUpdated()
|
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
|
msgBlock := state.msgBlock
|
||||||
if msgBlock == nil || state.prevHash == nil ||
|
if msgBlock == nil || state.prevHash == nil ||
|
||||||
!state.prevHash.IsEqual(latestHash) ||
|
!state.prevHash.IsEqual(latestHash) ||
|
||||||
|
@ -2769,7 +2766,7 @@ func handleGetWorkSubmission(s *rpcServer, hexData string) (interface{}, error)
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
latestHash, _ := s.server.blockManager.chainState.Best()
|
latestHash := s.server.blockManager.chain.BestSnapshot().Hash
|
||||||
if !msgBlock.Header.PrevBlock.IsEqual(latestHash) {
|
if !msgBlock.Header.PrevBlock.IsEqual(latestHash) {
|
||||||
rpcsLog.Debugf("Block submitted via getwork with previous "+
|
rpcsLog.Debugf("Block submitted via getwork with previous "+
|
||||||
"block %s is stale", msgBlock.Header.PrevBlock)
|
"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.
|
// 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() {
|
if currentHeight != 0 && !s.server.blockManager.IsCurrent() {
|
||||||
return nil, &btcjson.RPCError{
|
return nil, &btcjson.RPCError{
|
||||||
Code: btcjson.ErrRPCClientInInitialDownload,
|
Code: btcjson.ErrRPCClientInInitialDownload,
|
||||||
|
|
Loading…
Reference in a new issue