EOSIO Bios Boot 启动序列实战指南:使用 bios-boot-tutorial 脚本模拟主网冷启动全流程

原创2026-09-23 13:23:051,211 阅读
文章标签:区块链

EOSIO Bios Boot 启动序列实战指南:使用 bios-boot-tutorial 脚本模拟主网冷启动全流程

bios-boot-tutorial 是 EOSIO 开源仓库中随附的启动模拟工具集(位于 tutorials/bios-boot-tutorial/),其核心脚本 bios-boot-tutorial.py 会在单机上自动编排 nodeos、keosd、cleos 完成从创世块到 DPOS 出块网络形成的完整 BIOS Boot 启动序列。本文将以该 README 为主线,结合脚本源码、创世配置与系统合约实现,完整讲解环境准备、启动命令、每个阶段背后的操作与原理,帮助你复现一条全新的 EOSIO 链从零到投票上线的全过程。

一、什么是 Bios Boot 启动序列

在 EOSIO 中,一条全新链的启动并不像"启动一个节点"那么简单。要让链真正"活"起来,需要依次完成以下工作:以初始生产者身份出块、创建系统账户、安装系统合约、发行代币、初始化系统合约、创建带质押的普通账户、注册并启动生产者节点、投票形成生产者调度(producer schedule),最终把链的治理权从启动者 eosio 账户移交给链上的 21 个生产者。这一整套流程被统称为 Bios Boot 启动序列(Bios Boot Sequence),bios-boot-tutorial.py 的作用就是"模拟"这个过程——因此 README 开篇即说明:

The bios-boot-tutorial.py script simulates the EOSIO bios boot sequence.

启动序列的第一个阶段依赖一个极简系统合约 eosio.bios。从源码注释(contracts/contracts/eosio.bios/include/eosio.bios/eosio.bios.hpp)可以看到它的定位:

eosio.bios is the first sample of system contract ... a minimalist system contract because it only supplies the actions that are absolutely critical to bootstrap a chain and nothing more.

它只暴露启动一条链所必需的极少量动作,其中包括 setprods(设置生产者调度)、setpriv(设置特权账户)、setalimits(设置资源限额)、setparams(设置链参数)、activate(激活协议特性)等,同时声明了 newaccount、updateauth、setcode、setabi 等由 EOSIO 核心层实现、而非合约层实现的 native actions(原生动作)。启动完成后,eosio.bios 会被功能完整的 eosio.system 合约替换,进入常规运行期。

二、前置条件与依赖准备

README 明确列出了三项基础前置条件:

  1. Python 3.x:脚本本身是 Python 3 程序;
  2. CMake:用于编译链二进制与系统合约;
  3. git:用于拉取源码仓库。

在此基础上,还需要按顺序准备三套二进制/合约产物(对应 README 的 1~4 步):

1. 安装 EOSIO 核心二进制

编译安装 nodeos、cleos、keosd。本仓库提供了两套路径:

2. 安装 eosio.cdt(合约开发工具链)

eosio.cdt 用于将 C++ 合约编译为 WASM 与 ABI。README 要求安装其二进制发布版。

3. 编译 eosio.contracts 系统合约仓库

系统合约(eosio.system、eosio.token、eosio.msig、eosio.boot 等)位于独立的 eosio.contracts 仓库中,需要按其编译指南完成编译,但不要执行其部署(deploy)步骤——部署由本教程脚本统一完成。

编译完成后,合约产物默认位于 build/contracts/ 目录下,README 将该目录记为 EOSIO_CONTRACTS_DIRECTORY,后续启动命令需要替换成它的真实路径。

说明:当前 eos 仓库根目录的 contracts/ 目录仅包含 eosio.bios 与 eosio.boot 两个合约源码(其余系统合约以编译产物形式存在于 unittests/contracts/ 中供测试使用),而脚本启动阶段会按 eosio.token、eosio.msig、eosio.boot、eosio.system 的顺序从 --contracts-dir 加载,因此该目录必须指向完整编译过的 eosio.contracts 产物。

三、仓库文件结构

