github.com/BlockABC/godash@v0.0.0-20191112120524-f4aa3a32c566/cpuminer.go (about) 1 // Copyright (c) 2014 The btcsuite developers 2 // Copyright (c) 2016 The Dash developers 3 // Use of this source code is governed by an ISC 4 // license that can be found in the LICENSE file. 5 6 package main 7 8 import ( 9 "errors" 10 "fmt" 11 "math/rand" 12 "runtime" 13 "sync" 14 "time" 15 16 "github.com/BlockABC/godash/blockchain" 17 "github.com/BlockABC/godash/mining" 18 "github.com/BlockABC/godash/wire" 19 "github.com/BlockABC/godashutil" 20 ) 21 22 const ( 23 // maxNonce is the maximum value a nonce can be in a block header. 24 maxNonce = ^uint32(0) // 2^32 - 1 25 26 // maxExtraNonce is the maximum value an extra nonce used in a coinbase 27 // transaction can be. 28 maxExtraNonce = ^uint64(0) // 2^64 - 1 29 30 // hpsUpdateSecs is the number of seconds to wait in between each 31 // update to the hashes per second monitor. 32 hpsUpdateSecs = 10 33 34 // hashUpdateSec is the number of seconds each worker waits in between 35 // notifying the speed monitor with how many hashes have been completed 36 // while they are actively searching for a solution. This is done to 37 // reduce the amount of syncs between the workers that must be done to 38 // keep track of the hashes per second. 39 hashUpdateSecs = 15 40 ) 41 42 var ( 43 // defaultNumWorkers is the default number of workers to use for mining 44 // and is based on the number of processor cores. This helps ensure the 45 // system stays reasonably responsive under heavy load. 46 defaultNumWorkers = uint32(runtime.NumCPU()) 47 ) 48 49 // CPUMiner provides facilities for solving blocks (mining) using the CPU in 50 // a concurrency-safe manner. It consists of two main goroutines -- a speed 51 // monitor and a controller for worker goroutines which generate and solve 52 // blocks. The number of goroutines can be set via the SetMaxGoRoutines 53 // function, but the default is based on the number of processor cores in the 54 // system which is typically sufficient. 55 type CPUMiner struct { 56 sync.Mutex 57 policy *mining.Policy 58 txSource mining.TxSource 59 server *server 60 numWorkers uint32 61 started bool 62 discreteMining bool 63 submitBlockLock sync.Mutex 64 wg sync.WaitGroup 65 workerWg sync.WaitGroup 66 updateNumWorkers chan struct{} 67 queryHashesPerSec chan float64 68 updateHashes chan uint64 69 speedMonitorQuit chan struct{} 70 quit chan struct{} 71 } 72 73 // speedMonitor handles tracking the number of hashes per second the mining 74 // process is performing. It must be run as a goroutine. 75 func (m *CPUMiner) speedMonitor() { 76 minrLog.Tracef("CPU miner speed monitor started") 77 78 var hashesPerSec float64 79 var totalHashes uint64 80 ticker := time.NewTicker(time.Second * hpsUpdateSecs) 81 defer ticker.Stop() 82 83 out: 84 for { 85 select { 86 // Periodic updates from the workers with how many hashes they 87 // have performed. 88 case numHashes := <-m.updateHashes: 89 totalHashes += numHashes 90 91 // Time to update the hashes per second. 92 case <-ticker.C: 93 curHashesPerSec := float64(totalHashes) / hpsUpdateSecs 94 if hashesPerSec == 0 { 95 hashesPerSec = curHashesPerSec 96 } 97 hashesPerSec = (hashesPerSec + curHashesPerSec) / 2 98 totalHashes = 0 99 if hashesPerSec != 0 { 100 minrLog.Debugf("Hash speed: %6.0f kilohashes/s", 101 hashesPerSec/1000) 102 } 103 104 // Request for the number of hashes per second. 105 case m.queryHashesPerSec <- hashesPerSec: 106 // Nothing to do. 107 108 case <-m.speedMonitorQuit: 109 break out 110 } 111 } 112 113 m.wg.Done() 114 minrLog.Tracef("CPU miner speed monitor done") 115 } 116 117 // submitBlock submits the passed block to network after ensuring it passes all 118 // of the consensus validation rules. 119 func (m *CPUMiner) submitBlock(block *godashutil.Block) bool { 120 m.submitBlockLock.Lock() 121 defer m.submitBlockLock.Unlock() 122 123 // Ensure the block is not stale since a new block could have shown up 124 // while the solution was being found. Typically that condition is 125 // detected and all work on the stale block is halted to start work on 126 // a new block, but the check only happens periodically, so it is 127 // possible a block was found and submitted in between. 128 latestHash, _ := m.server.blockManager.chainState.Best() 129 msgBlock := block.MsgBlock() 130 if !msgBlock.Header.PrevBlock.IsEqual(latestHash) { 131 minrLog.Debugf("Block submitted via CPU miner with previous "+ 132 "block %s is stale", msgBlock.Header.PrevBlock) 133 return false 134 } 135 136 // Process this block using the same rules as blocks coming from other 137 // nodes. This will in turn relay it to the network like normal. 138 isOrphan, err := m.server.blockManager.ProcessBlock(block, blockchain.BFNone) 139 if err != nil { 140 // Anything other than a rule violation is an unexpected error, 141 // so log that error as an internal error. 142 if _, ok := err.(blockchain.RuleError); !ok { 143 minrLog.Errorf("Unexpected error while processing "+ 144 "block submitted via CPU miner: %v", err) 145 return false 146 } 147 148 minrLog.Debugf("Block submitted via CPU miner rejected: %v", err) 149 return false 150 } 151 if isOrphan { 152 minrLog.Debugf("Block submitted via CPU miner is an orphan") 153 return false 154 } 155 156 // The block was accepted. 157 coinbaseTx := block.MsgBlock().Transactions[0].TxOut[0] 158 minrLog.Infof("Block submitted via CPU miner accepted (hash %s, "+ 159 "amount %v)", block.Sha(), godashutil.Amount(coinbaseTx.Value)) 160 return true 161 } 162 163 // solveBlock attempts to find some combination of a nonce, extra nonce, and 164 // current timestamp which makes the passed block hash to a value less than the 165 // target difficulty. The timestamp is updated periodically and the passed 166 // block is modified with all tweaks during this process. This means that 167 // when the function returns true, the block is ready for submission. 168 // 169 // This function will return early with false when conditions that trigger a 170 // stale block such as a new block showing up or periodically when there are 171 // new transactions and enough time has elapsed without finding a solution. 172 func (m *CPUMiner) solveBlock(msgBlock *wire.MsgBlock, blockHeight int32, 173 ticker *time.Ticker, quit chan struct{}) bool { 174 175 // Choose a random extra nonce offset for this block template and 176 // worker. 177 enOffset, err := wire.RandomUint64() 178 if err != nil { 179 minrLog.Errorf("Unexpected error while generating random "+ 180 "extra nonce offset: %v", err) 181 enOffset = 0 182 } 183 184 // Create a couple of convenience variables. 185 header := &msgBlock.Header 186 targetDifficulty := blockchain.CompactToBig(header.Bits) 187 188 // Initial state. 189 lastGenerated := time.Now() 190 lastTxUpdate := m.txSource.LastUpdated() 191 hashesCompleted := uint64(0) 192 193 // Note that the entire extra nonce range is iterated and the offset is 194 // added relying on the fact that overflow will wrap around 0 as 195 // provided by the Go spec. 196 for extraNonce := uint64(0); extraNonce < maxExtraNonce; extraNonce++ { 197 // Update the extra nonce in the block template with the 198 // new value by regenerating the coinbase script and 199 // setting the merkle root to the new value. The 200 UpdateExtraNonce(msgBlock, blockHeight, extraNonce+enOffset) 201 202 // Search through the entire nonce range for a solution while 203 // periodically checking for early quit and stale block 204 // conditions along with updates to the speed monitor. 205 for i := uint32(0); i <= maxNonce; i++ { 206 select { 207 case <-quit: 208 return false 209 210 case <-ticker.C: 211 m.updateHashes <- hashesCompleted 212 hashesCompleted = 0 213 214 // The current block is stale if the best block 215 // has changed. 216 bestHash, _ := m.server.blockManager.chainState.Best() 217 if !header.PrevBlock.IsEqual(bestHash) { 218 return false 219 } 220 221 // The current block is stale if the memory pool 222 // has been updated since the block template was 223 // generated and it has been at least one 224 // minute. 225 if lastTxUpdate != m.txSource.LastUpdated() && 226 time.Now().After(lastGenerated.Add(time.Minute)) { 227 228 return false 229 } 230 231 UpdateBlockTime(msgBlock, m.server.blockManager) 232 233 default: 234 // Non-blocking select to fall through 235 } 236 237 // Update the nonce and hash the block header. Each 238 // hash is actually a double sha256 (two hashes), so 239 // increment the number of hashes completed for each 240 // attempt accordingly. 241 header.Nonce = i 242 hash := header.BlockSha() 243 hashesCompleted += 2 244 245 // The block is solved when the new block hash is less 246 // than the target difficulty. Yay! 247 if blockchain.ShaHashToBig(&hash).Cmp(targetDifficulty) <= 0 { 248 m.updateHashes <- hashesCompleted 249 return true 250 } 251 } 252 } 253 254 return false 255 } 256 257 // generateBlocks is a worker that is controlled by the miningWorkerController. 258 // It is self contained in that it creates block templates and attempts to solve 259 // them while detecting when it is performing stale work and reacting 260 // accordingly by generating a new block template. When a block is solved, it 261 // is submitted. 262 // 263 // It must be run as a goroutine. 264 func (m *CPUMiner) generateBlocks(quit chan struct{}) { 265 minrLog.Tracef("Starting generate blocks worker") 266 267 // Start a ticker which is used to signal checks for stale work and 268 // updates to the speed monitor. 269 ticker := time.NewTicker(time.Second * hashUpdateSecs) 270 defer ticker.Stop() 271 out: 272 for { 273 // Quit when the miner is stopped. 274 select { 275 case <-quit: 276 break out 277 default: 278 // Non-blocking select to fall through 279 } 280 281 // Wait until there is a connection to at least one other peer 282 // since there is no way to relay a found block or receive 283 // transactions to work on when there are no connected peers. 284 if m.server.ConnectedCount() == 0 { 285 time.Sleep(time.Second) 286 continue 287 } 288 289 // No point in searching for a solution before the chain is 290 // synced. Also, grab the same lock as used for block 291 // submission, since the current block will be changing and 292 // this would otherwise end up building a new block template on 293 // a block that is in the process of becoming stale. 294 m.submitBlockLock.Lock() 295 _, curHeight := m.server.blockManager.chainState.Best() 296 if curHeight != 0 && !m.server.blockManager.IsCurrent() { 297 m.submitBlockLock.Unlock() 298 time.Sleep(time.Second) 299 continue 300 } 301 302 // Choose a payment address at random. 303 rand.Seed(time.Now().UnixNano()) 304 payToAddr := cfg.miningAddrs[rand.Intn(len(cfg.miningAddrs))] 305 306 // Create a new block template using the available transactions 307 // in the memory pool as a source of transactions to potentially 308 // include in the block. 309 template, err := NewBlockTemplate(m.policy, m.server, payToAddr) 310 m.submitBlockLock.Unlock() 311 if err != nil { 312 errStr := fmt.Sprintf("Failed to create new block "+ 313 "template: %v", err) 314 minrLog.Errorf(errStr) 315 continue 316 } 317 318 // Attempt to solve the block. The function will exit early 319 // with false when conditions that trigger a stale block, so 320 // a new block template can be generated. When the return is 321 // true a solution was found, so submit the solved block. 322 if m.solveBlock(template.Block, curHeight+1, ticker, quit) { 323 block := godashutil.NewBlock(template.Block) 324 m.submitBlock(block) 325 } 326 } 327 328 m.workerWg.Done() 329 minrLog.Tracef("Generate blocks worker done") 330 } 331 332 // miningWorkerController launches the worker goroutines that are used to 333 // generate block templates and solve them. It also provides the ability to 334 // dynamically adjust the number of running worker goroutines. 335 // 336 // It must be run as a goroutine. 337 func (m *CPUMiner) miningWorkerController() { 338 // launchWorkers groups common code to launch a specified number of 339 // workers for generating blocks. 340 var runningWorkers []chan struct{} 341 launchWorkers := func(numWorkers uint32) { 342 for i := uint32(0); i < numWorkers; i++ { 343 quit := make(chan struct{}) 344 runningWorkers = append(runningWorkers, quit) 345 346 m.workerWg.Add(1) 347 go m.generateBlocks(quit) 348 } 349 } 350 351 // Launch the current number of workers by default. 352 runningWorkers = make([]chan struct{}, 0, m.numWorkers) 353 launchWorkers(m.numWorkers) 354 355 out: 356 for { 357 select { 358 // Update the number of running workers. 359 case <-m.updateNumWorkers: 360 // No change. 361 numRunning := uint32(len(runningWorkers)) 362 if m.numWorkers == numRunning { 363 continue 364 } 365 366 // Add new workers. 367 if m.numWorkers > numRunning { 368 launchWorkers(m.numWorkers - numRunning) 369 continue 370 } 371 372 // Signal the most recently created goroutines to exit. 373 for i := numRunning - 1; i >= m.numWorkers; i-- { 374 close(runningWorkers[i]) 375 runningWorkers[i] = nil 376 runningWorkers = runningWorkers[:i] 377 } 378 379 case <-m.quit: 380 for _, quit := range runningWorkers { 381 close(quit) 382 } 383 break out 384 } 385 } 386 387 // Wait until all workers shut down to stop the speed monitor since 388 // they rely on being able to send updates to it. 389 m.workerWg.Wait() 390 close(m.speedMonitorQuit) 391 m.wg.Done() 392 } 393 394 // Start begins the CPU mining process as well as the speed monitor used to 395 // track hashing metrics. Calling this function when the CPU miner has 396 // already been started will have no effect. 397 // 398 // This function is safe for concurrent access. 399 func (m *CPUMiner) Start() { 400 m.Lock() 401 defer m.Unlock() 402 403 // Nothing to do if the miner is already running or if running in discrete 404 // mode (using GenerateNBlocks). 405 if m.started || m.discreteMining { 406 return 407 } 408 409 m.quit = make(chan struct{}) 410 m.speedMonitorQuit = make(chan struct{}) 411 m.wg.Add(2) 412 go m.speedMonitor() 413 go m.miningWorkerController() 414 415 m.started = true 416 minrLog.Infof("CPU miner started") 417 } 418 419 // Stop gracefully stops the mining process by signalling all workers, and the 420 // speed monitor to quit. Calling this function when the CPU miner has not 421 // already been started will have no effect. 422 // 423 // This function is safe for concurrent access. 424 func (m *CPUMiner) Stop() { 425 m.Lock() 426 defer m.Unlock() 427 428 // Nothing to do if the miner is not currently running or if running in 429 // discrete mode (using GenerateNBlocks). 430 if !m.started || m.discreteMining { 431 return 432 } 433 434 close(m.quit) 435 m.wg.Wait() 436 m.started = false 437 minrLog.Infof("CPU miner stopped") 438 } 439 440 // IsMining returns whether or not the CPU miner has been started and is 441 // therefore currenting mining. 442 // 443 // This function is safe for concurrent access. 444 func (m *CPUMiner) IsMining() bool { 445 m.Lock() 446 defer m.Unlock() 447 448 return m.started 449 } 450 451 // HashesPerSecond returns the number of hashes per second the mining process 452 // is performing. 0 is returned if the miner is not currently running. 453 // 454 // This function is safe for concurrent access. 455 func (m *CPUMiner) HashesPerSecond() float64 { 456 m.Lock() 457 defer m.Unlock() 458 459 // Nothing to do if the miner is not currently running. 460 if !m.started { 461 return 0 462 } 463 464 return <-m.queryHashesPerSec 465 } 466 467 // SetNumWorkers sets the number of workers to create which solve blocks. Any 468 // negative values will cause a default number of workers to be used which is 469 // based on the number of processor cores in the system. A value of 0 will 470 // cause all CPU mining to be stopped. 471 // 472 // This function is safe for concurrent access. 473 func (m *CPUMiner) SetNumWorkers(numWorkers int32) { 474 if numWorkers == 0 { 475 m.Stop() 476 } 477 478 // Don't lock until after the first check since Stop does its own 479 // locking. 480 m.Lock() 481 defer m.Unlock() 482 483 // Use default if provided value is negative. 484 if numWorkers < 0 { 485 m.numWorkers = defaultNumWorkers 486 } else { 487 m.numWorkers = uint32(numWorkers) 488 } 489 490 // When the miner is already running, notify the controller about the 491 // the change. 492 if m.started { 493 m.updateNumWorkers <- struct{}{} 494 } 495 } 496 497 // NumWorkers returns the number of workers which are running to solve blocks. 498 // 499 // This function is safe for concurrent access. 500 func (m *CPUMiner) NumWorkers() int32 { 501 m.Lock() 502 defer m.Unlock() 503 504 return int32(m.numWorkers) 505 } 506 507 // GenerateNBlocks generates the requested number of blocks. It is self 508 // contained in that it creates block templates and attempts to solve them while 509 // detecting when it is performing stale work and reacting accordingly by 510 // generating a new block template. When a block is solved, it is submitted. 511 // The function returns a list of the hashes of generated blocks. 512 func (m *CPUMiner) GenerateNBlocks(n uint32) ([]*wire.ShaHash, error) { 513 m.Lock() 514 515 // Respond with an error if there's virtually 0 chance of CPU-mining a block. 516 if !m.server.chainParams.GenerateSupported { 517 m.Unlock() 518 return nil, errors.New("No support for `generate` on the current " + 519 "network, " + m.server.chainParams.Net.String() + 520 ", as it's unlikely to be possible to CPU-mine a block.") 521 } 522 523 // Respond with an error if server is already mining. 524 if m.started || m.discreteMining { 525 m.Unlock() 526 return nil, errors.New("Server is already CPU mining. Please call " + 527 "`setgenerate 0` before calling discrete `generate` commands.") 528 } 529 530 m.started = true 531 m.discreteMining = true 532 533 m.speedMonitorQuit = make(chan struct{}) 534 m.wg.Add(1) 535 go m.speedMonitor() 536 537 m.Unlock() 538 539 minrLog.Tracef("Generating %d blocks", n) 540 541 i := uint32(0) 542 blockHashes := make([]*wire.ShaHash, n, n) 543 544 // Start a ticker which is used to signal checks for stale work and 545 // updates to the speed monitor. 546 ticker := time.NewTicker(time.Second * hashUpdateSecs) 547 defer ticker.Stop() 548 549 for { 550 // Read updateNumWorkers in case someone tries a `setgenerate` while 551 // we're generating. We can ignore it as the `generate` RPC call only 552 // uses 1 worker. 553 select { 554 case <-m.updateNumWorkers: 555 default: 556 } 557 558 // Grab the lock used for block submission, since the current block will 559 // be changing and this would otherwise end up building a new block 560 // template on a block that is in the process of becoming stale. 561 m.submitBlockLock.Lock() 562 _, curHeight := m.server.blockManager.chainState.Best() 563 564 // Choose a payment address at random. 565 rand.Seed(time.Now().UnixNano()) 566 payToAddr := cfg.miningAddrs[rand.Intn(len(cfg.miningAddrs))] 567 568 // Create a new block template using the available transactions 569 // in the memory pool as a source of transactions to potentially 570 // include in the block. 571 template, err := NewBlockTemplate(m.policy, m.server, payToAddr) 572 m.submitBlockLock.Unlock() 573 if err != nil { 574 errStr := fmt.Sprintf("Failed to create new block "+ 575 "template: %v", err) 576 minrLog.Errorf(errStr) 577 continue 578 } 579 580 // Attempt to solve the block. The function will exit early 581 // with false when conditions that trigger a stale block, so 582 // a new block template can be generated. When the return is 583 // true a solution was found, so submit the solved block. 584 if m.solveBlock(template.Block, curHeight+1, ticker, nil) { 585 block := godashutil.NewBlock(template.Block) 586 m.submitBlock(block) 587 blockHashes[i] = block.Sha() 588 i++ 589 if i == n { 590 minrLog.Tracef("Generated %d blocks", i) 591 m.Lock() 592 close(m.speedMonitorQuit) 593 m.wg.Wait() 594 m.started = false 595 m.discreteMining = false 596 m.Unlock() 597 return blockHashes, nil 598 } 599 } 600 } 601 } 602 603 // newCPUMiner returns a new instance of a CPU miner for the provided server. 604 // Use Start to begin the mining process. See the documentation for CPUMiner 605 // type for more details. 606 func newCPUMiner(policy *mining.Policy, s *server) *CPUMiner { 607 return &CPUMiner{ 608 policy: policy, 609 txSource: s.txMemPool, 610 server: s, 611 numWorkers: defaultNumWorkers, 612 updateNumWorkers: make(chan struct{}), 613 queryHashesPerSec: make(chan float64), 614 updateHashes: make(chan uint64), 615 } 616 }