GO-HPB源码解读--挖矿流程(三)

在挖矿流程(三)的最后,看到在go self.mine(work, self.quitCurrentOp)中要传递一个work对象进去。因为挖矿流程需要使用到大量的work数据,所以单独分析一下work的数据结构和初始化流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Work is the workers current environment and holds
// all of the current state information
type Work struct {
config *config.ChainConfig
signer types.Signer

state *state.StateDB // apply state changes here
ancestors *set.Set // ancestor set (used for checking uncle parent validity)
family *set.Set // family set (used for checking uncle invalidity)
uncles *set.Set // uncle set
tcount int // tx count in cycle

Block *types.Block // the new block

header *types.Header
txs []*types.Transaction
receipts []*types.Receipt

createdAt time.Time
}
字段 类型 含义 备注
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;二是新块的时间和自己系统时间差不要超过1,也就是说计算出来的新块时间戳比当前时间加1s还大,那就只能等一会儿再执行了。比如当前时间是1,新区时间戳是8,那么就需要等7s了。
  2. 组织新块的header
  3. 初始化当前work对象
  4. 把appending的交易打包进来
  5. 使用共识引擎组装成一个完整的区块
  6. 提交挖矿任务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
    95
    func (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
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
makeCurrent(...)方法即初始化了一个work对象,之后会设置work的tcount、uncles等其他属性
// makeCurrent creates a new environment for the current cycle.
func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error {
state, err := self.chain.StateAt(parent.Root())
if err != nil {
return err
}
work := &Work{
config: self.config,
signer: types.NewBoeSigner(self.config.ChainId),
state: state,
ancestors: set.New(),
family: set.New(),
uncles: set.New(),
header: header,
createdAt: time.Now(),
}

// when 08 is processed ancestors contain 07 (quick block)
for _, ancestor := range self.chain.GetBlocksFromHash(parent.Hash(), 7) {
for _, uncle := range ancestor.Uncles() {
work.family.Add(uncle.Hash())
}
work.family.Add(ancestor.Hash())
work.ancestors.Add(ancestor.Hash())
}

// Keep track of transactions which return errors so they can be removed
work.tcount = 0
self.current = work
return nil
}