From e25b644d3b55f6f4f3a8c9a42f149c13d28f5815 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Wed, 11 Jun 2014 20:09:38 -0500 Subject: [PATCH] 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. --- blockmanager.go | 3 +- config.go | 53 ++++- cpuminer.go | 501 +++++++++++++++++++++++++++++++++++++++++++++++ doc.go | 10 +- rpcserver.go | 8 +- sample-btcd.conf | 39 ++++ server.go | 11 ++ 7 files changed, 608 insertions(+), 17 deletions(-) create mode 100644 cpuminer.go diff --git a/blockmanager.go b/blockmanager.go index be083ac7..67043715 100644 --- a/blockmanager.go +++ b/blockmanager.go @@ -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 diff --git a/config.go b/config.go index 56cb6730..91cb5383 100644 --- a/config.go +++ b/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 =,=,... 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 diff --git a/cpuminer.go b/cpuminer.go new file mode 100644 index 00000000..d0ea2485 --- /dev/null +++ b/cpuminer.go @@ -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), + } +} diff --git a/doc.go b/doc.go index f8cb6c6c..f42484b8 100644 --- a/doc.go +++ b/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 diff --git a/rpcserver.go b/rpcserver.go index 843be343..c8f6774a 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -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", } } diff --git a/sample-btcd.conf b/sample-btcd.conf index 3aa0ba89..0d2ecfee 100644 --- a/sample-btcd.conf +++ b/sample-btcd.conf @@ -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 ; ------------------------------------------------------------------------------ diff --git a/server.go b/server.go index d1751543..3049878c 100644 --- a/server.go +++ b/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 }