tutorials/bios-boot-tutorial/ 目录共四个文件,各司其职:

文件 作用
README.md 使用说明(本文主题文档)
bios-boot-tutorial.py 启动序列编排主脚本
accounts.json 预生成的用户与生产者账户密钥对
genesis.json 创世块配置(初始时间、初始密钥、链参数)

四、最小启动命令

按 README 第 5 步,克隆仓库后进入教程目录,执行(务必把 EOSIO_CONTRACTS_DIRECTORY 替换为实际合约目录):

$ cd ~
$ git clone https://github.com/EOSIO/eos.git
$ cd ./eos/tutorials/bios-boot-tutorial/
$ python3 bios-boot-tutorial.py --cleos=cleos --nodeos=nodeos --keosd=keosd --contracts-dir="EOSIO_CONTRACTS_DIRECTORY" -w -a

其中:

  • --cleos、--nodeos、--keosd 分别指定三个二进制的调用方式(可以给完整路径);
  • --contracts-dir 指定系统合约编译产物目录;
  • -w 启动钱包(对应 --wallet 步骤);
  • -a / --all 自动执行所有标记为 (*) 的步骤。

脚本提示语也明确:-a does almost everything. -h shows options. 不带任何参数直接运行时,会打印该提示并退出。

五、脚本编排的完整步骤清单

bios-boot-tutorial.py 将启动序列拆分为可独立执行、也可串联执行的子步骤(见 bios-boot-tutorial.py 中的 commands 列表)。每个步骤可用短参数或长参数单独触发:

短参 长参 说明 是否含于 -a
-k --kill 杀掉所有 nodeos 与 keosd 进程 (*)
-w --wallet 启动 keosd、创建钱包并导入密钥 (*)
-b --boot 启动引导(boot)节点 (*)
-s --sys 创建系统账户(eosio.*) (*)
-c --contracts 安装系统合约(token、msig) (*)
-t --tokens 创建代币 (*)
-S --sys-contract 设置系统合约 (*)
-I --init-sys-contract 初始化系统合约 (*)
-T --stake 创建带质押的账户 (*)
-p --reg-prod 注册生产者 (*)
-P --start-prod 启动生产者节点 (*)
-v --vote 为生产者投票 (*)
-R --claim 领取奖励(claimrewards) (*)
-x --proxy 代理投票 (*)
-q --resign 移交 eosio 权限 (*)
-m --msg-replace 用多签替换系统合约 否
-X --xfer 随机转账(无限循环) 否
-l --log 显示节点日志尾部 60 行 (*)

步骤内部按 commands 定义的顺序依次调用对应的 step* 函数,实现"先杀旧进程、再起钱包、再启动引导节点……"的严格时序。

六、分阶段深度解析:每个步骤做了什么

下面结合 bios-boot-tutorial.py 源码逐阶段说明,便于理解启动序列的真实调用链。

阶段 0:清理环境(-k)

stepKillAll() 执行 killall keosd nodeos || true 并等待 1.5 秒,确保上一次运行残留的进程被清理,避免端口与数据目录冲突。

阶段 1:启动钱包并导入密钥(-w)

startWallet() 会:

  1. 删除并重建 --wallet-dir(默认 ./wallet/);
  2. 以后台进程启动 keosd --unlock-timeout 999999999 --http-server-address 127.0.0.1:6666 --wallet-dir <dir>,即钱包监听在 127.0.0.1:6666;
  3. 用 cleos wallet create --to-console 创建默认钱包。

随后 importKeys() 依次导入:脚本内置默认私钥、前 --max-user-keys(默认 10)个用户账户的私钥、以及全部生产者账户的私钥(去重后)。这意味着钱包里预先持有链上所有相关账户的签名能力,供后续 cleos 命令直接使用。

阶段 2:启动引导节点(-b)

