github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/mining/cpuminer/cpuminer.go (about) 1 package cpuminer 2 3 import ( 4 "sync" 5 "time" 6 7 log "github.com/sirupsen/logrus" 8 9 "github.com/bytom/bytom/account" 10 "github.com/bytom/bytom/consensus/difficulty" 11 "github.com/bytom/bytom/event" 12 "github.com/bytom/bytom/mining" 13 "github.com/bytom/bytom/protocol" 14 "github.com/bytom/bytom/protocol/bc/types" 15 ) 16 17 const ( 18 maxNonce = ^uint64(0) // 2^64 - 1 19 defaultNumWorkers = 1 20 hashUpdateSecs = 1 21 logModule = "cpuminer" 22 ) 23 24 // CPUMiner provides facilities for solving blocks (mining) using the CPU in 25 // a concurrency-safe manner. 26 type CPUMiner struct { 27 sync.Mutex 28 chain *protocol.Chain 29 accountManager *account.Manager 30 txPool *protocol.TxPool 31 numWorkers uint64 32 started bool 33 discreteMining bool 34 workerWg sync.WaitGroup 35 updateNumWorkers chan struct{} 36 quit chan struct{} 37 eventDispatcher *event.Dispatcher 38 } 39 40 // solveBlock attempts to find some combination of a nonce, extra nonce, and 41 // current timestamp which makes the passed block hash to a value less than the 42 // target difficulty. 43 func (m *CPUMiner) solveBlock(block *types.Block, ticker *time.Ticker, quit chan struct{}) bool { 44 header := &block.BlockHeader 45 seed, err := m.chain.CalcNextSeed(&header.PreviousBlockHash) 46 if err != nil { 47 return false 48 } 49 50 for i := uint64(0); i <= maxNonce; i++ { 51 select { 52 case <-quit: 53 return false 54 case <-ticker.C: 55 if m.chain.BestBlockHeight() >= header.Height { 56 return false 57 } 58 default: 59 } 60 61 header.Nonce = i 62 headerHash := header.Hash() 63 if difficulty.CheckProofOfWork(&headerHash, seed, header.Bits) { 64 return true 65 } 66 } 67 return false 68 } 69 70 // generateBlocks is a worker that is controlled by the miningWorkerController. 71 // It is self contained in that it creates block templates and attempts to solve 72 // them while detecting when it is performing stale work and reacting 73 // accordingly by generating a new block template. When a block is solved, it 74 // is submitted. 75 // 76 // It must be run as a goroutine. 77 func (m *CPUMiner) generateBlocks(quit chan struct{}) { 78 ticker := time.NewTicker(time.Second * hashUpdateSecs) 79 defer ticker.Stop() 80 81 out: 82 for { 83 select { 84 case <-quit: 85 break out 86 default: 87 } 88 89 block, err := mining.NewBlockTemplate(m.chain, m.txPool, m.accountManager) 90 if err != nil { 91 log.Errorf("Mining: failed on create NewBlockTemplate: %v", err) 92 continue 93 } 94 95 if m.solveBlock(block, ticker, quit) { 96 if isOrphan, err := m.chain.ProcessBlock(block); err == nil { 97 log.WithFields(log.Fields{ 98 "module": logModule, 99 "height": block.BlockHeader.Height, 100 "isOrphan": isOrphan, 101 "tx": len(block.Transactions), 102 }).Info("Miner processed block") 103 104 // Broadcast the block and announce chain insertion event 105 if err = m.eventDispatcher.Post(event.NewMinedBlockEvent{Block: *block}); err != nil { 106 log.WithFields(log.Fields{"module": logModule, "height": block.BlockHeader.Height, "error": err}).Errorf("Miner fail on post block") 107 } 108 } else { 109 log.WithFields(log.Fields{"module": logModule, "height": block.BlockHeader.Height, "error": err}).Errorf("Miner fail on ProcessBlock") 110 } 111 } 112 } 113 114 m.workerWg.Done() 115 } 116 117 // miningWorkerController launches the worker goroutines that are used to 118 // generate block templates and solve them. It also provides the ability to 119 // dynamically adjust the number of running worker goroutines. 120 // 121 // It must be run as a goroutine. 122 func (m *CPUMiner) miningWorkerController() { 123 // launchWorkers groups common code to launch a specified number of 124 // workers for generating blocks. 125 var runningWorkers []chan struct{} 126 launchWorkers := func(numWorkers uint64) { 127 for i := uint64(0); i < numWorkers; i++ { 128 quit := make(chan struct{}) 129 runningWorkers = append(runningWorkers, quit) 130 131 m.workerWg.Add(1) 132 go m.generateBlocks(quit) 133 } 134 } 135 136 // Launch the current number of workers by default. 137 runningWorkers = make([]chan struct{}, 0, m.numWorkers) 138 launchWorkers(m.numWorkers) 139 140 out: 141 for { 142 select { 143 // Update the number of running workers. 144 case <-m.updateNumWorkers: 145 // No change. 146 numRunning := uint64(len(runningWorkers)) 147 if m.numWorkers == numRunning { 148 continue 149 } 150 151 // Add new workers. 152 if m.numWorkers > numRunning { 153 launchWorkers(m.numWorkers - numRunning) 154 continue 155 } 156 157 // Signal the most recently created goroutines to exit. 158 for i := numRunning - 1; i >= m.numWorkers; i-- { 159 close(runningWorkers[i]) 160 runningWorkers[i] = nil 161 runningWorkers = runningWorkers[:i] 162 } 163 164 case <-m.quit: 165 for _, quit := range runningWorkers { 166 close(quit) 167 } 168 break out 169 } 170 } 171 172 m.workerWg.Wait() 173 } 174 175 // Start begins the CPU mining process as well as the speed monitor used to 176 // track hashing metrics. Calling this function when the CPU miner has 177 // already been started will have no effect. 178 // 179 // This function is safe for concurrent access. 180 func (m *CPUMiner) Start() { 181 m.Lock() 182 defer m.Unlock() 183 184 // Nothing to do if the miner is already running 185 if m.started { 186 return 187 } 188 189 m.quit = make(chan struct{}) 190 go m.miningWorkerController() 191 192 m.started = true 193 log.Infof("CPU miner started") 194 } 195 196 // Stop gracefully stops the mining process by signalling all workers, and the 197 // speed monitor to quit. Calling this function when the CPU miner has not 198 // already been started will have no effect. 199 // 200 // This function is safe for concurrent access. 201 func (m *CPUMiner) Stop() { 202 m.Lock() 203 defer m.Unlock() 204 205 // Nothing to do if the miner is not currently running 206 if !m.started { 207 return 208 } 209 210 close(m.quit) 211 m.started = false 212 log.Info("CPU miner stopped") 213 } 214 215 // IsMining returns whether or not the CPU miner has been started and is 216 // therefore currenting mining. 217 // 218 // This function is safe for concurrent access. 219 func (m *CPUMiner) IsMining() bool { 220 m.Lock() 221 defer m.Unlock() 222 223 return m.started 224 } 225 226 // SetNumWorkers sets the number of workers to create which solve blocks. Any 227 // negative values will cause a default number of workers to be used which is 228 // based on the number of processor cores in the system. A value of 0 will 229 // cause all CPU mining to be stopped. 230 // 231 // This function is safe for concurrent access. 232 func (m *CPUMiner) SetNumWorkers(numWorkers int32) { 233 if numWorkers == 0 { 234 m.Stop() 235 } 236 237 // Don't lock until after the first check since Stop does its own 238 // locking. 239 m.Lock() 240 defer m.Unlock() 241 242 // Use default if provided value is negative. 243 if numWorkers < 0 { 244 m.numWorkers = defaultNumWorkers 245 } else { 246 m.numWorkers = uint64(numWorkers) 247 } 248 249 // When the miner is already running, notify the controller about the 250 // the change. 251 if m.started { 252 m.updateNumWorkers <- struct{}{} 253 } 254 } 255 256 // NumWorkers returns the number of workers which are running to solve blocks. 257 // 258 // This function is safe for concurrent access. 259 func (m *CPUMiner) NumWorkers() int32 { 260 m.Lock() 261 defer m.Unlock() 262 263 return int32(m.numWorkers) 264 } 265 266 // NewCPUMiner returns a new instance of a CPU miner for the provided configuration. 267 // Use Start to begin the mining process. See the documentation for CPUMiner 268 // type for more details. 269 func NewCPUMiner(c *protocol.Chain, accountManager *account.Manager, txPool *protocol.TxPool, dispatcher *event.Dispatcher) *CPUMiner { 270 return &CPUMiner{ 271 chain: c, 272 accountManager: accountManager, 273 txPool: txPool, 274 numWorkers: defaultNumWorkers, 275 updateNumWorkers: make(chan struct{}), 276 eventDispatcher: dispatcher, 277 } 278 }