在之前的文章中,我们介绍了PoA Clique共识下新增验证者节点的操作步骤。本文将以一个示例来说明如何操作。

本文中的示例仅供参考,实际操作中,请根据实际情况进行调整。 本文中的操作基于以太坊客户端Geth v1.13.15,docekr环境执行

1. 网络搭建说明

在本文中,我们将搭建一个具有三个验证者节点的PoA Clique私链网络,角色如下:

  • ndoe,地址:0xe23C2c6e7f785e74EB7AAeF96455B78C53adb2E3,初始验证者节点
  • node1,地址:0x346271527e300f3c47450b792a6ee362b5cb0005,新增验证者节点
  • node2,地址:0x68c86ac751aedcbd3a966c106b42755fac6327ac,新增验证者节点

2. 网络初始化

现在我们先初始化下各个节点,创世文件内容 genesis.json 如下:

{
    "config": {
        "chainId": 73258216,
        "homesteadBlock": 0,
        "eip150Block": 0,
        "eip155Block": 0,
        "eip158Block": 0,
        "byzantiumBlock": 0,
        "constantinopleBlock": 0,
        "petersburgBlock": 0,
        "istanbulBlock": 0,
        "berlinBlock": 0,
        "clique": {
            "period": 1,
            "epoch": 30000
        }
    },
    "difficulty": "1",
    "gasLimit": "0xFFFFFFFF",
    "extradata": "0x0000000000000000000000000000000000000000000000000000000000000000e23c2c6e7f785e74eb7aaef96455b78c53adb2e30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "alloc": {
        "e23c2c6e7f785e74eb7aaef96455b78c53adb2e3": {
            "balance": "10000000000000000000000"
        }
    }
}

初始化节点命令如下:

$ docker run --rm -v $(pwd)/node:/root/.ethereum -v $(pwd)/genesis.json:/root/genesis.json ethereum/client-go:v1.13.15 init /root/genesis.json
$ docker run --rm -v $(pwd)/node1:/root/.ethereum -v $(pwd)/genesis.json:/root/genesis.json ethereum/client-go:v1.13.15 init /root/genesis.json
$ docker run --rm -v $(pwd)/node2:/root/.ethereum -v $(pwd)/genesis.json:/root/genesis.json ethereum/client-go:v1.13.15 init /root/genesis.json

通过上面的命令,我们将三个节点的创世文件和数据目录都初始化好了。

3. 启动网络

docker-compose.yaml 文件内容如下:

services:
  node:
    image: ethereum/client-go:v1.13.15
    container_name: node
    volumes:
      - ./node:/root/.ethereum
      - ./genesis.json:/root/genesis.json
    ports:
      - 8545:8545  # HTTP RPC
      - 30303:30303  # P2P 网络
    command: >
      --networkid 73258216
      --http  
      --http.addr "0.0.0.0" 
      --http.port 8545 
      --http.api "eth,net,web3,personal,miner,admin,txpool,debug"
      --http.corsdomain "*"
      --txpool.globalslots 4096
      --txpool.globalqueue 8192
      --txpool.accountslots 128
      --txpool.accountqueue 256
      --unlock 0xe23c2c6e7f785e74eb7aaef96455b78c53adb2e3
      --password /root/.ethereum/password.txt
      --allow-insecure-unlock
      --mine
      --miner.etherbase "0xe23C2c6e7f785e74EB7AAeF96455B78C53adb2E3"
      --miner.gasprice 1
    networks:
      - ethereum-network
      
networks:
  ethereum-network:
    driver: bridge

启动网络命令如下:

# 启动初始节点
$ docker-compose up -d node

# 获取节点的enode信息
$ docker exec -it node geth attach
Welcome to the Geth JavaScript console!

instance: Geth/v1.13.15-stable-c5ba367e/linux-amd64/go1.21.9
coinbase: 0xe23c2c6e7f785e74eb7aaef96455b78c53adb2e3
at block: 4 (Wed Nov 20 2024 01:42:54 GMT+0000 (UTC))
 datadir: /root/.ethereum
 modules: admin:1.0 clique:1.0 debug:1.0 engine:1.0 eth:1.0 miner:1.0 net:1.0 rpc:1.0 txpool:1.0 web3:1.0

To exit, press ctrl-d or type exit
> admin.nodeInfo.enode
"enode://e6a79a96d1cb112e323d6b10e70199f3215cb0a78f7421da0f49ab6feea963dcad215ff54be182aebc66d0024c9ac6ab1ec293fa8087b288b862599207267cb4@127.0.0.1:30303"
>