stepStartBoot() 以 eosio 账户身份启动 0 号节点,即引导节点。关键启动参数(见 startNode(),bios-boot-tutorial.py 中实现):

  • --genesis-json:指定创世配置(默认 ./genesis.json);
  • --enable-stale-production --producer-name eosio:允许在无其他生产者的情况下由 eosio 独自持续出块;
  • --private-key '["<pub>","<pvt>"]':注入 eosio 的签名密钥;
  • --max-transaction-time=200:单交易最大执行时间 200ms,注释特别说明该值必须小于出块间隔(block_interval_ms,定义于 libraries/chain/include/eosio/chain/config.hpp,为 500ms);
  • --contracts-console:打印合约输出;
  • HTTP 监听 127.0.0.1:8000、P2P 监听 127.0.0.1:9000;
  • 仅 0 号节点额外启用 eosio::history_plugin 与 eosio::history_api_plugin;
  • 节点运行日志写入 <nodes-dir>00-eosio/stderr,命令行本身也会先写入该文件便于排查。

启动后脚本 sleep(10.0) 等待链稳定出块。

阶段 3:创建系统账户(-s)

createSystemAccounts() 遍历 systemAccounts 列表(见 bios-boot-tutorial.py 顶部),用 cleos create account eosio <name> <public_key> 逐一创建以下 10 个 eosio.* 系统账户:

eosio.bpay  eosio.msig  eosio.names  eosio.ram  eosio.ramfee
eosio.saving  eosio.stake  eosio.token  eosio.vpay  eosio.rex

这些账户是 EOSIO 系统合约运行所依赖的特殊账户(分别负责生产者奖励、多签、域名拍卖、内存买卖、质押、代币、REX 等职能)。

阶段 4:安装系统合约(-c)

stepInstallSystemContracts() 部署最先需要的两个合约:

cleos set contract eosio.token <contracts-dir>/eosio.token/
cleos set contract eosio.msig <contracts-dir>/eosio.msig/

此时 eosio 账户上尚未安装系统合约,链上仅有原生动作可用;eosio.token 与 eosio.msig 先行就位,为后续发币与多签做准备。

阶段 5:创建代币(-t)

stepCreateTokens() 分三步完成代币供给:

  1. 调用 eosio.token::create 创建最大供给为 10000000000.0000 <symbol>(默认 symbol 为 SYS,可用 --symbol 修改)的代币;
  2. allocateFunds() 使用 Pareto 分布(80/20 法则,参数 1.161)在全部用户与生产者之间分配总额 10 亿(1000000000)代币,资金分配写回 accounts[i]['funds'];
  3. 调用 eosio.token::issue 向 eosio 账户发行等额代币。

阶段 6:设置系统合约并激活协议特性(-S)

这是整个脚本中最关键、最复杂的阶段(stepSetSystemContract()),完整呈现了 v1.8+ 链启动必须遵循的协议特性激活顺序:

  1. 激活 PREACTIVATE_FEATURE:先通过 producer API 调用 POST /v1/producer/schedule_protocol_feature_activations,激活 digest 为 0ec7e080177b2c02b278d5088611686b49d739925a92d9bfcacd7fc6b74053bd 的 PREACTIVATE_FEATURE(该特性允许在部署完整系统合约之前预激活其他协议特性),然后 sleep(3);
  2. 部署 eosio.boot:cleos set contract eosio <contracts-dir>/eosio.boot/。eosio.boot 提供 activate、reqactivated 等动作(源码见 contracts/contracts/eosio.boot/),使链能够在 eosio.system 部署之前逐个激活其余协议特性;
  3. 逐个激活其余协议特性:脚本通过 cleos push action eosio activate '["<digest>"]' -p eosio@active 依次激活以下 16 个特性(digest 均硬编码于脚本中):
