diff --git a/blockmanager.go b/blockmanager.go index 6a569bf7fa..26ac4cacde 100644 --- a/blockmanager.go +++ b/blockmanager.go @@ -61,6 +61,11 @@ const ( // do expensive lottery data look ups for these blocks. It is // equivalent to 24 hours of work on mainnet. maxLotteryDataBlockDelta = 288 + + // templateRegenSeconds is the minimum required number of seconds elapsed + // before a new template is generated when the previous block hash has not + // changed. + templateRegenSeconds = 15 ) // zeroHash is the zero value hash (all zeros). It is defined as a convenience. @@ -244,6 +249,12 @@ type pauseMsg struct { unpause <-chan struct{} } +// postponeTemplateRegenMsg handles a request to reschedule block template +// regeneration to a future time. +type postponeTemplateRegenMsg struct { + reply chan postponeTemplateRegenResponse +} + // getCurrentTemplateMsg handles a request for the current mining block template. type getCurrentTemplateMsg struct { reply chan getCurrentTemplateResponse @@ -255,6 +266,12 @@ type getCurrentTemplateResponse struct { Template *BlockTemplate } +// postponeTemplateRegenResponse is a response sent to the reply channel of a +// postponeTemplateRegenMsg. +type postponeTemplateRegenResponse struct { + NextTemplateRegen time.Time +} + // setCurrentTemplateMsg handles a request to change the current mining block // template. type setCurrentTemplateMsg struct { @@ -409,6 +426,12 @@ type blockManager struct { lotteryDataBroadcast map[chainhash.Hash]struct{} lotteryDataBroadcastMutex sync.RWMutex + // the following fields handle block template regeneration. + isMining bool + lastRegen time.Time + nextRegen time.Time + regenPostponed bool + cachedCurrentTemplate *BlockTemplate cachedParentTemplate *BlockTemplate AggressiveMining bool @@ -1633,6 +1656,9 @@ func (b *blockManager) limitMap(m map[chainhash.Hash]struct{}, limit int) { // the fetching should proceed. func (b *blockManager) blockHandler() { candidatePeers := list.New() + ticker := time.NewTicker(time.Second) + b.lastRegen = b.server.txMemPool.LastUpdated() + b.nextRegen = b.lastRegen.Add(time.Second * templateRegenSeconds) out: for { select { @@ -1900,6 +1926,16 @@ out: msg.reply <- getCurrentTemplateResponse{ Template: cur, } + case postponeTemplateRegenMsg: + futureRegen := b.nextRegen.Add(time.Second * templateRegenSeconds) + if !b.regenPostponed { + b.lastRegen, b.nextRegen = b.nextRegen, futureRegen + b.regenPostponed = true + } + + msg.reply <- postponeTemplateRegenResponse{ + NextTemplateRegen: b.nextRegen, + } case setCurrentTemplateMsg: b.cachedCurrentTemplate = deepCopyBlockTemplate(msg.Template) @@ -1920,6 +1956,23 @@ out: "handler: %T", msg) } + case <-ticker.C: + mpLastUpdated := b.server.txMemPool.LastUpdated() + // Assert the mempool has been updated since the last block + // template regeneration. + if mpLastUpdated.After(b.lastRegen) { + // Regenerate block template when the threshold is met. + if mpLastUpdated.Equal(b.nextRegen) || + mpLastUpdated.After(b.nextRegen) { + futureRegen := b.nextRegen. + Add(time.Second * templateRegenSeconds) + if !b.regenPostponed { + b.lastRegen, b.nextRegen = b.nextRegen, futureRegen + b.regenPostponed = true + } + } + } + case <-b.quit: break out } @@ -2494,6 +2547,15 @@ func (b *blockManager) GetCurrentTemplate() *BlockTemplate { return response.Template } +// PostPoneTemplateRegen reschedules the block template generation to +// a future time. +func (b *blockManager) PostponeTemplateRegen() time.Time { + reply := make(chan postponeTemplateRegenResponse) + b.msgChan <- postponeTemplateRegenMsg{reply: reply} + response := <-reply + return response.NextTemplateRegen +} + // SetCurrentTemplate sets the current block template for mining. func (b *blockManager) SetCurrentTemplate(bt *BlockTemplate) { reply := make(chan setCurrentTemplateResponse) diff --git a/mempool/mempool.go b/mempool/mempool.go index 393e8514bc..0cfa8c9437 100644 --- a/mempool/mempool.go +++ b/mempool/mempool.go @@ -120,6 +120,10 @@ type Config struct { // to use for indexing the unconfirmed transactions in the memory pool. // This can be nil if the address index is not enabled. ExistsAddrIndex *indexers.ExistsAddrIndex + + // PostponeTemplateRegen defines the function to use to signal + // postponing a template regeneration to a future time. + PostponeTemplateRegen func() time.Time } // Policy houses the policy (configuration parameters) which is used to @@ -610,6 +614,12 @@ func (mp *TxPool) RemoveDoubleSpends(tx *dcrutil.Tx) { func (mp *TxPool) addTransaction(utxoView *blockchain.UtxoViewpoint, tx *dcrutil.Tx, txType stake.TxType, height int64, fee int64) { + if txType != stake.TxTypeSSGen { + futureRegen := mp.cfg.PostponeTemplateRegen() + if futureRegen.After(mp.LastUpdated()) { + log.Info("Block template regeneration postponed to %v", futureRegen) + } + } // Add the transaction to the pool and mark the referenced outpoints // as spent by the pool. msgTx := tx.MsgTx() diff --git a/server.go b/server.go index cbdb39a109..cceb94ee15 100644 --- a/server.go +++ b/server.go @@ -2551,6 +2551,9 @@ func newServer(listenAddrs []string, db database.DB, chainParams *chaincfg.Param PastMedianTime: func() time.Time { return bm.chain.BestSnapshot().MedianTime }, AddrIndex: s.addrIndex, ExistsAddrIndex: s.existsAddrIndex, + PostponeTemplateRegen: func() time.Time { + return bm.PostponeTemplateRegen() + }, } s.txMemPool = mempool.New(&txC)