4. 新增验证节点 node1

新的验证者节点启动时,需要有初始验证者节点做为引导节点,这一操作与新增同步节点类似。但作为验证节点,需要进行挖矿出块操作,因此在节点启动时需要增加挖矿参数 --mine--miner.etherbase 参数。

docker-compose.yaml 文件内容如下:

services:
  node1:
    image: ethereum/client-go:v1.13.15
    container_name: node1
    volumes:
      - ./node1:/root/.ethereum
      - ./genesis.json:/root/genesis.json
    ports:
      - 8546:8545  # HTTP RPC
      - 30304:30303  # P2P 网络
    command: >
      --networkid 73258216
      --http  
      --http.addr "0.0.0.0" 
      --http.port 8545 
      --http.api "eth,net,web3,personal,miner,admin,txpool,debug"
      --http.corsdomain "*"
      --txpool.globalslots 4096
      --txpool.globalqueue 8192
      --txpool.accountslots 128
      --txpool.accountqueue 256
      --unlock 0x346271527e300f3c47450b792a6ee362b5cb0005
      --password /root/.ethereum/password.txt
      --allow-insecure-unlock
      --bootnodes "enode://e6a79a96d1cb112e323d6b10e70199f3215cb0a78f7421da0f49ab6feea963dcad215ff54be182aebc66d0024c9ac6ab1ec293fa8087b288b862599207267cb4@node:30303"
      --syncmode full
      --mine
      --miner.etherbase "0x346271527e300f3c47450b792a6ee362b5cb0005"
      --miner.gasprice 1
    networks:
      - ethereum-network

通过如下命令启动新增 node1 节点,此时查看 node1 的日志,会看到如下的出错信息:

$ docker-compose up -d node1

# 查看 node1 的日志
$ docker logs -f node1
INFO [11-20|01:50:14.221] Commit new sealing work                  number=1 sealhash=ac65fe..c86c41 txs=0 gas=0 fees=0 elapsed="76.385µs"
WARN [11-20|01:50:14.221] Block sealing failed                     err="unauthorized signer"

此时需要我们在 node 节点中将 node1 的地址加入到验证者列表中,命令如下:

$ docker exec -it node geth attach
Welcome to the Geth JavaScript console!

instance: Geth/v1.13.15-stable-c5ba367e/linux-amd64/go1.21.9
coinbase: 0xe23c2c6e7f785e74eb7aaef96455b78c53adb2e3
at block: 545 (Wed Nov 20 2024 01:51:55 GMT+0000 (UTC))
 datadir: /root/.ethereum
 modules: admin:1.0 clique:1.0 debug:1.0 engine:1.0 eth:1.0 miner:1.0 net:1.0 rpc:1.0 txpool:1.0 web3:1.0

To exit, press ctrl-d or type exit
> clique.propose("0x346271527e300f3c47450b792a6ee362b5cb0005",true)
null
> clique.status()
{
  inturnPercent: 96.875,
  numBlocks: 64,
  sealerActivity: {
    0x346271527e300f3c47450b792a6ee362b5cb0005: 1,
    0xe23c2c6e7f785e74eb7aaef96455b78c53adb2e3: 63
  }
}
> exit

# 在 node1 节点中查看验证者列信息
$ docker exec -it node1 geth attach
Welcome to the Geth JavaScript console!

instance: Geth/v1.13.15-stable-c5ba367e/linux-amd64/go1.21.9
coinbase: 0x346271527e300f3c47450b792a6ee362b5cb0005
at block: 687 (Wed Nov 20 2024 01:54:17 GMT+0000 (UTC))
 datadir: /root/.ethereum
 modules: admin:1.0 clique:1.0 debug:1.0 engine:1.0 eth:1.0 miner:1.0 net:1.0 rpc:1.0 txpool:1.0 web3:1.0

To exit, press ctrl-d or type exit
> clique.status()
{
  inturnPercent: 0,
  numBlocks: 64,
  sealerActivity: {
    0x346271527e300f3c47450b792a6ee362b5cb0005: 32,
    0xe23c2c6e7f785e74eb7aaef96455b78c53adb2e3: 32
  }
}
>

可以看到,node1 节点已经成功加入到验证者列表中,并开始出块。

5. 新增验证节点 node2