特性 digest 前缀
KV_DATABASE 825ee6288fb1373eab...
ACTION_RETURN_VALUE c3a6138c5061cf2913...
CONFIGURABLE_WASM_LIMITS bf61537fd21c61a60e...
BLOCKCHAIN_PARAMETERS 5443fcf88330c586bc...
GET_SENDER f0af56d2c5a48d60a4...
FORWARD_SETCODE 2652f5f96006294109...
ONLY_BILL_FIRST_AUTHORIZER 8ba52fe7a3956c5cd3...
RESTRICT_ACTION_TO_SELF ad9e3d8f650687709f...
DISALLOW_EMPTY_PRODUCER_SCHEDULE 68dcaa34c0517d1966...
FIX_LINKAUTH_RESTRICTION e0fb64b1085cc55389...
REPLACE_DEFERRED ef43112c6543b88db2...
NO_DUPLICATE_DEFERRED_ID 4a90c00d55454dc5b0...
ONLY_LINK_TO_EXISTING_PERMISSION 1a99a59d87e06e09ec...
RAM_RESTRICTIONS 4e7bf348da00a94548...
WEBAUTHN_KEY 4fca8bd82bbd181e71...
WTMSIG_BLOCK_SIGNATURES 299dcb6af692324b89...
  1. 部署 eosio.system:cleos set contract eosio <contracts-dir>/eosio.system/;
  2. 设置 eosio.msig 特权:cleos push action eosio setpriv '["eosio.msig", 1]' -p eosio@active——只有安装 eosio.system 后 setpriv 才可用,将 eosio.msig 标记为特权账户(特权账户可以跳过普通账户的资源限制),为后续多签替换合约做准备。

阶段 7:初始化系统合约(-I)

stepInitSystemContract() 调用:

cleos push action eosio init '["0", "4,SYS"]' -p eosio@active

即初始化 eosio.system:第一个参数 0 为初始总票数,第二个参数 4,SYS 为系统代币精度与符号(精度 4 位小数,符号 SYS),与阶段 5 发行的代币保持一致。

阶段 8:创建带质押的账户(-T)

createStakedAccounts() 为每个用户计算资金分配并创建账户,核心逻辑:

  • 每账户资金 funds 按 ram + stakeNet + stakeCpu + unstaked 拆解,并 assert 校验守恒;
  • 若 funds 不足以覆盖 RAM 费用(--ram-funds,默认 0.1 个代币)则跳过该用户;
  • 先用 cleos system newaccount --transfer eosio <name> <pub> --stake-net "X" --stake-cpu "Y" --buy-ram "Z" 创建账户并质押(--transfer 表示质押资金从 eosio 划转);
  • 若有剩余未质押资金,再用 cleos transfer eosio <name> "..." 转账。
  • 命令封装了 retry() 重试逻辑:失败时持续重试直到成功,规避临时性网络/链上错误。

阶段 9:注册并启动生产者(-p、-P)

  • stepRegProducers():对每个预生成的生产者账户执行 cleos system regproducer <name> <pub> https://<name>.com/<pub> 注册为区块生产者,随后 cleos system listproducers 确认注册结果;
  • stepStartProducers():为每个生产者账户启动一个独立的 nodeos 实例(1~N 号节点)。startNode() 生成的命令与引导节点类似,但额外包含:
--p2p-peer-address localhost:9000  ...  --p2p-peer-address localhost:9000+i

即第 i 个生产者节点会与之前的所有节点(含引导节点)建立 P2P 连接,形成星型拓扑;各节点 HTTP 端口依次为 8000+i、P2P 端口为 9000+i,并设置 --max-clients 与 --p2p-max-nodes-per-host 为 numProducers + 10。之后 sleep(--producer-sync-delay)(默认 80 秒)等待所有节点同步到同一链头。

阶段 10:投票与代理投票(-v、-x)

  • vote():前 --num-voters(默认 10)个用户各自随机挑选 --num-producers-vote(默认 20)个生产者,执行 cleos system voteproducer prods <voter> <prod1> <prod2> ...;
  • proxyVotes():先让首个生产者账户注册为投票代理(regproxy),再让所有投票用户执行 cleos system voteproducer proxy <voter> <proxy> 把投票权代理出去,演示代理投票机制。

投票完成后链上形成有效生产者调度,DPOS 出块进入常规状态。

阶段 11:领取奖励(-R)

claimRewards() 读取 cleos get table eosio eosio producers -l 100,对存在 unpaid_blocks 且未领取过奖励的生产者逐一执行 cleos system claimrewards -j <owner>,并统计每次领取动作的 elapsed 耗时,验证生产者奖励发放链路。

