在实际开始之前,要先决定好要用哪种共识机制。之前的博客中提到 QBFT 在部分场景下无法使用 Zero Gas 进行上链,因此改用 IBFT 2.0 共识,博客如下:
那个时候用的是 Besu 的 22.4 版本。本次 Besu 使用的是 25 版本,经过这么几个大版本升级之后原有的问题应该已经修复了吧...那么本次我们就还是先试试 QBFT 吧。
话又说回来,为什么我对 QBFT 共识那么执着呢?主要是以下的 3 点:
- 即时最终性 (Immediate Finality): 这是 QBFT 最大的优势。一旦一个区块被添加到链上,它就是最终的,不可逆转的,不会发生分叉。这对于需要确定性交易记录的商业应用(如溯源、存证)至关重要。
- 高容错性: QBFT 是拜占庭容错的,在一个有 N 个验证者的网络中,只要有 2N/3 + 1 个验证者正常工作,网络就能达成共识并继续出块。
- 性能更优: 相较于其前辈 IBFT 2.0,QBFT 在网络消息传递和领导者选举方面进行了优化,性能更好。
那么事不宜迟,我们马上开始吧。
1. JDK 与 Besu 配置
从官网下载 25.8 Besu 客户端和 JDK 25 LTS,并上传到服务器
bash
[root@chain30 block_chain]# ls
besu-25.8.0.tar.gz ibm-semeru-open-jdk_x64_linux_25_36_openj9-0.55.0.tar.gz
对所有的 tar 压缩包进行解压
bash
[root@chain30 block_chain]# tar -zxvf besu-25.8.0.tar.gz && tar -zxvf ibm-semeru-open-jdk_x64_linux_25_36_openj9-0.55.0.tar.gz
删除原 tar 压缩包
bash
[root@chain30 block_chain]# rm -rf *.tar.gz
[root@chain30 block_chain]# ls
besu-25.8.0 jdk-25+36
配置 JDK 25,先创建 jre 模块
bash
[root@chain30 block_chain]# cd jdk-25+36/
[root@chain30 jdk-25+36]# ls
bin conf include jmods legal lib release
[root@chain30 jdk-25+36]# bin/jlink --module-path jmods --add-modules java.base,java.desktop --output jre
[root@chain30 jdk-25+36]# ls
bin conf include jmods jre legal lib release
配置 JAVA_HOME
bash
[root@chain30 block_chain]# vim /etc/profile
...
# JDK Configuration
export JAVA_HOME=/data/block_chain/jdk-25+36
export PATH=$JAVA_HOME/bin:$PATH
...
[root@chain30 block_chain]# source /etc/profile
[root@chain30 block_chain]# java -version
openjdk version "25" 2025-09-16 LTS
IBM Semeru Runtime Open Edition 25.0.0.0 (build 25+36-LTS)
Eclipse OpenJ9 VM 25.0.0.0 (build 25+36-openj9-0.55.0, JRE 25 Linux amd64-64-Bit Compressed References 20250916_60 (JIT enabled, AOT enabled)
OpenJ9 - 6fb31293be
OMR - b62e20077
JCL - 56cc1351c53 based on jdk-25+36)
接下来为 Besu 执行程序创建一个软连接到系统 /usr/local/bin 目录下,这样就能够直接使用 besu 指令了。
bash
[root@chain30 block_chain]# ln -s /data/block_chain/besu-25.8.0/bin/besu /usr/local/bin/besu
[root@chain30 block_chain]# besu -V
JVMJ9VM007W 无法识别命令行选项:-Xlog:jfr*=off
WARNING: A restricted method in java.lang.System has been called
WARNING: java.lang.System::load has been called by com.sun.jna.Native in an unnamed module (file:/data/block_chain/besu-25.8.0/lib/jna-5.16.0.jar)
WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module
WARNING: Restricted methods will be blocked in a future release unless native access is enabled
besu/v25.8.0/linux-x86_64/-ibmcorporation-eclipseopenj9vm-java-25
噢... 调用 besu 指令的时候就报了这个 Warning。这是由于 JDK 使用了25 版本造成的,要消灭这个报错可以通过配置环境变量来解决。如下:
bash
[root@chain30 block_chain]# vim /etc/profile
...
# JDK Configuration
export JAVA_HOME=/data/block_chain/jdk-25+36
export PATH=$JAVA_HOME/bin:$PATH
# BESU Configuration
export BESU_OPTS="--enable-native-access=ALL-UNNAMED --add-modules=ALL-SYSTEM"
...
[root@chain30 block_chain]# source /etc/profile
[root@chain30 block_chain]# besu -V
JVMJ9VM007W 无法识别命令行选项:-Xlog:jfr*=off
WARNING: Using incubator modules: jdk.incubator.vector
besu/v25.8.0/linux-x86_64/-ibmcorporation-eclipseopenj9vm-java-25
通过配置 BESU_OPTS 来为启动项传递参数,这样启动的时候就没有那么大段的错误报出了。
至此 JDK 和 Besu 的基本配置已经完成。
2. 节点部署
由于目前只有 4 台服务器,因此本次区块链系统将以"3+1"架构实现(即 3 个验证节点和 1 个 RPC 节点)。
在继续下去之前,我还是先解答一下什么是验证节点?什么又是 RPC 节点呢?它负责什么以及怎样理解?
验证节点
- 职责: 参与共识过程,负责创建和验证区块。它们是网络安全和运行的核心。
- 部署: 这 3 个节点应部署在不同的服务器上,确保物理隔离。根据 QBFT 的容错模型 (2 * 3 / 3 + 1 = 3),3 个验证者节点中只要有 (3-1)/3 即允许 1 个节点出现故障,网络依然可以正常运行。未来随着业务拓展会有更多联盟成员增加,可以再按比例增加验证者节点。
- 安全: 验证者节点不应该直接暴露给外部系统或最终用户访问。 它们的网络端口(特别是 P2P 端口)只应向其他联盟链节点(验证者和 RPC 节点)开放。
RPC 节点
- 职责: 作为外部世界与区块链交互的入口。它接收接入请求 (JSON-RPC),将其广播到网络中,并提供查询区块链数据的接口。
- 部署: 这个节点也应部署在独立的服务器上,必要时扩展成高可用的 RPC 集群。
- 特点: RPC 节点是网络的"普通"成员,它们同步所有区块数据,但不参与共识投票。这使得它可以承受较高的查询和交易负载,而不会影响核心共识的稳定性。
好了,接下来我们先部署三个验证节点。做法非常简单,我先选择其中一台服务器进行配置文件的创建,之后拷贝到其他机器上即可。假设我现在有三台服务器分别是 30、40、50(这里以 ip 最后一位来命名),用 30 这台机器进行配置文件创建。
2.1 创建创世块模版
bash
# 创建暂存目录
[root@chain30 block_chain]# mkdir genesis_files template_files
# 切换到 template_files 目录下并创建 qbft-genesis.json 文件
[root@chain30 block_chain]# cd template_files
[root@chain30 template_files]# vim qbft-genesis.json
写入以下创世块配置:
json
{
"genesis": {
"config": {
"chainId": 202510220902, # 链ID,用于区分不同的区块链网络
"berlinBlock": 0, # 从区块0开始启用柏林硬分叉功能
"londonBlock": 0, # 从区块0开始启用伦敦硬分叉功能
"grayGlacierBlock": 0, # 明确启用所有兼容的硬分叉,避免未来升级困惑
"contractSizeLimit": 2147483647, # 合约代码大小限制(字节),这里扩大到2GB,适合复杂智能合约
"qbft": {
"blockperiodseconds": 5, # 出块间隔时间(秒)
"epochlength": 20160, # 纪元长度,每20160个区块重置验证者列表
"requesttimeoutseconds": 10 # 请求超时时间(秒)
}
},
"nonce": "0x0", # 创世块随机数
"timestamp": "0x58ee34d0", # 创世块时间戳
"gasLimit": "0x989680", # 区块Gas上限,决定区块可包含的交易量
"difficulty": "0x1", # 挖矿难度,QBFT共识下设为最小值1
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", # 与PoW相关的哈希值,QBFT中设为0
"coinbase": "0x0000000000000000000000000000000000000000" # 矿工收益地址,QBFT中通常设为0地址
},
"blockchain": {
"nodes": {
"generate": true, # 自动生成节点密钥对
"count": 4 # 生成4个节点
}
}
}
2.2 生成创世块和节点配置
OK,之后可通过 operator 命令去生成多个创世块配置
bash
[root@chain30 template_files]# besu operator generate-blockchain-config \
--config-file=/data/block_chain/template_files/qbft-genesis.json \
--to=/data/block_chain/genesis_files/node_datas
JVMJ9VM007W 无法识别命令行选项:-Xlog:jfr*=off
WARNING: Using incubator modules: jdk.incubator.vector
2025-10-22 09:38:00.910+08:00 | main | INFO | GenerateBlockchainConfig | Generating 4 nodes keys.
2025-10-22 09:38:00.916+08:00 | main | INFO | GenerateBlockchainConfig | Generating keypair for node 0.
2025-10-22 09:38:01.034+08:00 | main | INFO | GenerateBlockchainConfig | Generating keypair for node 1.
2025-10-22 09:38:01.041+08:00 | main | INFO | GenerateBlockchainConfig | Generating keypair for node 2.
2025-10-22 09:38:01.049+08:00 | main | INFO | GenerateBlockchainConfig | Generating keypair for node 3.
2025-10-22 09:38:01.058+08:00 | main | INFO | GenerateBlockchainConfig | Generating QBFT extra data.
2025-10-22 09:38:01.063+08:00 | main | INFO | GenerateBlockchainConfig | Writing genesis file.
通过以上命令你会在 /data/block_chain/genesis_files 目录下找到 node_datas 目录,进入里面可以看到:
- genesis.json: 区块链创世文件。
- 一个 keys 目录,里面有 4 个子目录对应每个节点,而每个目录中将包含 key (私钥) 和 key.pub (公钥,即节点地址)。
bash
[root@chain30 node_datas]# tree -l
.
├── genesis.json
└── keys
├── 0x4d4f21d2726141b4a08f1099fe9deff78c423cde
│ ├── key.priv
│ └── key.pub
├── 0x59a7b52cc7160c86b4975503d6056a37363cc813
│ ├── key.priv
│ └── key.pub
├── 0xa79d4ff090005bffc3f5c066284af1ab8caa6524
│ ├── key.priv
│ └── key.pub
└── 0xbc0f3bd7c62eb46b41d8a180ec707d3db03f7e5b
├── key.priv
└── key.pub
5 directories, 9 files
先在 keys 目录下选出三个节点作为验证节点,然后分别对这三个节点进行 Hash 地址输出,这里以 "0x4d4f21d2726141b4a08f1099fe9deff78c423cde" 为例如下图所示:
bash
[root@chain30 node_datas]# besu \
--data-path=/data/block_chain/genesis_files/node_datas/keys/0x4d4f21d2726141b4a08f1099fe9deff78c423cde \
public-key export-address \
--ec-curve=secp256k1
JVMJ9VM007W 无法识别命令行选项:-Xlog:jfr*=off
WARNING: Using incubator modules: jdk.incubator.vector
2025-10-22 09:50:03.946+08:00 | main | INFO | KeyPairUtil | Generated new secp256k1 public key 0x4775dcc165ab07a9ec8d6feecb5d6e72d67e745b26bd748b5b09945614c8b80eeafab5e797a218a1d94396fd227c95b640b7f3a5cfbe9f230662877ebb47fb2e and stored it to /data/block_chain/genesis_files/node_datas/keys/0x4d4f21d2726141b4a08f1099fe9deff78c423cde/key
0x6b54507bee00e1beaea7b29a9b5f547d1dfa5f21
这里采用了 public-key export-address 指令进行地址的导出,其中 ec-curve 参数可以选择导出哪种椭圆曲线,可选 secp256k1 或者 secp256r1。
如上图所示,导出了地址是"0x6b54507bee00e1beaea7b29a9b5f547d1dfa5f21"。其余两个验证者节点也做同样操作并将所有的地址都保存一个名为 toEncode.json 的文件中。
2.3 生成验证者 extraData 信息
bash
[root@chain30 node_datas]# vim toEncode.json
...
[
"0x6b54507bee00e1beaea7b29a9b5f547d1dfa5f21",
"0x053b55b4170455b5f94fb374cf7fa60d5f74bd8e",
"0x2f8b6a08fe8de5059a2e42da91b0253bdfc5c5b4"
]
...
toEncode.json 生成后就可以继续生成 RLP 编码了,如下图:
bash
[root@chain30 node_datas]# besu rlp encode \
--from=/data/block_chain/genesis_files/node_datas/toEncode.json \
--type=QBFT_EXTRA_DATA
JVMJ9VM007W 无法识别命令行选项:-Xlog:jfr*=off
WARNING: Using incubator modules: jdk.incubator.vector
0xf865a00000000000000000000000000000000000000000000000000000000000000000f83f946b54507bee00e1beaea7b29a9b5f547d1dfa5f2194053b55b4170455b5f94fb374cf7fa60d5f74bd8e942f8b6a08fe8de5059a2e42da91b0253bdfc5c5b4c080c0
这里用到了 rlp encode 指令,其中 type 参数将告诉 besu 导出哪种共识的 extraData。这里共有两种可选,他们分别是 IBFT_EXTRA_DATA 或 QBFT_EXTRA_DATA。
看到这里或许会有小伙伴会问:
为什么要使用 rlp encode 指令去生成这一长串的东西呢?这个东西是什么?
答:这个"一长串"的东西对应的是创世块中的 extraData 字段内容。
那为什么要生成这个 extraData 内容呢?
答:extraData 字段主要用于存储验证者列表和投票信息,让网络知道:
- 当前哪些节点是验证者(这个看我们的生成过程就知道,我们是先选择节点然后通过收集地址,再通过地址生成 rlp 编码的)。
- 正在进行哪些验证者变更投票
- 共识协议的特定参数
为什么 Besu 要用 rlp 编码来表示 extraData 呢?
答:在翻阅了 Besu 官方文档后,我总结为以下 4 点:
- 标准化序列化:RLP 是以太坊的标准序列化格式
- 确定性的哈希:确保所有节点计算出的区块哈希一致
- 紧凑的存储:二进制格式比 JSON 更节省空间
- 高效的验证:节点可以快速解码并验证验证者签名
它们是如何工作的?
答:当节点启动时,besu 会先解码 extraData 获取初始验证着列表,之后验证自己(当前节点)是否在列表中,如果是验证着就开始参与共识并生块。
OK,解答完 extraData 问题后我们需要将这个 RLP 编码重新填回到 genesis.json 文档对应的 extraData 字段值。
bash
[root@chain30 block_chain]# vim genesis_files/node_datas/genesis.json
...
{
"config" : {
"chainId" : 202510220902,
"berlinBlock" : 0,
"londonBlock" : 0,
"grayGlacierBlock" : 0,
"contractSizeLimit" : 2147483647,
"qbft" : {
"blockperiodseconds" : 5,
"epochlength" : 20160,
"requesttimeoutseconds" : 10
}
},
"nonce" : "0x0",
"timestamp" : "0x58ee34d0",
"gasLimit" : "0x989680",
"difficulty" : "0x1",
"mixHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase" : "0x0000000000000000000000000000000000000000",
"extraData" : "0xf865a00000000000000000000000000000000000000000000000000000000000000000f83f946b54507bee00e1beaea7b29a9b5f547d1dfa5f2194053b55b4170455b5f94fb374cf7fa60d5f74bd8e942f8b6a08fe8de5059a2e42da91b0253bdfc5c5b4c080c0"
}
...
2.4 配置分发
OK,创世块已配置完成。接下来将开始分发配置信息到各个节点。以 30 机器为例,我需要在 /var/lib 路径下创建一个名为 besu 的目录,并且将其中一个验证着节点配置拷贝到里面(上一个环节之前生成的)。如下图:
bash
# 切换到 /var/lib 目录并创建 besu 目录
[root@chain30 node_datas]# cd /var/lib
[root@chain30 lib]# mkdir besu
# 将创世块和密钥配置拷贝到 /var/lib/besu 目录中
[root@chain30 lib]# cp /data/block_chain/genesis_files/node_datas/genesis.json ./besu
[root@chain30 lib]# cp -r /data/block_chain/genesis_files/node_datas/keys/0x4d4f21d2726141b4a08f1099fe9deff78c423cde ./besu
截止到这一步,/var/lib/besu 目录下的结构如下:
bash
[root@chain30 besu]# tree -l
.
├── 0x4d4f21d2726141b4a08f1099fe9deff78c423cde
│ ├── key
│ ├── key.priv
│ └── key.pub
└── genesis.json
接下来我们需要将"0x4d4f21d2726141b4a08f1099fe9deff78c423cde"改为"data"即可完成节点配置分发。
bash
[root@chain30 besu]# mv 0x4d4f21d2726141b4a08f1099fe9deff78c423cde data
[root@chain30 besu]# tree -l
.
├── data
│ ├── key
│ ├── key.priv
│ └── key.pub
└── genesis.json
1 directory, 4 files
OK,在开始下一步之前各位还需要将其余两个节点的配置文件都同步做好先。
2.5 通讯配置
好,配置文件都分发之后,我们的单个节点就基本能够启动了。但是问题又来了,你单个节点启动没用啊。区块链就是要多节点通讯做去中心化的呀。对咯,接下来就是要做关于节点的启动参数配置和通讯配置了。
bash
[root@chain30 besu]# besu \
--data-path=/var/lib/besu/data public-key export \
--to=/var/lib/besu/data/enode.txt
JVMJ9VM007W 无法识别命令行选项:-Xlog:jfr*=off
WARNING: Using incubator modules: jdk.incubator.vector
2025-10-22 10:53:38.072+08:00 | main | INFO | KeyPairUtil | Attempting to load public key from /var/lib/besu/data/key
2025-10-22 10:53:38.133+08:00 | main | INFO | KeyPairUtil | Loaded public key 0x4775dcc165ab07a9ec8d6feecb5d6e72d67e745b26bd748b5b09945614c8b80eeafab5e797a218a1d94396fd227c95b640b7f3a5cfbe9f230662877ebb47fb2e from /var/lib/besu/data/key
这里我们通过 public-key export 指令导出了节点的公钥信息,并存放在 /var/lib/besu/data/enode.txt 路径中。打开 enode.txt 文件即可得到公钥。接下来我们先导出所有验证者节点的公钥,然后将其全部收集起来。
bash
30_node_pk:0x4775dcc165ab07a9ec8d6feecb5d6e72d67e745b26bd748b5b09945614c8b80eeafab5e797a218a1d94396fd227c95b640b7f3a5cfbe9f230662877ebb47fb2e
40_node_pk:0xae2cd2c9d144ad0434bbbfd6790ff689a2ee092c30ec50a6cb70db68667c61eb192e68b845151d6eeafe8425b8d46bb9aa38b949149e842f3753af6f02fb3b67
50_node_pk:0xb78007f2eebbaa37de7426a2c842bb9a79a0f8663e433d6e1a5907c523cec3032771b7ee96f2a4deb60d3093191a04ae8c53f8a2fd0dfd2c4a3f285633402951
通过以上公钥我们就能够组装他们的通讯地址(bootnode)了。这个时候我们就要创建一个 toml 文件,将本节点的一些配置写入里面。通过 toml 文件进行配置能够减少命令行传入配置的痛苦。当然了,如果你是用 docker compose 进行配置的话就当我没说...
我这边的 toml 的基础配置如下:
toml
# -- DATA & NETWORK --
data-path="/var/lib/besu/data"
genesis-file="/var/lib/besu/genesis.json"
host-allowlist=["*"] # 允许访问节点 ip
# -- P2P NETWORKING --
p2p-enabled=true
p2p-host="192.168.200.30"
p2p-port=30303
max-peers=25 # 对于小型联盟链,25个对等节点上限足够
# -- API / RPC INTERFACES --
rpc-http-enabled=false # 【注意】验证者节点绝不开启HTTP RPC,最小化攻击面
rpc-ws-enabled=false # 【注意】验证者节点绝不开启WebSocket RPC
graphql-http-enabled=false # 关闭 GraphQL 接口
metrics-enabled=false # 此处关闭,之后会定向暴露给内部监控系统
# -- CONSENSUS (QBFT) --
min-gas-price=0 # 私有网络,不收取交易费
# -- PERFORMANCE & TUNING --
Xplugin-rocksdb-high-spec-enabled=true # 启用针对高规格服务器的 RocksDB 优化,提升I/O性能
但是这里并没有配置我们的通讯信息,接下来我们一同解决这个事情。在配置文件的最下方增加一个 bootnodes 数组,如下图:
toml
bootnodes=[
"enode://ae2cd2c9d144ad0434bbbfd6790ff689a2ee092c30ec50a6cb70db68667c61eb192e68b845151d6eeafe8425b8d46bb9aa38b949149e842f3753af6f02fb3b67@192.168.200.40:30303",
"enode://b78007f2eebbaa37de7426a2c842bb9a79a0f8663e433d6e1a5907c523cec3032771b7ee96f2a4deb60d3093191a04ae8c53f8a2fd0dfd2c4a3f285633402951@192.168.200.50:30303"
]
这个数组由 enode 字符串组成,字符串格式为:"enode://<公钥>@IP:PORT"。
这里的配置由两点需要注意的:
- 这个的<公钥>虽然是我们上面收集的公钥信息,但拷贝的时候避免将前面的"0x"字样也拷贝了。
- bootnodes 数组配置的是其他验证者节点的信息,不包含自己。
OK,如果你已经将其他验证者节点都配置好了,那么你已经完成了验证者节点的所有配置了。我们可以启动验证者们验证一下效果。

