在挖矿流程(三)的最后,看到在go self.mine(work, self.quitCurrentOp)中要传递一个work对象进去。因为挖矿流程需要使用到大量的work数据,所以单独分析一下work的数据结构和初始化流程
1 | // Work is the workers current environment and holds |
| 字段 | 类型 | 含义 | 备注 |
|---|---|---|---|
| config | *config.ChainConfig | chain的配置参数 | |
| signer | types.Signer | 签名 | |
| state | *state.StateDB | ||
| ancestors | *set.Set | 祖先哈希集合,也就是最近的7个区块的哈希, | |
| family | *set.Set | 家族哈希集合,也就是最近的7个区块的哈希以及每个区块的叔区块集合的哈希 | |
| uncles | *set.Set | 叔块集合 | |
| tcount | int | 交易周期内的交易数量 | |
| Block | *types.Block | ||
| header | *types.Header | ||
| txs | []*types.Transaction | ||
| receipts | []*types.Receipt | ||
| createdAt | time.Time |
startNewMinerRound()方法主要是创建了一个work并push到workch通道中,大至流程是:
- 确定区块的时间戳,因为上一区块可能来自节点同步,所以时间可能存在差异,这里确保了两个点,一是新块的时间一定要比上一区块的时间大,至少1;二是新块的时间和自己系统时间差不要超过1,也就是说计算出来的新块时间戳比当前时间加1s还大,那就只能等一会儿再执行了。比如当前时间是1,新区时间戳是8,那么就需要等7s了。
- 组织新块的header
- 初始化当前work对象
- 把appending的交易打包进来
- 使用共识引擎组装成一个完整的区块
- 提交挖矿任务work
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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95func (self *worker) startNewMinerRound() {
self.mu.Lock()
defer self.mu.Unlock()
self.uncleMu.Lock()
defer self.uncleMu.Unlock()
self.currentMu.Lock()
defer self.currentMu.Unlock()
tstart := time.Now()
parent := self.chain.CurrentBlock()
tstamp := tstart.Unix()
if parent.Time().Cmp(new(big.Int).SetInt64(tstamp)) >= 0 {
tstamp = parent.Time().Int64() + 1
}
// this will ensure we're not going off too far in the future
if now := time.Now().Unix(); tstamp > now+1 {
wait := time.Duration(tstamp-now) * time.Second
log.Info("Mining too far in the future", "wait", common.PrettyDuration(wait))
time.Sleep(wait)
}
num := parent.Number()
header := &types.Header{
ParentHash: parent.Hash(),
Number: num.Add(num, common.Big1),
GasLimit: bc.CalcGasLimit(parent),
GasUsed: new(big.Int),
Extra: self.extra,
Time: big.NewInt(tstamp),
}
// Only set the coinbase if we are mining (avoid spurious block rewards)
if atomic.LoadInt32(&self.mining) == 1 {
header.Coinbase = self.coinbase
}
pstate, _ := self.chain.StateAt(parent.Root())
if err := self.engine.PrepareBlockHeader(self.chain, header,pstate); err != nil {
log.Error("Failed to prepare header for mining", "err", err)
return
}
err := self.makeCurrent(parent, header)
if err != nil {
log.Error("Failed to create mining context", "err", err)
return
}
// Create the current work task and check any fork transitions needed
work := self.current
//if self.config.DAOForkSupport && self.config.DAOForkBlock != nil && self.config.DAOForkBlock.Cmp(header.Number) == 0 {
// misc.ApplyDAOHardFork(work.state)
//}
pending, err := txpool.GetTxPool().Pending()
if err != nil {
log.Error("Failed to fetch pending transactions", "err", err)
return
}
//log.Error("----read tx from pending is ", "number is", len(pending))
txs := types.NewTransactionsByPriceAndNonce(self.current.signer, pending)
work.commitTransactions(self.mux, txs, self.coinbase)
// compute uncles for the new block.
var (
uncles []*types.Header
badUncles []common.Hash
)
for hash, uncle := range self.possibleUncles {
if len(uncles) == 2 {
break
}
if err := self.commitUncle(work, uncle.Header()); err != nil {
log.Trace("Bad uncle found and will be removed", "hash", hash)
log.Trace(fmt.Sprint(uncle))
badUncles = append(badUncles, hash)
} else {
log.Debug("Committing new uncle to block", "hash", hash)
uncles = append(uncles, uncle.Header())
}
}
for _, hash := range badUncles {
delete(self.possibleUncles, hash)
}
// Create the new block to seal with the consensus engine
if work.Block, err = self.engine.Finalize(self.chain, header, work.state, work.txs, uncles, work.receipts); err != nil {
log.Error("Failed to finalize block for sealing", "err", err)
return
}
// We only care about logging if we're actually mining.
if atomic.LoadInt32(&self.mining) == 1 {
log.Info("Commit new mining work", "number", work.Block.Number(), "txs", work.tcount, "uncles", len(uncles), "elapsed", common.PrettyDuration(time.Since(tstart)))
self.unconfirmed.Shift(work.Block.NumberU64() - 1)
}
self.push(work)
}
1 | makeCurrent(...)方法即初始化了一个work对象,之后会设置work的tcount、uncles等其他属性 |