使用docker搭建以太坊私链

准备工作

1、宿主机安装 Docker 和 Go 编程语言。

2、在本地计算机上克隆 go-ethereum 代码库

git clone http://github.com/ethereum/go-ethereum.git 

3、宿主机编译以太坊客户端

make geth

该命令将在当前目录下的 build/bin 目录中生成可执行文件 geth。 4、创建一个新目录,用于存放节点相关数据。在该目录下创建一个名为“genesis.json”的文件,用于定义创世区块。并初始化用户,存储私链配置文件和数据。

以上内容在下面的部署过程中详细说明

docker-compose部署

以下只是一个大概的方法,步骤可供参考。由于第一次下载使用的基础镜像,以太坊源码版本都太低,导致没有第一笔交易无法进行挖矿,因此换了第二种方法,并使用了更高版本的以太坊源码和基础镜像,所有功能测试都通过

1、宿主机下载geth

下载位置https://geth.ethereum.org/downloads,然后解压

tar -xvf geth-linux-amd64-1.12.2-bed84606.tar.gz 

 此时无法识别 geth ,需要将其添加进系统路径中,可以先打印查看当前系统路径

echo $PATH

 分别执行以下两条命令

echo 'export PATH=$PATH:/root/go-ethereum/geth-linux-amd64-1.12.2-bed84606' >> ~/.bashrc
source ~/.bashrc

表示在~/.bashrc文件的末尾添加一行,将/root/go-ethereum/geth-linux-amd64-1.12.2-bed84606添加到$PATH中。source ~/.bashrc表示立即生效。

此时再执行 geth ,如果不添加任何参数,就开始更新主网了

 2、预先创建账户,初始化

新建一个目录,首先创建密码文件,

echo "123456" > .passwd

创建账户时使用以下语句分别创建两个节点,每个节点包含两个账户,分别存储于node1/node2目录下

for ((n=0;n<2;n++)); do geth account new --password .passwd --datadir ./node1; done

这个命令会在./node1目录下创建两个新的以太坊账户,并将它们的加密私钥存储到默认的账户目录中。--password将从名为.passwd的文件中读取密码,并将其用于加密私钥。--datadir表示想要使用的目录路径。

 账户1地址为 coinbase 地址,即挖矿得到的奖励地址,账户2为转账交易地址,地址如下表