OK,所有验证者节点都启动且能够完美地同步生成区块。
2.6 RPC 节点部署
RPC 节点与验证节点不一样,它会承载更多的外部调用,因此在 toml 配置上我们要做一点点小改动。配置如下:
toml
# -- DATA & NETWORK --
data-path="/var/lib/besu/data"
genesis-file="/var/lib/besu/genesis.json"
host-allowlist=["*"] # 允许访问节点 ip
# -- P2P NETWORKING --
p2p-enabled=true
p2p-host="192.168.200.60"
p2p-port=30303
max-peers=50 # RPC 节点可以连接更多的对等节点以更快地同步信息和广播交易
# -- API / RPC INTERFACES--
rpc-http-enabled=true # 【重点】开启HTTP RPC服务
rpc-http-host="0.0.0.0" # 【重点】监听所有网络接口,以便外部应用可以访问
rpc-http-port=8545 # 【重点】开放必要的端口即可
rpc-http-api=["ETH", "NET", "WEB3", "QBFT"] # 能够提供的 api 服务类型
rpc-http-cors-origins=["*"] # 允许外部跨域访问
rpc-ws-enabled=false # 目前没有 websocket 调用需要
graphql-http-enabled=false # 其他接口同样保持关闭
metrics-enabled=true # RPC节点开启监控,以便观察API调用量、交易池等性能指标
metrics-host="127.0.0.1" # 仅限本机访问
# -- CONSENSUS (QBFT) --
min-gas-price=0
# -- TRANSACTION POOL --
# 由于采用了分层事务池,这里要设定最大事务处理数量
tx-pool-layer-max-capacity="20000000"
# -- PERFORMANCE & TUNING --
Xplugin-rocksdb-high-spec-enabled=true
# -- BOOT NODES --
# RPC 节点的静态节点应优先指向所有验证者,确保信息能最快送达
bootnodes=[
"enode://4775dcc165ab07a9ec8d6feecb5d6e72d67e745b26bd748b5b09945614c8b80eeafab5e797a218a1d94396fd227c95b640b7f3a5cfbe9f230662877ebb47fb2e@192.168.200.30:30303",
"enode://ae2cd2c9d144ad0434bbbfd6790ff689a2ee092c30ec50a6cb70db68667c61eb192e68b845151d6eeafe8425b8d46bb9aa38b949149e842f3753af6f02fb3b67@192.168.200.40:30303",
"enode://b78007f2eebbaa37de7426a2c842bb9a79a0f8663e433d6e1a5907c523cec3032771b7ee96f2a4deb60d3093191a04ae8c53f8a2fd0dfd2c4a3f285633402951@192.168.200.50:30303"
]
之后我们也启动看看效果,如下图:

