Skip to content

Commit

Permalink
multi: automate block template regeneration.
Browse files Browse the repository at this point in the history
  • Loading branch information
dnldd committed May 24, 2018
1 parent 678ff1e commit 86f1d5f
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 24 deletions.
97 changes: 96 additions & 1 deletion blockmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -244,6 +249,13 @@ type pauseMsg struct {
unpause <-chan struct{}
}

// postponeTemplateRegenMsg handles a request to reschedule block template
// regeneration to a future time.
type postponeTemplateRegenMsg struct{}

// regenTemplateMsg is a handle a request to regenerate the block template.
type regenTemplateMsg struct{}

// getCurrentTemplateMsg handles a request for the current mining block template.
type getCurrentTemplateMsg struct {
reply chan getCurrentTemplateResponse
Expand Down Expand Up @@ -409,6 +421,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
Expand Down Expand Up @@ -1633,6 +1651,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 {
Expand Down Expand Up @@ -1900,6 +1921,48 @@ out:
msg.reply <- getCurrentTemplateResponse{
Template: cur,
}
case postponeTemplateRegenMsg:
if b.isMining {
futureRegen := b.nextRegen.Add(time.Second *
templateRegenSeconds)
if !b.regenPostponed {
b.lastRegen, b.nextRegen = b.nextRegen, futureRegen
b.regenPostponed = true
}
}

case regenTemplateMsg:
if b.isMining {
// Choose a payment address at random.
payToAddr := cfg.miningAddrs[rand.Intn(len(cfg.miningAddrs))]
template, err := NewBlockTemplate(b.server.rpcServer.policy,
b.server, payToAddr)
if err != nil {
bmgrLog.Errorf(
"Failed to create new block template: %v", err)
}

if template == nil {
// This happens if the template is returned nil because
// there are not enough voters on HEAD and there is
// currently an unsuitable parent cached template to
// try building off of.
bmgrLog.Error(
"Failed to create new block template: not " +
"enough voters and failed to find a suitable " +
"parent template to build from")
} else {
// Update cached templates.
b.SetParentTemplate(b.cachedCurrentTemplate)
b.SetCurrentTemplate(template)

// Set the next template regen time.
b.lastRegen = time.Now()
b.nextRegen = b.lastRegen.Add(time.Second *
templateRegenSeconds)
b.regenPostponed = false
}
}

case setCurrentTemplateMsg:
b.cachedCurrentTemplate = deepCopyBlockTemplate(msg.Template)
Expand All @@ -1920,6 +1983,26 @@ out:
"handler: %T", msg)
}

case <-ticker.C:
if b.isMining {
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
}
Expand Down Expand Up @@ -2494,6 +2577,17 @@ func (b *blockManager) GetCurrentTemplate() *BlockTemplate {
return response.Template
}

// PostPoneTemplateRegen reschedules the block template regeneration to
// a future time.
func (b *blockManager) PostponeTemplateRegen() {
b.msgChan <- postponeTemplateRegenMsg{}
}

// RegenTemplate regenerates the block template.
func (b *blockManager) RegenTemplate() {
b.msgChan <- regenTemplateMsg{}
}

