Implement a built-in concurrent CPU miner.
This commit implements a built-in concurrent CPU miner that can be enabled with the combination of the --generate and --miningaddr options. The --blockminsize, --blockmaxsize, and --blockprioritysize configuration options wich already existed prior to this commit control the block template generation and hence affect blocks mined via the new CPU miner. The following is a quick overview of the changes and design: - Starting btcd with --generate and no addresses specified via --miningaddr will give an error and exit immediately - Makes use of multiple worker goroutines which independently create block templates, solve them, and submit the solved blocks - The default number of worker threads are based on the number of processor cores in the system and can be dynamically changed at run-time - There is a separate speed monitor goroutine used to collate periodic updates from the workers to calculate overall hashing speed - The current mining state, number of workers, and hashes per second can be queried - Updated sample-btcd.conf file has been updated to include the coin generation (mining) settings - Updated doc.go for the new command line options In addition the old --getworkkey option is now deprecated in favor of the new --miningaddr option. This was changed for a few reasons: - There is no reason to have a separate list of keys for getwork and CPU mining - getwork is deprecated and will be going away in the future so that means the --getworkkey flag will also be going away - Having the work 'key' in the option can be confused with wanting a private key while --miningaddr make it a little more clear it is an address that is required Closes #137. Reviewed by @jrick.
This commit is contained in:
parent
8ce9c21148
commit
e25b644d3b
7 changed files with 608 additions and 17 deletions
|
@ -1059,6 +1059,7 @@ out:
|
|||
break out
|
||||
}
|
||||
}
|
||||
|
||||
b.wg.Done()
|
||||
bmgrLog.Trace("Block handler done")
|
||||
}
|
||||
|
@ -1295,7 +1296,7 @@ func (b *blockManager) CalcNextRequiredDifficulty(timestamp time.Time) (uint32,
|
|||
// chain. It is funneled through the block manager since btcchain is not safe
|
||||
// for concurrent access.
|
||||
func (b *blockManager) ProcessBlock(block *btcutil.Block) (bool, error) {
|
||||
reply := make(chan processBlockResponse)
|
||||
reply := make(chan processBlockResponse, 1)
|
||||
b.msgChan <- processBlockMsg{block: block, reply: reply}
|
||||
response := <-reply
|
||||
return response.isOrphan, response.err
|
||||
|
|
53
config.go
53
config.go
|
@ -42,6 +42,7 @@ const (
|
|||
blockMaxSizeMin = 1000
|
||||
blockMaxSizeMax = btcwire.MaxBlockPayload - 1000
|
||||
defaultBlockPrioritySize = 50000
|
||||
defaultGenerate = false
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -99,15 +100,17 @@ type config struct {
|
|||
DebugLevel string `short:"d" long:"debuglevel" description:"Logging level for all subsystems {trace, debug, info, warn, error, critical} -- You may also specify <subsystem>=<level>,<subsystem2>=<level>,... to set the log level for individual subsystems -- Use show to list available subsystems"`
|
||||
Upnp bool `long:"upnp" description:"Use UPnP to map our listening port outside of NAT"`
|
||||
FreeTxRelayLimit float64 `long:"limitfreerelay" description:"Limit relay of transactions with no transaction fee to the given amount in thousands of bytes per minute"`
|
||||
Generate bool `long:"generate" description:"Generate (mine) bitcoins using the CPU"`
|
||||
MiningAddrs []string `long:"miningaddr" description:"Add the specified payment address to the list of addresses to use for generated blocks -- At least one address is required if the generate option is set"`
|
||||
BlockMinSize uint32 `long:"blockminsize" description:"Mininum block size in bytes to be used when creating a block"`
|
||||
BlockMaxSize uint32 `long:"blockmaxsize" description:"Maximum block size in bytes to be used when creating a block"`
|
||||
BlockPrioritySize uint32 `long:"blockprioritysize" description:"Size in bytes for high-priority/low-fee transactions when creating a block"`
|
||||
GetWorkKeys []string `long:"getworkkey" description:"Use the specified payment address for blocks generated by getwork."`
|
||||
GetWorkKeys []string `long:"getworkkey" description:"DEPRECATED -- Use the --miningaddr option instead"`
|
||||
onionlookup func(string) ([]net.IP, error)
|
||||
lookup func(string) ([]net.IP, error)
|
||||
oniondial func(string, string) (net.Conn, error)
|
||||
dial func(string, string) (net.Conn, error)
|
||||
miningKeys []btcutil.Address
|
||||
miningAddrs []btcutil.Address
|
||||
}
|
||||
|
||||
// serviceOptions defines the configuration options for btcd as a service on
|
||||
|
@ -308,6 +311,7 @@ func loadConfig() (*config, []string, error) {
|
|||
BlockMinSize: defaultBlockMinSize,
|
||||
BlockMaxSize: defaultBlockMaxSize,
|
||||
BlockPrioritySize: defaultBlockPrioritySize,
|
||||
Generate: defaultGenerate,
|
||||
}
|
||||
|
||||
// Service options which are only added on Windows.
|
||||
|
@ -539,25 +543,58 @@ func loadConfig() (*config, []string, error) {
|
|||
cfg.BlockPrioritySize = minUint32(cfg.BlockPrioritySize, cfg.BlockMaxSize)
|
||||
cfg.BlockMinSize = minUint32(cfg.BlockMinSize, cfg.BlockMaxSize)
|
||||
|
||||
// Check keys are valid and saved parsed versions.
|
||||
cfg.miningKeys = make([]btcutil.Address, 0, len(cfg.GetWorkKeys))
|
||||
// Check getwork keys are valid and saved parsed versions.
|
||||
cfg.miningAddrs = make([]btcutil.Address, 0, len(cfg.GetWorkKeys)+
|
||||
len(cfg.MiningAddrs))
|
||||
for _, strAddr := range cfg.GetWorkKeys {
|
||||
addr, err := btcutil.DecodeAddress(strAddr, activeNetParams.Params)
|
||||
addr, err := btcutil.DecodeAddress(strAddr,
|
||||
activeNetParams.Params)
|
||||
if err != nil {
|
||||
str := "%s: the specified getworkkey '%s' failed to decode: %v"
|
||||
str := "%s: getworkkey '%s' failed to decode: %v"
|
||||
err := fmt.Errorf(str, funcName, strAddr, err)
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
parser.WriteHelp(os.Stderr)
|
||||
return nil, nil, err
|
||||
}
|
||||
if !addr.IsForNet(activeNetParams.Params) {
|
||||
str := "%s: the specified getworkkey '%s' is on the wrong network"
|
||||
str := "%s: getworkkey '%s' is on the wrong network"
|
||||
err := fmt.Errorf(str, funcName, strAddr)
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
parser.WriteHelp(os.Stderr)
|
||||
return nil, nil, err
|
||||
}
|
||||
cfg.miningKeys = append(cfg.miningKeys, addr)
|
||||
cfg.miningAddrs = append(cfg.miningAddrs, addr)
|
||||
}
|
||||
|
||||
// Check mining addresses are valid and saved parsed versions.
|
||||
for _, strAddr := range cfg.MiningAddrs {
|
||||
addr, err := btcutil.DecodeAddress(strAddr, activeNetParams.Params)
|
||||
if err != nil {
|
||||
str := "%s: mining address '%s' failed to decode: %v"
|
||||
err := fmt.Errorf(str, funcName, strAddr, err)
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
parser.WriteHelp(os.Stderr)
|
||||
return nil, nil, err
|
||||
}
|
||||
if !addr.IsForNet(activeNetParams.Params) {
|
||||
str := "%s: mining address '%s' is on the wrong network"
|
||||
err := fmt.Errorf(str, funcName, strAddr)
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
parser.WriteHelp(os.Stderr)
|
||||
return nil, nil, err
|
||||
}
|
||||
cfg.miningAddrs = append(cfg.miningAddrs, addr)
|
||||
}
|
||||
|
||||
// Ensure there is at least one mining address when the generate flag is
|
||||
// set.
|
||||
if cfg.Generate && len(cfg.MiningAddrs) == 0 {
|
||||
str := "%s: the generate flag is set, but there are no mining " +
|
||||
"addresses specified "
|
||||
err := fmt.Errorf(str, funcName)
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
parser.WriteHelp(os.Stderr)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Add default port to all listener addresses if needed and remove
|
||||
|
|
501
cpuminer.go
Normal file
501
cpuminer.go
Normal file
|
@ -0,0 +1,501 @@
|
|||
// Copyright (c) 2014 Conformal Systems LLC.
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/conformal/btcchain"
|
||||
"github.com/conformal/btcutil"
|
||||
"github.com/conformal/btcwire"
|
||||
"math/rand"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// maxNonce is the maximum value a nonce can be in a block header.
|
||||
maxNonce = ^uint32(0) // 2^32 - 1
|
||||
|
||||
// maxExtraNonce is the maximum value an extra nonce used in a coinbase
|
||||
// transaction can be.
|
||||
maxExtraNonce = ^uint64(0) // 2^64 - 1
|
||||
|
||||
// hpsUpdateSecs is the number of seconds to wait in between each
|
||||
// update to the hashes per second monitor.
|
||||
hpsUpdateSecs = 10
|
||||
|
||||
// hashUpdateSec is the number of seconds each worker waits in between
|
||||
// notifying the speed monitor with how many hashes have been completed
|
||||
// while they are actively searching for a solution. This is done to
|
||||
// reduce the amount of syncs between the workers that must be done to
|
||||
// keep track of the hashes per second.
|
||||
hashUpdateSecs = 15
|
||||
)
|
||||
|
||||
var (
|
||||
// defaultNumWorkers is the default number of workers to use for mining
|
||||
// and is based on the number of processor cores. This helps ensure the
|
||||
// system stays reasonably responsive under heavy load.
|
||||
defaultNumWorkers = uint32(runtime.NumCPU())
|
||||
)
|
||||
|
||||
// CPUMiner provides facilities for solving blocks (mining) using the CPU in
|
||||
// a concurrency-safe manner. It consists of two main goroutines -- a speed
|
||||
// monitor and a controller for worker goroutines which generate and solve
|
||||
// blocks. The number of goroutines can be set via the SetMaxGoRoutines
|
||||
// function, but the default is based on the number of processor cores in the
|
||||
// system which is typically sufficient.
|
||||
type CPUMiner struct {
|
||||
sync.Mutex
|
||||
server *server
|
||||
numWorkers uint32
|
||||
started bool
|
||||
submitBlockLock sync.Mutex
|
||||
wg sync.WaitGroup
|
||||
workerWg sync.WaitGroup
|
||||
updateNumWorkers chan struct{}
|
||||
queryHashesPerSec chan float64
|
||||
updateHashes chan uint64
|
||||
speedMonitorQuit chan struct{}
|
||||
quit chan struct{}
|
||||
}
|
||||
|
||||
// speedMonitor handles tracking the number of hashes per second the mining
|
||||
// process is performing. It must be run as a goroutine.
|
||||
func (m *CPUMiner) speedMonitor() {
|
||||
minrLog.Tracef("CPU miner speed monitor started")
|
||||
|
||||
var hashesPerSec float64
|
||||
var totalHashes uint64
|
||||
ticker := time.NewTicker(time.Second * hpsUpdateSecs)
|
||||
|
||||
out:
|
||||
for {
|
||||
select {
|
||||
// Periodic updates from the workers with how many hashes they
|
||||
// have performed.
|
||||
case numHashes := <-m.updateHashes:
|
||||
totalHashes += numHashes
|
||||
|
||||
// Time to update the hashes per second.
|
||||
case <-ticker.C:
|
||||
curHashesPerSec := float64(totalHashes) / hpsUpdateSecs
|
||||
if hashesPerSec == 0 {
|
||||
hashesPerSec = curHashesPerSec
|
||||
}
|
||||
hashesPerSec = (hashesPerSec + curHashesPerSec) / 2
|
||||
totalHashes = 0
|
||||
if hashesPerSec != 0 {
|
||||
minrLog.Debugf("Hash speed: %6.0f kilohashes/s",
|
||||
hashesPerSec/1000)
|
||||
}
|
||||
|
||||
// Request for the number of hashes per second.
|
||||
case m.queryHashesPerSec <- hashesPerSec:
|
||||
// Nothing to do.
|
||||
|
||||
case <-m.speedMonitorQuit:
|
||||
break out
|
||||
}
|
||||
}
|
||||
|
||||
m.wg.Done()
|
||||
minrLog.Tracef("CPU miner speed monitor done")
|
||||
}
|
||||
|
||||
// submitBlock submits the passed block to network after ensuring it passes all
|
||||
// of the consensus validation rules.
|
||||
func (m *CPUMiner) submitBlock(block *btcutil.Block) bool {
|
||||
m.submitBlockLock.Lock()
|
||||
defer m.submitBlockLock.Unlock()
|
||||
|
||||
// Ensure the block is not stale since a new block could have shown up
|
||||
// while the solution was being found. Typically that condition is
|
||||
// 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()
|
||||
msgBlock := block.MsgBlock()
|
||||
if !msgBlock.Header.PrevBlock.IsEqual(latestHash) {
|
||||
minrLog.Debugf("Block submitted via CPU miner with previous "+
|
||||
"block %s is stale", msgBlock.Header.PrevBlock)
|
||||
return false
|
||||
}
|
||||
|
||||
// Process this block using the same rules as blocks coming from other
|
||||
// nodes. This will in turn relay it to the network like normal.
|
||||
isOrphan, err := m.server.blockManager.ProcessBlock(block)
|
||||
if err != nil {
|
||||
// Anything other than a rule violation is an unexpected error,
|
||||
// so log that error as an internal error.
|
||||
if _, ok := err.(btcchain.RuleError); !ok {
|
||||
minrLog.Errorf("Unexpected error while processing "+
|
||||
"block submitted via CPU miner: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
minrLog.Debugf("Block submitted via CPU miner rejected: %v", err)
|
||||
return false
|
||||
}
|
||||
if isOrphan {
|
||||
minrLog.Debugf("Block submitted via CPU miner is an orphan")
|
||||
return false
|
||||
}
|
||||
|
||||
// The block was accepted.
|
||||
blockSha, _ := block.Sha()
|
||||
coinbaseTx := block.MsgBlock().Transactions[0].TxOut[0]
|
||||
minrLog.Infof("Block submitted via CPU miner accepted (hash %s, "+
|
||||
"amount %v)", blockSha, btcutil.Amount(coinbaseTx.Value))
|
||||
return true
|
||||
}
|
||||
|
||||
// solveBlock attempts to find some combination of a nonce, extra nonce, and
|
||||
// current timestamp which makes the passed block hash to a value less than the
|
||||
// target difficulty. The timestamp is updated periodically and the passed
|
||||
// block is modified with all tweaks during this process. This means that
|
||||
// when the function returns true, the block is ready for submission.
|
||||
//
|
||||
// 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 *btcwire.MsgBlock, blockHeight int64,
|
||||
ticker *time.Ticker, quit chan struct{}) bool {
|
||||
|
||||
// Choose a random extra nonce offset for this block template and
|
||||
// worker.
|
||||
enOffset, err := btcwire.RandomUint64()
|
||||
if err != nil {
|
||||
minrLog.Errorf("Unexpected error while generating random "+
|
||||
"extra nonce offset: %v", err)
|
||||
enOffset = 0
|
||||
}
|
||||
|
||||
// Create a couple of convenience variables.
|
||||
header := &msgBlock.Header
|
||||
targetDifficulty := btcchain.CompactToBig(header.Bits)
|
||||
|
||||
// Initial state.
|
||||
lastGenerated := time.Now()
|
||||
lastTxUpdate := m.server.txMemPool.LastUpdated()
|
||||
hashesCompleted := uint64(0)
|
||||
|
||||
// Note that the entire extra nonce range is iterated and the offset is
|
||||
// added relying on the fact that overflow will wrap around 0 as
|
||||
// provided by the Go spec.
|
||||
for extraNonce := uint64(0); extraNonce < maxExtraNonce; extraNonce++ {
|
||||
// Update the extra nonce in the block template with the
|
||||
// new value by regenerating the coinbase script and
|
||||
// setting the merkle root to the new value. The
|
||||
UpdateExtraNonce(msgBlock, blockHeight, extraNonce+enOffset)
|
||||
|
||||
// Search through the entire nonce range for a solution while
|
||||
// periodically checking for early quit and stale block
|
||||
// conditions along with updates to the speed monitor.
|
||||
for i := uint32(0); i <= maxNonce; i++ {
|
||||
select {
|
||||
case <-quit:
|
||||
return false
|
||||
|
||||
case <-ticker.C:
|
||||
m.updateHashes <- hashesCompleted
|
||||
hashesCompleted = 0
|
||||
|
||||
// The current block is stale if the best block
|
||||
// has changed.
|
||||
bestHash, _ := m.server.blockManager.chainState.Best()
|
||||
if !header.PrevBlock.IsEqual(bestHash) {
|
||||
return false
|
||||
}
|
||||
|
||||
// The current block is stale if the memory pool
|
||||
// has been updated since the block template was
|
||||
// generated and it has been at least one
|
||||
// minute.
|
||||
if lastTxUpdate != m.server.txMemPool.LastUpdated() &&
|
||||
time.Now().After(lastGenerated.Add(time.Minute)) {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
UpdateBlockTime(msgBlock, m.server.blockManager)
|
||||
|
||||
default:
|
||||
// Non-blocking select to fall through
|
||||
}
|
||||
|
||||
// Update the nonce and hash the block header. Each
|
||||
// hash is actually a double sha256 (two hashes), so
|
||||
// increment the number of hashes completed for each
|
||||
// attempt accordingly.
|
||||
header.Nonce = i
|
||||
hash, _ := header.BlockSha()
|
||||
hashesCompleted += 2
|
||||
|
||||
// The block is solved when the new block hash is less
|
||||
// than the target difficulty. Yay!
|
||||
if btcchain.ShaHashToBig(&hash).Cmp(targetDifficulty) <= 0 {
|
||||
m.updateHashes <- hashesCompleted
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// 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 must be run as a goroutine.
|
||||
func (m *CPUMiner) generateBlocks(quit chan struct{}) {
|
||||
minrLog.Tracef("Starting generate blocks worker")
|
||||
|
||||
// Start a ticker which is used to signal checks for stale work and
|
||||
// updates to the speed monitor.
|
||||
ticker := time.NewTicker(time.Second * hashUpdateSecs)
|
||||
out:
|
||||
for {
|
||||
// Quit when the miner is stopped.
|
||||
select {
|
||||
case <-quit:
|
||||
break out
|
||||
default:
|
||||
// Non-blocking select to fall through
|
||||
}
|
||||
|
||||
// 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.server.blockManager.chainState.Best()
|
||||
if curHeight != 0 && !m.server.blockManager.IsCurrent() {
|
||||
m.submitBlockLock.Unlock()
|
||||
time.Sleep(time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
// Choose a payment address at random.
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
payToAddr := cfg.miningAddrs[rand.Intn(len(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 := NewBlockTemplate(payToAddr, m.server.txMemPool)
|
||||
m.submitBlockLock.Unlock()
|
||||
if err != nil {
|
||||
errStr := fmt.Sprintf("Failed to create new block "+
|
||||
"template: %v", err)
|
||||
minrLog.Errorf(errStr)
|
||||
continue
|
||||
}
|
||||
|
||||
// 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, curHeight+1, ticker, quit) {
|
||||
block := btcutil.NewBlock(template.block)
|
||||
m.submitBlock(block)
|
||||
}
|
||||
}
|
||||
|
||||
ticker.Stop()
|
||||
m.workerWg.Done()
|
||||
minrLog.Tracef("Generate blocks worker done")
|
||||
}
|
||||
|
||||
// miningWorkerController launches the worker goroutines that are used to
|
||||
// generate block templates and solve them. It also provides the ability to
|
||||
// dynamically adjust the number of running worker goroutines.
|
||||
//
|
||||
// It must be run as a goroutine.
|
||||
func (m *CPUMiner) miningWorkerController() {
|
||||
// launchWorkers groups common code to launch a specified number of
|
||||
// workers for generating blocks.
|
||||
var runningWorkers []chan struct{}
|
||||
launchWorkers := func(numWorkers uint32) {
|
||||
for i := uint32(0); i < numWorkers; i++ {
|
||||
quit := make(chan struct{})
|
||||
runningWorkers = append(runningWorkers, quit)
|
||||
|
||||
m.workerWg.Add(1)
|
||||
go m.generateBlocks(quit)
|
||||
}
|
||||
}
|
||||
|
||||
// Launch the current number of workers by default.
|
||||
runningWorkers = make([]chan struct{}, 0, m.numWorkers)
|
||||
launchWorkers(m.numWorkers)
|
||||
|
||||
out:
|
||||
for {
|
||||
select {
|
||||
// Update the number of running workers.
|
||||
case <-m.updateNumWorkers:
|
||||
// No change.
|
||||
numRunning := uint32(len(runningWorkers))
|
||||
if m.numWorkers == numRunning {
|
||||
continue
|
||||
}
|
||||
|
||||
// Add new workers.
|
||||
if m.numWorkers > numRunning {
|
||||
launchWorkers(m.numWorkers - numRunning)
|
||||
continue
|
||||
}
|
||||
|
||||
// Signal the most recently created goroutines to exit.
|
||||
for i := numRunning - 1; i >= m.numWorkers; i-- {
|
||||
close(runningWorkers[i])
|
||||
runningWorkers[i] = nil
|
||||
runningWorkers = runningWorkers[:i]
|
||||
}
|
||||
|
||||
case <-m.quit:
|
||||
for _, quit := range runningWorkers {
|
||||
close(quit)
|
||||
}
|
||||
break out
|
||||
}
|
||||
}
|
||||
|
||||
// Wait until all workers shut down to stop the speed monitor since
|
||||
// they rely on being able to send updates to it.
|
||||
m.workerWg.Wait()
|
||||
close(m.speedMonitorQuit)
|
||||
m.wg.Done()
|
||||
}
|
||||
|
||||
// Start begins the CPU mining process as well as the speed monitor used to
|
||||
// track hashing metrics. Calling this function when the CPU miner has
|
||||
// already been started will have no effect.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (m *CPUMiner) Start() {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
// Nothing to do if the miner is already running.
|
||||
if m.started {
|
||||
return
|
||||
}
|
||||
|
||||
m.quit = make(chan struct{})
|
||||
m.speedMonitorQuit = make(chan struct{})
|
||||
m.wg.Add(2)
|
||||
go m.speedMonitor()
|
||||
go m.miningWorkerController()
|
||||
|
||||
m.started = true
|
||||
minrLog.Infof("CPU miner started")
|
||||
}
|
||||
|
||||
// Stop gracefully stops the mining process by signalling all workers, and the
|
||||
// speed monitor to quit. Calling this function when the CPU miner has not
|
||||
// already been started will have no effect.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (m *CPUMiner) Stop() {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
// Nothing to do if the miner is not currently running.
|
||||
if !m.started {
|
||||
return
|
||||
}
|
||||
|
||||
close(m.quit)
|
||||
m.wg.Wait()
|
||||
m.started = false
|
||||
minrLog.Infof("CPU miner stopped")
|
||||
}
|
||||
|
||||
// IsMining returns whether or not the CPU miner has been started and is
|
||||
// therefore currenting mining.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (m *CPUMiner) IsMining() bool {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
return m.started
|
||||
}
|
||||
|
||||
// HashesPerSecond returns the number of hashes per second the mining process
|
||||
// is performing. 0 is returned if the miner is not currently running.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (m *CPUMiner) HashesPerSecond() float64 {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
// Nothing to do if the miner is not currently running.
|
||||
if !m.started {
|
||||
return 0
|
||||
}
|
||||
|
||||
return <-m.queryHashesPerSec
|
||||
}
|
||||
|
||||
// SetNumWorkers sets the number of workers to create which solve blocks. Any
|
||||
// negative values will cause a default number of workers to be used which is
|
||||
// based on the number of processor cores in the system. A value of 0 will
|
||||
// cause all CPU mining to be stopped.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (m *CPUMiner) SetNumWorkers(numWorkers int32) {
|
||||
if numWorkers == 0 {
|
||||
m.Stop()
|
||||
}
|
||||
|
||||
// Don't lock until after the first check since Stop does its own
|
||||
// locking.
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
// Use default if provided value is negative.
|
||||
if numWorkers < 0 {
|
||||
m.numWorkers = defaultNumWorkers
|
||||
} else {
|
||||
m.numWorkers = uint32(numWorkers)
|
||||
}
|
||||
|
||||
// When the miner is already running, notify the controller about the
|
||||
// the change.
|
||||
if m.started {
|
||||
m.updateNumWorkers <- struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// NumWorkers returns the number of workers which are running to solve blocks.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (m *CPUMiner) NumWorkers() int32 {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
return int32(m.numWorkers)
|
||||
}
|
||||
|
||||
// newCPUMiner returns a new instance of a CPU miner for the provided server.
|
||||
// Use Start to begin the mining process. See the documentation for CPUMiner
|
||||
// type for more details.
|
||||
func newCPUMiner(s *server) *CPUMiner {
|
||||
return &CPUMiner{
|
||||
server: s,
|
||||
numWorkers: defaultNumWorkers,
|
||||
updateNumWorkers: make(chan struct{}),
|
||||
queryHashesPerSec: make(chan float64),
|
||||
updateHashes: make(chan uint64),
|
||||
}
|
||||
}
|
10
doc.go
10
doc.go
|
@ -77,16 +77,18 @@ Application Options:
|
|||
--limitfreerelay= Limit relay of transactions with no transaction fee
|
||||
to the given amount in thousands of bytes per minute
|
||||
(15)
|
||||
|
||||
--generate= Generate (mine) bitcoins using the CPU
|
||||
--miningaddr= Add the specified payment address to the list of
|
||||
addresses to use for generated blocks -- At least
|
||||
one address is required if the generate option is set
|
||||
--blockminsize= Mininum block size in bytes to be used when creating
|
||||
a block
|
||||
--blockmaxsize= Maximum block size in bytes to be used when creating
|
||||
a block (750000)
|
||||
--blockprioritysize= Size in bytes for high-priority/low-fee transactions
|
||||
when creating a block (50000)
|
||||
--getworkkey= Use the specified hex-encoded serialized public keys
|
||||
as the payment address for blocks generated by
|
||||
getwork.
|
||||
|
||||
--getworkkey= DEPRECATED -- Use the --miningaddr option instead
|
||||
Help Options:
|
||||
-h, --help Show this help message
|
||||
|
||||
|
|
|
@ -1515,7 +1515,7 @@ func handleGetWorkRequest(s *rpcServer) (interface{}, error) {
|
|||
|
||||
// Choose a payment address at random.
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
payToAddr := cfg.miningKeys[rand.Intn(len(cfg.miningKeys))]
|
||||
payToAddr := cfg.miningAddrs[rand.Intn(len(cfg.miningAddrs))]
|
||||
|
||||
template, err := NewBlockTemplate(payToAddr, s.server.txMemPool)
|
||||
if err != nil {
|
||||
|
@ -1779,12 +1779,12 @@ func handleGetWorkSubmission(s *rpcServer, hexData string) (interface{}, error)
|
|||
func handleGetWork(s *rpcServer, cmd btcjson.Cmd) (interface{}, error) {
|
||||
c := cmd.(*btcjson.GetWorkCmd)
|
||||
|
||||
// Respond with an error if there are no public keys to pay the created
|
||||
// Respond with an error if there are no addresses to pay the created
|
||||
// blocks to.
|
||||
if len(cfg.miningKeys) == 0 {
|
||||
if len(cfg.miningAddrs) == 0 {
|
||||
return nil, btcjson.Error{
|
||||
Code: btcjson.ErrInternal.Code,
|
||||
Message: "No payment addresses specified via --getworkkey",
|
||||
Message: "No payment addresses specified via --miningaddr",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -179,6 +179,45 @@
|
|||
; norpc=1
|
||||
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
; Coin Generation (Mining) Settings - The following options control the
|
||||
; generation of block templates used by external mining applications through RPC
|
||||
; calls as well as the built-in CPU miner (if enabled).
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
; Enable built-in CPU mining.
|
||||
;
|
||||
; NOTE: This is typically only useful for testing purposes such as testnet or
|
||||
; simnet since the difficutly on mainnet is far too high for CPU mining to be
|
||||
; worth your while.
|
||||
; generate=false
|
||||
|
||||
; Add addresses to pay mined blocks to for CPU mining and the block templates
|
||||
; generated for the getwork RPC as desired. One address per line.
|
||||
; miningaddr=1yourbitcoinaddress
|
||||
; miningaddr=1yourbitcoinaddress2
|
||||
; miningaddr=1yourbitcoinaddress3
|
||||
|
||||
; Specify the minimum block size in bytes to create. By default, only
|
||||
; transactions which have enough fees or a high enough priority will be included
|
||||
; in generated block templates. Specifying a minimum block size will instead
|
||||
; attempt to fill generated block templates up with transactions until it is at
|
||||
; least the specified number of bytes.
|
||||
; blockminsize=0
|
||||
|
||||
; Specify the maximum block size in bytes to create. This value will be limited
|
||||
; to the consensus limit if it is larger than this value.
|
||||
; blockmaxsize=750000
|
||||
|
||||
; Specify the size in bytes of the high-priority/low-fee area when creating a
|
||||
; block. Transactions which consist of large amounts, old inputs, and small
|
||||
; sizes have the highest priority. One consequence of this is that as low-fee
|
||||
; or free transactions age, they raise in priority thereby making them more
|
||||
; likely to be included in this section of a new block. This value is limited
|
||||
; by the blackmaxsize option and will be limited as needed.
|
||||
; blockprioritysize=50000
|
||||
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
; Debug
|
||||
; ------------------------------------------------------------------------------
|
||||
|
|
11
server.go
11
server.go
|
@ -74,6 +74,7 @@ type server struct {
|
|||
rpcServer *rpcServer
|
||||
blockManager *blockManager
|
||||
txMemPool *txMemPool
|
||||
cpuMiner *CPUMiner
|
||||
modifyRebroadcastInv chan interface{}
|
||||
newPeers chan *peer
|
||||
donePeers chan *peer
|
||||
|
@ -860,6 +861,11 @@ func (s *server) Start() {
|
|||
|
||||
s.rpcServer.Start()
|
||||
}
|
||||
|
||||
// Start the CPU miner if generation is enabled.
|
||||
if cfg.Generate {
|
||||
s.cpuMiner.Start()
|
||||
}
|
||||
}
|
||||
|
||||
// Stop gracefully shuts down the server by stopping and disconnecting all
|
||||
|
@ -882,6 +888,9 @@ func (s *server) Stop() error {
|
|||
}
|
||||
}
|
||||
|
||||
// Stop the CPU miner if needed
|
||||
s.cpuMiner.Stop()
|
||||
|
||||
// Shutdown the RPC server if it's not disabled.
|
||||
if !cfg.DisableRPC {
|
||||
s.rpcServer.Stop()
|
||||
|
@ -1177,6 +1186,7 @@ func newServer(listenAddrs []string, db btcdb.Db, netParams *btcnet.Params) (*se
|
|||
}
|
||||
s.blockManager = bm
|
||||
s.txMemPool = newTxMemPool(&s)
|
||||
s.cpuMiner = newCPUMiner(&s)
|
||||
|
||||
if !cfg.DisableRPC {
|
||||
s.rpcServer, err = newRPCServer(cfg.RPCListeners, &s)
|
||||
|
@ -1184,6 +1194,7 @@ func newServer(listenAddrs []string, db btcdb.Db, netParams *btcnet.Params) (*se
|
|||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &s, nil
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue