搭建以太坊私有链(PoA)

环境说明:

  1. 阿里云ECS上配置了 CentOS 7.6,上面运行了两个 node,其中 node1 用来做 bootnode, 创建一个账户,并分配一定的 ether;node2 创建一个账户,但是没钱。
  2. macOS Catalina 上运行了一个 node,创建一个账号,没钱。
  3. Raspberry Pi 4B model 上运行了 Ubuntu 20.04.1 for ARM64,运行一个nodePi4,创建一个账户,没钱。

1 阿里云ECS上的操作步骤

1.1 准备环境

通过SSH登录到阿里云服务器上,并执行系统更新以及必备软件安装命令

[root@Aliyun ~]# yum update -y && yum install git wget bzip2 vim gcc-c++ ntp epel-release nodejs tree -y

配置CentOS的防火墙,允许 30303、30304、8545、8645 可以被访问。

[root@Aliyun ~]# firewall-cmd --zone=public --add-port=8545/tcp --permanent
[root@Aliyun ~]# firewall-cmd --zone=public --add-port=30303/tcp --permanent
[root@Aliyun ~]# firewall-cmd --zone=public --add-port=8645/tcp --permanent
[root@Aliyun ~]# firewall-cmd --zone=public --add-port=30304/tcp --permanent
[root@Aliyun ~]# firewall-cmd --reload

1.2 安装Geth及Tools

Ethereum 在 这里 提供了针对各种版本的Geth及工具包的可执行文件下载,根据阿里云的服务器配置,选择下图所示的版本。

Geth & Tools for Linux 64bit

复制该版本的下载地址,然后在终端输入以下命令,将压缩包下载到当前路径中。

[root@Aliyun ~]# wget -c https://gethstore.blob.core.windows.net/builds/geth-alltools-linux-amd64-1.9.24-cc05b050.tar.gz ./

解压该压缩包

[root@Aliyun ~]# tar -zxvf geth-alltools-linux-amd64-1.9.24-cc05b050.tar.gz

于是在当前路径下出现了 geth-alltools-linux-amd64-1.9.24-cc05b050 文件夹,进入该文件夹,会看到以下文件。

在这里插入图片描述

将图片中看到的所有绿色的文件依次复制到 /usr/local/bin 目录下,这样可以全局调用,也省得配置环境变量。

[root@Aliyun geth-alltools-linux-amd64-1.9.24-cc05b050]# cp ./abigen /usr/local/bin/abigen
[root@Aliyun geth-alltools-linux-amd64-1.9.24-cc05b050]# cp ./bootnode /usr/local/bin/bootnode
[root@Aliyun geth-alltools-linux-amd64-1.9.24-cc05b050]# cp ./clef /usr/local/bin/clef
[root@Aliyun geth-alltools-linux-amd64-1.9.24-cc05b050]# cp ./evm /usr/local/bin/evm
[root@Aliyun geth-alltools-linux-amd64-1.9.24-cc05b050]# cp ./geth /usr/local/bin/geth
[root@Aliyun geth-alltools-linux-amd64-1.9.24-cc05b050]# cp ./puppeth /usr/local/bin/puppeth
[root@Aliyun geth-alltools-linux-amd64-1.9.24-cc05b050]# cp ./rlpdump /usr/local/bin/rlpdump

然后回到用户目录下,测试 geth 命令是否可用。

[root@Aliyun geth-alltools-linux-amd64-1.9.24-cc05b050]# cd ~
[root@Aliyun ~]# geth version
Geth
Version: 1.9.24-stable
Git Commit: cc05b050df5f88e80bb26aaf6d2f339c49c2d702
Git Commit Date: 20201112
Architecture: amd64
Protocol Versions: [65 64 63]
Go Version: go1.15.5
Operating System: linux
GOPATH=
GOROOT=go

如果看到以上的输出,就表示 Geth 安装成功了。

1.3 创建以太坊私有链的数据文件夹

创建两个node的文件夹

[root@Aliyun ~]# mkdir -p Ursamjnor/node1
[root@Aliyun ~]# mkdir -p Ursamjnor/node2

为 node1 创建账户

[root@Aliyun ~]# cd Ursamjnor
[root@Aliyun Ursamjnor]# geth --datadir node1/ account new

