原文地址
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>27: 减少难度,较少上一块区块难度的1/2048个倍数,倍数最大为99。 2)2^(periodCount-2) (又称难度炸弹) periodCount的计算公式:(上个区块的高度 - 3000000)/100000。从etherscan上查看最新的区块信息: 当前的高度是5577040,难度炸弹的值为:2^23 = 8388608。目前,这个炸弹的值相对来说可以忽略。 总结一下:目前难度调整的目的是将打包速度调整在9到18秒之间。也就是,大多数人说的,打包时间大约15秒。