如何搭建一个私有以太坊网络

本文档是参考官方文档实践的记录,如有不当支持欢迎指正。参考文档:

https://geth.ethereum.org/docs/interface/private-network

环境准备

安装geth等工具

参考文档:Install on Ubuntu via PPAs

创建工作目录

创建工作目录gethpoa,并在此目录下创建另外两个node节点目录。完成之后的目录结构如下:创建账户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$geth --datadir data account new
INFO [04-07|16:48:50.857] Maximum peer count ETH=50 LES=0 total=50
INFO [04-07|16:48:50.857] Smartcard socket not found, disabling err="stat /run/pcscd/pcscd.comm: no such file or directory"
Your new account is locked with a password. Please give a password. Do not forget this password.
Password:
Repeat password:

Your new key was generated

Public address of the key: 0x4852C36DeC855E487D46ddDD015A92612dE5Bf20
Path of the secret key file: data/keystore/UTC--2022-04-07T08-48-58.777243596Z--4852c36dec855e487d46dddd015a92612de5bf20

- You can share your public address with anyone. Others need it to interact with you.
- You must NEVER share the secret key with anyone! The key controls access to your funds!
- You must BACKUP your key file! Without the key, it's impossible to access account funds!
- You must REMEMBER your password! Without the password, it's impossible to decrypt the key!

执行完以上操作之后的目录结构如下:

1
2
3
4
5
6
7
8
9
10
$ tree gethpoa/
gethpoa/
├── node1
│ └── data
│      └── keystore
│      └── UTC--2022-04-07T11-43-40.475412645Z--7adc4a5a65ab33595ade0ed15903e8c406f69230
└── node2
└── data
   └── keystore
   └── UTC--2022-04-07T08-50-53.751660870Z--0691e168e8cc151a76a04705290702631a86c948

使用puppeth创建创世区块的配置

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
$ puppeth
+-----------------------------------------------------------+
| Welcome to puppeth, your Ethereum private network manager |
| |
| This tool lets you create a new Ethereum network down to |
| the genesis block, bootnodes, miners and ethstats servers |
| without the hassle that it would normally entail. |
| |
| Puppeth uses SSH to dial in to remote servers, and builds |
| its network components out of Docker containers using the |
| docker-compose toolset. |
+-----------------------------------------------------------+

# 首先指定一个名称
Please specify a network name to administer (no spaces, hyphens or capital letters please)
> blockpoa

Sweet, you can set this via --network=blockpoa next time!

INFO [04-07|19:55:26.726] Administering Ethereum network name=blockpoa
INFO [04-07|19:55:26.732] No remote machines to gather stats from

# 然后会提示选择要做的操作,我这里选择配置一个新的创世区块
What would you like to do? (default = stats)
1. Show network stats
2. Configure new genesis
3. Track new remote server
4. Deploy network components
> 2

# 创建创世区块配置还是导入已有的创世区块配置?
What would you like to do? (default = create)
1. Create new genesis from scratch
2. Import already existing genesis
> 1

# 选择共识引擎,这里选择Clique
Which consensus engine to use? (default = clique)
1. Ethash - proof-of-work
2. Clique - proof-of-authority
> 2

# 多少秒出一个区块,默认时15秒
How many seconds should blocks take? (default = 15)
> 3

# 哪些账户允许打包区块?当我选两个账户时报错:signed recently, must wait for others,因此这里只选了一个账户可以打包。
Which accounts are allowed to seal? (mandatory at least one)
> 0x7adC4A5a65ab33595adE0ed15903e8c406f69230
> 0x

# 哪些账户需要预先初始化一部分资金?
Which accounts should be pre-funded? (advisable at least one)
> 0x7adC4A5a65ab33595adE0ed15903e8c406f69230
> 0x0691e168E8cC151a76a04705290702631A86C948
> 0x

# 这些地址需要初始化1wei的资金吗?选择的yes,其实对这块不太了解为什么
Should the precompile-addresses (0x1 .. 0xff) be pre-funded with 1 wei? (advisable yes)
> yes

# 指定网络ID,我这里直接随机
Specify your chain/network ID if you want an explicit one (default = random)
>
INFO [04-07|19:59:20.325] Configured new genesis block

# 经过以上的配置,已经配置好了,现在再次让我选择要做什么操作。我这里选择2:管理已经存在的创世区块配置
What would you like to do? (default = stats)
1. Show network stats
2. Manage existing genesis
3. Track new remote server
4. Deploy network components
> 2

# 选择管理现有的创世区块配置之后,会出现下面三个选项,分别是修改,导出和删除;我这里选择导出创世区块的配置。
1. Modify existing configurations
2. Export genesis configurations
3. Remove genesis configuration
> 2