新增 node2 节点的操作与新增 node1 节点类似,需要在 node 节点中将 node2 的地址加入到验证者列表中,并启动 node2 节点。需要注意的是现有验证者可以提议增加新的验证者节点,超过 50% 的验证者投票赞成后,新的节点将成为验证者。在我们的示例中,此时只有两个验证者节点,要满足超过 50% 的投票,需要 node 节点和 node1 节点都同意,node2 节点才能成为验证者。

docker-compose.yaml 文件内容如下:

services:
  node2:
    image: ethereum/client-go:v1.13.15
    container_name: node2
    volumes:
      - ./node2:/root/.ethereum
      - ./genesis.json:/root/genesis.json
    ports:
      - 8547:8545  # HTTP RPC
      - 30305:30303  # P2P 网络
    command: >
      --networkid 73258216
      --http  
      --http.addr "0.0.0.0" 
      --http.port 8545 
      --http.api "eth,net,web3,personal,miner,admin,txpool,debug"
      --http.corsdomain "*"
      --txpool.globalslots 4096
      --txpool.globalqueue 8192
      --txpool.accountslots 128
      --txpool.accountqueue 256
      --unlock 0x68c86ac751aedcbd3a966c106b42755fac6327ac
      --password /root/.ethereum/password.txt
      --allow-insecure-unlock
      --bootnodes "enode://e6a79a96d1cb112e323d6b10e70199f3215cb0a78f7421da0f49ab6feea963dcad215ff54be182aebc66d0024c9ac6ab1ec293fa8087b288b862599207267cb4@node:30303"
      --syncmode full
      --mine
      --miner.etherbase "0x68c86ac751aedcbd3a966c106b42755fac6327ac"
      --miner.gasprice 1
    networks:
      - ethereum-network

操作命令如下:

# 启动 node2 节点
$ docker-compose up -d node2

# 在 node 节点中将 node2 的地址加入到验证者列表中
$ docker exec -it node geth attach
Welcome to the Geth JavaScript console!

instance: Geth/v1.13.15-stable-c5ba367e/linux-amd64/go1.21.9
coinbase: 0xe23c2c6e7f785e74eb7aaef96455b78c53adb2e3
at block: 1054 (Wed Nov 20 2024 02:00:24 GMT+0000 (UTC))
 datadir: /root/.ethereum
 modules: admin:1.0 clique:1.0 debug:1.0 engine:1.0 eth:1.0 miner:1.0 net:1.0 rpc:1.0 txpool:1.0 web3:1.0

To exit, press ctrl-d or type exit
> clique.status()
{
  inturnPercent: 0,
  numBlocks: 64,
  sealerActivity: {
    0x346271527e300f3c47450b792a6ee362b5cb0005: 32,
    0xe23c2c6e7f785e74eb7aaef96455b78c53adb2e3: 32
  }
}
> clique.propose("0x68c86ac751aedcbd3a966c106b42755fac6327ac",true)
null
> clique.status()
{
  inturnPercent: 0,
  numBlocks: 64,
  sealerActivity: {
    0x346271527e300f3c47450b792a6ee362b5cb0005: 32,
    0xe23c2c6e7f785e74eb7aaef96455b78c53adb2e3: 32
  }
}

# 可以看到现在 node2 节点还不在验证者列表中,此时我们需要在 node1 节点中同意
$ docker exec -it node1 geth attach
Welcome to the Geth JavaScript console!

instance: Geth/v1.13.15-stable-c5ba367e/linux-amd64/go1.21.9
coinbase: 0x346271527e300f3c47450b792a6ee362b5cb0005
at block: 687 (Wed Nov 20 2024 01:54:17 GMT+0000 (UTC))
 datadir: /root/.ethereum
 modules: admin:1.0 clique:1.0 debug:1.0 engine:1.0 eth:1.0 miner:1.0 net:1.0 rpc:1.0 txpool:1.0 web3:1.0

To exit, press ctrl-d or type exit
> clique.status()
{
  inturnPercent: 0,
  numBlocks: 64,
  sealerActivity: {
    0x346271527e300f3c47450b792a6ee362b5cb0005: 32,
    0xe23c2c6e7f785e74eb7aaef96455b78c53adb2e3: 32
  }
}
> > clique.propose("0x68c86ac751aedcbd3a966c106b42755fac6327ac",true)
null
# 现在 node2 已经在验证者列表中,但还未出块 
> clique.status()
{
  inturnPercent: 9.375,
  numBlocks: 64,
  sealerActivity: {
    0x346271527e300f3c47450b792a6ee362b5cb0005: 32,
    0x68c86ac751aedcbd3a966c106b42755fac6327ac: 0,
    0xe23c2c6e7f785e74eb7aaef96455b78c53adb2e3: 32
  }
}

