比特币全节点Go语言实现BTCD之挖矿流程及难度计算

全网中每新增2016个区块,全网难度将重新计算,该新难度值将依据前2016个区块的哈希算力而定

If it took fewer than two weeks to generate the 2,016 blocks, the expected difficultyvalue is increased proportionally (by as much as 300%) so that the next 2,016 blocksshould take exactly two weeks to generate if hashes are checked at the same rate.

  • If it took more than two weeks to generate the blocks, the expected difficulty value is decreased proportionally (by as much as 75%) for the same reason.

假设一个分叉的区块都是合格的,通常节点总是接受满足工作量难度的最长链,去掉更短链的没用的块。

下面分析代码:

// Start the CPU miner if generation is enabled.
if cfg.Generate {
   s.cpuMiner.Start()
}

开始挖矿,方法代码:

func (m *CPUMiner) Start() {
   m.Lock()
   defer m.Unlock()

   if m.started || m.discreteMining {
      return
   }

   m.quit = make(chan struct{})
   m.speedMonitorQuit = make(chan struct{})
   m.wg.Add(2)
   go m.speedMonitor()
   go m.miningWorkerController()

   m.started = true
   log.Infof("CPU miner started")
}

go m.miningWorkerController()

异步启动挖矿工作线程,代码:

func (m *CPUMiner) miningWorkerController() {
   // launchWorkers groups common code to launch a specified number of
   // workers for generating blocks.
   var runningWorkers []chan struct{}
   launchWorkers := func(numWorkers uint32) {
      for i := uint32(0); i < numWorkers; i++ {
         quit := make(chan struct{})
         runningWorkers = append(runningWorkers, quit)

         m.workerWg.Add(1)
         go m.generateBlocks(quit)
      }
   }

   // Launch the current number of workers by default.
   runningWorkers = make([]chan struct{}, 0, m.numWorkers)
   launchWorkers(m.numWorkers)

out:
   for {
      select {
      // Update the number of running workers.
      case <-m.updateNumWorkers:
         // No change.
         numRunning := uint32(len(runningWorkers))
         if m.numWorkers == numRunning {
            continue
         }

         // Add new workers.
         if m.numWorkers > numRunning {
            launchWorkers(m.numWorkers - numRunning)
            continue
         }

         // Signal the most recently created goroutines to exit.
         for i := numRunning - 1; i >= m.numWorkers; i-- {
            close(runningWorkers[i])
            runningWorkers[i] = nil
            runningWorkers = runningWorkers[:i]
         }

      case <-m.quit:
         for _, quit := range runningWorkers {
            close(quit)
         }
         break out
      }
   }

   // Wait until all workers shut down to stop the speed monitor since
   // they rely on being able to send updates to it.
   m.workerWg.Wait()
   close(m.speedMonitorQuit)
   m.wg.Done()
}
go m.generateBlocks(quit)

生成区块,具体怎么生产的?代码:

func (m *CPUMiner) generateBlocks(quit chan struct{}) {
   log.Tracef("Starting generate blocks worker")

   ticker := time.NewTicker(time.Second * hashUpdateSecs)
   defer ticker.Stop()
out:
   for {
      select {
      case <-quit:
         break out
      default:
         // Non-blocking select to fall through
      }

      if m.cfg.ConnectedCount() == 0 {
         time.Sleep(time.Second)
         continue
      }

      m.submitBlockLock.Lock()
      curHeight := m.g.BestSnapshot().Height
      if curHeight != 0 && !m.cfg.IsCurrent() {
         m.submitBlockLock.Unlock()
         time.Sleep(time.Second)
         continue
      }

      // 从挖矿地址账号随机选择一个地址作为入账地址
      rand.Seed(time.Now().UnixNano())
      payToAddr := m.cfg.MiningAddrs[rand.Intn(len(m.cfg.MiningAddrs))]

      // 创建区块模板
      template, err := m.g.NewBlockTemplate(payToAddr)
      m.submitBlockLock.Unlock()
      if err != nil {
         errStr := fmt.Sprintf("Failed to create new block "+
            "template: %v", err)
         log.Errorf(errStr)
         continue
      }

      // 开始POW,计算区块的hash符合难度目标
      if m.solveBlock(template.Block, curHeight+1, ticker, quit) {
         block := btcutil.NewBlock(template.Block)
         m.submitBlock(block)
      }
   }

   m.workerWg.Done()
   log.Tracef("Generate blocks worker done")
}
template, err := m.g.NewBlockTemplate(payToAddr)

创建新的块模板,在NewBlockTemplate方法里有调用:

reqDifficulty, err := g.chain.CalcNextRequiredDifficulty(ts)

这个方法是每2016个块计算新的难度目标

if m.solveBlock(template.Block, curHeight+1, ticker, quit) {

随机nonce计算块hash

if blockchain.HashToBig(&hash).Cmp(targetDifficulty) <= 0 {
   m.updateHashes <- hashesCompleted
   return true
}

满足条件返回true

func (m *CPUMiner) submitBlock(block *btcutil.Block) bool {

将区块广播到网络。

isOrphan, err := m.cfg.ProcessBlock(block, blockchain.BFNone)

ProcessBlock是在new server时配置的

s.cpuMiner = cpuminer.New(&cpuminer.Config{
   ChainParams:            chainParams,
   BlockTemplateGenerator: blockTemplateGenerator,
   MiningAddrs:            cfg.miningAddrs,
   ProcessBlock:           s.syncManager.ProcessBlock,
   ConnectedCount:         s.ConnectedCount,
   IsCurrent:              s.syncManager.IsCurrent,
})
func (sm *SyncManager) ProcessBlock(block *btcutil.Block, flags blockchain.BehaviorFlags) (bool, error) {
   reply := make(chan processBlockResponse, 1)
   sm.msgChan <- processBlockMsg{block: block, flags: flags, reply: reply}
   response := <-reply
   return response.isOrphan, response.err
}

创建一个processBlockMsg对象放到msgChan通道里。

然后就由一下方法处理块消息

func (sm *SyncManager) blockHandler() {







猜你喜欢

转载自blog.csdn.net/vohyeah/article/details/80704054