Ethereum源码阅读笔记-eth

转载请注明出处:www.huamo.online
字节杭州 求贤若渴:

  1. https://job.toutiao.com/s/JXTdQaH
  2. https://job.toutiao.com/s/JXTMWW3
  3. https://job.toutiao.com/s/JXT1tpC
  4. https://job.toutiao.com/s/JXTdu6h

go-ethereum/eth

eth包实现了以太坊协议(Ethereum protocol)

Ethereum定义

backend.go中,可以看到Ethereum结构体的定义,该对象实现了以太坊全节点服务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// eth/backend.go

type Ethereum struct {
config *Config
chainConfig *params.ChainConfig

// 用于关闭服务的channel
shutdownChan chan bool // 关闭Ethereum的channel
stopDbUpgrade func() error // 停止更新链数据库序列号(sequential key

// 各种处理器
txPool *core.TxPool
blockchain *core.BlockChain
protocolManager *ProtocolManager
lesServer LesServer

// 数据库接口
chainDb ethdb.Database // 区块链数据库

eventMux *event.TypeMux
engine consensus.Engine
accountManager *accounts.Manager

bloomRequests chan chan *bloombits.Retrieval // 接收布隆数据的channel来检索请求
bloomIndexer *core.ChainIndexer // 布隆索引器在区块导入期间运行

ApiBackend *EthApiBackend

miner *miner.Miner // 矿工
gasPrice *big.Int // 接受交易的gasPrice门槛以打入区块
etherbase common.Address // 挖到的ETH存入该地址

networkId uint64
netRPCService *ethapi.PublicNetAPI

lock sync.RWMutex // 用来保护各种字段(比如gaspriceetherbase
}

Ethereum对象的配置Config

Ethereum结构体中,第一个字段就是config *ConfigConfig本身也是一个结构体,定义在config.go文件中,包含了很多基础字段的定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// eth/config.go

type Config struct {
// 创世区块,如果数据库是空的,并且该字段不为空,那么该字段表示的区块就会被插入数据库中。
// 如果数据库是空的,该字段也是空的,就使用以太坊主网的创世区块。
Genesis *core.Genesis `toml:",omitempty"`

// 协议选项
NetworkId uint64 // Network ID用于选择要连接的节点
SyncMode downloader.SyncMode // 同步模式,3种
NoPruning bool

// 轻客户端选项
LightServ int `toml:",omitempty"` // 处理LES请求最大允许时间比率
LightPeers int `toml:",omitempty"` // LES客户端节点的最大数量

// 数据库选项
SkipBcVersionCheck bool `toml:"-"` // 跳过检查区块链版本
DatabaseHandles int `toml:"-"`
DatabaseCache int
TrieCache int
TrieTimeout time.Duration

// 挖矿相关的选项
Etherbase common.Address `toml:",omitempty"`
MinerThreads int `toml:",omitempty"`
ExtraData []byte `toml:",omitempty"`
GasPrice *big.Int

// Ethash工作证明选项
Ethash ethash.Config

// 交易池选项
TxPool core.TxPoolConfig

// GasPrice先知选项
GPO gasprice.Config

// 是否在VM中启用跟踪SHA3原像
EnablePreimageRecording bool

// 其余杂项
DocRoot string `toml:"-"`
}

代码中还提供了一个默认配置DefaultConfig,包含了用于以太坊主网络的默认设置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// eth/config.go

var DefaultConfig = Config{
// 快速同步模式,即快速下载区块头,只在链开头进行全同步
SyncMode: downloader.FastSync,
Ethash: ethash.Config{
// 缓存目录
CacheDir: "ethash",
CachesInMem: 2,
CachesOnDisk: 3,
DatasetsInMem: 1,
DatasetsOnDisk: 2,
},
// 网络ID为1
NetworkId: 1,
// 最多连100个LES客户端
LightPeers: 100,
DatabaseCache: 768,
TrieCache: 256,
TrieTimeout: 5 * time.Minute,
// 18香农,即 18 * 10^9
GasPrice: big.NewInt(18 * params.Shannon),

TxPool: core.DefaultTxPoolConfig,
GPO: gasprice.Config{
Blocks: 20,
Percentile: 60,
},
}

config.go还包含了init()方法,只要导入eth包,该方法就会被执行,进行必要的初始化工作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// eth/config.go

func init() {
// 使用系统HOME环境变量作为数据根目录
// 如果HOME为空,就尝试使用当前用户的HOME目录
// 如果依然失败,数据根目录就为当前目录
home := os.Getenv("HOME")
if home == "" {
if user, err := user.Current(); err == nil {
home = user.HomeDir
}
}
// 如果用windows系统
if runtime.GOOS == "windows" {
DefaultConfig.Ethash.DatasetDir = filepath.Join(home, "AppData", "Ethash")
// 如果不是windows系统,则为.ethash隐藏目录
} else {
DefaultConfig.Ethash.DatasetDir = filepath.Join(home, ".ethash")
}
}

新建一个Ethereum对象

backend.go中的New()方法会新建一个Ethereum对象,包括普通Ethereum对象的初始化工作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
// eth/backend.go

func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
// eth.Ethereum是全节点,不支持轻同步模式
// les.LightEthereum是轻节点,才支持轻同步模式
if config.SyncMode == downloader.LightSync {
return nil, errors.New("can't run eth.Ethereum in light sync mode, use les.LightEthereum")
}

// 不在3种同步模式之内,则报错
if !config.SyncMode.IsValid() {
return nil, fmt.Errorf("invalid sync mode %d", config.SyncMode)
}

