深入解读 Zcash z9 里程碑:Equihash 方案压缩、zkSNARK 优化与钱包密钥体系重构
2026/9/18 13:00:06 网站建设 项目流程

深入解读 Zcash z9 里程碑:Equihash 方案压缩、zkSNARK 优化与钱包密钥体系重构

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

Zcash 0.11.2.z9 是正式发布前的关键测试版本(testnet 里程碑),该版本以 Equihash PoW 算法优化、zkSNARK 验证性能提升和 Sprout 钱包密钥管理重构为主线,同时引入了多项内存安全修复与 DoS 防护。本文基于 release-notes-0.11.2.z9.md 展开,结合当前仓库源码深入剖析每个变更背后的实现细节,帮助你理解 Zcash 早期协议演进的关键节点。

注:文中引用的源码来自当前仓库的最新状态,部分模块(如 Sapling/Orchard)在 z9 之后经历了大幅演进,本文在引用时已标注当前源码中对应实现的位置,用于佐证历史变更的设计意图与后续演变。

1. 版本定位与变更总览

z9(0.11.2.z9)是 Zcash 主网上线前的重要测试里程碑,由 5 位核心开发者主导,共包含34 项提交,覆盖以下四大技术方向:

  • Equihash 共识算法优化:压缩解表示、哈希生成与 Zcash 规范对齐、参数一致性静态检查;
  • zkSNARK 性能与依赖升级:实现 zkSNARK 压缩、bigint 算术迁移到 libsnark、升级 libsodium 修复 AVX2 检测 bug;
  • 钱包密钥基础设施重构:为基本密钥库(CBasicKeyStore)添加 SpendingKey 支持,并简化 API;
  • 稳健性与安全修复:引入 NCC 审计发现的内存安全修复、上游 DoS 缓解措施、减少区块头请求数量等。

这一版本的变更不仅为 Zcash 1.0 正式版铺平道路,其底层设计理念(如 Equihash 参数的静态校验、密钥存储的最小化原则)至今仍体现在当前代码库中。

2. Equihash:从算法到实现的全面优化

2.1 Equihash 算法与 Zcash 参数

Equihash 是一种基于广义生日悖论的内存硬性 PoW 算法,其核心思想是:在2^n个哈希索引中寻找2^k个具有特定碰撞属性的索引组合,使得解只能通过大量内存存储和排序来高效求解。Zcash 采用的参数为n=200, k=9(主网、测试网)以及n=96, k=5(回归测试)。

当前源码 src/crypto/equihash.h 中保留了完整的参数实例:

static Equihash<96,3> Eh96_3; static Equihash<200,9> Eh200_9; static Equihash<96,5> Eh96_5; static Equihash<48,5> Eh48_5;

同时通过static_assert在编译期强制约束参数合法性(src/crypto/equihash.cpp 中的static_assert(sizeof(eh_index) == 4)等)。

2.2 将 Equihash 解以最小表示存储在区块头中

z9 中最重要的共识变更之一是:将 Equihash 解从"索引数组"压缩为"最小位级表示"存储在区块头中。其背景是,原始实现将解存储为eh_index(uint32)数组,而 Equihash 的解本质上是索引的集合,存在大量冗余位。

z9 的解决方案是引入CompressArray/ExpandArray字节级位压缩工具,将索引序列按位打包。当前源码 src/crypto/equihash.h 中保留了这一设计:

void CompressArray(const unsigned char* in, size_t in_len, unsigned char* out, size_t out_len, size_t bit_len, size_t byte_pad=0); void ExpandArray(const unsigned char* in, size_t in_len, unsigned char* out, size_t out_len, size_t bit_len, size_t byte_pad=0);

并配套GetMinimalFromIndices/GetIndicesFromMinimal完成解索引与最小表示的双向转换:

std::vector<unsigned char> GetMinimalFromIndices(const std::vector<eh_index>& indices, size_t cBitLen); std::vector<eh_index> GetIndicesFromMinimal(std::vector<unsigned char> minimal, size_t cBitLen);

解的压缩尺寸可由以下常量公式精确计算(src/crypto/equihash.h):

