共识引擎描述

在CPU挖矿部分,CpuAgent的mine函数,执行挖矿操作的时候调用了self.engine.Seal函数。这里的engine是就是共识引擎。Seal为其中很重要的一个接口。它实现了nonce值的寻找和hash的计算。并且该函数是保证共识并且不能伪造的一个重要的函数。

再PoW共识算法中,Seal函数实现了工作证明。该部分源码在consensus/ethhash下。

共识引擎接口

type Engine interface {

// 获取区块挖掘者, 即coinbase

Author(header *types.Header) (common.Address, error)

// VerifyHeader 用于校验区块头,通过共识规则来校验,验证区块可以在这里进行也科通通过VerifySeal方法
VerifyHeader(chain ChainReader, header *types.Header, seal bool) error
// VerifyHeaders与VerifyHeader相似,同时这个用于批量操作校验头。这个方法返回一个退出信号
// 用于终止操作,用于异步校验。
VerifyHeaders(chain ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error)

// VerifyUncles 用于校验叔块以符合共识引擎的规则
VerifyUncles(chain ChainReader, block *types.Block) error

// VerifySeal根据共识算法的规则校验区块头
VerifySeal(chain ChainReader, header *types.Header) error

// Prepare 用于初始化区块头的共识字段根据共识引擎。这些改变都是内联执行的。
Prepare(chain ChainReader, header *types.Header) error

// Finalize 完成所有的状态修改,并最终组装成块。
// 区块头和状态数据库在最终确认的时候可以被更新使之符合共识规则。
Finalize(chain ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error)

// Seal 根据输入区块打包生产一个新的区块
Seal(chain ChainReader, block *types.Block, stop <-chan struct{}) (*types.Block, error)

// CalcDifficulty 是难度调整算法,它返回新的区块的难度值。
CalcDifficulty(chain ChainReader, time uint64, parent *types.Header) *big.Int

// APIs 返回由共识引擎提供的RPC APIs
APIs(chain ChainReader) []rpc.API
}

ethhash 实现分析

ethhash 结构体

type Ethash struct {
config Config

// 缓存
caches *lru // In memory caches to avoid regenerating too often
// 内存数据集
datasets *lru // In memory datasets to avoid regenerating too often

// Mining related fields
rand *rand.Rand // Properly seeded random source for nonces
// 挖矿线程数量
threads int // Number of threads to mine on if mining
// channel 用于更新挖矿通知
update chan struct{} // Notification channel to update mining parameters
hashrate metrics.Meter // Meter tracking the average hashrate

// 测试网络相关参数
// The fields below are hooks for testing
shared *Ethash // Shared PoW verifier to avoid cache regeneration
fakeFail uint64 // Block number which fails PoW check even in fake mode
fakeDelay time.Duration // Time delay to sleep for before returning from verify

lock sync.Mutex // Ensures thread safety for the in-memory caches and mining fields
}

Ethhash是实现PoW的具体实现,由于要使用到大量的数据集,所有有两个指向lru的指针。并且通过threads控制挖矿线程数。并在测试模式或fake模式下,简单快速处理,使之快速得到结果。

Athor方法获取了挖出这个块的矿工地址。

func (ethash *Ethash) Author(header *types.Header) (common.Address, error) {
return header.Coinbase, nil
}

VerifyHeader 用于校验区块头部信息是否符合ethash共识引擎规则。

// VerifyHeader checks whether a header conforms to the consensus rules of the
// stock Ethereum ethash engine.
func (ethash *Ethash) VerifyHeader(chain consensus.ChainReader, header *types.Header, seal bool) error {
// 当处于ModeFullFake模式时,任意头部信息都接受
if ethash.config.PowMode == ModeFullFake {
return nil
}
// 如果该头部是已知的,不用校验,直接返回。
number := header.Number.Uint64()
if chain.GetHeader(header.Hash(), number) != nil {
return nil
}
parent := chain.GetHeader(header.ParentHash, number-1)
if parent == nil { // 获取父结点失败
return consensus.ErrUnknownAncestor
}
// 进一步进行头部校验
return ethash.verifyHeader(chain, header, parent, false, seal)
}

然后再看看verifyHeader的实现,

func (ethash *Ethash) verifyHeader(chain consensus.ChainReader, header, parent *types.Header, uncle bool, seal bool) error {
// 确保额外数据段具有合理的长度
if uint64(len(header.Extra)) > params.MaximumExtraDataSize {
return fmt.Errorf("extra-data too long: %d > %d", len(header.Extra), params.MaximumExtraDataSize)
}
// 校验时间戳
if uncle {
if header.Time.Cmp(math.MaxBig256) > 0 {
return errLargeBlockTime
}
} else {
if header.Time.Cmp(big.NewInt(time.Now().Add(allowedFutureBlockTime).Unix())) > 0 {
return consensus.ErrFutureBlock
}
}
if header.Time.Cmp(parent.Time) <= 0 {
return errZeroBlockTime
}
// 根据时间戳和父级块的难度校验块的难度。
expected := ethash.CalcDifficulty(chain, header.Time.Uint64(), parent)

if expected.Cmp(header.Difficulty) != 0 {
return fmt.Errorf("invalid difficulty: have %v, want %v", header.Difficulty, expected)
}
// 校验gas limit <= 2^63-1
cap := uint64(0x7fffffffffffffff)
if header.GasLimit > cap {
return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit, cap)
}
// 校验 gasUsed <= gasLimit
if header.GasUsed > header.GasLimit {
return fmt.Errorf("invalid gasUsed: have %d, gasLimit %d", header.GasUsed, header.GasLimit)
}

// gas limit 是否在允许范围内
diff := int64(parent.GasLimit) - int64(header.GasLimit)
if diff < 0 {
diff *= -1
}
limit := parent.GasLimit / params.GasLimitBoundDivisor

if uint64(diff) >= limit || header.GasLimit < params.MinGasLimit {
return fmt.Errorf("invalid gas limit: have %d, want %d += %d", header.GasLimit, parent.GasLimit, limit)
}
// 校验区块号应该是父块区块号 +1
if diff := new(big.Int).Sub(header.Number, parent.Number); diff.Cmp(big.NewInt(1)) != 0 {
return consensus.ErrInvalidNumber
}
// 校验特定的块是否符合要求
if seal {
if err := ethash.VerifySeal(chain, header); err != nil {
return err
}
}
// 如果所有检查通过,则验证硬分叉的特殊字段。
if err := misc.VerifyDAOHeaderExtraData(chain.Config(), header); err != nil {
return err
}
if err := misc.VerifyForkHashes(chain.Config(), header, uncle); err != nil {
return err
}
return nil
}