// SetCurrentTemplate sets the current block template for mining.
func (b *blockManager) SetCurrentTemplate(bt *BlockTemplate) {
reply := make(chan setCurrentTemplateResponse)
Expand All @@ -2518,7 +2612,7 @@ func (b *blockManager) SetParentTemplate(bt *BlockTemplate) {

// newBlockManager returns a new Decred block manager.
// Use Start to begin processing asynchronous block and inv updates.
func newBlockManager(s *server, indexManager blockchain.IndexManager, interrupt <-chan struct{}) (*blockManager, error) {
func newBlockManager(s *server, indexManager blockchain.IndexManager, isMining bool, interrupt <-chan struct{}) (*blockManager, error) {
bm := blockManager{
server: s,
rejectedTxns: make(map[chainhash.Hash]struct{}),
Expand All @@ -2529,6 +2623,7 @@ func newBlockManager(s *server, indexManager blockchain.IndexManager, interrupt
progressLogger: newBlockProgressLogger("Processed", bmgrLog),
msgChan: make(chan interface{}, cfg.MaxPeers*3),
headerList: list.New(),
isMining: isMining,
AggressiveMining: !cfg.NonAggressive,
quit: make(chan struct{}),
}
Expand Down
17 changes: 17 additions & 0 deletions mempool/mempool.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,14 @@ 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 in
// postponing block template regeneration to a future time.
PostponeTemplateRegen func()

// RegenTemplate defines the function to use in regenerating a the block
// template.
RegenTemplate func()
}

// Policy houses the policy (configuration parameters) which is used to
Expand Down Expand Up @@ -610,6 +618,15 @@ 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 {
if mp.cfg.RegenTemplate != nil {
mp.cfg.RegenTemplate()
}
} else {
if mp.cfg.PostponeTemplateRegen != nil {
mp.cfg.PostponeTemplateRegen()
}
}
// Add the transaction to the pool and mark the referenced outpoints
// as spent by the pool.
msgTx := tx.MsgTx()
Expand Down
26 changes: 14 additions & 12 deletions mempool/mempool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -392,18 +392,20 @@ func newPoolHarness(chainParams *chaincfg.Params) (*poolHarness, []spendableOutp
MinRelayTxFee: 1000, // 1 Satoshi per byte
StandardVerifyFlags: chain.StandardVerifyFlags,
},
ChainParams: chainParams,
NextStakeDifficulty: chain.NextStakeDifficulty,
FetchUtxoView: chain.FetchUtxoView,
BlockByHash: chain.BlockByHash,
BestHash: chain.BestHash,
BestHeight: chain.BestHeight,
PastMedianTime: chain.PastMedianTime,
CalcSequenceLock: chain.CalcSequenceLock,
SubsidyCache: subsidyCache,
SigCache: nil,
AddrIndex: nil,
ExistsAddrIndex: nil,
ChainParams: chainParams,
NextStakeDifficulty: chain.NextStakeDifficulty,
FetchUtxoView: chain.FetchUtxoView,
BlockByHash: chain.BlockByHash,
BestHash: chain.BestHash,
BestHeight: chain.BestHeight,
PastMedianTime: chain.PastMedianTime,
CalcSequenceLock: chain.CalcSequenceLock,
SubsidyCache: subsidyCache,
SigCache: nil,
AddrIndex: nil,
ExistsAddrIndex: nil,
PostponeTemplateRegen: nil,
RegenTemplate: nil,
}),
}

Expand Down
31 changes: 20 additions & 11 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -2513,7 +2513,14 @@ func newServer(listenAddrs []string, db database.DB, chainParams *chaincfg.Param
if len(indexes) > 0 {
indexManager = indexers.NewManager(db, indexes, chainParams)
}
bm, err := newBlockManager(&s, indexManager, interrupt)

// Set mining flag if config has mining addresses.
var isMining bool
if len(cfg.miningAddrs) > 0 {
isMining = true
}

bm, err := newBlockManager(&s, indexManager, isMining, interrupt)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -2541,16 +2548,18 @@ func newServer(listenAddrs []string, db database.DB, chainParams *chaincfg.Param
bm.chainState.Unlock()
return sDiff, nil
},
FetchUtxoView: bm.chain.FetchUtxoView,
BlockByHash: bm.chain.BlockByHash,
BestHash: func() *chainhash.Hash { return &bm.chain.BestSnapshot().Hash },
BestHeight: func() int64 { return bm.chain.BestSnapshot().Height },
CalcSequenceLock: bm.chain.CalcSequenceLock,
SubsidyCache: bm.chain.FetchSubsidyCache(),
SigCache: s.sigCache,
PastMedianTime: func() time.Time { return bm.chain.BestSnapshot().MedianTime },
AddrIndex: s.addrIndex,
ExistsAddrIndex: s.existsAddrIndex,
FetchUtxoView: bm.chain.FetchUtxoView,
BlockByHash: bm.chain.BlockByHash,
BestHash: func() *chainhash.Hash { return &bm.chain.BestSnapshot().Hash },
BestHeight: func() int64 { return bm.chain.BestSnapshot().Height },
CalcSequenceLock: bm.chain.CalcSequenceLock,
SubsidyCache: bm.chain.FetchSubsidyCache(),
SigCache: s.sigCache,
PastMedianTime: func() time.Time { return bm.chain.BestSnapshot().MedianTime },
AddrIndex: s.addrIndex,
ExistsAddrIndex: s.existsAddrIndex,
PostponeTemplateRegen: func() { bm.PostponeTemplateRegen() },
RegenTemplate: func() { bm.RegenTemplate() },
}
s.txMemPool = mempool.New(&txC)

Expand Down

0 comments on commit 86f1d5f

Please sign in to comment.