inline constexpr size_t equihash_solution_size(unsigned int N, unsigned int K) { return (1 << K)*(N/(K+1)+1)/8; } enum : size_t { SolutionWidth=(1 << K)*(CollisionBitLength+1)/8 };

以 n=200, k=9 为例:SolutionWidth = 512 * (200/10 + 1) / 8 = 512 * 21 / 8 = 1344字节。这一"最小表示"设计直接影响到区块头大小,进而影响到对等网络中的区块头消息体量——这正是第 2.4 节 MAX_HEADERS_RESULTS 调整的关联动机。

2.3 哈希生成与 Zcash 规范对齐

z9 的另一项关键修正是Equihash 哈希生成逻辑与 Zcash 官方规范对齐。此前 Equihash 的哈希输入排列与规范存在偏差,可能导致与其他实现不兼容;z9 中按规范重新实现了哈希生成流程,并使用 BLAKE2b 作为底层哈希函数(当前 src/crypto/equihash.h 中通过rust::Box<blake2b::State>封装了 Rust 实现的 BLAKE2b 状态机):

struct eh_HashState { rust::Box<blake2b::State> inner; eh_HashState(size_t length, unsigned char personalization[blake2b::PERSONALBYTES]); void Update(const unsigned char *input, size_t inputLen); void Finalize(unsigned char *hash, size_t hLen); };

同时,z9 引入了对 Equihash 参数一致性、MAX_HEADERS_RESULTSMAX_PROTOCOL_MESSAGE_LENGTH之间关系的静态检查,防止非法参数组合导致的缓冲区溢出。

2.4 网络协议层联动:MAX_HEADERS_RESULTS 调低至 160