这里执行后会要求我们输入新建账户的密码(两次输入),这里假设我们设定密码是“pwdfornode1”,然后终端会出现我们创建的账户地址,这个地址以及刚才的密码最好存放在文本文件中,便于以后使用。

[root@Aliyun Ursamjnor]# echo "node1的账户地址" >> accounts.txt
[root@Aliyun Ursamjnor]# echo "pwdfornode1" >> node1/password.txt

为 node2 创建账户的操作步骤跟node1的一样

1.4 用puppeth配置以太坊私有链

以下是在终端进行的操作,按照提示输入数字或者信息后完成配置。

[root@Aliyun Ursamjnor]# puppeth
Please specify a network name to administer (no spaces, hyphens or capital letters please)
> ursamjnor
Sweet, you can set this via --network=ursamjnor next time!
INFO [09-17|11:43:03.307] Administering Ethereum network       	name=ursamjnor
WARN [09-17|11:43:03.367] No previous configurations found     	path=/root/.puppeth/ursamjnor
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
Which consensus engine to use? (default = clique)
 1. Ethash - proof-of-work
 2. Clique - proof-of-authority
> 2
How many seconds should blocks take? (default = 15)
> 10
Which accounts are allowed to seal? (mandatory at least one)
> 0x //这里是node1的账户地址
> 0x
Which accounts should be pre-funded? (advisable at least one)
> 0x //再输入一遍node1的账户地址
> 0x
Should the precompile-addresses (0x1 .. 0xff) be pre-funded with 1 wei? (advisable yes)
> yes
Specify your chain/network ID if you want an explicit one (default = random)
> 8848 //这里输入一个大于2018的整数
INFO [09-20|10:07:39.675] Configured new genesis block
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
Which folder to save the genesis specs into? (default = current)
  Will create ursamjnor.json, ursamjnor-aleth.json, ursamjnor-harmony.json, ursamjnor-parity.json
>
INFO [09-20|10:07:58.879] Saved native genesis chain spec      	path=ursamjnor.json
ERROR[09-20|10:07:58.879] Failed to create Aleth chain spec    	err="unsupported consensus engine"
ERROR[09-20|10:07:58.915] Failed to create Parity chain spec   	err="unsupported consensus engine"
INFO [09-20|10:07:58.916] Saved genesis chain spec             	client=harmony path=ursamjnor-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
> ^C // Ctrl+c to exit

以上全部完成后会在当前路径下出现 ursamjnor.jsonursamjnor-harmony.json 两个文件,其中 ursamjnor.json 是我们的genesis block配置信息文件。

接下来用 ursamjnor.json 初始化node1和node2。

[root@Aliyun Ursamjnor]# geth --datadir node1 init ursamjnor.json

执行成功后,会看到终端返回:Successfully wrote genesis state 字样,表明初始化成功。

node2的初始化也一样,只需要把 --datadir 后面的 node1 改成 node2 即可。

1.5 启动 node1

启动命令如下:

[root@Aliyun Ursamjnor]# geth --networkid 8848 --datadir node1 --http --http.port 8545 --http.addr "0.0.0.0" --port 30303 --http.corsdomain "*" --http.api "eth,net,web3,personal,miner" --unlock 0 --password ./node1/password.txt --allow-insecure-unlock console

这里有必要把一些参数解释一下,省得以后自己也忘了。

  • –networkid:我们创建的私有链的网络id
  • –datadir:节点的数据文件夹
  • –http:表示允许远程调用。这个参数以前叫rpc,后来在新版的Geth中改成http了,当然用rpc也可以,只不过这个参数很快就被丢弃了,还是早点改的好,这个可以在Geth的帮助中看到。
  • –http.port:表示允许远程调用的端口。默认是8545。这个参数以前是rpcport。
  • –http.addr:把这个值写成“0.0.0.0”表示允许远程访问,否则只能本地访问。这个参数以前是rpcaddr。
  • –port:表示网络监听端口,默认值是30303。
  • –http.corsdomain:允许跨域请求的域列表,这里指定为“*”。这个参数以前是rpccorsdomain
  • –http.api:允许远程调用的API,用逗号间隔,凡是列出来的,在远程调用时均可以使用。
  • –unlock:表示被解锁账户的编号,0表示node数据文件中第一个被创建的账户,这个账户被解锁才能使用该账户进行交易。
  • –password:表示解锁账户时的账户密码,就是在创建账户时输入的密码。
  • –allow-insecure-unlock:允许使用不安全的账户解锁(???翻译的不好,大概是这个意思吧)。
  • console:表示打开Geth JavaScript console。

