OSDN Git Service

451687f4077f748d518721096ec27f9b8bbbeb2f
[bytom/vapor.git] / mining / miner / miner.go
1 package miner
2
3 import (
4         "errors"
5         "sync"
6         "time"
7
8         "github.com/vapor/config"
9
10         log "github.com/sirupsen/logrus"
11
12         "github.com/vapor/account"
13         "github.com/vapor/common"
14         "github.com/vapor/consensus"
15         engine "github.com/vapor/consensus/consensus"
16         "github.com/vapor/consensus/consensus/dpos"
17         "github.com/vapor/crypto"
18         "github.com/vapor/crypto/ed25519/chainkd"
19         "github.com/vapor/mining"
20         "github.com/vapor/protocol"
21         "github.com/vapor/protocol/bc"
22         "github.com/vapor/protocol/bc/types"
23         "github.com/vapor/protocol/vm/vmutil"
24 )
25
26 const (
27         maxNonce          = ^uint64(0) // 2^64 - 1
28         defaultNumWorkers = 1
29         hashUpdateSecs    = 1
30         module            = "miner"
31 )
32
33 var ConsensusEngine engine.Engine
34
35 // Miner creates blocks and searches for proof-of-work values.
36 type Miner struct {
37         sync.Mutex
38         chain            *protocol.Chain
39         accountManager   *account.Manager
40         txPool           *protocol.TxPool
41         numWorkers       uint64
42         started          bool
43         discreteMining   bool
44         workerWg         sync.WaitGroup
45         updateNumWorkers chan struct{}
46         quit             chan struct{}
47         newBlockCh       chan *bc.Hash
48         Authoritys       map[string]string
49         position         uint64
50         engine           engine.Engine
51 }
52
53 func NewMiner(c *protocol.Chain, accountManager *account.Manager, txPool *protocol.TxPool, newBlockCh chan *bc.Hash, engine engine.Engine) *Miner {
54         authoritys := make(map[string]string)
55         var position uint64
56         dpos, ok := engine.(*dpos.Dpos)
57         if !ok {
58                 log.Error("Only the dpos engine was allowed")
59                 return nil
60         }
61         dpos.Authorize(config.CommonConfig.Consensus.Dpos.Coinbase)
62         /*
63                 for index, xpub := range consensus.ActiveNetParams.SignBlockXPubs {
64                         pubHash := crypto.Ripemd160(xpub.PublicKey())
65                         address, _ := common.NewPeginAddressWitnessScriptHash(pubHash, &consensus.ActiveNetParams)
66                         control, _ := vmutil.P2WPKHProgram([]byte(pubHash))
67                         //key := hex.EncodeToString(control)
68                         //authoritys[key] = xpub.String()
69                         authoritys[address.EncodeAddress()] = xpub.String()
70                         if accountManager.IsLocalControlProgram(control) {
71                                 position = uint64(index)
72                                 dpos.Authorize(address.EncodeAddress())
73                         }
74                 }
75         */
76         //c.SetAuthoritys(authoritys)
77         //c.SetPosition(position)
78         c.SetConsensusEngine(dpos)
79         ConsensusEngine = dpos
80         return &Miner{
81                 chain:            c,
82                 accountManager:   accountManager,
83                 txPool:           txPool,
84                 numWorkers:       defaultNumWorkers,
85                 updateNumWorkers: make(chan struct{}),
86                 newBlockCh:       newBlockCh,
87                 Authoritys:       authoritys,
88                 position:         position,
89                 engine:           dpos,
90         }
91 }
92
93 func (m *Miner) generateProof(block types.Block) (types.Proof, error) {
94         var xPrv chainkd.XPrv
95         if consensus.ActiveNetParams.Signer == "" {
96                 return types.Proof{}, errors.New("Signer is empty")
97         }
98         xPrv.UnmarshalText([]byte(consensus.ActiveNetParams.Signer))
99         sign := xPrv.Sign(block.BlockCommitment.TransactionsMerkleRoot.Bytes())
100         pubHash := crypto.Ripemd160(xPrv.XPub().PublicKey())
101
102         address, _ := common.NewPeginAddressWitnessScriptHash(pubHash, &consensus.ActiveNetParams)
103         control, err := vmutil.P2WPKHProgram([]byte(pubHash))
104         if err != nil {
105                 return types.Proof{}, err
106         }
107         return types.Proof{Sign: sign, ControlProgram: control, Address: address.ScriptAddress()}, nil
108 }
109
110 // generateBlocks is a worker that is controlled by the miningWorkerController.
111 // It is self contained in that it creates block templates and attempts to solve
112 // them while detecting when it is performing stale work and reacting
113 // accordingly by generating a new block template.  When a block is solved, it
114 // is submitted.
115 //
116 // It must be run as a goroutine.
117 func (m *Miner) generateBlocks(quit chan struct{}) {
118         ticker := time.NewTicker(time.Second * hashUpdateSecs)
119         defer ticker.Stop()
120
121 out:
122         for {
123                 select {
124                 case <-quit:
125                         break out
126                 default:
127                 }
128                 /*
129                         engine, ok := m.engine.(*dpos.Dpos)
130                         if !ok {
131                                 log.Error("Only the dpos engine was allowed")
132                                 return
133                         }
134
135                                 header := m.chain.BestBlockHeader()
136                                 isSeal, err := engine.IsSealer(m.chain, header.Hash(), header, uint64(time.Now().Unix()))
137                                 if err != nil {
138                                         log.WithFields(log.Fields{"module": module, "error": err}).Error("Determine whether seal is wrong")
139                                         continue
140                                 }
141                 */
142                 isSeal := true
143                 if isSeal {
144                         block, err := mining.NewBlockTemplate1(m.chain, m.txPool, m.accountManager, m.engine)
145                         if err != nil {
146                                 log.Errorf("Mining: failed on create NewBlockTemplate: %v", err)
147                                 time.Sleep(3 * time.Second)
148                                 continue
149                         }
150                         if block == nil {
151                                 time.Sleep(3 * time.Second)
152                                 continue
153                         }
154                         block, err = m.engine.Seal(m.chain, block)
155                         if err != nil {
156                                 log.Errorf("Seal, %v", err)
157                                 continue
158                         }
159                         m.chain.SetConsensusEngine(m.engine)
160                         if isOrphan, err := m.chain.ProcessBlock(block); err == nil {
161                                 log.WithFields(log.Fields{
162                                         "height":   block.BlockHeader.Height,
163                                         "isOrphan": isOrphan,
164                                         "tx":       len(block.Transactions),
165                                 }).Info("Miner processed block")
166
167                                 blockHash := block.Hash()
168                                 m.newBlockCh <- &blockHash
169                         } else {
170                                 log.WithField("height", block.BlockHeader.Height).Errorf("Miner fail on ProcessBlock, %v", err)
171                         }
172                 }
173                 time.Sleep(3 * time.Second)
174         }
175
176         m.workerWg.Done()
177 }
178
179 // miningWorkerController launches the worker goroutines that are used to
180 // generate block templates and solve them.  It also provides the ability to
181 // dynamically adjust the number of running worker goroutines.
182 //
183 // It must be run as a goroutine.
184 func (m *Miner) miningWorkerController() {
185         // launchWorkers groups common code to launch a specified number of
186         // workers for generating blocks.
187         var runningWorkers []chan struct{}
188         launchWorkers := func(numWorkers uint64) {
189                 for i := uint64(0); i < numWorkers; i++ {
190                         quit := make(chan struct{})
191                         runningWorkers = append(runningWorkers, quit)
192
193                         m.workerWg.Add(1)
194                         go m.generateBlocks(quit)
195                 }
196         }
197
198         // Launch the current number of workers by default.
199         runningWorkers = make([]chan struct{}, 0, m.numWorkers)
200         launchWorkers(m.numWorkers)
201
202 out:
203         for {
204                 select {
205                 // Update the number of running workers.
206                 case <-m.updateNumWorkers:
207                         // No change.
208                         numRunning := uint64(len(runningWorkers))
209                         if m.numWorkers == numRunning {
210                                 continue
211                         }
212
213                         // Add new workers.
214                         if m.numWorkers > numRunning {
215                                 launchWorkers(m.numWorkers - numRunning)
216                                 continue
217                         }
218
219                         // Signal the most recently created goroutines to exit.
220                         for i := numRunning - 1; i >= m.numWorkers; i-- {
221                                 close(runningWorkers[i])
222                                 runningWorkers[i] = nil
223                                 runningWorkers = runningWorkers[:i]
224                         }
225
226                 case <-m.quit:
227                         for _, quit := range runningWorkers {
228                                 close(quit)
229                         }
230                         break out
231                 }
232         }
233
234         m.workerWg.Wait()
235 }
236
237 // Start begins the CPU mining process as well as the speed monitor used to
238 // track hashing metrics.  Calling this function when the CPU miner has
239 // already been started will have no effect.
240 //
241 // This function is safe for concurrent access.
242 func (m *Miner) Start() {
243         m.Lock()
244         defer m.Unlock()
245
246         // Nothing to do if the miner is already running
247         if m.started {
248                 return
249         }
250
251         m.quit = make(chan struct{})
252         go m.miningWorkerController()
253
254         m.started = true
255         log.Infof("CPU miner started")
256 }
257
258 // Stop gracefully stops the mining process by signalling all workers, and the
259 // speed monitor to quit.  Calling this function when the CPU miner has not
260 // already been started will have no effect.
261 //
262 // This function is safe for concurrent access.
263 func (m *Miner) Stop() {
264         m.Lock()
265         defer m.Unlock()
266
267         // Nothing to do if the miner is not currently running
268         if !m.started {
269                 return
270         }
271
272         close(m.quit)
273         m.started = false
274         log.Info("CPU miner stopped")
275 }
276
277 // IsMining returns whether or not the CPU miner has been started and is
278 // therefore currenting mining.
279 //
280 // This function is safe for concurrent access.
281 func (m *Miner) IsMining() bool {
282         m.Lock()
283         defer m.Unlock()
284
285         return m.started
286 }
287
288 // SetNumWorkers sets the number of workers to create which solve blocks.  Any
289 // negative values will cause a default number of workers to be used which is
290 // based on the number of processor cores in the system.  A value of 0 will
291 // cause all CPU mining to be stopped.
292 //
293 // This function is safe for concurrent access.
294 func (m *Miner) SetNumWorkers(numWorkers int32) {
295         if numWorkers == 0 {
296                 m.Stop()
297         }
298
299         // Don't lock until after the first check since Stop does its own
300         // locking.
301         m.Lock()
302         defer m.Unlock()
303
304         // Use default if provided value is negative.
305         if numWorkers < 0 {
306                 m.numWorkers = defaultNumWorkers
307         } else {
308                 m.numWorkers = uint64(numWorkers)
309         }
310
311         // When the miner is already running, notify the controller about the
312         // the change.
313         if m.started {
314                 m.updateNumWorkers <- struct{}{}
315         }
316 }
317
318 // NumWorkers returns the number of workers which are running to solve blocks.
319 //
320 // This function is safe for concurrent access.
321 func (m *Miner) NumWorkers() int32 {
322         m.Lock()
323         defer m.Unlock()
324
325         return int32(m.numWorkers)
326 }