Zcash RPC 回归测试指南:深入理解 qa/ 目录下的 pull-tester 与 rpc-tests 体系
【免费下载链接】zcashZcash - Internet Money项目地址: https://gitcode.com/GitHub_Trending/zc/zcash
Zcash(zcashd)在 qa/ 目录下维护了一套完整的 RPC 回归测试体系,用于在每次 Pull Request 合并前自动化验证节点、钱包与 P2P 协议行为。本文以 qa/README.md 为主线,结合 qa/pull-tester/rpc-tests.py、qa/rpc-tests/test_framework 等源码,系统讲解测试依赖安装、单测/批量/全量运行方式、并行调度参数、cache/缓存机制,以及如何基于测试框架编写新的回归测试,帮助你在本地完整复现 Zcash 的 CI 回归验证流程。
一、测试体系概览:pull-tester 与 rpc-tests 的分工
Zcash 仓库的测试代码分为两个紧密协作的目录:
- qa/pull-tester/:包含测试调度器 rpc-tests.py 以及由
configure生成的配置模板 tests_config.ini.in。它的职责是读取构建配置、汇总测试脚本列表、按并行度调度子进程、汇总通过率并生成 RPC 覆盖率报告。 - qa/rpc-tests/:存放每一个独立的测试脚本(
.py)以及供所有脚本复用的测试框架 test_framework/。每个脚本通过继承框架基类BitcoinTestFramework编写,最终以独立子进程方式被 pull-tester 调用。
根据 qa/README.md,仓库对每个 Pull Request 都会执行完整的构建并通过回归测试套件验证;你也可以在本地运行全部测试或只运行其中某一个。二者是"调度器 + 用例集"的关系:rpc-tests.py通过子进程逐个调用qa/rpc-tests/下的脚本,并将未识别的命令行参数原样转发给测试脚本(见 rpc-tests.py 的模块注释)。
二、环境准备:安装测试依赖
运行测试前需要安装以下 Python 库(回归测试通过 JSON-RPC 驱动真实zcashd进程,因此需要额外的网络协议支持):
zmq(pyzmq):供 ZMQ 推送类测试(如zmq_test.py)使用;base58:用于地址与序列化数据的 Base58 编解码。
Unix(Ubuntu / Debian 系发行版):
sudo apt-get install python3-zmq python3-base58OS X:
pip3 install pyzmq base58需要说明的是,ZMQ 测试只有在节点启用 ZMQ 支持(--enable-zmq)时才会被调度执行。若本机未安装zmqPython 库,运行rpc-tests.py会直接报错并提示"import zmqfailed. Use--nozmqto run without the ZMQ tests",此时可用--nozmq参数跳过 ZMQ 用例(见 rpc-tests.py)。
三、运行测试:从单测到全量回归
make rpc-tests是官方推荐入口。在 Makefile.am 中,该目标定义为:
rpc-tests: $(BITCOIND_BIN) qa/pull-tester/rpc-tests.py $(RPC_TEST)它依赖zcashd可执行文件构建完成,保证被测二进制与源码一致。这也是 qa/README.md 特别强调的一点:直接调用qa/pull-tester/rpc-tests.py虽然也能运行,但不会确保zcashd与最近的代码改动保持同步。
3.1 运行单个测试
RPC_TEST=<testname> make rpc-tests例如:
RPC_TEST=wallet make rpc-tests RPC_TEST=wallet_z_sendmany make rpc-tests3.2 运行任意组合
RPC_TEST="<testname1> <testname2> <testname3> ..." make rpc-tests例如同时验证钱包与区块两个维度:
RPC_TEST="wallet.py blockchain.py" make rpc-tests调度器对传入的脚本名做了宽容处理:接受带或不带.py后缀的写法,只要脚本名存在于其内部的ALL_SCRIPTS注册表中即会被选中(见 rpc-tests.py)。若指定的名字不在注册表中,则会打印 "No valid test scripts specified" 提示并退出。
3.3 运行完整回归套件
make rpc-tests不带RPC_TEST时,调度器会依次运行:SERIAL_SCRIPTS(串行用例)→FLAKY_SCRIPTS(偶发失败用例)→BASE_SCRIPTS(基础用例),并在启用 ZMQ 时追加ZMQ_SCRIPTS。这套默认集合覆盖了钱包、挖矿、mempool、P2P 拒绝规则、网络升级激活等主要功能面(见 rpc-tests.py)。
3.4 运行扩展套件(含耗时较长的用例)
RPC_TEST="--extended" make rpc-tests--extended会在基础套件之上追加EXTENDED_SCRIPTS,其中包括pruning.py、getblocktemplate_longpoll.py、rpcbind_test.py、hardforkdetection.py、invalidateblock.py、maxuploadtarget.py等常规 CI 不执行的用例(见 rpc-tests.py)。这些用例运行时间更长,适合本地深度验证。
四、调度器参数详解:控制并行、覆盖率与单测行为
rpc-tests.py是一个完整的 argparse 程序(见 rpc-tests.py),它自己解析一批"调度级"参数,其余参数原样透传给每个测试脚本。
4.1 调度级参数
| 参数 | 简写 | 默认值 | 说明 |
|---|---|---|---|
--jobs=n | -j | 4 | 并行运行的测试脚本数,默认 4 个并发 |
--coverage | — | 关闭 | 生成 RPC 接口基础覆盖率报告 |
--extended | — | 关闭 | 在基础套件上追加扩展用例 |
--exclude=a,b | -x | 空 | 以逗号分隔、不带.py后缀地排除某些用例 |
--nozmq | — | 关闭 | 显式跳过 ZMQ 测试(当未安装 pyzmq 时必需) |
--deterministic | -d | 关闭 | 让输出更接近确定性,便于多次运行结果对比 |
--machines/--rpcgroup | -m/-r | -1 | 将用例分片到多台机器并行执行(需成对使用,-r必须小于-m) |
--force | -f | 关闭 | 在默认禁用 RPC 测试的平台(如 Windows)上强制运行 |
其中--machines与--rpcgroup的校验逻辑见 rpc-tests.py:两者必须同时提供,且-r索引须小于-m机器数,调度器会按"向上取整的均匀分片"把测试列表拆给每台机器。
4.2 透传给单个测试脚本的参数
qa/README.md 列出了作用于"每次单独测试运行"的选项,其实际解析位置在 test_framework.py 的BitcoinTestFramework.main()中:
-h, --help 显示帮助并退出 --nocleanup 测试结束或出错时保留 zcashd 进程与 test.* 数据目录 --noshutdown 测试执行完毕后不停止 zcashd --srcdir=SRCDIR 包含 zcashd/zcash-cli 的源码目录(默认:../../src) --tmpdir=TMPDIR 数据目录的根目录(默认由 tempfile 生成,前缀 test) --tracerpc 打印测试过程中发起的全部 RPC 调用 --coveragedir=COVERAGEDIR 把被测试覆盖到的 RPC 命令写入该目录补充几个源码层面的细节:
--tracerpc通过logging.basicConfig(level=logging.DEBUG)打开authproxy的调试日志,从而输出每一条 RPC 请求(见 test_framework.py);- 每个测试脚本还会额外获得
--portseed=<n>,用于在并行运行时为不同测试分配互不冲突的 RPC/P2P 端口号(见 rpc-tests.py)。端口分配规则在 util.py 中定义:节点数上限MAX_NODES = 8,端口下限PORT_MIN = 11000,每个协议各保留 5000 个端口; - 框架还隐藏了一个
--cachedir选项,默认指向qa/cache,用于缓存预生成的测试数据目录(见 test_framework.py)。
4.3 覆盖率报告
追加--coverage后,调度器会在临时目录初始化覆盖率数据(RPCCoverage),每个测试脚本把执行过的 RPC 命令写入coverage.*文件,最终与zcash-cli help导出的完整命令清单rpc_interface.txt做差集,输出"未被任何测试触达的 RPC 命令"清单(见 rpc-tests.py)。若所有命令都被覆盖,则会打印 "All RPC commands covered."。
4.4 调试输出
设置环境变量可开启额外的调试输出:
PYTHON_DEBUG=1 qa/pull-tester/rpc-tests.py wallet在PYTHON_DEBUG=1时,调度器会以非确定模式逐行打印测试进度点(见 rpc-tests.py),方便观察长耗时用例的执行节奏。
五、缓存机制:200 区块的共享测试基座
qa/README.md 说明了回归测试的高效复用设计:
第一次运行回归测试时,会生成一条 200 区块的
-regtest区块链,以及四个节点的钱包,并保存在cache/目录中。每个节点的钱包里包含来自 25 个成熟区块的矿工补贴(25×10=250 ZEC)。
从源码可以还原这条机制的完整实现链:
- 当并行运行多个用例(
len(test_list) > 1 and jobs > 1)时,调度器会先调用 create_cache.py 预生成缓存(见 rpc-tests.py)。该脚本通过继承BitcoinTestFramework但设置num_nodes = 0、空跑run_test()的方式,只完成区块链与钱包的初始化; - 每个测试脚本在
setup_chain()阶段通过initialize_chain()把cache/下的区块链与钱包复制到自己的临时目录,作为初始测试状态(见 test_framework.py); - 框架默认
cache_behavior = 'current',表示"使用当前分支生成的缓存";ComparisonTestFramework则使用'clean',即不依赖共享缓存、每次从零开始,确保 P2P 对比测试的纯净性(见 test_framework.py)。
这套"复制而非重建"的策略让数百个测试用例共享同一条链的初始状态,避免了每个用例重复挖 200 个区块的开销。
5.1 缓存损坏后的恢复
如果测试环境进入坏状态(例如残留的僵尸zcashd占用了端口、缓存链数据损坏),qa/README.md 给出的恢复手段是:
rm -rf cache killall zcashd删除cache/后,下一次运行会自动重新生成 200 区块的缓存与钱包;killall zcashd则清理掉可能残留的节点进程。另外,调度器在启动时会基于当前时间戳生成一个伪随机端口偏移(portseed_offset),以便"跳过"僵尸节点可能占用的端口段(见 rpc-tests.py),减少偶发端口冲突。
六、测试脚本的组织:串行、易碎与基础分类
调度器把测试脚本按运行特性分为四类(见 rpc-tests.py):
SERIAL_SCRIPTS:必须串行执行的用例,因为其涉及的屏蔽交易花费(shielded spends)会占满所有 CPU 核心,并行会拖垮整体吞吐。当前包括mergetoaddress_sapling.py、mergetoaddress_ua_nu5.py、mergetoaddress_ua_sapling.py、wallet_shieldingcoinbase.py。调度器遇到串行脚本时会立即中断并行添加(见 rpc-tests.py);FLAKY_SCRIPTS:已知偶发失败但尚未定位根因的用例,当前为mempool_nu_activation.py与mempool_packages.py;BASE_SCRIPTS:基础回归套件,按运行时长从长到短排序(<5m→<2m→<60s→<30s),长用例排在前以便并行调度时整体时间最短。其中包含wallet.py、sprout_sapling_migration.py、turnstile.py、finalsaplingroot.py、finalorchardroot.py、wallet_orchard.py、p2p-fullblocktest.py、nuparams.py、feature_zip244_blockcommitments.py、shielded_balance_accounting_coinbase.py等 120+ 个用例;EXTENDED_SCRIPTS:CI 不执行、仅在--extended时追加的深度用例,如pruning.py、getblocktemplate_longpoll.py、rpcbind_test.py、forknotify.py、hardforkdetection.py、receivedby.py、maxblocksinflight.py、maxuploadtarget.py、p2p-acceptblock.py、wallet_db_flush.py等。
BASE_SCRIPTS中还有一类特殊的带参数条目,例如'txn_doublespend.py --mineblock',表明同一脚本可携带不同参数被调度多次,这要求脚本本身支持命令行参数分支。
七、编写测试:基于 test_framework 的两种范式
qa/README.md 鼓励为新增或既有功能编写测试,并指引读者继续阅读 qa/rpc-tests/ 获取框架细节。qa/rpc-tests/README.md 进一步给出了框架组件的功能地图:
| 模块 | 用途 |
|---|---|
| test_framework.py | RPC 回归测试的基类(BitcoinTestFramework) |
| util.py | 通用工具函数(节点启动、区块/内存池同步、端口分配等) |
| mininode.py | P2P 连接与网络消息对象的底层支持 |
| comptool.py | 对比式(comparison-tool 风格)P2P 测试框架 |
| script.py | 交易脚本操作工具(源自 python-bitcoinlib) |
| blockstore.py | 磁盘落盘的区块与交易存储 |
| key.py | 基于 OpenSSL EC_Key 的密钥封装(源自 python-bitcoinlib) |
| bignum.py | 供 script.py 使用的大数辅助 |
| blocktools.py | 构造区块与交易的辅助函数 |
此外,框架目录还包含 Zcash 特有的模块:equihash.py(Equihash 工作证明)、zip244.py(区块承诺)、zip317.py(费用规则)、flyclient.py(FlyClient 轻客户端)以及 authproxy.py(JSON-RPC 客户端封装)。
7.1 标准 RPC 测试(BitcoinTestFramework)
绝大多数钱包与节点测试采用这种范式:继承BitcoinTestFramework,实现run_test(),框架自动完成"建链 → 启动 4 节点 → 连接成链形网络 → 执行用例 → 停止并清理"的完整生命周期。框架默认启动 4 个节点并按0-1-2-3顺序连接;setup_network()还支持split=True把网络拆成0/1与2/3两个阵营,用于测试链分叉与重组(见 test_framework.py)。
7.2 P2P 测试(Mininode 与 Comptool)
Mininode范式用于"自由形式"的协议级测试:测试进程内有一个专用线程处理与zcashd的所有网络通信(基于 Pythonasyncore),另一个线程承载测试逻辑。NodeConn负责建立连接,派生自NodeConnCB的回调类会收到感兴趣的事件——注意派生类的__init__中必须调用self.create_callback_map()来建立 P2P 消息与回调函数的映射。同一个回调处理器可以复用到多个NodeConn,所有连接创建完毕后调用NetworkThread.start()启动网络线程,P2P 测试中同样可以直接使用 RPC 调用。典型示例见 p2p-acceptblock.py 与 maxblocksinflight.py。
Comptool范式用于"对比式"测试:将被测zcashd与一个或多个参照节点(--refbinary指定参照二进制,--testbinary指定被测二进制)的行为进行对比,或与预期结果对比。要点包括:
num_nodes决定启动的节点数;单节点时用--testbinary更换被测二进制,多节点时--refbinary作用于第 2 个及之后的节点;- 实现生成器函数
get_tests(),不断yield出TestInstance; - 每个
TestInstance由[object, outcome, hash]三元组列表构成:object是CBlock/CTransaction/CBlockHeader(后者用于让测试器按需投递完整头链,支持乱序投递区块);outcome为True/False/None(None表示比较所有被测节点是否对最优链达成一致);hash为可选的最优链尖期望哈希; - 辅助开关
sync_every_block(False时一次inv全部区块、只校验最后一个;True时逐块同步校验)与sync_every_transaction(类比前者,None结果时还会跨节点对比整个内存池内容)。
Comptool 的典型示例见 invalidblockrequest.py 与 p2p-fullblocktest.py。
八、运行前提与注意事项
- 构建配置:RPC 测试要求钱包、工具、守护进程三个组件全部启用。
configure时若未启用(对应--enable-wallet、--with-utils、--with-daemon),tests_config.ini.in 中的ENABLE_WALLET、ENABLE_UTILS、ENABLE_BITCOIND会被注释掉,rpc-tests.py会打印 "RPC tests require wallet support" 类提示并直接退出(见 rpc-tests.py); - 平台限制:Windows 上 RPC 测试默认禁用,需
--force强制执行(见 rpc-tests.py); - 并行度:默认
--jobs=4,可根据机器核数与负载调整;串行脚本不受--jobs影响,始终单独执行; - 测试状态隔离:每个测试脚本使用
--portseed派生独立端口、复制缓存链到独立tmpdir,互不干扰;退出码0表示全部通过,1表示存在失败用例(见 rpc-tests.py)。
九、小结
Zcash 的回归测试体系以 qa/pull-tester/rpc-tests.py 为调度中枢、以 qa/rpc-tests/ 下的 120+ 测试脚本为用例集、以 test_framework 为公共底座,配合cache/200 区块缓存与端口种子机制,实现了"一次构建、并行复用、全量回归"的高效验证闭环。无论你是想在本地跑通make rpc-tests、排查某个钱包用例的偶发失败,还是打算为新的网络升级或 RPC 接口补充回归用例,沿着"安装依赖 → 单测复现 → 全量回归 → 阅读框架 → 编写用例"这条路径即可快速上手。
【免费下载链接】zcashZcash - Internet Money项目地址: https://gitcode.com/GitHub_Trending/zc/zcash
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考