这时可以看到node1被成功启动,而且可以看到以下输出:

Welcome to the Geth JavaScript console!

instance: Geth/v1.9.24-stable-cc05b050/linux-amd64/go1.15.5
coinbase: 0xf0ce9c088f3f7e9c43387dfe5666405e4873206e
at block: 490 (Sun Nov 15 2020 18:19:25 GMT+0800 (CST))
 datadir: /root/Ursamjnor/node1
 modules: admin:1.0 clique:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0

To exit, press ctrl-d
>

输入命令时会被终端的节点同步信息覆盖,不过没关系,直接输入就行,不受影响。这里我们输入eth.coinbase 查看coinbase账户地址:

> eth.coinbase
"0xf0。。。。。6e"

这里返回的是node1的第一个账户地址,我这里把地址中的部分字符隐藏了。

查看账户有多少钱用eth.getBalance(“账户地址”) 命令

> eth.getBalance(eth.coinbase)
9.046256971665327767466483203803742801036717552003169065582623750618213253e+74

好多钱,但是可惜不是真钱,只能在我们这个私有链上用。

我们可以通过personal.newAccount() 命令再给node1创建一个账户。

> personal.newAccount("123456") 
"0x35。。。。。4a" 

我写了个最简单的123456做密码,实际操作时应该改成复杂一些的,而且最好把密码也保存一下,用于解锁账户。返回值是新创建的账户地址。

通过eth.accounts命令可以看到当前两个账户放在accounts数组中,新创建的是第二个元素。

> eth.accounts
["0x0xf0。。。。。6e", "0x35。。。。。4a"]

查看第二个账户有多少钱

> eth.getBalance(eth.accounts[1])
0

没有钱,但是我们可以从第一个账户上转点钱给第二个账户。在对这个账户进行操作之前,我们最好也把这个账户解锁,因为第一个账户是在启动node1的时候解锁的,而第二个账户是在之后建立的,所以解锁需要通过personal.unlockAccount("账户地址","账户密码",解锁的秒数)

> personal.unlockAccount(eth.accounts[1],"123456",150000)
true

返回true表示解锁成功。

发送交易的命令是web3.eth.sendTranscation({from:to:value:}) ,我们使用这个命令从账户1上转一些钱给账户2。

  • from:是交易的发起方地址,就是付钱的地址。
  • to:是收钱地址。
  • value:是交易额,以太币。
> web3.eth.sendTransaction({from: eth.coinbase, to: eth.accounts[1], value: web3.toWei(300,"ether")})
"0x7d....................88"

返回的是一个交易的Hash值,这时交易虽然发布了,但是并不能被确认,因为网络中没有矿工来记账,所以要开启node1的挖矿功能。如果这时查看第二个账户的余额,还是 0

> eth.getBalance(eth.accounts[1])
0

执行以下命令:

> miner.start()

然后再次执行 eth.getBalance(eth.accounts[1]) ,会看到它有钱了。

> eth.getBalance(eth.accounts[1])
300000000000000000000

退出这个交互式命令行工具,可以输入 exit 退出,也可以输入 CTRL-D 退出。

把刚才的启动命令存放到 startnode1.sh 中,便于以后使用。

[root@Aliyun Ursamjnor]# vi startnode1.sh

输入 ‘i’,进入插入模式,将下述内容填写在文件中。

#!/bin/bash

geth --networkid 8848 --datadir node1 --http --http.port 8545 --http.addr "0.0.0.0" --port 30303 --http.corsdomain "*" --http.api "eth,net,web3,personal,miner" --unlock 0 --password ./node1/password.txt --allow-insecure-unlock console

ESC退出插入模式,输入“:wq”回车执行保存并退出功能。

命令中最后一个 console 是开启JavaScript CLI模式,如果不想开启,把这个参数去掉即可。