# 选择导出之后会询问导出到哪个文件夹,选择默认的当前文件夹; 下面有两个是报错,从字面意思理解是这两个配置不支持poa的共识协议,所以这次忽略。
Which folder to save the genesis specs into? (default = current)
Will create blockpoa.json, blockpoa-aleth.json, blockpoa-harmony.json, blockpoa-parity.json
>
INFO [04-07|19:59:47.758] Saved native genesis chain spec path=blockpoa.json
ERROR[04-07|19:59:47.758] Failed to create Aleth chain spec err="unsupported consensus engine"
ERROR[04-07|19:59:47.758] Failed to create Parity chain spec err="unsupported consensus engine"
INFO [04-07|19:59:47.761] Saved genesis chain spec client=harmony path=blockpoa-harmony.json

What would you like to do? (default = stats)
1. Show network stats
2. Manage existing genesis
3. Track new remote server
4. Deploy network components

配置完成之后的创世区块配置json文件如下:

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
{
"config": {
"chainId": 3030,
"homesteadBlock": 0,
"eip150Block": 0,
"eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"petersburgBlock": 0,
"istanbulBlock": 0,
"clique": {
"period": 3,
"epoch": 30000
}
},
"nonce": "0x0",
"timestamp": "0x624ed1f0",
"extraData": "0x00000000000000000000000000000000000000000000000000000000000000007adc4a5a65ab33595ade0ed15903e8c406f692300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x47b760",
"difficulty": "0x1",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase": "0x0000000000000000000000000000000000000000",
"alloc": {
"0000000000000000000000000000000000000000": {
"balance": "0x1"
},
"0691e168e8cc151a76a04705290702631a86c948": {
"balance": "0x200000000000000000000000000000000000000000000000000000000000000"
},
"7adc4a5a65ab33595ade0ed15903e8c406f69230": {
"balance": "0x200000000000000000000000000000000000000000000000000000000000000"
}
},
"number": "0x0",
"gasUsed": "0x0",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"baseFeePerGas": null
}

导出创世区块配置之后的目录结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
$ tree gethpoa/
gethpoa/
├── blockpoa-harmony.json
├── blockpoa.json
├── node1
│ └── data
│      └── keystore
│      └── UTC--2022-04-07T11-43-40.475412645Z--7adc4a5a65ab33595ade0ed15903e8c406f69230
└── node2
└── data
   └── keystore
   └── UTC--2022-04-07T08-50-53.751660870Z--0691e168e8cc151a76a04705290702631a86c948

初始化创世区块配置

初始化创世区块配置(分别在每个节点目录下执行此命令)

1
2
3
4
5
6
7
8
9
10
11
12
$ geth --datadir data init ../blockpoa.json
INFO [04-07|20:22:34.955] Maximum peer count ETH=50 LES=0 total=50
INFO [04-07|20:22:34.955] Smartcard socket not found, disabling err="stat /run/pcscd/pcscd.comm: no such file or directory"
INFO [04-07|20:22:34.957] Set global gas cap cap=50,000,000
INFO [04-07|20:22:34.957] Allocated cache and file handles database=/home/guozhe/work/blockchain/gethpoa/node1/data/geth/chaindata cache=16.00MiB handles=16
INFO [04-07|20:22:34.975] Writing custom genesis block
INFO [04-07|20:22:34.984] Persisted trie from memory database nodes=357 size=50.70KiB time=1.847678ms gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [04-07|20:22:34.986] Successfully wrote genesis state database=chaindata hash=9d6e3d..f10d54
INFO [04-07|20:22:34.986] Allocated cache and file handles database=/home/guozhe/work/blockchain/gethpoa/node1/data/geth/lightchaindata cache=16.00MiB handles=16
INFO [04-07|20:22:35.004] Writing custom genesis block
INFO [04-07|20:22:35.012] Persisted trie from memory database nodes=357 size=50.70KiB time=2.258964ms gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [04-07|20:22:35.013] Successfully wrote genesis state database=lightchaindata hash=9d6e3d..f10d54

启动引导节点

创建一个目录bnode,然后进入到此目录执行bootnode -genkey boot.key命令生成boot.key。执行完成之后,工作目录下会有boot.key文件,整体目录结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ tree gethpoa/
gethpoa/
├── blockpoa-harmony.json
├── blockpoa.json
├── bnode
│   └── boot.key
├── node1
│ └── data
│      └── keystore
│      └── UTC--2022-04-07T11-43-40.475412645Z--7adc4a5a65ab33595ade0ed15903e8c406f69230
└── node2
└── data
   └── keystore
   └── UTC--2022-04-07T08-50-53.751660870Z--0691e168e8cc151a76a04705290702631a86c948