由于 Equihash 解采用最小表示后区块头体积变化,z9 将单次 getheaders 响应中的最大区块头数量从 2000 调低至160(修复 issue #1289)。当前源码 src/main.h 保留了该值及相应的静态一致性检查:

/** Number of headers sent in one getheaders result. ... Changing this value is a protocol upgrade. */ static const unsigned int MAX_HEADERS_RESULTS = 160; #define equihash_parameters_acceptable(N, K) \ ((CBlockHeader::HEADER_SIZE + equihash_solution_size(N, K))*MAX_HEADERS_RESULTS < \ MAX_PROTOCOL_MESSAGE_LENGTH-1000)

该宏确保了"区块头大小 × 响应数量"不会突破协议消息长度上限,属于典型的资源约束型 DoS 防护。在 src/main.cpp 中,MAX_HEADERS_RESULTS被用于 getheaders 响应截断与分页判断逻辑,例如:

  • int nLimit = MAX_HEADERS_RESULTS;
  • if (nCount > MAX_HEADERS_RESULTS) { ... }
  • if (nCount == MAX_HEADERS_RESULTS && pindexLast && !hasNewHeaders) { ... }

z9 将该值大幅调低,意在限制单个消息的内存占用,同时依靠分页机制(后续消息继续请求)保证区块头同步的完整性。

3. zkSNARK 压缩与 libsnark 升级

Zcash 的隐私交易依赖zkSNARK(零知识简洁非交互式证明)来验证 JoinSplit 操作的正确性。z9 的两项核心改进:

  • 实现 zkSNARK 压缩:压缩证明数据,减小交易体积,降低网络传输与链上存储开销;
  • 将 bigint 算术实现迁移到 libsnark:统一大整数运算的实现来源,避免重复维护,并同步更新 libsnark 依赖

这两项变更直接触发了proving/verifying keys 的更新——证明密钥与验证密钥随电路实现变化而必须同步更新,否则新旧节点无法验证彼此生成的证明。这一"密钥与电路版本强绑定"的原则至今仍是 Zcash 协议升级的核心约束:当前源码 src/rust/src/orchard_ffi.rs 中关于 Orchard 电路变更导致验证密钥变化的注释,正是同一设计理念的延续:

"NU6.2 changed the Orchard circuit (and thus the verifying key), so a batch is typed to a specific circuit..."

3.1 MONTGOMERY_OUTPUT 全局启用

z9 的另一项底层优化是启用 MONTGOMERY_OUTPUT 宏。Montgomery 表示是椭圆曲线/有限域运算中的一种高效表示法,将模乘转化为更廉价的移位与加法操作。全局启用 MONTGOMERY_OUTPUT 意味着:

  • 证明与验证计算中的域元素以 Montgomery 形式输出与交互;
  • 减少运算过程中的表示转换开销,提升整体证明/验证吞吐。

结合"执行曲线参数初始化"(在 gtest 测试套件启动时预初始化曲线参数)的提交,z9 在测试基础设施层面也为曲线运算的稳定性提供了保障。

3.2 依赖升级:libsodium AVX2 检测修复

Taylor Hornby 的提交将libsodium 升级,以修复其AVX2 指令集检测 bug。该 bug 会导致在不支持 AVX2 的 CPU 上错误地启用 AVX2 代码路径,进而产生非法指令崩溃(SIGILL)。此外还引入了配套的编译期优化策略:

  • 统一优化标志,单一事实来源:此前不同构建目标使用不一致的优化标志,z9 将其收敛为单一来源,避免标志漂移;
  • 新增-fwrapv-fno-strict-aliasing-fwrapv保证有符号整数溢出行为可预期(回绕而非 UB),-fno-strict-aliasing关闭严格别名规则,降低因编译器激进优化引入的安全回归风险;
  • 使用 libsodium 的s < L检查:不再自行重复检查标量是否小于曲线阶 L,而是信任 libsodium 内部已经完成该检查,消除重复验证逻辑可能引入的不一致。

3.3 覆盖率构建的硬化策略

为支持覆盖率测试(coverage build),z9 在覆盖率构建中禁用了安全加固(hardening)选项(如栈保护、PIE 等),原因在于加固特性会干扰插桩与覆盖率数据的采集。同时将 gtest 覆盖率产物与中间文件纳入make clean的清理范围,并从覆盖率报告中剔除非 libsnark 依赖与测试框架代码,使覆盖率数据更聚焦于核心业务逻辑。相关测试基础设施位于 src/gtest 目录(如 src/gtest/main.cpp),当前仓库的 src/gtest/test_equihash.cpp 依然承载着 Equihash 解的编解码与合法性测试。

4. 钱包密钥基础设施:SpendingKey 与密钥库重构

4.1 为基本密钥库添加 SpendingKey 支持

z9 为CBasicKeyStore引入了SpendingKey(花费密钥)支持,这是 Zcash 钱包可以持有和花费屏蔽资金的基础。当前 src/keystore.h 中CBasicKeyStore已演进为包含完整的花费密钥族:

class CBasicKeyStore : public CKeyStore { protected: SproutSpendingKeyMap mapSproutSpendingKeys; SproutViewingKeyMap mapSproutViewingKeys; NoteDecryptorMap mapNoteDecryptors; SaplingSpendingKeyMap mapSaplingSpendingKeys; SaplingFullViewingKeyMap mapSaplingFullViewingKeys; ... };

z9 中引入的是早期的AddSproutSpendingKey/HaveSproutSpendingKey/GetSproutSpendingKey接口,用于管理 Sprout 屏蔽池的花费密钥,并通过 src/keystore.cpp 中的CKeyStore派生类(CBasicKeyStore/CCryptoKeyStore)实现。CCryptoKeyStore 在此基础上对花费密钥进行加密存储(src/keystore.h 中CryptedSproutSpendingKeyMapCryptedSaplingSpendingKeyMap),这正是发布说明中提到的"zkey 与加密钱包"文档补充的背景。

4.2 API 简化:合并 AddSpendingKeyPaymentAddress

z9 将AddSpendingKeyPaymentAddress合并进AddSpendingKey,统一了密钥添加的入口。这一简化在后续版本中得到继承——当前 src/wallet/wallet.cpp 中AddSpendingKeyToWallet通过std::visit对 Sprout / Sapling 等不同类型的花费密钥进行统一分发处理:

KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SproutSpendingKey &sk) const; KeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SaplingExtendedSpendingKey &sk) const;

