7 log "github.com/sirupsen/logrus"
9 "github.com/vapor/account"
10 "github.com/vapor/event"
11 "github.com/vapor/mining"
12 "github.com/vapor/protocol"
13 "github.com/vapor/protocol/bc/types"
17 maxNonce = ^uint64(0) // 2^64 - 1
20 logModule = "cpuminer"
23 // CPUMiner provides facilities for solving blocks (mining) using the CPU in
24 // a concurrency-safe manner.
25 type CPUMiner struct {
28 accountManager *account.Manager
29 txPool *protocol.TxPool
33 workerWg sync.WaitGroup
34 updateNumWorkers chan struct{}
36 eventDispatcher *event.Dispatcher
39 // solveBlock attempts to find some combination of a nonce, extra nonce, and
40 // current timestamp which makes the passed block hash to a value less than the
42 func (m *CPUMiner) solveBlock(block *types.Block, ticker *time.Ticker, quit chan struct{}) bool {
43 header := &block.BlockHeader
45 for i := uint64(0); i <= maxNonce; i++ {
50 if m.chain.BestBlockHeight() >= header.Height {
61 // generateBlocks is a worker that is controlled by the miningWorkerController.
62 // It is self contained in that it creates block templates and attempts to solve
63 // them while detecting when it is performing stale work and reacting
64 // accordingly by generating a new block template. When a block is solved, it
67 // It must be run as a goroutine.
68 func (m *CPUMiner) generateBlocks(quit chan struct{}) {
69 ticker := time.NewTicker(time.Second * hashUpdateSecs)
80 block, err := mining.NewBlockTemplate(m.chain, m.txPool, m.accountManager)
82 log.Errorf("Mining: failed on create NewBlockTemplate: %v", err)
86 if m.solveBlock(block, ticker, quit) {
87 if isOrphan, err := m.chain.ProcessBlock(block); err == nil {
88 log.WithFields(log.Fields{
90 "height": block.BlockHeader.Height,
92 "tx": len(block.Transactions),
93 }).Info("Miner processed block")
95 // Broadcast the block and announce chain insertion event
96 if err = m.eventDispatcher.Post(event.NewMinedBlockEvent{Block: *block}); err != nil {
97 log.WithFields(log.Fields{"module": logModule, "height": block.BlockHeader.Height, "error": err}).Errorf("Miner fail on post block")
100 log.WithFields(log.Fields{"module": logModule, "height": block.BlockHeader.Height, "error": err}).Errorf("Miner fail on ProcessBlock")
108 // miningWorkerController launches the worker goroutines that are used to
109 // generate block templates and solve them. It also provides the ability to
110 // dynamically adjust the number of running worker goroutines.
112 // It must be run as a goroutine.
113 func (m *CPUMiner) miningWorkerController() {
114 // launchWorkers groups common code to launch a specified number of
115 // workers for generating blocks.
116 var runningWorkers []chan struct{}
117 launchWorkers := func(numWorkers uint64) {
118 for i := uint64(0); i < numWorkers; i++ {
119 quit := make(chan struct{})
120 runningWorkers = append(runningWorkers, quit)
123 go m.generateBlocks(quit)
127 // Launch the current number of workers by default.
128 runningWorkers = make([]chan struct{}, 0, m.numWorkers)
129 launchWorkers(m.numWorkers)
134 // Update the number of running workers.
135 case <-m.updateNumWorkers:
137 numRunning := uint64(len(runningWorkers))
138 if m.numWorkers == numRunning {
143 if m.numWorkers > numRunning {
144 launchWorkers(m.numWorkers - numRunning)
148 // Signal the most recently created goroutines to exit.
149 for i := numRunning - 1; i >= m.numWorkers; i-- {
150 close(runningWorkers[i])
151 runningWorkers[i] = nil
152 runningWorkers = runningWorkers[:i]
156 for _, quit := range runningWorkers {
166 // Start begins the CPU mining process as well as the speed monitor used to
167 // track hashing metrics. Calling this function when the CPU miner has
168 // already been started will have no effect.
170 // This function is safe for concurrent access.
171 func (m *CPUMiner) Start() {
175 // Nothing to do if the miner is already running
180 m.quit = make(chan struct{})
181 go m.miningWorkerController()
184 log.Infof("CPU miner started")
187 // Stop gracefully stops the mining process by signalling all workers, and the
188 // speed monitor to quit. Calling this function when the CPU miner has not
189 // already been started will have no effect.
191 // This function is safe for concurrent access.
192 func (m *CPUMiner) Stop() {
196 // Nothing to do if the miner is not currently running
203 log.Info("CPU miner stopped")
206 // IsMining returns whether or not the CPU miner has been started and is
207 // therefore currenting mining.
209 // This function is safe for concurrent access.
210 func (m *CPUMiner) IsMining() bool {
217 // SetNumWorkers sets the number of workers to create which solve blocks. Any
218 // negative values will cause a default number of workers to be used which is
219 // based on the number of processor cores in the system. A value of 0 will
220 // cause all CPU mining to be stopped.
222 // This function is safe for concurrent access.
223 func (m *CPUMiner) SetNumWorkers(numWorkers int32) {
228 // Don't lock until after the first check since Stop does its own
233 // Use default if provided value is negative.
235 m.numWorkers = defaultNumWorkers
237 m.numWorkers = uint64(numWorkers)
240 // When the miner is already running, notify the controller about the
243 m.updateNumWorkers <- struct{}{}
247 // NumWorkers returns the number of workers which are running to solve blocks.
249 // This function is safe for concurrent access.
250 func (m *CPUMiner) NumWorkers() int32 {
254 return int32(m.numWorkers)
257 // NewCPUMiner returns a new instance of a CPU miner for the provided configuration.
258 // Use Start to begin the mining process. See the documentation for CPUMiner
259 // type for more details.
260 func NewCPUMiner(c *protocol.Chain, accountManager *account.Manager, txPool *protocol.TxPool, dispatcher *event.Dispatcher) *CPUMiner {
263 accountManager: accountManager,
265 numWorkers: defaultNumWorkers,
266 updateNumWorkers: make(chan struct{}),
267 eventDispatcher: dispatcher,