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