Skip to content
This repository has been archived by the owner on Aug 7, 2023. It is now read-only.

Commit

Permalink
bmgr: Remove block manager chain state.
Browse files Browse the repository at this point in the history
This removes the block manager chain state in favor of using the
blockchain.BlockChain instance now that it is safe for concurrency.
  • Loading branch information
davecgh committed Oct 26, 2016
1 parent 2cfc647 commit 2274d36
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 104 deletions.
57 changes: 1 addition & 56 deletions blockmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -176,7 +151,6 @@ type blockManager struct {
processingReqs bool
syncPeer *serverPeer
msgChan chan interface{}
chainState chainState
wg sync.WaitGroup
quit chan struct{}

Expand All @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
}

Expand Down
10 changes: 5 additions & 5 deletions cpuminer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 "+
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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())
Expand Down
42 changes: 11 additions & 31 deletions mining.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,39 +280,27 @@ 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
// timestamp is truncated to a second boundary before comparison since a
// 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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
21 changes: 9 additions & 12 deletions rpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) ||
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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) ||
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 2274d36

Please sign in to comment.