给 startnode1.sh文件添加可执行权限。

[root@Aliyun Ursamjnor]# chmod +x ./startnode1.sh

以后要启动node1,只需要在终端执行该文件即可。

[root@Aliyun Ursamjnor]# ./startnode1.sh

1.6 启动 node2

因为node2跟node1在同一台机器上,所以启动时需要选择不同的“http.port”、“port”。这里只给出具体命令文件内容,其他跟node1的操作相同。

#!/bin/bash

geth --networkid 8848 --datadir node2 --http --http.port 8645 --http.addr "0.0.0.0" --port 30304 --http.corsdomain "*" --http.api "eth,net,web3,personal,miner" --unlock 0 --password ./node2/password.txt --allow-insecure-unlock console

node2 启动后,需要加入node1启动的网络,必须知道node1的 enode 值才可以。

在node1的 Geth JavaScript console 可以找到 enode的值:

enode://1fb9c826c44.............064@127.0.0.1:30303

我们需要把这个值告诉给node2,于是在node2的 Geth JavaScript console 中执行以下命令:

> admin.addPeer("enode://1fb9c826c44.............064@127.0.0.1:30303")
true

返回值为true表示加入成功,这里可以获取以下node2的账户,然后从node1转一些钱过去。

在node1的 Geth JavaScript console 中执行

> web3.eth.sendTransaction({from: eth.coinbase, to: "node2 的账户地址", value: web3.toWei(100,"ether")})
"0x..........................."

接下来在node2的 Geth JavaScript console 里执行eth.getBalance(eth.coinbase)查询一下,查到node2的账户上也有钱了。

2 在Mac上配置node3

2.1 准备工作

安装或更新Homebrew。这个是Mac系统的软件管理工具,如果没有Homebrew,就需要在终端里安装一下。

~ $ sh -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

执行后有可能会启动xcode的管理工具界面,点击install就行。

安装必要的软件工具:

  • tree:这个工具可以让我们在终端看到目录的树形结构,比较有用。
  • go-lang:go语言开发和编译必备软件包
~ $ brew install tree
➜  ~ $ brew install go
  • Geth:Ethereum的客户端工具。

这个客户端工具最好别在终端运行命令安装,奇慢无比,建议还是访问 Ethereum 的Download页面,找到 Mac 选项卡,选择对应的版本下载最好。
或者,如果你打算在 Mac 上开发智能合约的话,通过安装 Ganache 也能同时安装了 Geth,

2.2 创建数据文件夹

在用户目录下也创建一个用来存放数据的文件夹,最好跟network的名字一样,然后进入文件夹后,从阿里云服务器上把 ursamjnor.json 文件下载到该目录下:

~ $ mkdir ursamjnor
➜  ~ $ cd ursamjnor
➜  ursamjnor $ mkdir node3
➜  ursamjnor $ scp root@阿里云服务器地址:/root/Ursamjnor/ursamjnor.json ./

2.3 初始化node3并启动

➜  ursamjnor $ geth --datadir node3 init ursamjnor.json
 ...
➜  ursamjnor $ geth --datadir node3 account new
 ...
➜  ursamjnor $ geth echo "123456" >> ./node3/password.txt
➜  ursamjnor $ geth --networkid 8848 --datadir node3 --http --http.port 8545 --http.addr "0.0.0.0" --port 30303 --http.corsdomain "*" --http.api "eth,net,web3,personal,miner" --unlock 0 --password ./node3/password.txt --allow-insecure-unlock console

同样也最好把启动命令写入 .sh 文件中,便于以后调用。

2.4 加入node1的enode

在node3的 Geth JavaScript console 中执行以下命令:

> admin.addPeer("enode://1fb9c826c44.............064@阿里云服务器地址:30303")
true

注意这里的@后面不是127.0.0.1 ,而是阿里云服务的外网访问地址。

返回值为true表示加入成功。

2.5 交易一下

在 node3 的 Geth JavaScript console 里查看刚才创建的那个账户的余额

> eth.getBanlance(eth.coinbase)
0

因为在启动 node3 之前已经为其创建了账户,所以这个被创建的账户会默认为 eth.coinbase。

我们回到 node1 的 Geth JavaScript console 中执行

