OSDN Git Service

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