Skip to content

Commit

Permalink
multi: subscribe cpuminer for block template updates.
Browse files Browse the repository at this point in the history
This updates the CPU miner to subscribe to template updates instead of creating its own, this excludes GenerateNBlocks. Some field names for the
background template generator and CPU miner have
also been shortened.
  • Loading branch information
dnldd committed Nov 19, 2018
1 parent eb426b2 commit 8816cc5
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 93 deletions.
160 changes: 70 additions & 90 deletions cpuminer.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ package main
import (
"encoding/binary"
"errors"
"fmt"
"math/rand"
"sync"
"time"
Expand Down Expand Up @@ -65,9 +64,11 @@ type cpuminerConfig struct {
// PermitConnectionlessMining allows single node mining on simnet.
PermitConnectionlessMining bool

// BlockTemplateGenerator identifies the instance to use in order to
// generate block templates that the miner will attempt to solve.
BlockTemplateGenerator *BlkTmplGenerator
// bg is the background process responsible for generating block templates.
bg *BgBlkTmplGenerator

// tg is the main workhorse for block template regeneration.
tg *BlkTmplGenerator

// MiningAddrs is a list of payment addresses to use for the generated
// blocks. Each generated block will randomly choose one of them.
Expand Down Expand Up @@ -103,7 +104,8 @@ type cpuminerConfig struct {
// system which is typically sufficient.
type CPUMiner struct {
sync.Mutex
g *BlkTmplGenerator
bg *BgBlkTmplGenerator
tg *BlkTmplGenerator
cfg *cpuminerConfig
numWorkers uint32
started bool
Expand Down Expand Up @@ -229,7 +231,7 @@ func (m *CPUMiner) submitBlock(block *dcrutil.Block) bool {
// This function will return early with false when conditions that trigger a
// stale block such as a new block showing up or periodically when there are
// new transactions and enough time has elapsed without finding a solution.
func (m *CPUMiner) solveBlock(msgBlock *wire.MsgBlock, ticker *time.Ticker, quit chan struct{}) bool {
func (m *CPUMiner) solveBlock(header *wire.BlockHeader, ticker *time.Ticker, quit chan struct{}) bool {
// Choose a random extra nonce offset for this block template and
// worker.
enOffset, err := wire.RandomUint64()
Expand All @@ -239,13 +241,11 @@ func (m *CPUMiner) solveBlock(msgBlock *wire.MsgBlock, ticker *time.Ticker, quit
enOffset = 0
}

// Create a couple of convenience variables.
header := &msgBlock.Header
targetDifficulty := blockchain.CompactToBig(header.Bits)

// Initial state.
lastGenerated := time.Now()
lastTxUpdate := m.g.txSource.LastUpdated()
lastTxUpdate := m.tg.txSource.LastUpdated()
hashesCompleted := uint64(0)

// Note that the entire extra nonce range is iterated and the offset is
Expand All @@ -272,21 +272,21 @@ func (m *CPUMiner) solveBlock(msgBlock *wire.MsgBlock, ticker *time.Ticker, quit
// generated and it has been at least 3 seconds,
// or if it's been one minute.
now := time.Now()
if (lastTxUpdate != m.g.txSource.LastUpdated() &&
if (lastTxUpdate != m.tg.txSource.LastUpdated() &&
now.After(lastGenerated.Add(3*time.Second))) ||
now.After(lastGenerated.Add(60*time.Second)) {
return false
}

err = m.g.UpdateBlockTime(header)
err = m.tg.UpdateBlockTime(header)
if err != nil {
minrLog.Warnf("CPU miner unable to update block template "+
"time: %v", err)
return false
}

default:
// Non-blocking select to fall through
// Non-blocking receive fallthrough.
}

// Update the nonce and hash the block header.
Expand All @@ -307,10 +307,9 @@ func (m *CPUMiner) solveBlock(msgBlock *wire.MsgBlock, ticker *time.Ticker, quit
}

// generateBlocks is a worker that is controlled by the miningWorkerController.
// It is self contained in that it creates block templates and attempts to solve
// them while detecting when it is performing stale work and reacting
// accordingly by generating a new block template. When a block is solved, it
// is submitted.
// It subscribes to block template updates and attempts to solve them while
// detecting stale work. When a block is solved block it is relayed to
// the network.
//
// It must be run as a goroutine.
func (m *CPUMiner) generateBlocks(quit chan struct{}) {
Expand All @@ -321,79 +320,57 @@ func (m *CPUMiner) generateBlocks(quit chan struct{}) {
ticker := time.NewTicker(333 * time.Millisecond)
defer ticker.Stop()

// Subscribe for block template updates.
updateChan := m.bg.RequestTemplateUpdate()

out:
for {
// Quit when the miner is stopped.
select {
case <-quit:
break out
default:
// Non-blocking select to fall through
}

// If not in connectionless mode, wait until there is a connection to
// at least one other peer since there is no way to relay a found
// block or receive transactions to work on when there are no
// connected peers.
if !m.cfg.PermitConnectionlessMining && m.cfg.ConnectedCount() == 0 {
time.Sleep(time.Second)
continue
}

// No point in searching for a solution before the chain is
// synced. Also, grab the same lock as used for block
// submission, since the current block will 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.g.chain.BestSnapshot().Height
if curHeight != 0 && !m.cfg.IsCurrent() {
m.submitBlockLock.Unlock()
time.Sleep(time.Second)
continue
}

// Choose a payment address at random.
rand.Seed(time.Now().UnixNano())
payToAddr := m.cfg.MiningAddrs[rand.Intn(len(m.cfg.MiningAddrs))]

// Create a new block template using the available transactions
// in the memory pool as a source of transactions to potentially
// include in the block.
template, err := m.g.NewBlockTemplate(payToAddr)
m.submitBlockLock.Unlock()
if err != nil {
errStr := fmt.Sprintf("Failed to create new block "+
"template: %v", err)
minrLog.Errorf(errStr)
continue
}
case template := <-updateChan:
// Resubscribe for block template updates after receiving an
// update.
updateChan = m.bg.RequestTemplateUpdate()

// If not in connectionless mode, wait until there is a connection
// to at least one other peer since there is no way to relay a
// found block or receive transactions to work on when there are
// no connected peers.
if !m.cfg.PermitConnectionlessMining && m.cfg.ConnectedCount() == 0 {
time.Sleep(time.Second)
continue
}

// Not enough voters.
if template == nil {
continue
}
// This prevents memory exhaustion issues when mining aggressively
// on a simulation network.
if m.cfg.PermitConnectionlessMining {
if m.minedOnParents[template.Block.Header.PrevBlock] >=
maxSimnetToMine {
minrLog.Tracef("too many blocks mined on parent, " +
"stopping until there are enough votes on these to " +
"make a new block")
continue
}
}

// This prevents you from causing memory exhaustion issues
// when mining aggressively in a simulation network.
if m.cfg.PermitConnectionlessMining {
if m.minedOnParents[template.Block.Header.PrevBlock] >=
maxSimnetToMine {
minrLog.Tracef("too many blocks mined on parent, stopping " +
"until there are enough votes on these to make a new " +
"block")
continue
minrLog.Debugf("new block template from background generator "+
"(height: %v, hash: %v)", template.Block.Header.Height,
template.Block.BlockHash())

// Attempt to solve the block. The function will exit early
// with false when conditions that trigger a stale block, so
// a new block template can be generated. When the return is
// true a solution was found, so submit the solved block.
if m.solveBlock(&template.Block.Header, ticker, quit) {
blk := dcrutil.NewBlock(template.Block)
m.submitBlock(blk)
m.minedOnParents[template.Block.Header.PrevBlock]++
}
}

// Attempt to solve the block. The function will exit early
// with false when conditions that trigger a stale block, so
// a new block template can be generated. When the return is
// true a solution was found, so submit the solved block.
if m.solveBlock(template.Block, ticker, quit) {
block := dcrutil.NewBlock(template.Block)
m.submitBlock(block)
m.minedOnParents[template.Block.Header.PrevBlock]++
default:
// Non-blocking receive fallthrough.
}
}

Expand Down Expand Up @@ -639,29 +616,31 @@ func (m *CPUMiner) GenerateNBlocks(n uint32) ([]*chainhash.Hash, error) {
// Create a new block template using the available transactions
// in the memory pool as a source of transactions to potentially
// include in the block.
template, err := m.g.NewBlockTemplate(payToAddr)
template, err := m.tg.NewBlockTemplate(payToAddr)
m.submitBlockLock.Unlock()
if err != nil {
errStr := fmt.Sprintf("Failed to create new block "+
"template: %v", err)
minrLog.Errorf(errStr)
minrLog.Errorf("Failed to create new block template: %v", err)
continue
}

if template == nil {
errStr := fmt.Sprintf("Not enough voters on parent block " +
minrLog.Debugf("Not enough voters on parent block " +
"and failed to pull parent template")
minrLog.Debugf(errStr)
continue
}

minrLog.Debugf("new block template from background generator "+
"(height: %v, hash: %v)", template.Block.Header.Height,
template.Block.BlockHash())

// Attempt to solve the block. The function will exit early
// with false when conditions that trigger a stale block, so
// a new block template can be generated. When the return is
// true a solution was found, so submit the solved block.
if m.solveBlock(template.Block, ticker, nil) {
block := dcrutil.NewBlock(template.Block)
m.submitBlock(block)
blockHashes[i] = block.Hash()
if m.solveBlock(&template.Block.Header, ticker, nil) {
blk := dcrutil.NewBlock(template.Block)
m.submitBlock(blk)
blockHashes[i] = blk.Hash()
i++
if i == n {
minrLog.Tracef("Generated %d blocks", i)
Expand All @@ -682,7 +661,8 @@ func (m *CPUMiner) GenerateNBlocks(n uint32) ([]*chainhash.Hash, error) {
// type for more details.
func newCPUMiner(cfg *cpuminerConfig) *CPUMiner {
return &CPUMiner{
g: cfg.BlockTemplateGenerator,
bg: cfg.bg,
tg: cfg.tg,
cfg: cfg,
numWorkers: defaultNumWorkers,
updateNumWorkers: make(chan struct{}),
Expand Down
5 changes: 2 additions & 3 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -2555,16 +2555,15 @@ func newServer(listenAddrs []string, db database.DB, chainParams *chaincfg.Param
tg := newBlkTmplGenerator(&policy, s.txMemPool, s.timeSource, s.sigCache,
s.chainParams, bm.chain, bm)

// Create the background block template generator if the config has a
// mining address.
if len(cfg.miningAddrs) > 0 {
s.bg = newBgBlkTmplGenerator(tg, cfg.miningAddrs, cfg.SimNet)
}

s.cpuMiner = newCPUMiner(&cpuminerConfig{
bg: s.bg,
tg: tg,
ChainParams: s.chainParams,
PermitConnectionlessMining: cfg.SimNet,
BlockTemplateGenerator: tg,
MiningAddrs: cfg.miningAddrs,
ProcessBlock: bm.ProcessBlock,
ConnectedCount: s.ConnectedCount,
Expand Down

0 comments on commit 8816cc5

Please sign in to comment.