# 稍等一段时间就会发现 node2 节点已经在验证者列表中,并开始出块
> clique.status()
{
  inturnPercent: 100,
  numBlocks: 64,
  sealerActivity: {
    0x346271527e300f3c47450b792a6ee362b5cb0005: 22,
    0x68c86ac751aedcbd3a966c106b42755fac6327ac: 21,
    0xe23c2c6e7f785e74eb7aaef96455b78c53adb2e3: 21
  }
}

6. 删除验证者节点 node2

删除验证者节点操作与新增操作类似,但现有验证者可以提议移除某个验证者,超过 50% 的验证者投票赞成后,目标节点将被移除。在我们的示例中,此时只有两个验证者节点,要满足超过 50% 的投票,需要 node 节点和 node1 节点都同意,node2 节点才能被移除。

操作如下:

# 在 node 节点中提议移除 node2 节点
$ docker exec -it node geth attach
Welcome to the Geth JavaScript console!

instance: Geth/v1.13.15-stable-c5ba367e/linux-amd64/go1.21.9
coinbase: 0xe23c2c6e7f785e74eb7aaef96455b78c53adb2e3
at block: 1412 (Wed Nov 20 2024 02:06:22 GMT+0000 (UTC))
 datadir: /root/.ethereum
 modules: admin:1.0 clique:1.0 debug:1.0 engine:1.0 eth:1.0 miner:1.0 net:1.0 rpc:1.0 txpool:1.0 web3:1.0

To exit, press ctrl-d or type exit
> clique.propose("0x68c86ac751aedcbd3a966c106b42755fac6327ac",false)
null

# 在 node1 节点中查看验证者列表,可以看到 node2 节点还在验证者列表中,此时需要我们在 node1 节点中也提议移除 node2 节点
docker exec -it node1 geth attach
Welcome to the Geth JavaScript console!

instance: Geth/v1.13.15-stable-c5ba367e/linux-amd64/go1.21.9
coinbase: 0x346271527e300f3c47450b792a6ee362b5cb0005
at block: 1554 (Wed Nov 20 2024 02:08:44 GMT+0000 (UTC))
 datadir: /root/.ethereum
 modules: admin:1.0 clique:1.0 debug:1.0 engine:1.0 eth:1.0 miner:1.0 net:1.0 rpc:1.0 txpool:1.0 web3:1.0

To exit, press ctrl-d or type exit
> clique.status()
{
  inturnPercent: 100,
  numBlocks: 64,
  sealerActivity: {
    0x346271527e300f3c47450b792a6ee362b5cb0005: 21,
    0x68c86ac751aedcbd3a966c106b42755fac6327ac: 21,
    0xe23c2c6e7f785e74eb7aaef96455b78c53adb2e3: 22
  }
}
> clique.propose("0x68c86ac751aedcbd3a966c106b42755fac6327ac",false)
null
# 等待一段时间,node2 节点将被移除
> clique.status()
{
  inturnPercent: 0,
  numBlocks: 64,
  sealerActivity: {
    0x346271527e300f3c47450b792a6ee362b5cb0005: 32,
    0xe23c2c6e7f785e74eb7aaef96455b78c53adb2e3: 32
  }
}

# 此时查看 node2 节点的日志,可以看到出块时会报错:
$ docker logs -f node2 
...
INFO [11-20|02:09:42.192] Commit new sealing work                  number=1613 sealhash=a19d84..4daac8 txs=0 gas=0 fees=0 elapsed="51.961µs"
WARN [11-20|02:09:42.192] Block sealing failed                     err="unauthorized signer"
...

现在我们已经成功移除了 node2 节点的验证者身份,但它还可以作为同步节点参与网络服务。


至此,我们已经成功搭建了一个以太坊的验证者网络,并完成了验证者节点的新增和删除操作。


孟斯特

声明:本作品采用署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)进行许可,使用时请注明出处。
Author: mengbin
blog: mengbin
Github: mengbin92
cnblogs: 恋水无意
腾讯云开发者社区:孟斯特