OSDN Git Service

Merge pull request #33 from Bytom/dev_optimize_consensus
[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         engine           engine.Engine
49 }
50
51 func NewMiner(c *protocol.Chain, accountManager *account.Manager, txPool *protocol.TxPool, newBlockCh chan *bc.Hash, engine engine.Engine) *Miner {
52         dpos, ok := engine.(*dpos.Dpos)
53         if !ok {
54                 log.Error("Only the dpos engine was allowed")
55                 return nil
56         }
57         dpos.Authorize(config.CommonConfig.Consensus.Dpos.Coinbase)
58         c.SetConsensusEngine(dpos)
59         ConsensusEngine = dpos
60         return &Miner{
61                 chain:            c,
62                 accountManager:   accountManager,
63                 txPool:           txPool,
64                 numWorkers:       defaultNumWorkers,
65                 updateNumWorkers: make(chan struct{}),
66                 newBlockCh:       newBlockCh,
67                 engine:           dpos,
68         }
69 }
70
71 func (m *Miner) generateProof(block types.Block) (types.Proof, error) {
72         var xPrv chainkd.XPrv
73         if consensus.ActiveNetParams.Signer == "" {
74                 return types.Proof{}, errors.New("Signer is empty")
75         }
76         xPrv.UnmarshalText([]byte(consensus.ActiveNetParams.Signer))
77         sign := xPrv.Sign(block.BlockCommitment.TransactionsMerkleRoot.Bytes())
78         pubHash := crypto.Ripemd160(xPrv.XPub().PublicKey())
79
80         address, _ := common.NewPeginAddressWitnessScriptHash(pubHash, &consensus.ActiveNetParams)
81         control, err := vmutil.P2WPKHProgram([]byte(pubHash))
82         if err != nil {
83                 return types.Proof{}, err
84         }
85         return types.Proof{Sign: sign, ControlProgram: control, Address: address.ScriptAddress()}, nil
86 }
87
88 // generateBlocks is a worker that is controlled by the miningWorkerController.
89 // It is self contained in that it creates block templates and attempts to solve
90 // them while detecting when it is performing stale work and reacting
91 // accordingly by generating a new block template.  When a block is solved, it
92 // is submitted.
93 //
94 // It must be run as a goroutine.
95 func (m *Miner) generateBlocks(quit chan struct{}) {
96
97 out:
98         for {
99                 select {
100                 case <-quit:
101                         break out
102                 default:
103                 }
104
105                 block, err := mining.NewBlockTemplate(m.chain, m.txPool, m.accountManager, m.engine)
106                 if err != nil {
107                         log.Errorf("Mining: failed on create NewBlockTemplate: %v", err)
108                         time.Sleep(1 * time.Second)
109                         continue
110                 }
111                 if block == nil {
112                         time.Sleep(1 * time.Second)
113                         continue
114                 }
115                 block, err = m.engine.Seal(m.chain, block)
116                 if err != nil {
117                         log.Errorf("Seal, %v", err)
118                         continue
119                 }
120                 m.chain.SetConsensusEngine(m.engine)
121                 if isOrphan, err := m.chain.ProcessBlock(block); err == nil {
122                         log.WithFields(log.Fields{
123                                 "height":   block.BlockHeader.Height,
124                                 "isOrphan": isOrphan,
125                                 "tx":       len(block.Transactions),
126                         }).Info("Miner processed block")
127
128                         blockHash := block.Hash()
129                         m.newBlockCh <- &blockHash
130                 } else {
131                         log.WithField("height", block.BlockHeader.Height).Errorf("Miner fail on ProcessBlock, %v", err)
132                 }
133                 // confirm block
134                 m.sendConfirmTx(block.Height - 1)
135                 time.Sleep(time.Duration(config.CommonConfig.Consensus.Dpos.Period) * time.Second)
136         }
137
138         m.workerWg.Done()
139 }
140
141 func (m *Miner) sendConfirmTx(height uint64) error {
142         return nil
143 }
144
145 // miningWorkerController launches the worker goroutines that are used to
146 // generate block templates and solve them.  It also provides the ability to
147 // dynamically adjust the number of running worker goroutines.
148 //
149 // It must be run as a goroutine.
150 func (m *Miner) miningWorkerController() {
151         // launchWorkers groups common code to launch a specified number of
152         // workers for generating blocks.
153         var runningWorkers []chan struct{}
154         launchWorkers := func(numWorkers uint64) {
155                 for i := uint64(0); i < numWorkers; i++ {
156                         quit := make(chan struct{})
157                         runningWorkers = append(runningWorkers, quit)
158
159                         m.workerWg.Add(1)
160                         go m.generateBlocks(quit)
161                 }
162         }
163
164         // Launch the current number of workers by default.
165         runningWorkers = make([]chan struct{}, 0, m.numWorkers)
166         launchWorkers(m.numWorkers)
167
168 out:
169         for {
170                 select {
171                 // Update the number of running workers.
172                 case <-m.updateNumWorkers:
173                         // No change.
174                         numRunning := uint64(len(runningWorkers))
175                         if m.numWorkers == numRunning {
176                                 continue
177                         }
178
179                         // Add new workers.
180                         if m.numWorkers > numRunning {
181                                 launchWorkers(m.numWorkers - numRunning)
182                                 continue
183                         }
184
185                         // Signal the most recently created goroutines to exit.
186                         for i := numRunning - 1; i >= m.numWorkers; i-- {
187                                 close(runningWorkers[i])
188                                 runningWorkers[i] = nil
189                                 runningWorkers = runningWorkers[:i]
190                         }
191
192                 case <-m.quit:
193                         for _, quit := range runningWorkers {
194                                 close(quit)
195                         }
196                         break out
197                 }
198         }
199
200         m.workerWg.Wait()
201 }
202
203 // Start begins the CPU mining process as well as the speed monitor used to
204 // track hashing metrics.  Calling this function when the CPU miner has
205 // already been started will have no effect.
206 //
207 // This function is safe for concurrent access.
208 func (m *Miner) Start() {
209         m.Lock()
210         defer m.Unlock()
211
212         // Nothing to do if the miner is already running
213         if m.started {
214                 return
215         }
216
217         m.quit = make(chan struct{})
218         go m.miningWorkerController()
219
220         m.started = true
221         log.Infof("CPU miner started")
222 }
223
224 // Stop gracefully stops the mining process by signalling all workers, and the
225 // speed monitor to quit.  Calling this function when the CPU miner has not
226 // already been started will have no effect.
227 //
228 // This function is safe for concurrent access.
229 func (m *Miner) Stop() {
230         m.Lock()
231         defer m.Unlock()
232
233         // Nothing to do if the miner is not currently running
234         if !m.started {
235                 return
236         }
237
238         close(m.quit)
239         m.started = false
240         log.Info("CPU miner stopped")
241 }
242
243 // IsMining returns whether or not the CPU miner has been started and is
244 // therefore currenting mining.
245 //
246 // This function is safe for concurrent access.
247 func (m *Miner) IsMining() bool {
248         m.Lock()
249         defer m.Unlock()
250
251         return m.started
252 }
253
254 // SetNumWorkers sets the number of workers to create which solve blocks.  Any
255 // negative values will cause a default number of workers to be used which is
256 // based on the number of processor cores in the system.  A value of 0 will
257 // cause all CPU mining to be stopped.
258 //
259 // This function is safe for concurrent access.
260 func (m *Miner) SetNumWorkers(numWorkers int32) {
261         if numWorkers == 0 {
262                 m.Stop()
263         }
264
265         // Don't lock until after the first check since Stop does its own
266         // locking.
267         m.Lock()
268         defer m.Unlock()
269
270         // Use default if provided value is negative.
271         if numWorkers < 0 {
272                 m.numWorkers = defaultNumWorkers
273         } else {
274                 m.numWorkers = uint64(numWorkers)
275         }
276
277         // When the miner is already running, notify the controller about the
278         // the change.
279         if m.started {
280                 m.updateNumWorkers <- struct{}{}
281         }
282 }
283
284 // NumWorkers returns the number of workers which are running to solve blocks.
285 //
286 // This function is safe for concurrent access.
287 func (m *Miner) NumWorkers() int32 {
288         m.Lock()
289         defer m.Unlock()
290
291         return int32(m.numWorkers)
292 }