diff --git a/mining.go b/mining.go index e20045fb..186e3902 100644 --- a/mining.go +++ b/mining.go @@ -631,28 +631,21 @@ func logSkippedDeps(tx *dcrutil.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() - - return chainState.pastMedianTime.Add(time.Second), nil +func minimumMedianTime(best *blockchain.BestState) time.Time { + return best.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() - +func medianAdjustedTime(best *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. + // block timestamp does not support a precision greater than one second. newTimestamp := timeSource.AdjustedTime() - minTimestamp := chainState.pastMedianTime.Add(time.Second) + minTimestamp := minimumMedianTime(best) if newTimestamp.Before(minTimestamp) { newTimestamp = minTimestamp } @@ -661,7 +654,7 @@ func medianAdjustedTime(chainState *chainState, timeSource blockchain.MedianTime newTimestamp = newTimestamp.Add( time.Duration(-cfg.MiningTimeOffset) * time.Second) - return newTimestamp, nil + return newTimestamp } // maybeInsertStakeTx checks to make sure that a stake tx is @@ -763,16 +756,15 @@ func deepCopyBlockTemplate(blockTemplate *BlockTemplate) *BlockTemplate { // Safe for concurrent access. func handleTooFewVoters(subsidyCache *blockchain.SubsidyCache, nextHeight int64, miningAddress dcrutil.Address, bm *blockManager) (*BlockTemplate, error) { timeSource := bm.server.timeSource - chainState := &bm.chainState stakeValidationHeight := bm.server.chainParams.StakeValidationHeight curTemplate := bm.GetCurrentTemplate() // Check to see if we've fallen off the chain, for example if a // reorganization had recently occurred. If this is the case, // nuke the templates. - prevBlockHash := chainState.GetTopPrevHash() + best := bm.chain.BestSnapshot() if curTemplate != nil { - if !prevBlockHash.IsEqual( + if !best.PrevHash.IsEqual( &curTemplate.Block.Header.PrevBlock) { minrLog.Debugf("Cached mining templates are no longer current, " + "resetting") @@ -789,10 +781,7 @@ func handleTooFewVoters(subsidyCache *blockchain.SubsidyCache, nextHeight int64, cptCopy := deepCopyBlockTemplate(curTemplate) // Update the timestamp of the old template. - ts, err := medianAdjustedTime(chainState, timeSource) - if err != nil { - return nil, err - } + ts := medianAdjustedTime(best, timeSource) cptCopy.Block.Header.Timestamp = ts // If we're on testnet, the time since this last block @@ -814,7 +803,7 @@ func handleTooFewVoters(subsidyCache *blockchain.SubsidyCache, nextHeight int64, // than the previous extra nonce, so we don't remine the // same block and choose the same winners as before. en := cptCopy.extractCoinbaseExtraNonce() + 1 - err = UpdateExtraNonce(cptCopy.Block, cptCopy.Height, en) + err := UpdateExtraNonce(cptCopy.Block, cptCopy.Height, en) if err != nil { return nil, err } @@ -847,11 +836,10 @@ func handleTooFewVoters(subsidyCache *blockchain.SubsidyCache, nextHeight int64, // and the contents of that stake tree. In the future // we should have the option of readding some // transactions from this block, too. - bestHash, _ := chainState.Best() - topBlock, err := bm.chain.BlockByHash(bestHash) + topBlock, err := bm.chain.BlockByHash(&best.Hash) if err != nil { str := fmt.Sprintf("unable to get tip block %s", - prevBlockHash) + best.PrevHash) return nil, miningRuleError(ErrGetTopBlock, str) } btMsgBlock := new(wire.MsgBlock) @@ -887,10 +875,7 @@ func handleTooFewVoters(subsidyCache *blockchain.SubsidyCache, nextHeight int64, btMsgBlock.Header = topBlock.MsgBlock().Header // Set a fresh timestamp. - ts, err := medianAdjustedTime(chainState, timeSource) - if err != nil { - return nil, err - } + ts := medianAdjustedTime(best, timeSource) btMsgBlock.Header.Timestamp = ts // If we're on testnet, the time since this last block @@ -959,7 +944,6 @@ func handleTooFewVoters(subsidyCache *blockchain.SubsidyCache, nextHeight int64, // from occurring in case the template is mined on by the CPUminer. func handleCreatedBlockTemplate(blockTemplate *BlockTemplate, bm *blockManager) (*BlockTemplate, error) { curTemplate := bm.GetCurrentTemplate() - nextBlockHeight := blockTemplate.Height stakeValidationHeight := bm.server.chainParams.StakeValidationHeight // This is where we begin storing block templates, when either the @@ -1865,12 +1849,8 @@ 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, miningRuleError(ErrGettingMedianTime, err.Error()) - } + ts := medianAdjustedTime(chainBest, timeSource) reqDifficulty, err := blockManager.chain.CalcNextRequiredDifficulty(ts) - if err != nil { return nil, miningRuleError(ErrGettingDifficulty, err.Error()) } @@ -2053,11 +2033,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, + newTimestamp := medianAdjustedTime(bManager.chain.BestSnapshot(), bManager.server.timeSource) - if err != nil { - return miningRuleError(ErrGettingMedianTime, err.Error()) - } msgBlock.Header.Timestamp = newTimestamp // If running on a network that requires recalculating the difficulty, diff --git a/rpcserver.go b/rpcserver.go index 405a67a9..20c8c4a3 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -980,10 +980,9 @@ func handleCreateRawSSGenTx(s *rpcServer, cmd interface{}, closeChan <-chan stru ssgenPayTypes, ssgenPkhs, sstxAmts, _, _, _ := stake.SStxStakeOutputInfo(minimalOutputs) - // Get the current reward. - blockHash, curHeight := s.server.blockManager.chainState.Best() + best := s.server.blockManager.chain.BestSnapshot() stakeVoteSubsidy := blockchain.CalcStakeVoteSubsidy( - s.chain.FetchSubsidyCache(), curHeight, activeNetParams.Params) + s.chain.FetchSubsidyCache(), best.Height, activeNetParams.Params) // Calculate the output values from this data. ssgenCalcAmts := stake.CalculateRewards(sstxAmts, @@ -1034,8 +1033,8 @@ func handleCreateRawSSGenTx(s *rpcServer, cmd interface{}, closeChan <-chan stru // outputs. // // Block reference output. - blockRefScript, err := txscript.GenerateSSGenBlockRef(*blockHash, - uint32(curHeight)) + blockRefScript, err := txscript.GenerateSSGenBlockRef(best.Hash, + uint32(best.Height)) if err != nil { return nil, rpcInvalidError("Could not generate SSGen block "+ "reference: %v", err) @@ -1546,7 +1545,7 @@ func handleEstimateStakeDiff(s *rpcServer, cmd interface{}, closeChan <-chan str // The expected stake difficulty. Average the number of fresh stake // since the last retarget to get the number of tickets per block, // then use that to estimate the next stake difficulty. - _, bestHeight := s.server.blockManager.chainState.Best() + bestHeight := s.server.blockManager.chain.BestSnapshot().Height lastAdjustment := (bestHeight / activeNetParams.StakeDiffWindowSize) * activeNetParams.StakeDiffWindowSize nextAdjustment := ((bestHeight / activeNetParams.StakeDiffWindowSize) + @@ -2356,10 +2355,11 @@ func (state *gbtWorkState) updateBlockTemplate(s *rpcServer, useCoinbaseValue bo // generated. var msgBlock *wire.MsgBlock var targetDifficulty string - latestHash, _ := s.server.blockManager.chainState.Best() + best := s.server.blockManager.chain.BestSnapshot() + latestHash := best.Hash template := state.template if template == nil || state.prevHash == nil || - !state.prevHash.IsEqual(latestHash) || + !state.prevHash.IsEqual(&best.Hash) || (state.lastTxUpdate != lastTxUpdate && time.Now().After(state.lastGenerated.Add(time.Second* gbtRegenerateSeconds))) { @@ -2400,19 +2400,14 @@ 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 rpcInternalError(err.Error(), context) - } + minTimestamp := minimumMedianTime(best) // Update work state to ensure another block template isn't // generated until needed. state.template = deepCopyBlockTemplate(template) state.lastGenerated = time.Now() state.lastTxUpdate = lastTxUpdate - state.prevHash = latestHash + state.prevHash = &latestHash state.minTimestamp = minTimestamp rpcsLog.Debugf("Generated block template (timestamp %v, "+ @@ -2422,7 +2417,7 @@ func (state *gbtWorkState) updateBlockTemplate(s *rpcServer, useCoinbaseValue bo // Notify any clients that are long polling about the new // template. - state.notifyLongPollers(latestHash, lastTxUpdate) + state.notifyLongPollers(&best.Hash, lastTxUpdate) } else { // At this point, there is a saved block template and another // request for a template was made, but either the available @@ -2848,8 +2843,8 @@ func handleGetBlockTemplateRequest(s *rpcServer, request *dcrjson.TemplateReques } // No point in generating or accepting work before the chain is synced. - _, currentHeight := s.server.blockManager.chainState.Best() - if currentHeight != 0 && !s.server.blockManager.IsCurrent() { + bestHeight := s.server.blockManager.chain.BestSnapshot().Height + if bestHeight != 0 && !s.server.blockManager.IsCurrent() { return nil, &dcrjson.RPCError{ Code: dcrjson.ErrRPCClientInInitialDownload, Message: "Decred is downloading blocks...", @@ -2997,7 +2992,7 @@ func handleGetBlockTemplateProposal(s *rpcServer, request *dcrjson.TemplateReque block := dcrutil.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 @@ -4055,7 +4050,7 @@ func handleGetWorkRequest(s *rpcServer) (interface{}, error) { // 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() msgBlock := state.msgBlock // The current code pulls down a new template every second, however @@ -4064,14 +4059,14 @@ func handleGetWorkRequest(s *rpcServer) (interface{}, error) { // based on the votes present every second or so, and then, if needed, // generate a new block template. TODO cj if msgBlock == nil || state.prevHash == nil || - !state.prevHash.IsEqual(latestHash) || + !state.prevHash.IsEqual(&best.Hash) || (state.lastTxUpdate != lastTxUpdate && time.Now().After(state.lastGenerated.Add(time.Second))) { // Reset the extra nonce and clear all expired cached template // variations if the best block changed. - if state.prevHash != nil && !state.prevHash.IsEqual(latestHash) { + if state.prevHash != nil && !state.prevHash.IsEqual(&best.Hash) { state.extraNonce = 0 - pruneOldBlockTemplates(s, latestHeight) + pruneOldBlockTemplates(s, best.Height) } // Reset the previous best hash the block template was @@ -4105,7 +4100,7 @@ func handleGetWorkRequest(s *rpcServer) (interface{}, error) { state.msgBlock = msgBlock state.lastGenerated = time.Now() state.lastTxUpdate = lastTxUpdate - state.prevHash = latestHash + state.prevHash = &best.Hash rpcsLog.Debugf("Generated block template (timestamp %v, extra "+ "nonce %d, target %064x, merkle root %s)", @@ -4146,7 +4141,7 @@ func handleGetWorkRequest(s *rpcServer) (interface{}, error) { // setting the merkle root to the new value. en := extractCoinbaseExtraNonce(msgBlock) + 1 state.extraNonce++ - err := UpdateExtraNonce(msgBlock, latestHeight+1, en) + err := UpdateExtraNonce(msgBlock, best.Height+1, en) if err != nil { errStr := fmt.Sprintf("Failed to update extra nonce: "+ "%v", err) @@ -4358,8 +4353,8 @@ 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() - if currentHeight != 0 && !s.server.blockManager.IsCurrent() { + bestHeight := s.server.blockManager.chain.BestSnapshot().Height + if bestHeight != 0 && !s.server.blockManager.IsCurrent() { return nil, &dcrjson.RPCError{ Code: dcrjson.ErrRPCClientInInitialDownload, Message: "Decred is downloading blocks...", @@ -4467,7 +4462,7 @@ func handlePing(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (inter // handleRebroadcastMissed implements the rebroadcastmissed command. func handleRebroadcastMissed(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - hash, height := s.server.blockManager.chainState.Best() + best := s.server.blockManager.chain.BestSnapshot() mt, err := s.server.blockManager.chain.MissedTickets() if err != nil { return nil, rpcInternalError("Could not get missed tickets "+ @@ -4481,8 +4476,8 @@ func handleRebroadcastMissed(s *rpcServer, cmd interface{}, closeChan <-chan str } missedTicketsNtfn := &blockchain.TicketNotificationsData{ - Hash: *hash, - Height: height, + Hash: best.Hash, + Height: best.Height, StakeDifficulty: stakeDiff, TicketsSpent: []chainhash.Hash{}, TicketsMissed: mt, @@ -4496,7 +4491,7 @@ func handleRebroadcastMissed(s *rpcServer, cmd interface{}, closeChan <-chan str // handleRebroadcastWinners implements the rebroadcastwinners command. func handleRebroadcastWinners(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { - _, height := s.server.blockManager.chainState.Best() + bestHeight := s.server.blockManager.chain.BestSnapshot().Height blocks, err := s.server.blockManager.TipGeneration() if err != nil { return nil, rpcInternalError("Could not get generation "+ @@ -4512,7 +4507,7 @@ func handleRebroadcastWinners(s *rpcServer, cmd interface{}, closeChan <-chan st } ntfnData := &WinningTicketsNtfnData{ BlockHash: blocks[i], - BlockHeight: height, + BlockHeight: bestHeight, Tickets: winningTickets, } @@ -5392,9 +5387,7 @@ func ticketFeeInfoForRange(s *rpcServer, start int64, end int64, txType stake.Tx func handleTicketFeeInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*dcrjson.TicketFeeInfoCmd) - s.server.blockManager.chainState.Lock() - bestHeight := s.server.blockManager.chainState.newestHeight - s.server.blockManager.chainState.Unlock() + bestHeight := s.server.blockManager.chain.BestSnapshot().Height // Memory pool first. feeInfoMempool := feeInfoForMempool(s, stake.TxTypeSStx) @@ -5505,7 +5498,7 @@ func handleTicketVWAP(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) // The default VWAP is for the past WorkDiffWindows * WorkDiffWindowSize // many blocks. - _, bestHeight := s.server.blockManager.chainState.Best() + bestHeight := s.server.blockManager.chain.BestSnapshot().Height var start uint32 if c.Start == nil { toEval := activeNetParams.WorkDiffWindows * @@ -5562,9 +5555,7 @@ func handleTicketVWAP(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) func handleTxFeeInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*dcrjson.TxFeeInfoCmd) - s.server.blockManager.chainState.Lock() - bestHeight := s.server.blockManager.chainState.newestHeight - s.server.blockManager.chainState.Unlock() + bestHeight := s.server.blockManager.chain.BestSnapshot().Height // Memory pool first. feeInfoMempool := feeInfoForMempool(s, stake.TxTypeRegular)