阶段 12:移交权限(-q)

stepResign() 完成启动序列的"交权"动作,将链的超级权限从启动者手中移交出去:

  • resign('eosio', 'eosio.prods'):把 eosio 的 owner、active 权限改为由 eosio.prods@active 控制;
  • 对全部 10 个 eosio.* 系统账户执行 resign(a, 'eosio'):把这些账户权限移交给 eosio@active。

updateAuth() 通过 cleos push action eosio updateauth ... -p <account>@<permission> 将对应权限的授权改为 {accounts: [{permission: {actor: controller, permission: 'active'}, weight: 1}]},即由 controller 的 active 权限代管。这模拟了真实主网启动后 eosio 权限由 21 个超级节点(eosio.prods)共同治理的最终形态。

阶段 13:多签替换合约(-m,不在 -a 中)

msigReplaceSystem() 演示了用多签升级系统合约的完整闭环(以替换为快速退款版 eosio.system 为例,wasm 路径为 ./fast.refund/eosio.system/eosio.system.wasm):

  1. buyrambytes 为 eosio 购买额外 RAM(200000 字节)以备合约升级占用;
  2. msigProposeReplaceSystem:由首个用户账户发起多签提案 fast.unstake,请求所有生产者账户批准,交易内容为 eosio setcode(以十六进制方式内嵌新合约字节码);
  3. msigApproveReplaceSystem:所有生产者账户逐一 multisig approve;
  4. msigExecReplaceSystem:发起者执行 multisig exec 落地升级。

该步骤不在 -a 默认流程中,需要 -m 单独触发,用于展示启动完成后如何以去中心化方式治理链。

阶段 14:随机转账压测(-X,不在 -a 中)

randomTransfer() 进入无限循环,随机选择 --num-senders 范围内的两个账户互转 0.0001 <symbol>,命令带 || true 容忍失败,可用于观察链在高频小额转账下的运行表现,配合 -l 查看节点日志。

七、完整命令行参数参考

脚本通过 argparse 定义参数(见 bios-boot-tutorial.py 命令行参数区),除步骤开关外,常用配置参数如下:

参数 默认值 说明
--public-key EOS8Znrtgwt8TfpmbVpTKvA2oB8Nqey625CLN8bCN3TEbgx86Dsvr 引导密钥公钥(与 genesis.json 的 initial_key 一致)
--private-key 5K463ynhZoCDDa4RDcr63cUwWLTnKqmdcoTKTHBjqoKfv4u5V7p 引导密钥私钥
--cleos ../../build/programs/cleos/cleos --wallet-url http://127.0.0.1:6666 cleos 命令(脚本会自动追加 --url http://127.0.0.1:<http-port>)
--nodeos ../../build/programs/nodeos/nodeos nodeos 二进制路径
--keosd ../../build/programs/keosd/keosd keosd 二进制路径
--contracts-dir ../../build/contracts/ 系统合约编译产物目录(EOSIO_CONTRACTS_DIRECTORY)
--nodes-dir ./nodes/ 各节点数据/日志目录
--genesis ./genesis.json 创世配置路径
--wallet-dir ./wallet/ 钱包目录
--log-path ./output.log 脚本日志文件
--symbol SYS 系统代币符号
--user-limit 3000 最多使用的用户数(0 = 不限)
--max-user-keys 10 导入钱包的最大用户密钥数
--ram-funds 0.1 每个用户用于购买 RAM 的资金
--min-stake 0.9 分配未质押资金前的最小质押额
--max-unstaked 10 最大未质押资金
--producer-limit 0 最多使用的生产者数(0 = 不限)
--min-producer-funds 1000.0000 生产者最低资金(不足则补齐)
--num-producers-vote 20 每个用户投票的生产者数量
--num-voters 10 参与投票的用户数
--num-senders 10 随机转账的用户范围
--producer-sync-delay 80 等待生产者节点同步的秒数
-H / --http-port 8000 cleos 使用的 HTTP 端口

八、创世配置详解

genesis.json 定义引导节点的创世参数:

