OSDN Git Service

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