Zcash 4.2.0-rc1 技术解析:ed25519-zebra 共识签名验证、ZIP-313 默认费用与挖矿模板性能优化
2026/9/18 18:26:55 网站建设 项目流程

Zcash 4.2.0-rc1 技术解析:ed25519-zebra 共识签名验证、ZIP-313 默认费用与挖矿模板性能优化

【免费下载链接】zcashZcash - Internet Money项目地址: https://gitcode.com/GitHub_Trending/zc/zcash

本指南围绕 Zcash 节点软件 zcashd 4.2.0-rc1 版本(release-notes-4.2.0-rc1.md)的核心变更展开:共识层签名校验从 libsodium 切换到 Rust 实现 ed25519-zebra、按 ZIP-313 将默认费用下调至 0.00001 ZEC、getblocktemplate 为屏蔽矿工费地址预计算区块模板以提升挖矿响应速度,以及 RPC 测试框架从 shell 脚本迁移到 Python。读完本文,你将理解这些改动背后的动机、对应的源码实现位置与测试验证方式,并能直接据此定位代码、复现行为。

一、共识签名校验切换:告别 libsodium,全面启用 ed25519-zebra

1.1 变更背景

自 Canopy 网络升级以来,Zcash 的 Ed25519 签名验证实际上已经由 Rust 库ed25519-zebra承担;4.2.0-rc1 的这次改动是彻底移除 zcashd 对 libsodium 的 ed25519 校验依赖,让 zcashd 与 ed25519-zebra 的绑定成为唯一实现。触发这一系列工作的是业界对 libsodium 等传统 Ed25519 实现中"非常规编码(non-canonical encoding)"与"小阶点(small-order point)"处理不一致问题的深入研究:不同实现之间对这些边界情况的接受/拒绝行为存在分歧,而共识规则要求所有节点行为完全一致。

Zcash 通过ZIP 215精确定义了必须采用的 Ed25519 验证规则(宽松化、接受非常规编码、必须使用乘 8 的验证方程),ed25519-zebra 正是完全按 ZIP 215 语义实现并经过共识测试向量验证的 Rust 库。

1.2 源码实现:Rust 桥接层

仓库将 ed25519-zebra 声明在 Cargo.toml:

ed25519-zebra = "4"

Rust 侧封装位于 src/rust/src/ed25519.rs,通过 cxx 桥暴露三个函数:

  • generate_keypair:基于OsRng生成签名/验证密钥对;
  • sign:用签名私钥对消息签名;
  • verify:执行 ZIP 215 语义的验证。

其中verify的注释明确了 Zcash 特有的共识属性(src/rust/src/ed25519.rs):

  • A_bytesR_bytes必须是 twisted Edwards 形式的 Curve25519 点的编码,且必须接受非常规编码
  • s_bytes必须是小于曲线素数阶l的整数;
  • 必须满足验证方程[8][s]B = [8]R + [8][k]A
  • RFC 8032 允许的替代方程[s]B = R + [k]A不得使用

C++ 侧通过rust/ed25519.h头文件调用,编译单元在 src/Makefile.am 中登记(rust/src/ed25519.rsrust/gen/include/rust/ed25519.hrust/gen/src/ed25519.cpp)。

1.3 共识调用点:JoinSplit 签名验证

屏蔽交易(JoinSplit)的 Ed25519 签名验证发生在 src/main.cpp 的ContextualCheckShieldedInputs()