节点账户1地址(挖矿账户账户2地址(资金账户
node1 0xb03CDd04856f4429A7e834A3aC7f3CeD6B23d002 0x202b971c3b9D7366532942E50Cb701DbbA893B0F
node2 0x7B9fc9e4CAAda8Eec649274e2860C67E0976420F 0xd5ad8AA9eB84820bC7361FE3227f687ed3C26ECe

执行完后,会出现两个目录,其中存储了私钥文件keystore

3、创建创世区块

创世区块为文件genesis.json,位于/ethnode目录下

{
  "config": {
    "chainId": 150,
    "homesteadBlock": 0,
    "eip150Block": 0,
    "eip155Block": 0,
    "eip158Block": 0
  },
  "alloc": {},
  "coinbase": "0x0000000000000000000000000000000000000000",
  "difficulty": "0x10000",
  "extraData": "",
  "gasLimit": "0x2fefd8",
  "nonce": "0x0000000000000042",
  "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "timestamp": "0x00"
}

4、初始化

执行以下命令初始化两个节点

geth init --datadir ./node1 genesis.json

返回内容

部分返回解释:

Maximum peer count:最大对等节点数。在这个例子中,ETH网络的最大对等节点数为50。
Set global gas cap:设置全局gas上限。在这个例子中,全局gas上限为50,000,000。
Writing custom genesis block:正在写入自定义创世块。
Successfully wrote genesis state:成功写入创世状态。

5、编写Dockerfile

使用了两个镜像,其中golang 1.10的alpine镜像为基础环境,从 github 上下载源码编译,然后使用第二个镜像alpine:latest构建最终的Geth节点,并在其中运行Geth二进制文件。

#编译geth所需镜像
FROM golang:1.10-alpine as builder
​
RUN apk add --no-cache make gcc musl-dev linux-headers git
RUN git clone --depth 1 --branch release/1.8 https://github.com/ethereum/go-ethereum /go-ethereum
RUN cd /go-ethereum && make all
​
#运行geth的基础镜像
FROM alpine:latest
#安装ca证书,用于节点间安全通信
RUN apk add --no-cache ca-certificates
#从builder镜像中复制编译好的Geth二进制文件到/usr/local/bin/目录中
COPY --from=builder /go-ethereum/build/bin/* /usr/local/bin/
​
WORKDIR "/opt"
​
ENV coinbase=""
ENV datadir=""
#启动一个Geth节点,并将其配置为使用指定的数据目录、coinbase地址和密码文件。
CMD exec geth --datadir ./$datadir  --verbosity=4 --rpc --rpcapi "eth,web3,personal,net,miner,admin,debug,db" --rpcaddr "0.0.0.0" --rpccorsdomain "*" --syncmode=full --mine --unlock $coinbase --password .passwd
#通过RPC接口与Geth节点进行通信
EXPOSE 8545
#通过P2P网络与其他节点进行通信
EXPOSE 30303

6、配置docker-compose

定义两个容器,命名为sealnode-1/2,配置环境,将他们连接到一个网络下进行通信。

正常来说docker-compose.yaml文件要通过image指定使用哪个镜像,但是该文件通过build关键字指定 dockerfile 构建上下文,也就是将使用当前目录中的Dockerfile来构建镜像。

version: "3"
​
services:
  sealnode-1:
    container_name: sealnode-1
    hostname: sealnode-1
    #环境变量,geth节点的coinbase地址和数据目录
    environment:
      coinbase: 82a6d98ea4a194258b37c9d6376d2857f626fe78
      datadir: node1
    #构建上下文
    build:
      context: .
    #端口映射,本机-容器
    ports:
      - 8011:8545
      - 30011:30303
    #将当前目录映射至容器/opt目录中
    volumes:
      - .:/opt
    #网络配置
    networks:
      - mynet
​
  sealnode-2:
    container_name: sealnode-2
    hostname: sealnode-2
    environment:
      coinbase: db9a80b86bb062519f0d9d4475fd990f5ea351ff
      datadir: node2
    build:
      context: .
    ports:
      - 8012:8545
      - 30012:30303
    volumes:
      - .:/opt
    networks:
      - mynet
​
#定义两个桥接网络,默认和用于容器间通信的mynet
networks:
  default:
    driver: bridge
  mynet:
    driver: bridge

7、启动

启动三个终端,分别用于挂起 docker 查看日志和查看两个容器

终端1

挂起 docker 查看日志

docker-compose up

 在不断监控两个节点状态中

以上内容表示正在挖矿,删除死节点、重新计算下载器 QoS 值,还可以看到生成 DAG 的信息。DAG 是以太坊挖矿所需的数据集,用于计算区块的哈希值。在生成 DAG 期间,节点会执行一些计算操作,并将结果保存到本地硬盘中

终端2

另起终端,先查看当前Docker Compose项目中正在运行的容器的状态信息,包括容器ID、名称、状态、端口映射等信息,进入容器内部,连接到geth控制台

docker exec -it sealnode-1 /bin/sh
geth attach node1/geth.ipc

终端3

该终端进入第二个容器,同样的操作不演示

附目录结构

当前目录结构

├── .passwd
├── docker-compose.yaml
├── dockerfile
├── genesis.json
├── node1
│   ├── geth
│   ├── geth.ipc
│   └── keystore
└── node2
    ├── geth
    ├── geth.ipc
    └── keystore

shell脚本部署

通过 shell 脚本开启两个容器,并使他们处于同一网络下。目录结构如下

├── .passwd
├── go-ethereum-1.11.4
├── dockerfile
├── script.sh
├── script1.sh
├── node1
│   ├── genesis.json
│   └── data
│       ├── geth
│       ├── geth.ipc
│       └── keystore
└── node2
    ├── genesis.json
    └── data
        ├── geth
        ├── geth.ipc
        └── keystore

1、创建虚拟网络

docker network create ethproject

 在启动容器时添加--network=ethproject参数,两个节点就会在同一网络下

2、创建创世区块

与上面的一样,可以用一样的genesis.json文件,这里把网络id改成了666

{
  "config": {
    "chainId": 666,
    "homesteadBlock": 0,
    "eip150Block": 0,
    "eip155Block": 0,
    "eip158Block": 0
  },
  "alloc": {
    "0xd8e6708334bdf3e9144a62859c99998caa7df32d": {
      "balance": "1000000000000000000000000000"
    },
    "0x4beff4fceff14ef1c24956fb81387d21f24ca04b": {
      "balance": "1000000000000000000000000000"
    }
  },
  "coinbase": "0x0000000000000000000000000000000000000000",
  "difficulty": "100000",
  "extraData": "",
  "gasLimit": "0x2fefd8",
  "nonce": "0x0000000000000042",
  "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "timestamp": "0x00"
}

3、初始化用户脚本

和方法一差不多,只是把这些放到脚本里一次运行了,

#!/bin/bash
#先删除节点同步的链上信息与账户信息
rm -rf ./node1/data
rm -rf ./node2/data
​
#在两个节点的/data路径下分别创建两个用户
echo "111111" > .passwd
for ((n=0;n<2;n++)); do geth account new --password .passwd --datadir ./node1/data; done
for ((n=0;n<2;n++)); do geth account new --password .passwd --datadir ./node2/data; done
​
#初始化
geth init --datadir ./node1/data ./node1/genesis.json
geth init --datadir ./node2/data ./node2/genesis.json

4、编写dockerfile创建镜像

dockerfile,由于使用的新版,和上面的区别主要是基础镜像和CMD启动命令不同

FROM golang:1.19-alpine as builder
  
RUN apk add --no-cache make gcc musl-dev linux-headers git
RUN mkdir -p /go-ethereum
COPY ./go-ethereum-1.11.4/ /go-ethereum/
RUN go env -w GOPROXY=https://goproxy.cn,direct
RUN cd /go-ethereum && make all
​
FROM alpine:latest
RUN apk add --no-cache ca-certificates
COPY --from=builder /go-ethereum/build/bin/* /usr/local/bin/
​
WORKDIR "/opt"
COPY ./ ./
​
ENV datadir=""
ENV coinbase=""
​
CMD exec geth --datadir ./data --networkid 666 --verbosity=4 --port 30001 --nodiscover --syncmode=full --mine --miner.etherbase $coinbase --allow-insecure-unlock --http --http.addr 0.0.0.0 --http.port 8001 --http.corsdomain "*" --http.api eth,web3,personal,net,miner,admin,debug,db --password .passwd
​
EXPOSE 8001
EXPOSE 30001

参数解释(如果不是v1.11.4版本,可以阅读源码文件中的 readme 文档,或查看geth -help查看最新参数)
--verbosity:用于控制节点的日志详细程度,值越高越详细,4为debug,最详细为5
--nodiscover:用于控制节点是否启用对等节点的自动发现机制,默认情况下,以太坊节点会尝试通过网络自动发现其他节点,以建立对等连接。若启动该参数,则不会主动发现其他节点,需要手动添加其他节点
--syncmode:用于控制以太坊节点的同步模式,包括full,light,snap,full表示同步所有区块信息
--miner.etherbase:用于指定挖矿奖励应该发送到的以太坊账户地址
--allow-insecure-unlock:允许通过不安全的方式解锁账户,开启此选项可使用 http 解锁账户进行转账

构建镜像

完成后可查看镜像

5、容器启动脚本

分别获取两个节点下的第一个账户地址,作为挖矿奖励,然后使用刚刚创建的镜像分别启动两个容器,注意端口映射关系,这样可以通过不同的端口访问不同的节点

#!/bin/bash
  
coinbase1=""
for file in ./node1/data/keystore/*
do
  if [ -f "$file" ]
  then
    name1=$(echo "$file" | tail -c 41)
    coinbase="0x${name1}"
    coinbase1=$(echo "$coinbase" | head -n 1)
    echo "$coinbase1"
    break
  fi
done
​
coinbase2=""
for file in ./node2/data/keystore/*
do
  if [ -f "$file" ]
  then
    name2=$(echo "$file" | tail -c 41)
    coinbase="0x${name2}"
    coinbase2=$(echo "$coinbase" | head -n 1)
    echo "$coinbase2"
    break
  fi
done
​
docker run -d --name ethnode1 --network=ethproject -p 8001:8001 -p 30001:30001 -v ~/eth-docker/node1/data:/opt/data -e coinbase=$coinbase1 eth_docker
​
docker run -d --name ethnode2 --network=ethproject -p 8002:8001 -p 30002:30001 -v ~/eth-docker/node2/data:/opt/data -e coinbase=$coinbase2 eth_docker

执行过程中会输出两个挖矿奖励接收的地址,以及两个容器id,再次查看容器,就在运行中了

 

6、远程连接节点并绑定对等节点

使用 rpc 远程连接节点1 ,同理,8002端口可连接节点2

geth attach http://192.168.123.215:8001

这里用 节点1 连接 节点2 ,可以另起一个终端进入 节点2 ,然后查看当前节点enode信息

admin.nodeInfo.enode

 

复制下来,将红框部分改为对应的 ip 和端口,去掉disport,切换到 节点1 ,执行以下命令,

>admin.addPeer("enode://xxx@192.168.123.215:30002")

语法正确会返回 true ,但不代表连接成功,需要执行admin.peersnet.peerCount确定,如果出现以下数组即为绑定成功,这个时候两个节点可以任意查看绑定的节点信息

 

7、功能测试

包含以下命令

#查看当前节点账户
> eth.accounts
#查看账户1的余额(单位wei转换为Eth)
> web3.fromWei(eth.getBalance(eth.accounts[0]), "ether")
#查看账户2的余额
> web3.fromWei(eth.getBalance(eth.accounts[1]), "ether")
#从账户1转入3个ETH给账户2
> eth.sendTransaction({from:eth.accounts[0], to:eth.accounts[1], value:web3.toWei(3, "ether")})
#设置挖矿收益账户
> miner.setEtherbase(eth.accounts[0])
#挖矿
> miner.start()
#停止挖矿
> miner.stop()
#查看区块高度
> eth.blockNumber
#解锁账户
> personal.unlockAccount(eth.accounts[0],”111111”)
#查看交易池状态
> txpool.status

我的测试过程如下,先分别开启挖矿,十分钟后查看区块高度,发现两个节点是一样的,即代表同步成功。然后分别查看两个地址账户余额,第一个是挖矿奖励地址,第二个是普通地址。另一个节点同样有钱,这里只展示一个,结果如下

然后测试转账功能,转账之前需要先解锁账户,否则会报错

 解锁后给第二个账户转账30,再次查看余额

 功能测试完成

posted @ 2023-09-06 23:18  励志成为蔡徐坤  阅读(859)  评论(0编辑  收藏  举报