// 创建链数据库
chainDb, err := CreateDB(ctx, config, "chaindata")
if err != nil {
return nil, err
}

// 检查刚创建的数据库版本,并且启动一个后台进程以便必要时进行升级。
// 返回一个停止函数,该函数一直阻塞,直到这个后台进程被安全的停掉。
stopDbUpgrade := upgradeDeduplicateData(chainDb)

// 在db中写入或者更新创世区块
// 如果可兼容(即没有在本地开始块下面指定一个分叉块),那么链配置将会被更新
// 如果发生冲突,会返回一个配置冲突错误,并且返回一个新的,未被改写的链配置。
chainConfig, genesisHash, genesisErr := core.SetupGenesisBlock(chainDb, config.Genesis)
if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok {
return nil, genesisErr
}
log.Info("Initialised chain configuration", "config", chainConfig)

eth := &Ethereum{
config: config,
chainDb: chainDb,
chainConfig: chainConfig,
eventMux: ctx.EventMux,
accountManager: ctx.AccountManager,
// 为该以太坊服务创建指定的共识引擎实例
engine: CreateConsensusEngine(ctx, &config.Ethash, chainConfig, chainDb),
shutdownChan: make(chan bool),
stopDbUpgrade: stopDbUpgrade,
networkId: config.NetworkId,
gasPrice: config.GasPrice,
etherbase: config.Etherbase,
bloomRequests: make(chan chan *bloombits.Retrieval),
// BloomBitsBlocks是一个布隆位向量包含的区块数量。
// NewBloomIndexer返回一个链索引器,为日志快速过滤生成链的布隆位数据。
bloomIndexer: NewBloomIndexer(chainDb, params.BloomBitsBlocks),
}

// eth协议支持的版本号ProtocolVersions有[63, 62],主要是第一个。
log.Info("Initialising Ethereum protocol", "versions", ProtocolVersions, "network", config.NetworkId)

// 如果不跳过检查区块链版本号
if !config.SkipBcVersionCheck {
// 从数据库中获取"BlockchainVersion"字段的值
bcVersion := core.GetBlockChainVersion(chainDb)
// core.BlockChainVersion为常量3,确保不兼容的数据库会强制重新同步
// 如果不是3,且不为0,那么将会报错,提示需要进行数据库升级(代码并不做自动升级)
if bcVersion != core.BlockChainVersion && bcVersion != 0 {
return nil, fmt.Errorf("Blockchain DB version mismatch (%d / %d). Run geth upgradedb.\n", bcVersion, core.BlockChainVersion)
}
// 将3写入数据库的"BlockchainVersion"字段中,以确保一致
core.WriteBlockChainVersion(chainDb, core.BlockChainVersion)
}
// 从config中构造智能合约解释器配置,以及trie树相关的配置。
var (
vmConfig = vm.Config{EnablePreimageRecording: config.EnablePreimageRecording}
cacheConfig = &core.CacheConfig{Disabled: config.NoPruning, TrieNodeLimit: config.TrieCache, TrieTimeLimit: config.TrieTimeout}
)
// 利用数据库中的可用信息初始化一条区块链。
// 还初始化了默认的Ethereum验证器(验证从外面传来的区块)
// 和Ethereum处理器(用来修改状态数据)
eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, eth.chainConfig, eth.engine, vmConfig)
if err != nil {
return nil, err
}
// 如果出现了不兼容的配置升级情况,则回退区块链。
// 将区块链的初始区块重置为错误信息中要求的区块。
// Rewind the chain in case of an incompatible config upgrade.
if compat, ok := genesisErr.(*params.ConfigCompatError); ok {
log.Warn("Rewinding chain to upgrade configuration", "err", compat)
eth.blockchain.SetHead(compat.RewindTo)
// 将chainConfig写入到数据库中。
core.WriteChainConfig(chainDb, genesisHash, chainConfig)
}
// 开始创建一个协程将区块链头事件发给索引器以实现级联后台处理。
eth.bloomIndexer.Start(eth.blockchain)

// 本地交易日志用来服务节点重启
if config.TxPool.Journal != "" {
// 将用户路径解析到数据目录下
config.TxPool.Journal = ctx.ResolvePath(config.TxPool.Journal)
}
// 创建一个新的交易池,用来集合,排序,和过滤从网络传入的交易。
eth.txPool = core.NewTxPool(config.TxPool, eth.chainConfig, eth.blockchain)

// 创建一个新的Ethereum子协议管理器
// 管理着有Ethereum网络的节点
if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.SyncMode, config.NetworkId, eth.eventMux, eth.txPool, eth.engine, eth.blockchain, chainDb); err != nil {
return nil, err
}
// 新建一个矿工
eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.engine)
// 配置矿工的额外数据
// 包括以太坊版本号(例如 v1.8.4),golang版本,节点主机操作系统名称
// 并且额外数据长度不能超过32字节
eth.miner.SetExtra(makeExtraData(config.ExtraData))

// 全节点的ethapi.Backend
eth.ApiBackend = &EthApiBackend{eth, nil}
gpoParams := config.GPO
if gpoParams.Default == nil {
gpoParams.Default = config.GasPrice
}
// Oracle基于最近一些区块的内容推荐gasprice
// 对于轻客户端和全客户端都是适用的
eth.ApiBackend.gpo = gasprice.NewOracle(eth.ApiBackend, gpoParams)

return eth, nil
}

总体来看,eth包以及eth.Ethereum对象都是一个集大成者,调用了各个包的方法,把他们组装到一起,整体构成一个对外的以太坊服务,所以这个包应该是一个接近顶层的封装。

参考资料

转载请注明出处:www.huamo.online