可以看到当前 RPC 节点已经能够寻址找到对应的三个验证者节点了,并且在启动时全量导入生成区块(FullImportBlockStep 字样),从第 1561 个区块开始再实时同步生成区块。
3. 验证部署
3.1 设置访问权限
就这样就能够调用 API 接口验证了吗?
NO。
由于新版本的 Besu 是自带访问鉴权的(我记得以前我分享的版本还是用 Orion 和 Tessera 来鉴权),因此还需要先初始化超管用户。不过还好 Besu 提供了 JWT 和用户名密码两种鉴权的方式。为了方便验证,我先用用户名密码的方式来创建用户先。
先使用 password hash 对密码进行加密处理
toml
[root@chain60 besu]# besu password hash --password=123456
JVMJ9VM007W 无法识别命令行选项:-Xlog:jfr*=off
WARNING: Using incubator modules: jdk.incubator.vector
$2a$10$fOFYtaG5TrfSh32pPfz7LeArorHCXY0ZpM6bj4U4wuSPQ.nYTPmfG
获取密码后就可以创建 auth.toml 配置了
toml
[Users.admin]
password = "$2a$10$fOFYtaG5TrfSh32pPfz7LeArorHCXY0ZpM6bj4U4wuSPQ.nYTPmfG"
permissions=["*:*"]
由于创建的是超管用户,因此权限限制那边选择了":",代表着所有方法都有权访问。
好了,接下来只需要在配置文件中引用这个 auth.toml 文件就可以了。修改我们的 config.toml 文件,并添加 rpc-http-authentication-credentials-file 配置
toml
# -- DATA & NETWORK --
data-path="/var/lib/besu/data"
genesis-file="/var/lib/besu/genesis.json"
host-allowlist=["*"] # 允许访问节点 ip
# -- P2P NETWORKING --
p2p-enabled=true
p2p-host="192.168.200.60"
p2p-port=30303
max-peers=50 # RPC 节点可以连接更多的对等节点以更快地同步信息和广播交易
# -- API / RPC INTERFACES--
rpc-http-enabled=true # 【重点】开启HTTP RPC服务
rpc-http-host="0.0.0.0" # 【重点】监听所有网络接口,以便外部应用可以访问
rpc-http-port=8545 # 【重点】开放必要的端口即可
rpc-http-api=["ETH", "NET", "WEB3", "QBFT"] # 能够提供的 api 服务类型
rpc-http-cors-origins=["*"] # 允许外部跨域访问
rpc-ws-enabled=false # 目前没有 websocket 调用需要
graphql-http-enabled=false # 其他接口同样保持关闭
metrics-enabled=true # RPC节点开启监控,以便观察API调用量、交易池等性能指标
metrics-host="127.0.0.1" # 仅限本机访问
# -- CONSENSUS (QBFT) --
min-gas-price=0
# -- AUTHENTICATION --
rpc-http-authentication-enabled=true # 开启 RPC 接口鉴权
rpc-http-authentication-credentials-file="/var/lib/besu/auth.toml" # 鉴权配置
# -- TRANSACTION POOL --
tx-pool-layer-max-capacity="20000000"
# -- PERFORMANCE & TUNING --
Xplugin-rocksdb-high-spec-enabled=true
# -- BOOT NODES --
# RPC 节点的静态节点应优先指向所有验证者,确保信息能最快送达
bootnodes=[
"enode://4775dcc165ab07a9ec8d6feecb5d6e72d67e745b26bd748b5b09945614c8b80eeafab5e797a218a1d94396fd227c95b640b7f3a5cfbe9f230662877ebb47fb2e@192.168.200.30:30303",
"enode://ae2cd2c9d144ad0434bbbfd6790ff689a2ee092c30ec50a6cb70db68667c61eb192e68b845151d6eeafe8425b8d46bb9aa38b949149e842f3753af6f02fb3b67@192.168.200.40:30303",
"enode://b78007f2eebbaa37de7426a2c842bb9a79a0f8663e433d6e1a5907c523cec3032771b7ee96f2a4deb60d3093191a04ae8c53f8a2fd0dfd2c4a3f285633402951@192.168.200.50:30303"
]
让我们重启看看效果吧。
3.2 访问验证
要看效果我们可以通过 Postman 或 ApiPost 等远程访问工具,调用 Besu 提供的 API 接口来看效果。由于 Besu
官网中已经提供了 API 接口的集合文件,我们可以直接下载调用来验证,如下图:

但是导入后发现,由于 RPC 节点已经开启了访问权限,因此所有接口在不带 token 的前提下,都会返回"Host not authorized."错误提示。
那么如何获取 token 呢?
这个时候我们就可以通过 /login 接口(这个接口在 Besu 提供的集合里面没有)来做模拟登录获取 token 信息。

获取到这个 token 信息之后就可以加入到 header 中调用访问其他方法了

至此,我们的 Besu 区块链的基本部署就完成了。
下一章将继续讲如何使用 Vert.x 构建一个前置的数据上链应用,同时也会公开响应的代码给大家,敬请留意(时间紧任务重,估计又是几个月后的事情了)。
(未完待续...)