OSDN Git Service

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