OSDN Git Service

Rename legacy (#433)
[bytom/bytom.git] / mining / cpuminer / cpuminer.go
1 // Copyright (c) 2014-2016 The btcsuite developers
2 // Use of this source code is governed by an ISC
3 // license that can be found in the LICENSE file.
4
5 package cpuminer
6
7 import (
8         "sync"
9         "time"
10
11         log "github.com/sirupsen/logrus"
12
13         "github.com/bytom/blockchain/account"
14         "github.com/bytom/consensus/difficulty"
15         "github.com/bytom/mining"
16         "github.com/bytom/protocol"
17         "github.com/bytom/protocol/bc/types"
18 )
19
20 const (
21         maxNonce          = ^uint64(0) // 2^32 - 1
22         defaultNumWorkers = 1
23         hashUpdateSecs    = 1
24 )
25
26 // CPUMiner provides facilities for solving blocks (mining) using the CPU in
27 // a concurrency-safe manner.
28 type CPUMiner struct {
29         sync.Mutex
30         chain             *protocol.Chain
31         accountManager    *account.Manager
32         txPool            *protocol.TxPool
33         numWorkers        uint64
34         started           bool
35         discreteMining    bool
36         wg                sync.WaitGroup
37         workerWg          sync.WaitGroup
38         updateNumWorkers  chan struct{}
39         queryHashesPerSec chan float64
40         updateHashes      chan uint64
41         speedMonitorQuit  chan struct{}
42         quit              chan struct{}
43 }
44
45 // solveBlock attempts to find some combination of a nonce, extra nonce, and
46 // current timestamp which makes the passed block hash to a value less than the
47 // target difficulty.
48 func (m *CPUMiner) solveBlock(block *types.Block, ticker *time.Ticker, quit chan struct{}) bool {
49         header := &block.BlockHeader
50         seed, err := m.chain.GetSeed(header.Height, &header.PreviousBlockHash)
51         if err != nil {
52                 return false
53         }
54
55         for i := uint64(0); i <= maxNonce; i++ {
56                 select {
57                 case <-quit:
58                         return false
59                 case <-ticker.C:
60                         if m.chain.Height() >= header.Height {
61                                 return false
62                         }
63                 default:
64                 }
65
66                 header.Nonce = i
67                 headerHash := header.Hash()
68                 if difficulty.CheckProofOfWork(&headerHash, seed, header.Bits) {
69                         return true
70                 }
71         }
72         return false
73 }
74
75 // generateBlocks is a worker that is controlled by the miningWorkerController.
76 // It is self contained in that it creates block templates and attempts to solve
77 // them while detecting when it is performing stale work and reacting
78 // accordingly by generating a new block template.  When a block is solved, it
79 // is submitted.
80 //
81 // It must be run as a goroutine.
82 func (m *CPUMiner) generateBlocks(quit chan struct{}) {
83         ticker := time.NewTicker(time.Second * hashUpdateSecs)
84         defer ticker.Stop()
85
86 out:
87         for {
88                 select {
89                 case <-quit:
90                         break out
91                 default:
92                 }
93
94                 block, err := mining.NewBlockTemplate(m.chain, m.txPool, m.accountManager)
95                 if err != nil {
96                         log.Errorf("Mining: failed on create NewBlockTemplate: %v", err)
97                         continue
98                 }
99
100                 if m.solveBlock(block, ticker, quit) {
101                         if isOrphan, err := m.chain.ProcessBlock(block); err == nil {
102                                 log.WithFields(log.Fields{
103                                         "height":   block.BlockHeader.Height,
104                                         "isOrphan": isOrphan,
105                                         "tx":       len(block.Transactions),
106                                 }).Info("Miner processed block")
107                         } else {
108                                 log.WithField("height", block.BlockHeader.Height).Errorf("Miner fail on ProcessBlock %v", err)
109                         }
110                 }
111         }
112
113         m.workerWg.Done()
114 }
115
116 // miningWorkerController launches the worker goroutines that are used to
117 // generate block templates and solve them.  It also provides the ability to
118 // dynamically adjust the number of running worker goroutines.
119 //
120 // It must be run as a goroutine.
121 func (m *CPUMiner) miningWorkerController() {
122         // launchWorkers groups common code to launch a specified number of
123         // workers for generating blocks.
124         var runningWorkers []chan struct{}
125         launchWorkers := func(numWorkers uint64) {
126                 for i := uint64(0); i < numWorkers; i++ {
127                         quit := make(chan struct{})
128                         runningWorkers = append(runningWorkers, quit)
129
130                         m.workerWg.Add(1)
131                         go m.generateBlocks(quit)
132                 }
133         }
134
135         // Launch the current number of workers by default.
136         runningWorkers = make([]chan struct{}, 0, m.numWorkers)
137         launchWorkers(m.numWorkers)
138
139 out:
140         for {
141                 select {
142                 // Update the number of running workers.
143                 case <-m.updateNumWorkers:
144                         // No change.
145                         numRunning := uint64(len(runningWorkers))
146                         if m.numWorkers == numRunning {
147                                 continue
148                         }
149
150                         // Add new workers.
151                         if m.numWorkers > numRunning {
152                                 launchWorkers(m.numWorkers - numRunning)
153                                 continue
154                         }
155
156                         // Signal the most recently created goroutines to exit.
157                         for i := numRunning - 1; i >= m.numWorkers; i-- {
158                                 close(runningWorkers[i])
159                                 runningWorkers[i] = nil
160                                 runningWorkers = runningWorkers[:i]
161                         }
162
163                 case <-m.quit:
164                         for _, quit := range runningWorkers {
165                                 close(quit)
166                         }
167                         break out
168                 }
169         }
170
171         // Wait until all workers shut down to stop the speed monitor since
172         // they rely on being able to send updates to it.
173         m.workerWg.Wait()
174         close(m.speedMonitorQuit)
175         m.wg.Done()
176 }
177
178 // Start begins the CPU mining process as well as the speed monitor used to
179 // track hashing metrics.  Calling this function when the CPU miner has
180 // already been started will have no effect.
181 //
182 // This function is safe for concurrent access.
183 func (m *CPUMiner) Start() {
184         m.Lock()
185         defer m.Unlock()
186
187         // Nothing to do if the miner is already running or if running in
188         // discrete mode (using GenerateNBlocks).
189         if m.started || m.discreteMining {
190                 return
191         }
192
193         m.quit = make(chan struct{})
194         m.speedMonitorQuit = make(chan struct{})
195         m.wg.Add(1)
196         go m.miningWorkerController()
197
198         m.started = true
199         log.Infof("CPU miner started")
200 }
201
202 // Stop gracefully stops the mining process by signalling all workers, and the
203 // speed monitor to quit.  Calling this function when the CPU miner has not
204 // already been started will have no effect.
205 //
206 // This function is safe for concurrent access.
207 func (m *CPUMiner) Stop() {
208         m.Lock()
209         defer m.Unlock()
210
211         // Nothing to do if the miner is not currently running or if running in
212         // discrete mode (using GenerateNBlocks).
213         if !m.started || m.discreteMining {
214                 return
215         }
216
217         close(m.quit)
218         m.wg.Wait()
219         m.started = false
220         log.Info("CPU miner stopped")
221 }
222
223 // IsMining returns whether or not the CPU miner has been started and is
224 // therefore currenting mining.
225 //
226 // This function is safe for concurrent access.
227 func (m *CPUMiner) IsMining() bool {
228         m.Lock()
229         defer m.Unlock()
230
231         return m.started
232 }
233
234 // SetNumWorkers sets the number of workers to create which solve blocks.  Any
235 // negative values will cause a default number of workers to be used which is
236 // based on the number of processor cores in the system.  A value of 0 will
237 // cause all CPU mining to be stopped.
238 //
239 // This function is safe for concurrent access.
240 func (m *CPUMiner) SetNumWorkers(numWorkers int32) {
241         if numWorkers == 0 {
242                 m.Stop()
243         }
244
245         // Don't lock until after the first check since Stop does its own
246         // locking.
247         m.Lock()
248         defer m.Unlock()
249
250         // Use default if provided value is negative.
251         if numWorkers < 0 {
252                 m.numWorkers = defaultNumWorkers
253         } else {
254                 m.numWorkers = uint64(numWorkers)
255         }
256
257         // When the miner is already running, notify the controller about the
258         // the change.
259         if m.started {
260                 m.updateNumWorkers <- struct{}{}
261         }
262 }
263
264 // NumWorkers returns the number of workers which are running to solve blocks.
265 //
266 // This function is safe for concurrent access.
267 func (m *CPUMiner) NumWorkers() int32 {
268         m.Lock()
269         defer m.Unlock()
270
271         return int32(m.numWorkers)
272 }
273
274 // NewCPUMiner returns a new instance of a CPU miner for the provided configuration.
275 // Use Start to begin the mining process.  See the documentation for CPUMiner
276 // type for more details.
277 func NewCPUMiner(c *protocol.Chain, accountManager *account.Manager, txPool *protocol.TxPool) *CPUMiner {
278         return &CPUMiner{
279                 chain:             c,
280                 accountManager:    accountManager,
281                 txPool:            txPool,
282                 numWorkers:        defaultNumWorkers,
283                 updateNumWorkers:  make(chan struct{}),
284                 queryHashesPerSec: make(chan float64),
285                 updateHashes:      make(chan uint64),
286         }
287 }