OSDN Git Service

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