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  }