GETH 本地多节点私链docker搭建手册
到底有多久没有沉下心来学点有意思的技术了?可能是一年吧。上班以后的热情都被工作磨灭了,希望这次能够重新拾起年轻时的梦想和热血。
最近 NFT 比较火,背后的智能合约逐渐成熟,Web3 似乎到了即将快速发展时期。为了能够研究一下底层的技术,在本地搭建私链并进行调试最容易接触到整个智能合约运作的原理。我这里并没有选择直接本地安装GETH
,为了更加真实的模拟多个节点的情况,使用docker
来搭建多个容器部署GETH
节点会更加的有意思些。同时除了建立本地私链,还可以接入本地的区块浏览器和监控进行观察。编写合约的流程就放到下一篇文章 吧。
前期准备
环境
- Mac OS Big Sur 11.4
- Python 3.9
- Docker Client:
- Cloud integration: v1.0.22
- Version: 20.10.13
- Docker Server: Docker Desktop 4.6.1 (76265)
- Engine:
- Version: 20.10.13
- Go version: go1.16.15
- containerd:
- Version: 1.5.10
- runc:
- Version: 1.0.3
- docker-init:
- Version: 0.19.0
Docker image
- ethereum/client-go:latest
- kamael/eth-netstats:latest
- alethio/ethereum-lite-explorer:latest
Git repo
- eth-net-intelligence-api
搭建流程
节点初始化
首先需要起 2+个 docker 容器,其中一个作为Main
节点,其他的作为Slave
节点,将Slave
节点连接到Main
节点即可完成多 peers 的私链状态。
- 启动
Main
节点容器
docker run --entrypoint /bin/sh -p 30303:30303 -p 30303:30303/udp -p 8545:8545 -it -v $(PATH):/root --privileged=true --name eth-main ethereum/client-go
其中,-p 30303:30303/udp
是不可或缺的,节点的发现与连接是通过 UDP 进行的通信,不加上这一条就会导致无法addPeers
。8545
则作为 RPC 通信端口。挂载的目的是为了之后方便修改配置和查看 log。这里容器的启动,都没有选择容器自带的 entrypoint 来启动GETH
,主要是为了定制化的启动想要的GETH
。
- 启动
Slave
节点容器
docker run --entrypoint /bin/sh -it -v $(PATH):/root --privileged=true --name eth-slave ethereum/client-go
Slave
节点则不需要额外暴露端口,通过 docker 内部的网桥可以直接互联,当然也可能建立新的docker network
。对了,这两个容器的挂载文件夹最好的不同的。
- 启动
GETH
节点
为了创建自定义的本地 Private Network,需要创建genesis.json
。
{
"config": {
"chainId": 2333,
"homesteadBlock": 0,
"eip150Block": 0,
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0
},
"coinbase": "0x0000000000000000000000000000000000000000",
"difficulty": "10",
"extraData": "",
"gasLimit": "0xffffffff",
"nonce": "0x0000000000000042",
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp": "0x00",
"alloc": {
"a65cf03166298503d8e81053167bb8d97213f4a2": { "balance": "100000000" }
}
}
这里面主要需要修改的是chainId
,每个网络都有唯一的 id,节点之间的发现也是通过这个完成。其他config
里的参数尽量保持,之前漏配上一个参数会导致无法进行创世块的初始化。difficulty
则决定了挖矿的难度,因为是本地测试,可以小一点。gasLimit
可以设置大一点,方便跑复杂的合约。coinbase
是挖矿获得收益存入的钱包地址。alloc
是可以通过设定一些初始钱包的余额。
再完成了genesis.json
的编辑后,需要进行初始化。这一步在Main
和Slave
里都需要操作。需要把同一份配置文件放到每个容器挂载的目录里,保证初始化的信息是一致,不然之后节点之间因为 hash 不同无法互联。
geth --datadir /root/data init genesis.json
启动节点
启动
Main
节点
geth --networkid 2333 --http --http.addr=0.0.0.0 --http.port=8545 --http.api "web3,eth,debug,personal,net,admin,txpool" --http.corsdomain "*" --allow-insecure-unlock --datadir data --nodiscover --nat extip:172.17.0.2 -verbosity 10 --ipcdisable --vmdebug --ws --ws.addr=0.0.0.0 --ws.origins=* --graphql --graphql.corsdomain=* --graphql.vhosts=* --txpool.lifetime 0h5m0s --rpc.allow-unprotected-txs --identity "Main" console 2>> main.log
启动
Slave
节点
geth --networkid 2333 --http --http.addr=0.0.0.0 --http.port=8545 --http.api "web3,eth,debug,personal,net" --http.corsdomain "*" --allow-insecure-unlock --datadir data --nodiscover --nat extip:172.17.0.3 -verbosity 10 --ipcdisable --vmdebug --ws --ws.addr=0.0.0.0 --ws.origins=* --graphql --graphql.corsdomain=* --graphql.vhosts=* --miner.threads 1 --txpool.lifetime 0h5m0s --rpc.allow-unprotected-txs --identity "Slave" console 2>> slave.log
这两个启动命令真的是又臭又长,主要是增加了很多配置,可以将其保存下来写到bash脚本
里进行启动。接下来解释一下相关参数的作用:
- –networkid: 和
genesis.json
里的保持一致 - –http: 支持 http 接口调用,后面的相关参数都是为了配置 http 接口
- –allow-insecure-unlock: 允许不安全的解锁账户,方便调试
- –datadir: 之前初始化的节点数据位置
- –nodiscover: 如果不加上这个,在节点启动后,会有大量外部节点进行探测
- –nat: 开启 NAT 模式,不然容器节点无法互联,后面的地址是 docker 容器的地址。在容器启动这里非常关键,很多情况的节点无法
addPeers
就是这个原因 - –verbosity: 日志等级,方便排查问题
- –ipcdisable: 节点之间的互联没有通过 ipc 的方式,这里使用网络通信的方式
- –ws: 启用 websocket 方便之后搭建节点监控,–graphql 也是一样的作用
- –rpc.allow-unprotected-txs: 常规情况不会需要开启这个,由于 ERC1820 协议需要注册固定地址,需要开启这个配置,之后在部署合约的时候会详细讲解相关内容
- –identity: 定义节点名称
- –txpool.lifetime: 对于一些交易,如果长时间没有回应,自动终结,避免一些 tx 卡住
- –miner.threads:
Slave
节点用来控制 miner 线程
- AddPeers
刚才虽然启动了节点,但是由于开启了--nodiscover
,节点无法直接通过发现的方式自动互联,因此需要手动进行添加节点。通过查询 node 的信息,然后使用admin.addPeers()
添加节点。
实际上,即使admin.addPeers()
返回True
也不代表真的就添加节点成功,需要通过admin.peers
进行检查。
通过以下的curl
也可以验证节点是否成功添加,以及 http 接口是否正常。
curl --location --request POST 'localhost:8545' --header 'Content-Type: application/json' --data-raw '{ "jsonrpc": "2.0", "id": 1, "method": "admin_peers", "params": []}'
挖矿
首先确保要有 account,不然需要先去创建。然后选择其中一个节点作为挖矿收益汇入的钱包。
> miner.setEtherbase(eth.accounts[0])
通过这种方式,可以挖到一个block
就停止。这里是保持每一笔交易都需要手动确认挖矿。另一种也可以将Slave
节点启动
> miner.start();admin.sleepBlocks(1),miner.stop()
监控
本地区块浏览器
docker run -d -p 8000:80 -e APP_NODE_URL="http://0.0.0.0:8545" alethio/ethereum-lite-explorer
节点状态 Dashboard
启动监控
$ docker run -d -p 3000:3000 -e WS_SECRET=mysecret --name eth-netstats kamael/eth-netstats
$ git clone https://github.com/cubedro/eth-net-intelligence-api
常见问题
Q1: 无法成功 addPeers
A1: 网络配置问题,或者启动参数没有配置正确
Q2: tx 交易一直 pending
A2: 退出Geth
节点终端,进入data/geth
删去transactions.rlp
然后重新启动节点即可,每个节点都需要删去这个文件
一些玩法
至此已经可以成功的在本地搭建起 ETH Private Network 了,之后就可以在上面随心所欲的玩耍了。结合remix
在本地进行编写合约以及运行合约会更加的容易些。之后再来补上关于本地跑合约,创建自己的 Token,以及部署 ERC1820 在本地私链。