Zcash 4.1.1 发布解读:-O3 发布构建优化与 Founder's Reward 报告修正
【免费下载链接】zcashZcash - Internet Money项目地址: https://gitcode.com/GitHub_Trending/zc/zcash
本指南以 Zcash 仓库的 4.1.1 版本发布说明 为核心,深入解读该版本的两项关键技术变更:发布构建由-O1切换为-O3以修复 v4.1.0 引入的性能回归,以及getblocktemplateRPC 对 Founders' Reward(创始人奖励)金额的正确报告逻辑。同时结合仓库源码,详细说明getblocksubsidy的用法、奖励分配与验证的底层实现,并梳理本版本在 RPC 与工程化(lint、脚本)方面的其他改进,帮助开发者理解该版本的技术细节并正确迁移使用。
版本概览
Zcash 4.1.1 是紧接 4.1.0 之后的一个维护性版本,核心目标有两个:
- 修复发布构建的性能回归:将发布构建的
CFLAGS/CXXFLAGS优化级别从-O1提升到-O3,以恢复因优化不足导致的运行性能下降; - 修正
getblocktemplate中 Founders' Reward 金额的报告:在 Canopy 网络升级激活前正确返回foundersreward字段,激活后移除该字段(Founders' Reward 恰在 Canopy 激活时按 ZIP 207 的规定到期)。
除此之外,版本还包含大量工程化改进:getrawtransaction新增可选区块哈希参数、增强的 lint 检查体系(include guard、shellcheck、locale 依赖、Python 编码等)、脚本 shebang 规范化以及uint256序列化实现替换等。
优化发布构建:从 -O1 到 -O3
背景:v4.1.0 的性能回归
发布说明明确指出,v4.1.0 的发布构建存在性能回归,原因是其发布构建仅使用-O1优化级别。-O1只开启最基本的优化(如死代码消除、分支优化等),不会启用-O2/-O3中的循环展开、函数内联、向量化等更激进的优化手段,导致生成的二进制在执行加密哈希、签名验证、区块处理等 CPU 密集任务时性能不理想。
构建系统中的实现证据
该变更由 Daira Hopwood 提交(changelog 中 "Set release CFLAGS/CXXFLAGS to use -O3.")。在构建系统中可以找到与之配套的发布标志定义:
- depends/hosts/linux.mk:
linux_release_CFLAGS=-O3 - depends/hosts/freebsd.mk:
freebsd_release_CFLAGS=-O3 - depends/hosts/darwin.mk:
darwin_release_CFLAGS=-O3 - depends/hosts/mingw32.mk:
mingw32_release_CFLAGS=-O3
这表示在四个主要目标平台(Linux、FreeBSD、macOS、Windows/MinGW)的 depends 交叉编译发布构建中,均以-O3作为统一优化级别。
同时,configure.ac 中可以看到编译标志的默认处理逻辑:当用户未显式覆盖CXXFLAGS时,configure 会清空默认值并按平台重新探测与组合DEBUG_CXXFLAGS、HARDENED_CXXFLAGS等标志;这保证了发布构建所采用的优化级别由项目自身统一控制,而不是依赖环境变量中的随机默认值。
-O3 与 -O1 的实际差异
从编译器语义角度,-O3在-O2(已启用几乎所有不增加代码体积的优化)的基础上进一步启用-finline-functions(积极函数内联)、-funswitch-loops(循环切换)、-fpredictive-commoning(预测性公共子表达式)等优化,对 Zcash 这类包含大量密码学计算(Equihash 挖矿算法、SHA-256 系列、Pedersen 哈希等)的节点软件而言,能够显著提升热路径的执行效率。
构建验证方式
读者可在本地按标准流程验证该优化效果(以 Linux 为例):
./zcutil/build.sh -j$(nproc)构建完成后,可用以下命令确认二进制实际采用的优化级别(GCC/Clang 均支持):
readelf -p .GCC.command.line src/zcashd # 或 strings src/zcashd | grep -i "O3"getblocktemplate 中 Founders' Reward 报告修正
问题背景:ZIP 207 与 Founders' Reward 到期
Zcash 的 Founders' Reward(创始人奖励)机制规定:在区块补贴(block subsidy)中提取 20%(nBlockSubsidy / 5)分配给一组创始人地址,该机制从创世后的第一个区块开始,持续到第一次减半(halving)前的最后一个区块。根据 ZIP 207 的规定,Founders' Reward 恰好在 Canopy 网络升级激活时到期,届时区块奖励的分配将切换到资助流(Funding Streams)体系。
getblocktemplate 中的修正逻辑
修正的核心代码位于 src/rpc/mining.cpp 的getblocktemplate实现中。当模板中的 coinbase 交易被输出时(coinbasetxn模式),代码按以下逻辑决定是否附带foundersreward字段:
if (tx.IsCoinBase()) { // Show founders' reward if it is required auto nextHeight = pindexPrev->nHeight+1; bool canopyActive = consensus.NetworkUpgradeActive(nextHeight, Consensus::UPGRADE_CANOPY); if (!canopyActive && nextHeight > 0 && nextHeight <= consensus.GetLastFoundersRewardBlockHeight(nextHeight)) { CAmount nBlockSubsidy = consensus.GetBlockSubsidy(nextHeight); entry.pushKV("foundersreward", nBlockSubsidy / 5); } entry.pushKV("required", true); txCoinbase = entry; }判定条件包含三个要素,缺一不可:
| 条件 | 含义 |
|---|---|
!canopyActive | 下一个区块高度尚未激活 Canopy 升级(即奖励仍处于 Founders' Reward 阶段) |
nextHeight > 0 | 排除创世区块(高度 0 无奖励) |
nextHeight <= consensus.GetLastFoundersRewardBlockHeight(nextHeight) | 未超过最后一次 Founders' Reward 区块高度 |
GetLastFoundersRewardBlockHeight在 src/consensus/params.cpp 中实现,其定义非常简洁:
int Params::GetLastFoundersRewardBlockHeight(int nHeight) const { return HalvingHeight(nHeight, 1) - 1; }即第一次减半高度减 1——Founders' Reward 覆盖从区块 1 到第一次减半前最后一个区块的完整区间。
修正前该字段的判定存在偏差(未正确处理 Canopy 激活前后两种状态),导致 pre-Canopy 阶段报告金额不准确、post-Canopy 阶段仍残留字段;修正后行为为:
- Canopy 激活前:正确返回
foundersreward(金额为区块补贴的 1/5); - Canopy 激活后:移除该字段,此时区块补贴的分配由资助流承担。
用 getblocksubsidy 获取奖励明细
由于getblocktemplate的输出不再承载完整的奖励拆分信息,发布说明建议使用getblocksubsidy HEIGHT获取资助流金额,并传入getblocktemplate返回的高度。该 RPC 也在同一文件 src/rpc/mining.cpp 中实现,并注册于 "mining" 类别(src/rpc/mining.cpp)。
调用方式(CLI):
zcash-cli getblocksubsidy 1000参数说明:
| 参数 | 类型 | 说明 |
|---|---|---|
height | numeric(可选) | 区块高度;缺省时使用当前链高。必须>= 0,否则返回 "Block height out of range" 错误 |
返回值结构(完整字段):
| 字段 | 类型 | 含义 |
|---|---|---|
miner | numeric | 矿工实际获得的奖励金额(ZEC) |
founders | numeric | Founders' Reward 金额(ZEC),Canopy 激活后为 0 |
fundingstreamstotal | numeric | 直接资助流(Funding Streams)总金额(ZEC) |
lockboxtotal | numeric | 发送到开发基金锁盒(lockbox)的总金额(ZEC) |
totalblocksubsidy | numeric | 区块补贴总额(ZEC) |
fundingstreams | array | 资助流描述数组(仅当资助流激活时出现),每项含recipient、specification、value、valueZat、address |
lockboxstreams | array | 锁盒流描述数组(仅当锁盒流激活时出现),每项含recipient、specification、value、valueZat |
实现要点:getblocksubsidy同样以canopyActive = consensus.NetworkUpgradeActive(nHeight, Consensus::UPGRADE_CANOPY)作为分支依据(src/rpc/mining.cpp):
- Canopy 激活后:遍历
consensus.GetActiveFundingStreams(nHeight),将透明地址(CScript)与 Sapling 地址两种资助流收款人分别解析为可读地址,累加得到fundingstreamstotal与lockboxtotal; - Canopy 激活前:若高度处于 Founders' Reward 区间,则
nFoundersReward = nBlockSubsidy / 5; - 最后统一计算矿工奖励:
nMinerReward = nBlockSubsidy - nFoundersReward - nFundingStreamsTotal - nLockboxTotal。
RPC 调用示例(JSON-RPC 形式):
{ "jsonrpc": "1.0", "method": "getblocksubsidy", "params": [1000], "id": 1 }奖励分配的底层实现:矿工如何构造 coinbase
getblocktemplate报告的金额,与矿工实际构造区块时在 coinbase 中写入的输出必须严格一致。该逻辑位于 src/miner.cpp 的区块构造器中:
auto miner_reward = block_subsidy; // founders' reward or funding stream amounts will be subtracted below if (nHeight > 0) { if (chainparams.GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_CANOPY)) { // 构造资助流输出:对每个激活的资助流按 fsinfo.Value(block_subsidy) 计算金额, // 透明地址输出到 vout,Sapling 地址通过 saplingBuilder.add_recipient 添加 for (const auto& [fsinfo, fs] : consensus.GetActiveFundingStreams(nHeight)) { const auto amount = fsinfo.Value(block_subsidy); miner_reward -= amount; // ... } } else if (nHeight <= chainparams.GetConsensus().GetLastFoundersRewardBlockHeight(nHeight)) { // Founders reward is 20% of the block subsidy const auto vFoundersReward = miner_reward / 5; miner_reward -= vFoundersReward; mtx.vout.push_back(CTxOut(vFoundersReward, chainparams.GetFoundersRewardScriptAtHeight(nHeight))); } else { // Founders reward ends without replacement if Canopy is not activated by the // last Founders' Reward block height + 1. } }可见矿工侧与 RPC 侧共享同一判定函数GetLastFoundersRewardBlockHeight,且 Founders' Reward 的 20%(miner_reward / 5)划分与getblocktemplate中的nBlockSubsidy / 5完全对应。
验证与共识检查
Founders' Reward 的支付金额属于共识规则的一部分。在区块验证路径 src/main.cpp 中,节点会校验 coinbase 输出中是否存在金额恰为consensusParams.GetBlockSubsidy(nHeight) / 5的 Founder 输出(if (output.nValue == (consensusParams.GetBlockSubsidy(nHeight) / 5))),确保每个区块都按协议支付了正确的创始人奖励。
此外,src/metrics.cpp 在节点运行时的指标展示中同样使用(height > 0) && (height <= consensusParams.GetLastFoundersRewardBlockHeight(height))判断是否显示 Founders' Reward 统计,保持各模块口径一致。
区块补贴计算:慢启动与减半
理解奖励金额的前提是掌握GetBlockSubsidy的完整计算逻辑(src/consensus/params.cpp)。Zcash 采用"挖矿慢启动(mining slow start)"机制,补贴不是从创世起即为满额:
CAmount Params::GetBlockSubsidy(int nHeight) const { CAmount nSubsidy = 12.5 * COIN; // Mining slow start if (nHeight < this->SubsidySlowStartShift()) { nSubsidy /= this->nSubsidySlowStartInterval; nSubsidy *= nHeight; return nSubsidy; } else if (nHeight < this->nSubsidySlowStartInterval) { nSubsidy /= this->nSubsidySlowStartInterval; nSubsidy *= (nHeight+1); return nSubsidy; } // ...减半计算 if (this->NetworkUpgradeActive(nHeight, Consensus::UPGRADE_BLOSSOM)) { return (nSubsidy / Consensus::BLOSSOM_POW_TARGET_SPACING_RATIO) >> halvings; } else { return nSubsidy >> halvings; } }计算规则(对应 ZIP 208):
- 高度小于慢启动区间一半时:
SlowStartRate · height; - 处于慢启动区间后半段时:
SlowStartRate · (height + 1); - 慢启动结束后:按
floor(MaxBlockSubsidy / 2^Halving(height))计算,Blossom 激活后额外除以BLOSSOM_POW_TARGET_SPACING_RATIO(区块间隔缩短为原来的 3/4,单位高度补贴相应减少); - 减半次数达到 64 及以上时补贴归零。
getblocksubsidy的帮助文本也明确说明其"takes into account the mining slow start and the founders reward"(src/rpc/mining.cpp),即查询结果已经综合了慢启动与奖励扣减。
测试佐证
仓库的 src/gtest/test_foundersreward.cpp 提供了一组针对 Founders' Reward 的单元测试,覆盖了:
- 特定高度下 Founder 奖励脚本与地址的正确性(如 testnet 高度 1、53126、53127 的地址与脚本哈希逐一断言);
- 高度越界(0 与
maxHeight+1)时GetFoundersRewardScriptAtHeight/GetFoundersRewardAddressAtHeight触发断言死亡(EXPECT_DEATH); - 从高度 1 到最后一个 Founder 高度之间出现的唯一 Founder 地址数量校验(
checkNumberOfUniqueAddresses)。
这些测试位于 src/Makefile.gtest.include 的构建清单中,可通过make check执行验证。
其他值得关注的变化
getrawtransaction 新增 blockhash 参数
由 Alfredo Garcia 提交,为getrawtransaction增加了可选的第三个参数myblockhash,用于在指定区块中定位交易(src/rpc/rawtransaction.cpp):
zcash-cli getrawtransaction "mytxid" 1 "myblockhash"实现上,当提供区块哈希时,节点会在区块索引中查找该区块(找不到时抛出 "Block hash not found"),并调用GetTransaction(..., blockindex)在该区块内查询交易;若交易不在该区块中,则返回 "No such transaction found in the provided block"。同时,输出会附带in_active_chain字段标记该区块是否在主链上。该参数还缓解了-txindex未开启时无法查询历史链上交易的限制。
构建与 lint 基础设施改进
4.1.1 版本合并了大量来自 Bitcoin Core 上游及 Zcash 自研的工程化改进,显著提升了代码质量门槛:
- include guard 规范化:新增
lint-include-guards.sh,统一检查头文件 include guard 的命名规范与一致性("Document include guard convention"、"Fix missing or inconsistent include guards"、"Use Zcash-specific include guards for new files" 等); - shellcheck 静态检查:新增 shell 脚本 lint,修复了
#!/bin/bash到#!/usr/bin/env bash的 shebang 规范化("Obsolete #!/bin/bash shebang"、"Use consistent shebangs"),并处理 shellcheck v0.6.0 引入的新警告; - locale 依赖检查:新增
lint-locale-dependence.sh,强制所有 shell 脚本以export LC_ALL=C开头,消除脚本在不同 locale 下的行为差异; - Python 编码规范:强制 Python 代码在打开文本文件时显式指定 UTF-8/ASCII 编码,避免默认编码差异;
- 空白字符检查:
lint-whitespace改用 perl 实现(摆脱 BSD grep 对-P的限制),并支持检查最近 N 个提交或未暂存更改,新增 tab 字符检查并排除导入的第三方依赖子树; - 重复 include 检查:在 Travis CI 中增加对重复
#include的检测; - bctest/bitcoin-util-test 清理:
bitcoin-util-test.py支持手动运行、新增 verbose 模式、增加格式敏感的输出比对与空输出文件失败判定,并转换至 Python 3。
其他实现级改动
- uint256 序列化替换(Wladimir J. van der Laan):
uint256中的sprintf被替换为HexStr与反向迭代器实现,减少格式串隐患并统一十六进制输出路径; - 按需编译 Equihash 状态(adityapk00):当编译配置禁用挖矿(
ENABLE_MINING未定义)时不再编译ehHashState::*,缩小非挖矿节点二进制体积; - GetNextWorkRequired 文档澄清(Daira Hopwood):在 src/pow.cpp 相关实现中补充注释,说明其计算与协议规范(zips PR 418)的等价性;
- 头文件守卫命名修正(Dan Raviv):修复使用保留标识符(以双下划线或下划线加大写字母开头)的头文件守卫,避免与编译器保留字冲突;
- gtest 框架补充:新增
test_foundersreward.cpp等 Founders' Reward 专项测试(见上文)。
升级与使用建议
- 节点运维:若在 v4.1.0 上运行并观察到 CPU 占用偏高或同步变慢,升级到 4.1.1 可获得发布构建优化带来的性能改善;
- 矿池/挖矿软件:依赖
getblocktemplate解析foundersreward字段的程序必须升级适配——Canopy 激活后该字段将不再存在,应改由getblocksubsidy HEIGHT查询各资助流的金额与收款地址(字段fundingstreams[].address、valueZat),以getblocktemplate返回的height作为参数; - 区块浏览器/分析工具:查询链上交易时可利用
getrawtransaction的新参数在指定区块内精确定位交易,并借助in_active_chain判断区块状态; - 开发构建:本地开发调试不受发布优化级别影响,
configure.ac中--enable-debug仍使用-O0与-g组合,便于断点调试。
总结
Zcash 4.1.1 虽是一个维护性版本,但两项核心变更都具有实际价值:-O3发布构建修复了性能回归,让运行zcashd的节点在密码学计算密集路径上恢复应有的效率;getblocktemplate的 Founders' Reward 修正则保证了 Canopy 升级前后区块奖励报告与共识规则严格一致,并引导矿工生态迁移到更完整的getblocksubsidy接口。配合大规模的 lint 与脚本工程化整理,该版本为后续网络升级奠定了更健壮的代码基础。
【免费下载链接】zcashZcash - Internet Money项目地址: https://gitcode.com/GitHub_Trending/zc/zcash
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考