if (!tx.vJoinSplit.empty()) { if (!ed25519::verify(tx.joinSplitPubKey, tx.joinSplitSig, {dataToBeSigned.begin(), 32})) { // 若旧共识分支 ID 下验证通过,则提示节点需要升级 if (ed25519::verify(tx.joinSplitPubKey, tx.joinSplitSig, {prevDataToBeSigned.begin(), 32})) { return state.DoS(... "old-consensus-branch-id ..."); } return state.DoS(... REJECT_INVALID, "bad-txns-invalid-joinsplit-signature"); } }

这里的dataToBeSigned是屏蔽组件的签名哈希(SIGHASH_ALL),验证失败时还会回退用上一 epoch 的共识分支 ID 再验一次,以区分"签名本身错误"与"共识分支过期",从而引导用户升级。这正是 Canopy 之后一直沿用的 ed25519-zebra 验证路径,4.2.0-rc1 只是把这条路径变成唯一依赖。

1.4 测试验证:与 libsodium 的兼容性回归

src/gtest/test_consensus.cpp 同时包含两套关键测试:

  • ConsensusTests.LibsodiumPubkeyValidation:构造了 libsodium ≤ 1.0.15 会接受、而 ≥ 1.0.16 会拒绝的小阶公钥/小阶 R 点测试向量(比如以"zcash ed25519 libsodium compatibility"为消息的 6 组向量),并断言ed25519::verify的结果与期望一致——这些正是历史上各实现分歧的边界样本;
  • ConsensusTests.ZIP215TestVectors:来自 Zcash Foundation 的 ZIP 215 官方测试向量,逐条验证非常规编码被接受、小阶点行为正确。

此外 src/gtest/test_checktransaction.cpp 中 JoinSplit 构造也直接使用ed25519::sign生成签名。也就是说,切换后旧库兼容性不再依赖外部依赖行为,而是由仓库内的确定性测试向量锁定。

二、ZIP-313 默认费用下调:0.00001 ZEC 与中继阈值解耦

2.1 变更内容

按 ZIP-313 要求,将默认费用从 0.0001 ZEC(10,000 zatoshis)降至0.00001 ZEC(1,000 zatoshis);同时保证任何支付了至少新最低费用的交易,无论体积多大都能满足交易中继阈值(即"最小中继费"不得高于DEFAULT_FEE)。

2.2 源码中的常量与计算

核心常量定义在 src/amount.h:

/** The legacy default fee that was defined in ZIP 313. */ static const CAmount LEGACY_DEFAULT_FEE = 1000; // 单位 zatoshis,即 0.00001 ZEC

CFeeRate::GetFeeForRelay是本次"中继费不超过默认费"规则的关键实现(src/amount.cpp):

CAmount CFeeRate::GetFeeForRelay(size_t nSize) const { return std::min(GetFee(nSize), LEGACY_DEFAULT_FEE); }

即:按体积算出的中继费如果超过 1000 zatoshis,就封顶为 1000 zatoshis。这样大体积交易支付 1000 zatoshis 也能被中继,彻底摆脱"大交易必须付更多费才能过中继"的旧限制。

在内存池准入处(src/main.cpp),AcceptToMemoryPool::minRelayTxFee.GetFeeForRelay(nSize)计算最小中继费,nModifiedFees < minRelayFee的交易会被以REJECT_INSUFFICIENTFEE("min relay fee not met")拒绝;由于上述封顶逻辑,任何支付 ≥ 1000 zatoshis 的交易都不会在此关卡被拒。

钱包侧同样受益:-minrelaytxfee对应的全局minRelayTxFeeWalletTxBuilder(见 src/wallet/rpcwallet.cpp 等多处)作为费率依据,而钱包默认费率下限同样以LEGACY_DEFAULT_FEE兜底。policy 模块的历史注释(src/policy/policy.h)也说明了这一脉络:zcashd 的中继费率从 v1.0.0 时代的 5000 zats/1000 bytes,经 v1.0.3(1000)、v1.0.7-1(100)一路下调,最终在 #4916 引入GetFeeForRelay确保"付了LEGACY_DEFAULT_FEE的交易总能被中继",同时默认费按 ZIP 313 定为 1000 zats。

2.3 关联的 ZIP 317 费用机制(补充理解)

4.2.0-rc1 时代的费用体系中,ZIP 317 提供了"常规费用(conventional fee)"概念(src/zip317.cpp):CalculateConventionalFee = MARGINAL_FEE * max(GRACE_ACTIONS, logicalActionCount),其中MARGINAL_FEE = 5000GRACE_ACTIONS = 2(src/zip317.h)。内存池驱逐逻辑(src/mempool_limit.cpp)中,凡支付的费用低于常规费用的交易会在驱逐权重上附加LOW_FEE_PENALTY = 40000(src/mempool_limit.h),使其更容易被优先驱逐。本次变更同时把"低费惩罚"的判定阈值降到 1000 zatoshis,与 ZIP-313 的默认费保持一致。相关的-txunpaidactionlimit参数用于限制进入内存池的"未付费用动作"数量(src/init.cpp)。

三、getblocktemplate 性能优化:预计算屏蔽 Coinbase 与更快的轮询间隔

3.1 变更动机

当矿工使用**屏蔽矿工地址(shielded miner address)**时,每个区块模板都需要生成一次屏蔽 Coinbase 交易,而屏蔽 Coinbase 的生成(含 Sapling/Orchard 证明相关计算)开销较大。旧实现是在新区块到达后才重新生成模板,导致矿工在新块之上开始工作的时延显著。4.2.0-rc1 做了两处优化:

  1. 预计算下一区块的 Coinbase:在等待期间就把"下一个区块之后那个区块"的屏蔽 Coinbase 先算好并缓存,新区块一到即可秒回模板;
  2. 缩短模板刷新间隔:将"把新内存池交易纳入区块"的等待时间从上游 Bitcoin 继承的 1 分钟缩短为10 秒(原值对 Zcash 75 秒的出块时间并不合适)。

3.2 源码实现细节

实现在 src/rpc/mining.cpp 的getblocktemplate中。等待循环(第 631-661 行)维护了一个checktxtime = now + 10s的绝对截止时间:

if (!cached_next_cb_mtx && IsShieldedMinerAddress(minerAddress)) { cached_next_cb_height = nHeight + 2; cached_next_cb_mtx = CreateCoinbaseTransaction( Params(), CAmount{0}, minerAddress, cached_next_cb_height); next_cb_mtx = cached_next_cb_mtx; } bool timedout = g_best_block_cv.wait_until(lock, checktxtime) == std::cv_status::timeout;

缓存只在高度未变化时复用(第 597-601 行:cached_next_cb_height != nHeight + 2时作废);若等待期间高度发生意外跳变(重组或连续到达多块),Coinbase 会被置空重新生成(第 662-665 行)。当预计算的空块 Coinbase 被使用而内存池里又有待打包交易时,代码会把nTransactionsUpdatedLast重置为 0(第 690 行),强制下一次调用重建包含这些交易的模板,避免交易"卡住"。

生成模板的主体是BlockAssembler(Params()).CreateNewBlock(minerAddress, next_cb_mtx)(第 707 行),模板在pindexPrev != chainActive.Tip()或内存池变化超过 5 秒时重建(第 678-679 行)。矿工因此能够在新区块到达后几乎立即拿到可开始挖矿的模板。

四、RPC 测试框架迁移:rpc-tests.sh → rpc-tests.py

4.1 变更内容

跟随上游 Bitcoin 的做法,Zcash 将 RPC 回归测试的入口从旧的 shell 脚本迁移为统一的 Python 脚本 qa/pull-tester/rpc-tests.py,统一并简化了测试框架。旧脚本(如run-bitcoind-for-test.sh)被移除。

4.2 运行方式与关键参数

rpc-tests.py是一个 Python 3 脚本,通过子进程逐个/并行调用qa/rpc-tests/下的测试用例,未识别的参数会透传给底层测试脚本。核心命令行参数(qa/pull-tester/rpc-tests.py):

参数说明默认值
--jobs, -j并行运行的测试脚本数量4
--exclude, -x以逗号分隔排除指定脚本(不带.py后缀)
--extended除基础测试外再运行扩展测试集关闭
--nozmq不运行 ZMQ 相关测试关闭
--force, -f在默认禁用的平台(如 Windows)上强制运行关闭
--coverage为 RPC 接口生成基础覆盖率报告关闭
--deterministic, -d使输出更接近确定性,便于比较多次运行关闭
--machines, -m/--rpcgroup, -r将测试分片到多台机器(需成对提供分片索引)-1(不分片)
--help, -h打印帮助并退出

脚本通过configparser读取configure生成的tests_config.ini(qa/pull-tester/tests_config.ini.in),据此判断钱包、工具、守护进程、ZMQ 组件是否启用;若三者(wallet/utils/daemon)未同时启用则直接退出。Windows 平台默认跳过测试,需--force

测试列表按运行时长排序,并区分三类集合(qa/pull-tester/rpc-tests.py):

  • SERIAL_SCRIPTS:屏蔽消费密集、会吃满所有 CPU 核的测试(mergetoaddress_sapling.pymergetoaddress_ua_nu5.pymergetoaddress_ua_sapling.pywallet_shieldingcoinbase.py),必须串行;
  • FLAKY_SCRIPTS:已知偶发失败的测试(如mempool_nu_activation.pymempool_packages.py),单独归类便于排查;
  • BASE_SCRIPTS/ZMQ_SCRIPTS/EXTENDED_SCRIPTS:按耗时降序排列,最长者优先启动以最大化并行收益。

指定单个测试时,脚本会从ALL_SCRIPTS中匹配(接受带或不带.py后缀),例如:

# 并行 4 个任务,运行全部基础测试,排除两个已知偶发失败的用例 qa/pull-tester/rpc-tests.py -j 4 -x mempool_nu_activation,mempool_packages

测试框架与拉取测试器的重构(并行运行、--portseed随机端口偏移、每节点独立 RPC 认证对、单一链缓存目录等)在这一版本中一并落地,同时full_test_suite.py(qa/zcash/full_test_suite.py)也开始调用rpc-tests.py作为完整测试套件的一部分。

五、其他值得关注的变更摘要

从 Changelog 中还可以提炼出以下高价值改动,供排查与升级参考:

5.1 依赖与代码现代化

  • std::variant / std::optional 迁移:Jack Grigg 通过 scripted-diff 将boost::variantboost::optional迁移到 C++17 标准库的std::variantstd::optional,并移除了对应 lint 白名单;boost::signals2被替换为std::function(Marco Falke)。
  • 移除 BOOST_FOREACH / PAIRTYPE(Jorge Timón):删除boost/foreach.hpp等旧式宏,引入src/reverse_iterator.h并在全库启用clang-format的 ForEachMacros 清理。
  • 依赖升级:Boost 升级到 1.75(Kris Nuttycombe);macOS 最低支持版本提升到 10.14(fanquake);util: Replace boost::signals2 with std::function
  • 构建系统:configure.ac 引入简化工具依赖声明的宏(Nate Wilcox);Windows 交叉构建产物.lib纳入 gitignore 与 clean 流程(Daira Hopwood)。

5.2 钱包与 RPC

  • sendmany 钱包重构(furszy):为AvailableCoins增加fOnlySpendable过滤标志、nMinDepth深度过滤、按目标地址过滤,并移除SendManyInputUTXO类与多处冗余循环;错误信息细化为 "Insufficient transparent funds"。
  • z_操作默认费用*:z_*系列操作的默认费用同样降至 1000 zatoshis(Daira Hopwood)。
  • RPC 增强getinfo增加更多版本信息(Larry Ruane);getblock的 verbose 输出包含交易十六进制(Josh Ellithorpe);z_importviewingkey错误信息附带地址(Alfredo Garcia)。
  • 挖矿修复:修复空 keypool 下挖矿导致的崩溃(Daniel Kraft);为Consensus::Params增加fPowNoRetargeting字段用于禁用 nBits 重算(Eric Lombrozo),regtest 模式下不重定难度。

5.3 测试与 QA

  • 新增测试p2p-fullblocktest.py(Casey Rodarmor);gettxoutsetinfoCLevelDBBatchCLevelDBIterator测试及 RPC 测试基础覆盖率报告(James O'Beirne);sync_blocks()sync_mempools()增加超时(Suhas Daftuar)。
  • 框架清理:移除未维护的test_script.py;RPC 测试在 Windows 的默认禁用行为保留;测试框架为每个节点使用不同的 RPC 认证对并附加随机端口偏移。

六、升级与验证建议

若你正在评估或使用 4.2.0-rc1,建议按以下顺序验证:

  1. 共识行为:运行src/gtest/test_consensus(ConsensusTests 组)确认 ed25519-zebra 与 ZIP 215 向量全部通过;该测试同时覆盖与旧 libsodium 行为的兼容矩阵,可作为主网/测试网升级前的快速健康检查。
  2. 费用行为:用getinforelayfee字段)与getnetworkinfo检查中继费率;构造支付 1000 zatoshis 的手续费交易,确认其能被正常中继进内存池而不被REJECT_INSUFFICIENTFEE拦截(可对照 src/main.cpp 的判定逻辑)。
  3. 挖矿响应:若使用屏蔽矿工地址,观察新区块到达后getblocktemplate返回新模板的延迟是否明显缩短;代码中 10 秒轮询与 Coinbase 预计算逻辑见 src/rpc/mining.cpp。
  4. 测试框架:在启用 wallet/utils/daemon 的构建下直接运行qa/pull-tester/rpc-tests.py -j 4体验并行回归;需要排除已知偶发失败用例时使用-x

以上所有行为均可直接在仓库对应路径的源码与测试中复现,无需依赖任何外部服务。

【免费下载链接】zcashZcash - Internet Money项目地址: https://gitcode.com/GitHub_Trending/zc/zcash

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询