字段 值 说明
initial_timestamp 2018-03-02T12:00:00.000 创世时间戳
initial_key EOS8Znrtgwt8TfpmbVpTKvA2oB8Nqey625CLN8bCN3TEbgx86Dsvr 初始出块者公钥(与脚本 --public-key 一致)
initial_chain_id 全零哈希 初始链 ID
initial_configuration 见下 链级资源参数

initial_configuration 包含的链参数(即 eosio.system 中 setparams 管理的 blockchain_parameters 初始值):max_block_net_usage=1048576、target_block_net_usage_pct=1000、max_transaction_net_usage=524288、base_per_transaction_net_usage=12、net_usage_leeway=500、context_free_discount_net_usage_num/den=20/100、max_block_cpu_usage=100000、target_block_cpu_usage_pct=500、max_transaction_cpu_usage=90000、min_transaction_cpu_usage=100、max_transaction_lifetime=3600、deferred_trx_expiration_window=600、max_transaction_delay=3888000、max_inline_action_size=4096、max_inline_action_depth=4、max_authority_depth=6。这些参数共同约束链上每区块/每交易的 CPU 与 NET 使用上限,可在 libraries/chain/chain_config.cpp 中看到其在核心层的解析与校验逻辑。

九、账户数据说明

accounts.json 是一个大型 JSON 文件,包含:

  • producers:30 个预生成的生产者账户(producer111a ~ producer111z、producer1111 ~ producer1114),各含 name、pvt、pub 字段;
  • users:按 useraaaaaaaa 风格批量预生成的普通用户账户(默认取前 3000 个,可通过 --user-limit 调整)。

脚本启动时读取该文件,按 --user-limit / --producer-limit 截断后合并为 accounts 列表,firstProducer 即用户列表长度,生产者从该下标开始。脚本中还保留了一个辅助函数 produceNewAccounts(),可用 cleos create key --to-console 生成更多 user<hex> 账户并追加到 newusers 文件,方便扩展账户规模。

十、运行输出与排错

  • 脚本每个外部命令都会先打印再执行:bios-boot-tutorial.py: <command>,并追加写入 --log-path 指定的日志文件(默认 ./output.log);
  • 关键操作(如创建质押账户、注册生产者、投票)使用 retry() 包装,失败会持续重试并打印 *** Retry,可容忍链上瞬时失败;
  • 引导节点与其他节点的输出(stderr)分别写入 <nodes-dir>00-eosio/stderr 与 <nodes-dir>NN-<producer>/stderr,需要排查时可用 -l / --log 直接显示引导节点日志尾部 60 行,或自行 tail 对应文件;
  • 再次运行前建议先用 -k 清理残留进程;-a 已在流程开头包含 kill 步骤。

十一、适用前提与限制

  • 本教程面向 EOSIO 2.x 时代的链启动流程,脚本中的协议特性 digest 与系统合约部署顺序以当前仓库实现为准(对应 v2.1.x 发布分支,见 docs/30_release-notes/);
  • 脚本默认使用内置的固定密钥对,仅适用于本地测试网络,切勿用于生产环境密钥管理;
  • 系统合约必须来自编译产物目录 EOSIO_CONTRACTS_DIRECTORY(README 强调只编译不部署),脚本本身不负责编译合约;
  • 对真实主网而言,Bios Boot 涉及更多治理与安全环节,本脚本主要用于开发、测试与理解启动机制的模拟环境,相关概念可进一步参考 docs/01_nodeos/07_concepts/ 等文档。

十二、小结

bios-boot-tutorial.py 以不到 500 行的 Python 编排代码,完整复现了 EOSIO 链从创世块、系统合约、代币发行、协议特性激活、账户质押、生产者注册投票到权限移交的整个冷启动生命周期。无论你是想搭建一条本地开发链、验证系统合约升级流程,还是深入理解 DPOS 链的启动机制,都可以以本教程为起点,逐阶段(-s、-c、-t、-S、-I、-T、-p、-P、-v、-q)观察每一步在链上产生的实际效果。

登录后查看全文
eos