【以太坊源码分析】从geth的编译启动过程谈以太坊源码

一、geth编译以及启动

1. 安装Go的环境,注意需要1.9之后的版本

首先下载源码包并解压:

wget https://storage.googleapis.com/golang/go1.9.src.tar.gz
tar -xf go1.9.src.tar.gz
cd go/src
mv go /usr/local/go1.9

然后打开/etc/profile文件,最后一行加入

export PATH=$PATH:/usr/local/go1.9/bin

最后执行

source /etc/profile

2. 下载go-ethereum并编译启动

git clone https://github.com/ethereum/go-ethereum.git
cd ethereum/go-ethereum
#编译
make all
#启动一个默认节点
build/bin/geth --datadir=./dev/data0 --networkid 1 console

二、geth编译及启动部分源码分析

从上面的启动过程我们分两步解析整个过程,一个是编译,一个是geth命令的源码实现。

1. 编译

首先我们找到Makefile文件,对应的geth编译命令如下:

geth:
	build/env.sh go run build/ci.go install ./cmd/geth
	@echo "Done building."
	@echo "Run \"$(GOBIN)/geth\" to launch geth."

可以看出来,这个指令主要分两步

① build/env.sh

这里主要是检查环境变量,如果环境变量没有设置好,则新建一个临时环境。

② go run build/ci.go install ./cmd/geth

这里主要是初始化ethereum需要的集成环境(Code Integer),简单分为以下几步(源码位置build/ci.go)
  • 检查go的版本是否符合要求,同时检测需要下载的第三方库。
  • 准备install cmd文件夹下面的所有命令(geth、evm等,本文是以geth为例),构造install需要的命令(linux下),如果检测到是在arm平台下,则重新构造。
  • 使用上一步中构造的命令,对cmd文件夹下所有的命令进行go build
    到这里,基本上ethereum所需要的环境已经构建好了,接下来就是geth客户端的具体分析了。

2. Geth客户端(cmd/geth)

①首先是init函数中的命令行解析,这里用到了go中的命令行库,具体可见https://github.com/urfave/cli,在编译好的环境下可以用geth help查看geth的所有命令。
②Main函数中启动命令行。
③Geth函数启动ethereum节点,这个过程比较复杂,分为以下几个小步骤 :

  • 节点配置,通过配置文件或者默认配置创建一个node,其中ethereum的相关服务有四个部分(eth,whisper,node,dashboard)。值得注意的是这几个部分都实现了service接口,所以可以直接向node注册。
  • StartNode这个函数主要工作是启动前的预准备工作了,具体有以下四个工作:
    • ①将上一步中注册的service都交给p2p.server,由这个server逐个启动service;
    • ②解锁账号,注册钱包反馈时间;
    • ③启动rpc服务,监听钱包相关事件;
    • ④如果支持挖矿,则开启挖矿服务 。
  • 最后是以阻塞的方式等待一个stop信号。

3. 思维导图

【以太坊源码分析】从geth的编译启动过程谈以太坊源码
注:以上思维导图是我在看源码时的一个记录,基本上对应了上述过程,读者可以对照着这个图阅读源码

三、其他客户端

cmd目录下除了geth之外还有其他的命令,在此不再做介绍,下面给一个简单的功能,笔者有兴趣可以根据上面的思路自行阅读源码:

  • abigen: 一个源代码生成器,它将Ethereum智能合约定义(代码) 转换为易于使用的、编译时类型安全的Go package。如果合约字节码也available的话,它可以在普通的Ethereum智能合约ABI上扩展功能。同时也能编译Solidity源文件,使开发更加精简。
  • bootnode:此Ethereum客户端实现的剥离版本只参与 网络节点发现协议,但不运行任何更高级别的应用协议。 它可以用作轻量级引导节点,以帮助在私有网络中查找peers。
  • evm:能够在可配置环境和执行模式下运行字节码片段的Developer utility版本的的EVM(Ethereum Virtual Machine)。 其目的是允许对EVM操作码进行封装,细粒度的调试。
  • faucet:暂时不知道其使用场景,其help没有相关的解释,后续看下源码再来补充。
  • geth:主要Ethereum CLI客户端。它是Ethereum网络(以太坊主网,测试网络或私有网)的入口点,使用此命令可以使节点作为full node(默认),或者archive node(保留所有历史状态)或light node(检索数据实时)运行。其他进程可以通过暴露在HTTP,WebSocket和/或IPC传输之上的JSON RPC端点作为通向Ethereum网络的网关使用。
  • puppeth:搭建私链时使用的工具。
  • rlpdump:开发者通用工具,用来把二进制RLP (Recursive Length Prefix) (Ethereum 协议中用于网络及一致性的数据编码) 转换成用户友好的分层表示。
  • swarm:swarm守护进程和工具,这是swarm网络的进入点。

四、总结

到这里,相信读者已经大概明白了geth启动的整个过程了,对于geth的各个子命令的实现过程笔者不再详述了,依据这篇博客的思路复原各个子命令的执行过程应该不难。
由于笔者水平有限,这也是笔者第一次对这种大型项目做源码分析,以下分享一些自己在ethereum中能看到的东西,希望能对读到此文的读者有一定感悟。

  • 这是一个比较规范的大型项目的架构,从环境变量到集成环境,再从集成环境到具体的cmd命令,架构清晰且可扩展性很强。据笔者了解,fabirc中也有类似的架构。
  • 上文中的源码分析其实相当于是从最底层到最上层一步步组装node这个对象,反过来看整个过程,其实就是node需要什么,那我们就在上一步中构建什么。其实整个代码的过程都是组建对象的过程。
  • node模块事实上没有做任何和区块链相关的东西,甚至都没有创建相关模块,它就像一个商场,每个组成部分都实现了service接口,然后注册到node中。node就像一个管理者,不参与具体实现,只负责管理各个模块。也就是说我们如果想增加什么服务,就实现service接口,然后注册进去即可,十分简单。

五、参考资料

https://github.com/ZtesoftCS/go-ethereum-code-analysis 以太坊源码分析
https://blog.****.net/ddffr/article/details/79159741 geth启动客户端
https://github.com/ethereum/go-ethereum 以太坊源码
https://blog.****.net/zhuweiqin2937/article/details/82532268 以太坊目录结构说明
https://blog.****.net/ddffr/article/details/79159741 以太坊启动源码分析

其他:笔者水平有限,如有错误请联系[email protected]