该设计使得上层 RPC(如 src/wallet/rpcdump.cpp 中的导入逻辑)无需关心具体密钥类型,只需调用统一入口。

4.3 独立的 SpendingKey 密钥库锁

z9 为 SpendingKey 密钥库操作引入了独立锁(separate lock)。在早期实现中,密钥库操作共用同一把锁,导致花费密钥的读写与普通密钥操作相互阻塞;引入独立锁后:

  • 花费密钥的查询与更新可与普通密钥操作并行;
  • 减少锁竞争,提升钱包在多线程场景下的响应性;
  • 为后续加密密钥库(CCryptoKeyStore)的并发安全奠定了基础。

5. 稳定性、安全性与审计修复

5.1 NCC 审计发现的内存安全与正确性修复

Robert C. Seacord 提交了NCC 安全审计中发现的内存安全与正确性修复。这类修复通常涉及:

  • 缓冲区边界检查(数组索引与长度计算的溢出防护);
  • 整数溢出与符号问题的处理;
  • 未初始化内存、悬垂指针等 C/C++ 常见内存错误。

结合 z9 同期引入的-fwrapv-fno-strict-aliasing编译选项,可见该版本对编译期防御与运行期正确性的双重重视。

5.2 上游 DoS 缓解措施

Patrick Strateman 从上游 Bitcoin Core 拉取了DoS 缓解措施(PR #1258)。这类缓解通常包括:

  • 限制单条消息可触发的资源消耗(如区块头请求数量);
  • 对异常节点行为的惩罚机制(ban score);
  • 连接与消息频率限制。

z9 同时配套调低了MAX_HEADERS_RESULTS(见 2.4 节),两者协同降低了内存膨胀型攻击的风险。

5.3 网络层修复与 RPC 消息优化

  • nMinPingUsecTime 初始化修复(Wladimir J. van der Laan):修正节点对等方最小 ping 延迟未正确初始化的缺陷,避免统计信息异常;
  • zcash-cli stop 消息更新(Gaurav Rana):改善停止节点的用户提示文案;
  • 注释澄清(Tom Ritter):在 src/zcash/NoteEncryption 相关代码中澄清 Note Encryption 的 nonce 空间约束,防止后续开发者误用 nonce 导致重放攻击。

6. 验证基准与文档

Simon Liu 的三项提交补充了工程化细节:

  1. 修复 #1193:在验证基准测试中不再无谓创建数千个 CTransaction 对象,消除基准测试的内存与 CPU 浪费,使基准数据更真实;
  2. 关闭 #701:补充Payment RPC 接口文档,该文档现演化为 doc/payment-api.md;
  3. 补充 zkey 与加密钱包的说明:明确 zkey(花费密钥导出/导入)与加密钱包的关系,相关实践在 doc/book/src 用户指南中有更完整的叙述。

7. 版本号与发布配套

z9 提交了版本号递增,并为本版本编写了发布说明。Zcash 使用z<数字>后缀标识主网上线前的测试里程碑版本,z9 之后是 z10、z11 等,最终通向 1.0 正式版。此类发布说明的完整清单见 doc/release-notes,后续版本的说明(如 release-notes-1.0.0.md)记录了 z9 各项优化在正式版中的落地结果。

8. 结语:z9 的技术遗产

z9 虽然只是一个过渡性测试版本,但其技术决策深刻影响了 Zcash 后续架构:

  • Equihash 解的最小表示奠定了区块头定长与 P2P 消息体积控制的基础,其equihash_solution_size公式与equihash_parameters_acceptable一致性检查至今仍在使用;
  • zkSNARK 压缩与 libsnark 统一确立了"电路变更 ⇒ 密钥更新"的升级纪律;
  • SpendingKey 密钥库重构直接演化为当前 Sprout/Sapling/Orchard 多池密钥管理体系与加密密钥库;
  • 编译选项与内存安全修复为后续依赖升级与审计提供了工程样板。

对于希望深入理解 Zcash 共识层与钱包内核演进的开发者,z9 是一个值得回望的里程碑:它同时展现了共识优化、密码学工程与安全加固如何在同一个版本中协同落地。

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

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

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

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

立即咨询