> web3.eth.sendTransaction({from: eth.coinbase, to: "node3 的账户地址", value: web3.toWei(100,"ether")})
"0x..........................."

因为 node1 的 miner 始终是 start 状态,所以很快交易就会被确认,我们再次回到 node3 的 Geth JavaScript console 中,查看账户余额

> eth.getBanlance(eth.coinbase)
100000000000000000000

至此,我们的私有链上已经出现了两台不同位置的机器(一台阿里云,一台我手边的Mac)以及三个node,其中只有node1具有miner功能。

3 在Raspberry Pi上配置nodePi4

3.1 准备工作

Raspberry Pi 4B 的 CPU 是 64位的ARM,访问 Ubuntu 的官网,找到 Download 菜单,然后选择下图中的链接点击
在这里插入图片描述
在下图的页面中,按照箭头的指示去下载镜像文件即可。如果觉得官网慢,可以自己去国内的镜像源里找对应的版本下载。
在这里插入图片描述

其实网上有很多好心人写好安装过程了,这里给个传送门:
Raspberry Pi 安装 Ubuntu Server 20.04 LTS (无显示器, 无网线) – 极夜喵

以及下面这个博文
树莓派4安装ubuntu server20.04 &&WIFI 配置(提供WIFI配置文件 —避免入坑,亲测有效!!!)

参考上面的文章把Ubuntu安装好之后,就可以通过Mac的终端去操作Raspberry Pi 了,包括将Raspberry Pi上的Ubuntu的软件更新,并安装必备的软件环境(git、golang、libgmp3-dev)。

~ ssh ubuntu@你的树莓派在本地局域网的地址
......
ubuntu ~ $ sudo apt-get update
ubuntu ~ $ sudo apt-get upgrade
ubuntu ~ $ sudo apt install tree vim git golang libgmp3-dev
ubuntu ~ $ mkdir Ursamjnor
ubuntu ~ $ exit

以上执行完成后就可以先退出ssh了,因为后面需要通过Mac把准备好的Geth客户端以及创世区块配置文件传送到树莓派上。

由于Raspberry Pi下载Geth的客户端比较慢,可以在Mac上先下载了之后,通过scp命令传输给Raspberry Pi。

下载的Geth版本是ARM64的

在这里插入图片描述
传送命令

~ scp -P 22 geth-linux-arm64-1.9.24*.tar.gz ubuntu@你的树莓派本地局域网地址:/home/ubuntu

3.2 创建 nodePi4 数据文件夹并启动

创建好文件夹之后,还要把刚才在Mac的Ursamjnor目录中从阿里云服务上下载的 ursamjnor.json 文件传输到Raspberry Pi中,这样才能对 nodePi4 初始化为同一个network的node。

~ scp ./Ursamjnor/ursamjnor.json ubuntu@192.168.31.150:/user/ubuntu/Ursamjnor~ ssh ubuntu@你的树莓派在本地局域网的地址
 ......
ubuntu ~ $ cd Ursamjnor
ubuntu Ursamjnor $ mkdir nodePi4
ubuntu Ursamjnor $ cd nodePi4
ubuntu Ursamjnor $ geth --datadir nodePi4 init ursamjnor.json
ubuntu Ursamjnor $ geth --datadir nodePi4 account new
......
ubuntu Ursamjnor $ echo "123456" >> ./nodePi4/password.txt
ubuntu Ursamjnor $ geth --networkid 8848 --datadir nodePi4 --http --http.port 8545 --http.addr "0.0.0.0" --port 30303 --http.corsdomain "*" --http.api "eth,net,web3,personal,miner" --unlock 0 --password ./node4/password.txt --allow-insecure-unlock console

当Raspberry Pi上的nodePi4成功启动后,还要把node1的enode值添加到peer中才可以成功连接网络并同步节点信息。添加方法同Mac上的操作。

可以试着在node1的 Geth JavaScript console 中操作向Raspberry Pi的账户转一笔钱,并查看账户的变化。

这篇文章编写时参考了以下文章,在此对文章作者表示感谢!
[1] CentOS 7.6 以太坊Ethereum環境建置 - Geth篇
[2] Build Proof of Authority Ethereum Network with Geth

注:

2020年11月15日第一次编写
2020年11月16日重新编辑过

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