使用bootnode和刚才生成的boot.key启动引导节点:

提示信息说bootnode是开发工具,如果是生产环境的话使用一个正常的节点作为引导节点。

1
2
3
4
5
$ bootnode -nodekey boot.key
enode://5b0427c6efd2b8c1e107061527a7bfc39b74ab42db1561f8da8302622b0504d7f3afe3e7ba74b7561ddc00c256d4cacea756ff983080cb78bd8f56fd89959e88@127.0.0.1:0?discport=30301
Note: you're using cmd/bootnode, a developer tool.
We recommend using a regular node as bootstrap node for production deployments.
INFO [04-07|17:04:08.597] New local node record seq=1,649,322,248,596 id=1ae5556065ce551c ip=<nil> udp=0 tcp=0

启动成员节点

node1node2目录下创建password.txt文件,然后把账户密钥的密码保存到此文件中。然后分别在两个目录下运行下面的命令

1
2
3
4
5
6
7
# node1目录下执行
$geth --datadir data --networkid 3030 --bootnodes enode://5b0427c6efd2b8c1e107061527a7bfc39b74ab42db1561f8da8302622b0504d7f3afe3e7ba74b7561ddc00c256d4cacea756ff983080cb78bd8f56fd89959e88@127.0.0.1:0?discport=30301 --ipcdisable --syncmode full --http --allow-insecure-unlock --http.addr 0.0.0.0 --unlock 0x7adc4a5a65ab33595ade0ed15903e8c406f69230 --password password.txt --mine console


# node2目录下执行
geth --datadir data --networkid 3030 --bootnodes enode://5b0427c6efd2b8c1e107061527a7bfc39b74ab42db1561f8da8302622b0504d7f3afe3e7ba74b7561ddc00c256d4cacea756ff983080cb78bd8f56fd89959e88@127.0.0.1:0?discport=30301 --port 30304 --ipcdisable --syncmode full --http --allow-insecure-unlock --http.addr 0.0.0.0 --http.port 8546 console

如果关掉终端之后需要重启网络的话,直接执行上面两个命令即可,不用再启用引导节点

验证

配置matemask

配置网络,把刚才生成的账户导入到matemask。

发送一个交易

image-20220407204329123

后台日志:

1
INFO [04-07|20:41:00.283] Submitted transaction                    hash=0xce38e66cbc6267110fc31a4f07fbfd740c784439ba87666c5fddf1c036136f46 from=0x7adC4A5a65ab33595adE0ed15903e8c406f69230 nonce=0 recipient=0x2560be5793F9AA00963e163A1287807Feb897e2F value=111,000,000,000,000,000,000

错误处理

因为没有启用rpcapi接口,因此使用matemask无法配置私有网络
解决方法,启用rpcapi功能

如果您需要在其他的机器上连此node节点,则命令选项需要添加--http.addr 0.0.0.0选项。

Do not forget --http.addr 0.0.0.0, if you want to access RPC from other containers and/or hosts. By default, geth binds to the local interface and RPC endpoints are not accessible from the outside.

1
geth <other-flags> --http --http.addr 0.0.0.0
发送ETH失败,日志报错:err=“header not found”

如果在matemask上面发送ETH之后日志报下面的错误,检查一下网络配置是否正确。我出现这个错误是因为网络ID配置不正确导致。

1
WARN [04-07|20:39:29.911] Served eth_getTransactionCount           conn=192.168.31.153:64458 reqid=5607614675047                        duration="84.518µs"  err="header not found"
WARN [04-05|14:46:19.861] Block sealing failed err=“signed recently, must wait for others”

我配置两个节点作为签名节点时报此错误,然后只保留一个签名节点可以正常打包区块也不报次错误了。目前原因尚不清楚。

Fatal: Account unlock with HTTP access is forbidden!

--http--unlock两个选项不能同时启用,应该是考虑到安全的问题。
也可以使用--allow-insecure-unlock选项:Allow insecure account unlocking when account-related RPCs are exposed by http。但是生产环境不建议使用此选项。

错误记录:WARN [04-05|13:28:32.568] Block sealing failed err=“unauthorized signer”

如果运行一个签名的节点时出现上面的错误,可以查看genesis.json文件中的extradata是否包含启动时解锁的用户地址,如果不包含则使用包含的账户地址进行启动签名者节点。

后续工作

因为现在启动节点都是在终端执行命令启动的,终端一旦关闭网络也就关了。可以有两种方案:

  1. 使用docker启动
  2. 使用后台运行的方式执行启动命令

个人更倾向于使用第一种,后面有时间再用docker启动。