worker负责整个挖矿打包流程,代码查看miner/worker.go文件。
1)打包流程
commitNewWork是打包流程的开始,具体的调用关系如下:
*)步骤6 - 创建Work,所谓的Work即目前打包的执行环境。
*)步骤7 - 执行pending的交易,记录交易的receipts。
*)步骤8 - 生成具体的区块信息。
*)步骤10 - 生成好的区块数据,发送给miner.CpuAgent。CpuAgent调用Engine的Seal函数打包。
*)步骤13 - 打包后的Work发送回miner(通过chan)。
*)步骤15/16 - 更新链信息以及发出区块生成事件。
从上图可以看出,consensus.Engine是打包逻辑的接口(consensus/consensus.go):
// Engine is an algorithm agnostic consensus engine.
type Engine interface {
// Author retrieves the Ethereum address of the account that minted the given
// block, which may be different from the header's coinbase if a consensus
// engine is based on signatures.
Author(header *types.Header) (common.Address, error)
// VerifyHeader checks whether a header conforms to the consensus rules of a
// given engine. Verifying the seal may be done optionally here, or explicitly
// via the VerifySeal method.
VerifyHeader(chain ChainReader, header *types.Header, seal bool) error
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers
// concurrently. The method returns a quit channel to abort the operations and
// a results channel to retrieve the async verifications (the order is that of
// the input slice).
VerifyHeaders(chain ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error)
// VerifyUncles verifies that the given block's uncles conform to the consensus
// rules of a given engine.
VerifyUncles(chain ChainReader, block *types.Block) error
// VerifySeal checks whether the crypto seal on a header is valid according to
// the consensus rules of the given engine.
VerifySeal(chain ChainReader, header *types.Header) error
// Prepare initializes the consensus fields of a block header according to the
// rules of a particular engine. The changes are executed inline.
Prepare(chain ChainReader, header *types.Header) error
// Finalize runs any post-transaction state modifications (e.g. block rewards)
// and assembles the final block.
// Note: The block header and state database might be updated to reflect any
// consensus rules that happen at finalization (e.g. block rewards).
Finalize(chain ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error)
// Seal generates a new block for the given input block with the local miner's
// seal place on top.
Seal(chain ChainReader, block *types.Block, stop <-chan struct{}) (*types.Block, error)
// CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty
// that a new block should have.
CalcDifficulty(chain ChainReader, time uint64, parent *types.Header) *big.Int
// APIs returns the RPC APIs this consensus engine provides.
APIs(chain ChainReader) []rpc.API
}
// PoW is a consensus engine based on proof-of-work.
type PoW interface {
Engine
// Hashrate returns the current mining hashrate of a PoW consensus engine.
Hashrate() float64
}
在consensus目录下还有Engine的两个具体实现:
1)clique (测试网络使用),基于POA(proof of authority) 2)ethash(正式网络使用),基于POW(proof of work)。
2)打包出块速度
在上图中的Engine的接口中,有个接口函数 - CalcDifficulty。这个接口是计算当前区块的难度,调节以太网络的出块速度。
查看ethash的实现,CalcDifficulty函数实现在consensus/ethash/consensus.go文件:
// CalcDifficulty is the difficulty adjustment algorithm. It returns
// the difficulty that a new block should have when created at time
// given the parent block's time and difficulty.
func CalcDifficulty(config *params.ChainConfig, time uint64, parent *types.Header) *big.Int {
next := new(big.Int).Add(parent.Number, big1)
switch {
case config.IsByzantium(next):
return calcDifficultyByzantium(time, parent)
case config.IsHomestead(next):
return calcDifficultyHomestead(time, parent)
default:
return calcDifficultyFrontier(time, parent)
}
}
以太根据块的高度做了一些难度策略的调整。这里着重看一下,最新的策略:calcDifficultyByzantium函数。
// calcDifficultyByzantium is the difficulty adjustment algorithm. It returns // the difficulty that a new block should have when created at time given the // parent block's time and difficulty. The calculation uses the Byzantium rules. func calcDifficultyByzantium(time uint64, parent *types.Header) *big.Int { // https://github.com/ethereum/EIPs/issues/100. // algorithm: // diff = (parent_diff + // (parent_diff / 2048 * max((2 if len(parent.uncles) else 1) - ((timestamp - parent.timestamp) // 9), -99)) // ) + 2^(periodCount - 2) bigTime := new(big.Int).SetUint64(time) bigParentTime := new(big.Int).Set(parent.Time) // holds intermediate values to make the algo easier to read & audit x := new(big.Int) y := new(big.Int) // (2 if len(parent_uncles) else 1) - (block_timestamp - parent_timestamp) // 9 x.Sub(bigTime, bigParentTime) x.Div(x, big9) if parent.UncleHash == types.EmptyUncleHash { x.Sub(big1, x) } else { x.Sub(big2, x) } // max((2 if len(parent_uncles) else 1) - (block_timestamp - parent_timestamp) // 9, -99) if x.Cmp(bigMinus99) < 0 { x.Set(bigMinus99) } // parent_diff + (parent_diff / 2048 * max((2 if len(parent.uncles) else 1) - ((timestamp - parent.timestamp) // 9), -99)) y.Div(parent.Difficulty, params.DifficultyBoundDivisor) x.Mul(y, x) x.Add(parent.Difficulty, x) // minimum difficulty can ever be (before exponential factor) if x.Cmp(params.MinimumDifficulty) < 0 { x.Set(params.MinimumDifficulty) } // calculate a fake block number for the ice-age delay: // https://github.com/ethereum/EIPs/pull/669 // fake_block_number = min(0, block.number - 3_000_000 fakeBlockNumber := new(big.Int) if parent.Number.Cmp(big2999999) >= 0 { fakeBlockNumber = fakeBlockNumber.Sub(parent.Number, big2999999) // Note, parent is 1 less than the actual block number } // for the exponential factor periodCount := fakeBlockNumber periodCount.Div(periodCount, expDiffPeriod) // the exponential factor, commonly referred to as "the bomb" // diff = diff + 2^(periodCount - 2) if periodCount.Cmp(big1) > 0 { y.Sub(periodCount, big2) y.Exp(big2, y, nil) x.Add(x, y) } return x }
在函数的注释中,明确给出了难度调整的算法。算法调整分为两个部分:
1)parent_diff/2048*max(1-(时间差/9), -99) (如果没有叔块)
简单的说,假设当前块和上一块的时间差为X秒:
X<9: 增加难度,增加上一块区块难度的1/2048。
9<X<18: 保持上一块区块难度不变。
X>27: 减少难度,较少上一块区块难度的1/2048个倍数,倍数最大为99。
2)2^(periodCount-2) (又称难度炸弹)
periodCount的计算公式:(上个区块的高度 - 3000000)/100000。从etherscan上查看最新的区块信息:
当前的高度是5577040,难度炸弹的值为:2^23 = 8388608。目前,这个炸弹的值相对来说可以忽略。
总结一下:目前难度调整的目的是将打包速度调整在9到18秒之间。也就是,大多数人说的,打包时间大约15秒。