以太坊源代码 - 挖矿打包流程以及速度

原文地址


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秒。



猜你喜欢

转载自blog.csdn.net/metal1/article/details/80501519