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  }