全网中每新增2016个区块,全网难度将重新计算,该新难度值将依据前2016个区块的哈希算力而定
If it took fewer than two weeks to generate the 2,016 blocks
, the expected difficulty
value is increased proportionally (by as much as 300%) so that the next 2,016 blocks
should 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.
假设一个分叉的区块都是合格的,通常节点总是接受满足工作量难度的最长链,去掉更短链的没用的块。
下面分析代码:
1
2
3
4
5 1// Start the CPU miner if generation is enabled.
2if cfg.Generate {
3 s.cpuMiner.Start()
4}
5
开始挖矿,方法代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 1func (m *CPUMiner) Start() {
2 m.Lock()
3 defer m.Unlock()
4
5 if m.started || m.discreteMining {
6 return
7}
8
9 m.quit = make(chan struct{})
10 m.speedMonitorQuit = make(chan struct{})
11 m.wg.Add(2)
12 go m.speedMonitor()
13 go m.miningWorkerController()
14
15 m.started = true
16log.Infof("CPU miner started")
17}
18
1
2 1go m.miningWorkerController()
2
异步启动挖矿工作线程,代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57 1func (m *CPUMiner) miningWorkerController() {
2 // launchWorkers groups common code to launch a specified number of
3 // workers for generating blocks.
4 var runningWorkers []chan struct{}
5 launchWorkers := func(numWorkers uint32) {
6 for i := uint32(0); i < numWorkers; i++ {
7 quit := make(chan struct{})
8 runningWorkers = append(runningWorkers, quit)
9
10 m.workerWg.Add(1)
11 go m.generateBlocks(quit)
12 }
13 }
14
15 // Launch the current number of workers by default.
16 runningWorkers = make([]chan struct{}, 0, m.numWorkers)
17 launchWorkers(m.numWorkers)
18
19out:
20 for {
21 select {
22 // Update the number of running workers.
23 case <-m.updateNumWorkers:
24 // No change.
25 numRunning := uint32(len(runningWorkers))
26 if m.numWorkers == numRunning {
27 continue
28}
29
30 // Add new workers.
31 if m.numWorkers > numRunning {
32 launchWorkers(m.numWorkers - numRunning)
33 continue
34}
35
36 // Signal the most recently created goroutines to exit.
37 for i := numRunning - 1; i >= m.numWorkers; i-- {
38 close(runningWorkers[i])
39 runningWorkers[i] = nil
40 runningWorkers = runningWorkers[:i]
41 }
42
43 case <-m.quit:
44 for _, quit := range runningWorkers {
45 close(quit)
46 }
47 break out
48 }
49 }
50
51 // Wait until all workers shut down to stop the speed monitor since
52 // they rely on being able to send updates to it.
53 m.workerWg.Wait()
54 close(m.speedMonitorQuit)
55 m.wg.Done()
56}
57
1
2 1go m.generateBlocks(quit)
2
生成区块,具体怎么生产的?代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52 1func (m *CPUMiner) generateBlocks(quit chan struct{}) {
2 log.Tracef("Starting generate blocks worker")
3
4 ticker := time.NewTicker(time.Second * hashUpdateSecs)
5 defer ticker.Stop()
6out:
7 for {
8 select {
9 case <-quit:
10 break out
11 default:
12 // Non-blocking select to fall through
13 }
14
15 if m.cfg.ConnectedCount() == 0 {
16 time.Sleep(time.Second)
17 continue
18}
19
20 m.submitBlockLock.Lock()
21 curHeight := m.g.BestSnapshot().Height
22 if curHeight != 0 && !m.cfg.IsCurrent() {
23 m.submitBlockLock.Unlock()
24 time.Sleep(time.Second)
25 continue
26}
27
28 // 从挖矿地址账号随机选择一个地址作为入账地址
29 rand.Seed(time.Now().UnixNano())
30 payToAddr := m.cfg.MiningAddrs[rand.Intn(len(m.cfg.MiningAddrs))]
31
32 // 创建区块模板
33 template, err := m.g.NewBlockTemplate(payToAddr)
34 m.submitBlockLock.Unlock()
35 if err != nil {
36 errStr := fmt.Sprintf("Failed to create new block "+
37 "template: %v", err)
38 log.Errorf(errStr)
39 continue
40}
41
42 // 开始POW,计算区块的hash符合难度目标
43 if m.solveBlock(template.Block, curHeight+1, ticker, quit) {
44 block := btcutil.NewBlock(template.Block)
45 m.submitBlock(block)
46 }
47 }
48
49 m.workerWg.Done()
50 log.Tracef("Generate blocks worker done")
51}
52
1
2 1template, err := m.g.NewBlockTemplate(payToAddr)
2
创建新的块模板,在NewBlockTemplate方法里有调用:
1
2 1reqDifficulty, err := g.chain.CalcNextRequiredDifficulty(ts)
2
这个方法是每2016个块计算新的难度目标
1
2 1if m.solveBlock(template.Block, curHeight+1, ticker, quit) {
2
随机nonce计算块hash
1
2
3
4
5 1if blockchain.HashToBig(&hash).Cmp(targetDifficulty) <= 0 {
2 m.updateHashes <- hashesCompleted
3 return true
4}
5
满足条件返回true
1
2 1func (m *CPUMiner) submitBlock(block *btcutil.Block) bool {
2
将区块广播到网络。
1
2 1isOrphan, err := m.cfg.ProcessBlock(block, blockchain.BFNone)
2
ProcessBlock是在new server时配置的
1
2
3
4
5
6
7
8
9 1s.cpuMiner = cpuminer.New(&cpuminer.Config{
2 ChainParams: chainParams,
3 BlockTemplateGenerator: blockTemplateGenerator,
4 MiningAddrs: cfg.miningAddrs,
5 ProcessBlock: s.syncManager.ProcessBlock,
6 ConnectedCount: s.ConnectedCount,
7 IsCurrent: s.syncManager.IsCurrent,
8})
9
1
2
3
4
5
6
7 1func (sm *SyncManager) ProcessBlock(block *btcutil.Block, flags blockchain.BehaviorFlags) (bool, error) {
2 reply := make(chan processBlockResponse, 1)
3 sm.msgChan <- processBlockMsg{block: block, flags: flags, reply: reply}
4 response := <-reply
5 return response.isOrphan, response.err
6}
7
创建一个processBlockMsg对象放到msgChan通道里。
然后就由一下方法处理块消息
1
2 1func (sm *SyncManager) blockHandler() {
2