
Zcash 2.0.4 版本深度解读Sprout 余额校验修复、矿工地址选择与 Testnet 旋转门共识规则【免费下载链接】zcashZcash - Internet Money项目地址: https://gitcode.com/GitHub_Trending/zc/zcashZcash 2.0.4release-notes-2.0.4是一次以正确性为核心的维护版本聚焦三大技术主题修复钱包对 Sprout z-address 余额的校验缺陷涉及 note commitment 与密文值不一致的攻击面、修正从上游 Bitcoin Core 继承的矿工固定地址选择行为-mineraddress选项、以及在 Testnet 启用 ZIP 209 旋转门turnstile共识规则。本文基于 release-notes-2.0.4.md 展开并结合 src/wallet/wallet.cpp、src/miner.cpp、src/main.cpp 等当前仓库源码逐一还原每个修复的底层实现与实操影响帮助读者理解升级后如何正确重扫链上数据、如何配置矿工出块地址、以及旋转门共识规则如何保障屏蔽池价值守恒。一、版本概况与升级要点Zcash 2.0.4 属于 Sapling 激活后的维护版本其发布背景与核心技术动作如下钱包正确性修复修复 Sprout note 校验缺陷升级后需执行zcashd -rescan重扫区块链以修正 z-address 余额显示矿工行为修复合并上游 Bitcoin Core 0.12 的修复内部矿工与getblocktemplateRPC 不再长期复用固定透明地址每出一个块默认选择新地址同时新增-mineraddress选项供矿工固定出块地址Testnet 共识规则新增Testnet 节点强制拒绝会导致 Sprout 或 Sapling 屏蔽价值池发生旋转门违规turnstile violation的区块该规则后续将在主网启用依赖与工具链升级OpenSSL 1.1.0h→1.1.1a、Boost v1.66.0→v1.69.0、Rust v1.28.0→v1.32.0、Proton 0.17.0→0.26.0链参数更新基于区块 497000 更新nMinimumChainWork并新增对应检查点checkpoint。对运行 zcashd 的节点运营者而言本版本的升级动作集中在两点重扫修正历史 Sprout 余额与确认矿工地址策略使用固定地址则显式配置-mineraddress。以下各节逐一展开。二、Sprout note 校验缺陷修复钱包余额为何会出错2.1 缺陷本质note commitment 与密文值不一致Zcash 的 Sprout 屏蔽交易中每个输出 note 由两部分关键数据组成note commitmentcommitments[n]链上公开的、对 note 内容的承诺值是交易被网络认可的依据密文ciphertext发送方用接收方密钥加密的消息包含 note 明文金额、随机数rho、r等。正常情况下发送方会保证两者一致。但攻击者可以故意构造一笔交易使note commitment 中编码的金额与密文中实际提供加密的金额不一致。此时网络验证的是 commitment而接收方钱包解密得到的是密文里的金额两者一旦分叉钱包就会把密文金额当作真实余额累加从而显示一个不正确通常被夸大的 Sprout z-address 余额。2.2 修复位置wallet.cpp 中的一致性校验release notes 中 Sean Bowe 的提交 (wallet) Check that the commitment matches the note plaintext provided by the sender 正是针对此缺陷。在 src/wallet/wallet.cpp 的CWallet::GetSproutNoteNullifier中可以看到该校验的落地实现auto note_pt libzcash::SproutNotePlaintext::decrypt( dec, jsdesc.ciphertexts[n], jsdesc.ephemeralKey, hSig, (unsigned char) n); auto note note_pt.note(address); // Check note plaintext against note commitment if (note.cm() ! jsdesc.commitments[n]) { throw libzcash::note_decryption_failed(); }其逻辑是解密得到 Sprout note 明文后用明文重建出 notenote_pt.note(address)重新计算其 commitmentnote.cm()与链上 JoinSplit 描述jsdesc中的commitments[n]比对不一致即抛出note_decryption_failed该 note 不会被计入钱包余额。从当前源码的 src/zcash/Note.hpp 可以看到SproutNote的cm()由a_pk、金额、rho、r派生因此只要密文中的任意字段被篡改重算的 commitment 必然与链上承诺值不符从而被拦截。2.3 Sapling 不受影响的原因release notes 明确说明Sapling z-address 不受此缺陷影响。从当前仓库的 src/wallet/wallet.cppDecryptSaplingNote实现可以推断Sapling note 的明文解密流程通过 Rust 侧的try_sapling_note_decryption与 Sprout 的SproutNotePlaintext::decrypt走的是不同代码路径且 Sapling 协议层面对 note 明文与 commitment 的绑定约束更严格因此该缺陷仅在 Sprout 协议路径上出现。2.4 升级后必须执行的操作zcashd -rescan由于缺陷影响的是历史已入账 note 的余额统计仅升级二进制文件并不会自动修正既有的错误余额。release notes 给出的官方操作是zcashd -rescan-rescan会指示 zcashd 钱包重新扫描整条链上的交易对每个属于本钱包的 Sprout note 重新执行解密 → 重算 commitment → 比对链上承诺的校验剔除被篡改 note 后再重新统计余额。执行完成后Sprout z-address 显示的余额才会正确。作为配套措施release notes 同时致谢 Alexis Enston 报告了该问题相关修复见 Security Announcement 2019-03-19 与 Pull request原文外部链接读者可自行查阅。三、矿工地址选择行为修复与 -mineraddress 配置3.1 上游继承缺陷固定透明地址问题Zcash 从上游 Bitcoin Core 继承了一个缺陷内部矿工与getblocktemplateRPC 会一直使用同一个固定透明地址作为出块收款地址直到用户调用getnewaddressRPC 为止。也就是说在节点启动到用户第一次调用getnewaddress之间挖出的所有块奖励都落入同一个可能并非用户本意的地址而正确行为应当是每个新块都从钱包密钥池中选取一个新的透明地址。该问题在 Bitcoin 0.12 已修复Zcash 2.0.4 合并了这一改动。release notes 中 Jonas Schnelli 的相关提交揭示了修复的技术层次detach wallet from miner将矿工与钱包解耦矿工不再隐式依赖钱包内部状态fix GetScriptForMining() CReserveKey::keepKey() issue修复挖矿脚本生成时保留密钥的缺陷add CReserveScript to allow modular script keeping/returning引入CReserveScript抽象使预留/归还地址脚本可模块化。3.2 当前源码中的实现印证在 src/miner.cpp 中可以看到该机制在 Zcash 中的落地形态class MinerAddressScript : public CReserveScript { // CReserveScript requires implementing this function, so that if an // internal (not-visible) wallet address is used, the wallet can mark it as // important when a block is mined (so it then appears to the user). // If -mineraddress is set, the user already knows about and is managing the // address, so we dont need to do anything here. void KeepScript() {} };MinerAddressScript继承自CReserveScript其KeepScript()为空实现——若用户显式配置了-mineraddress说明地址由用户自行管理无需钱包再标记反之矿工每轮从钱包密钥池预留新地址出块后通过该回调使地址在钱包中可见。随后 src/miner.cpp 的GetMinerAddress读取-mineraddress参数并解析为MinerAddress作为出块收款地址。3.3 -mineraddress 参数说明与使用对于希望每个块都使用同一地址收款的矿工release notes 给出的方案是显式指定-mineraddress。从当前仓库 src/init.cpp 的帮助文本可见其定义-mineraddressaddr Send mined coins to a specific single address该参数支持透明 P2PKH 地址t1.../t2...、Sapling 地址zs1...以及包含合法接收者的统一地址UA从 src/init.cpp 的解析逻辑看无法解析为合法 Zcash 地址时会直接报错退出。实测命令行用法示例# 使用固定透明地址出块 zcashd -mineraddresst1T8yaLVhNqxA5KJcmiqqFN88e8DNp2PBfF # 使用 Sapling 地址出块需配合屏蔽挖矿相关设置 zcashd -mineraddresszs1z7rejlpsa98s2rrrfkwmaxu53e4ue0ulcrw0h4x5g8jl04tak0d3mm47vdtahatqrlkngh9slyasrc/gtest/test_miner.cpp 中专门为-mineraddress编写了参数解析测试覆盖非法地址、前后空白、透明/Sapling 地址等场景验证了解析与校验路径的健壮性。四、Testnet 新共识规则ZIP 209 旋转门Turnstile约束4.1 什么是旋转门违规turnstile violationZcash 的屏蔽池Sprout、Sapling 等设计了一条价值守恒铁律某一时刻屏蔽池中的总价值 历史进入池中的资金 − 历史流出池中的资金。这个约束被称为旋转门turnstile——如同单向闸机资金不能凭空多出来。若某个区块导致某屏蔽池的累计余额超出合法范围例如金额溢出、为负或超过货币供应总量则该区块构成旋转门违规在无约束的网络中可能成为铸造假币或价值失真攻击的起点。4.2 ZIP 209 的启用策略release notes 说明Zcash 2.0.4 在Testnet上强制实施该共识规则标记导致 Sprout 或 Sapling 池旋转门违规的区块为无效主网将在未来版本启用当前仓库的ZIP209Enabled()开关即为该部署策略的体现。从 src/chainparams.cpp 可看到 Testnet 的配置nSproutValuePoolCheckpointHeight 440329; nSproutValuePoolCheckpointBalance 40000029096803; fZIP209Enabled true; hashSproutValuePoolCheckpointBlock uint256S(000a95d08ba5dcbabe881fc6471d11807bcca7df5f1795c99f3ec4580db4279b);而 src/chainparams.cpp 则展示了主网Mainnet侧的准备数据fZIP209Enabled true与硬编码的 Sprout 池检查点高度 520633、余额 22145062442933表明主网同样具备完整的旋转门审计基础设施只是按升级计划分阶段开放。值得注意的细节release notes 中 Sean Bowe 的提交说明regtest 模式当时未启用 ZIP209而 src/chainparams.cpp 提供了SetRegTestZIP209Enabled()供开发者调试旋转门违规时显式开启。4.3 区块验证端的落地实现在区块连接验证路径 src/main.cpp 中旋转门检查以chainparams.ZIP209Enabled()为总开关if (chainparams.ZIP209Enabled()) { // Sprout if (!MoneyRange(pindex-nChainSproutValue.value())) { return state.DoS(100, error(%s: turnstile violation in Sprout shielded value pool at height %d (sprout%d, sapling%d, orchard%d, lockbox%d), __func__, ...), REJECT_INVALID, turnstile-violation-sprout-shielded-pool); } // SaplingnChainSaplingValue与 OrchardnChainOrchardValue同理 }即区块所代表的各屏蔽池累计余额必须落在MoneyRange的合法区间内否则该区块被判REJECT_INVALID并携带明确的拒绝原因字符串turnstile-violation-sprout-shielded-pool/-sapling-shielded-pool等。同时 src/main.cpp 处理了一个重要的迁移场景旧版本客户端早于 1.0.14写入的区块索引可能缺少 Sprout 池价值字段nChainSproutValue std::nullopt此时节点会中止并提示用户以-reindex重启从磁盘重新计算每块 Sprout 价值增量后再继续验证。4.4 配套机制硬编码检查点回退与矿工侧规避回退检查点为防止重组reorg导致历史池余额回溯src/main.cpp 的FallbackSproutValuePoolBalance在检查点高度将池余额硬编码回退到链参数中记录的权威值并校验块哈希确保旋转门审计从可信起点开始矿工侧规避release notes 中 (testnet/regtest) Avoid mining transactions that would violate the turnstile 表明矿工在组装区块时也会检查候选交易是否会导致池价值越界从源头避免挖出将被共识规则拒绝的区块。五、其他值得关注的变化依赖链升级OpenSSL 1.1.0h→1.1.1a、Boost v1.66.0→v1.69.0、Rust v1.28.0→v1.32.0、Proton 0.17.0→0.26.0含针对最小化构建的补丁及其回归修复见 Cargo.toml 与 rust-toolchain.toml 可继续追踪当前依赖版本RPC 增强Jonathan Duke Leto 将size_on_disk回移植到getblockchaininfoRPC便于运营者直接查询区块链目录占用并配套了对应测试链参数与检查点基于区块 497000 更新nMinimumChainWork并新增检查点release notes 中 Simon Liu 的提交强化对最重链nChainWork下限的约束文档与安全补充了 DNS rebinding 攻击相关文档警告issue #3841以及针对 issue #3869 的负责任披露声明SECURITY.md 可查阅当前项目的安全报告流程。六、升级与验证建议实操清单升级前备份钱包升级属于二进制替换钱包数据目录需保持完好切勿在未备份时执行任何重扫操作执行重扫以zcashd -rescan启动等待区块重扫完成确认 Sprout z-address 余额已修正若节点曾由早于 1.0.14 的版本升级而来且旋转门验证报错则需改用-reindex核对矿工地址若运行挖矿节点且希望固定出块地址在启动参数中显式加入-mineraddressaddr透明 P2PKH / Sapling / 含合法接收者的 UA否则默认行为将是每个新块使用新的透明地址Testnet 节点须知确认已运行 2.0.4 以同步最新 Testnet 共识规则主网节点暂不受旋转门共识强制执行影响但后续版本将启用应保持升级节奏依赖环境本版本工具链升级Rust 1.32、OpenSSL 1.1.1a 等主要影响源码构建者预编译二进制用户无需额外操作从源码构建请参考 INSTALL 与 doc/developer-notes.md。参考文件索引均在当前仓库内可继续深挖版本说明doc/release-notes/release-notes-2.0.4.mdSprout 余额校验修复src/wallet/wallet.cpp、src/zcash/Note.hpp矿工地址选择与-mineraddresssrc/miner.cpp、src/init.cpp、src/gtest/test_miner.cppZIP 209 旋转门共识src/main.cpp、src/chainparams.cppRPC 变更getblockchaininfo的size_on_disk字段见 src/rpc/blockchain.cpp【免费下载链接】zcashZcash - Internet Money项目地址: https://gitcode.com/GitHub_Trending/zc/zcash创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考