OSDN Git Service

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