以太坊源码解读:共识模块入口设计
一、前言
主要是先梳理一下consensus模块的入口文件consensus.go
,这里体现了以太坊共识设计的整个设计思路。为后面进一步分析共识算法做好基础工作。
1.1 模块文件结构
先来看下consensus
模块的文件目录:
.
|____misc ## 貌似是硬分叉相关的处理,还没研究,先这么认为
| |____forks.go
| |____dao.go
|____consensus.go ## 共识的通用规则设计,pow的入口设计就在这里
|____ethash ## pow共识算法的具体实现
| |____sealer.go
| |____algorithm.go
| |____sealer_test.go
| |____ethash_test.go
| |____consensus.go
| |____algorithm_test.go
| |____api.go
| |____ethash.go
| |____consensus_test.go
|____clique ## poa共识算法的具体实现,这个主要是供私链使用。
| |____clique.go
| |____snapshot_test.go
| |____api.go
| |____snapshot.go
|____errors.go
从中可以大概看出,以太坊目前实现了pow和poa两种共识算法。
其中poa是是一种POW与POS(proof of stake股权证明)混合的算法,主要是针对私有链使用的,以太坊测试网(Kovan)便是采用PoA算法,貌似将以太坊部署为联盟链也是用的这种共识,具体还没有研究。
这里我们主要关注的是pow,poa等以后有空了再来考虑。
1.2 consensus.go文件简介
这个文件中,主要分为3部分:
- pow算法入口
- 共识通用引擎接口
- 读取链信息的部分接口
1.2.1 Pow算法入口
这是对pow的一个封装,
先来看代码结构:
type PoW interface {
Engine
Hashrate() float64 //返回当前节点的算力
}
从中可以看出,pow算法,只要满足Engine接口,就可以完成自己对整个共识过程。这也给了我们一个思路,如果我们要自己实现共识,同样需要继承了Engine。
1.2.2 共识通用引擎接口Engine
这也是整个共识的核心,一个通用的共识引擎接口,如果要在不改变以太坊整体运转的前提下,设计新的共识规则,那就必须要实现以下的接口,具体的内容都在注释中表明了:
type Engine interface {
// 获取当前挖矿矿工的以太坊地址
Author(header *types.Header) (common.Address, error)
// 校验块头部信息是否符合共识规则,是否封印
VerifyHeader(chain ChainReader, header *types.Header, seal bool) error
// 批量校验块头部,这个方法返回一个退出信号用于终止操作,用于异步校验。
VerifyHeaders(chain ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error)
// 较验叔块是否满足规则
VerifyUncles(chain ChainReader, block *types.Block) error
// VerifySeal()函数基于跟Seal()完全一样的算法原理,通过验证区块的某些属性(Header.Nonce,Header.MixDigest等)是否正确,来确定该区块是否已经经过Seal操作。
VerifySeal(chain ChainReader, header *types.Header) error
// 用于初始化区块头的共识字段根据共识引擎。这些改变都是内联执行的。
Prepare(chain ChainReader, header *types.Header) error
// 完成所有的状态修改,并最终组装成块。
// 区块头和状态数据库在最终确认的时候可以被更新使之符合共识规则。
// receipts表示返回显示交易结果
Finalize(chain ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error)
// Seal()函数可对一个调用过Finalize()的区块进行授权或封印,并将封印过程产生的一些值赋予区块中剩余尚未赋值的成员(Header.Nonce, Header.MixDigest)。Seal()成功时返回的区块全部成员齐整,可视为一个正常区块,可被广播到整个网络中,也可以被插入区块链等。
Seal(chain ChainReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error
// 返回前一个块的hash
SealHash(header *types.Header) common.Hash
// 计算下一个块的难度
CalcDifficulty(chain ChainReader, time uint64, parent *types.Header) *big.Int
// 返回由共识引擎提供的RPC APIs
APIs(chain ChainReader) []rpc.API
// 关闭共识,就是关闭挖矿的线程相关
Close() error
}
这里我们大致了解了共识需要实现的规则,先不做详细探讨,心里先有个底,具体内容会在后续文章中进一步分析
1.2.3 读取链信息的部分接口
共识毕竟是要涉及到链相关内容的操作的,在这个共识入口处,以太坊提供了一些访问本地链中块或者块头部的接口。
type ChainReader interface {
// 获取本地链的配置信息
Config() *params.ChainConfig
// 返回本地链当前块
CurrentHeader() *types.Header
// 根据输入内容(块hash和块号),从链中返回一个块的头部信息
GetHeader(hash common.Hash, number uint64) *types.Header
// 通过块号从db中返回块
GetHeaderByNumber(number uint64) *types.Header
// 通过块hash返回块
GetHeaderByHash(hash common.Hash) *types.Header
// 根据输入内容(块hash和块号),从db中返回一个块
GetBlock(hash common.Hash, number uint64) *types.Block
}
1.3 总结
consensus.go这个文件告诉了我们,要实现一个公式算法需要实现特定引擎,为我们设计自己共识算法提供了一个比较好的思路。
参考文章
二、以太坊码源解析:POW(1)
见本人区块链专栏
三、以太坊码源分析:POW(2)
见本人区块链专栏