Zcash 1.1.1-rc2 版本解读:zcash-cli 参数隐私与 scriptSig SIGHASH 类型解码
【免费下载链接】zcashZcash - Internet Money项目地址: https://gitcode.com/GitHub_Trending/zc/zcash
本文基于 Zcash 开源仓库的官方发布说明 release-notes-1.1.1-rc2.md,系统解读该版本的两项核心用户可见变更:zcash-cli -stdin参数输入模式带来的敏感信息隐私保护,以及各交易解析接口输出中scriptSig.asm开始附带 SIGHASH 类型解码的行为变化。读完本文,你将掌握在命令行中安全传递钱包口令/私钥的标准做法,并能准确理解getrawtransaction、decoderawtransaction、REST 接口与zcash-tx输出中[ALL]、[SINGLE]等标注的含义及底层实现原理,同时一览该版本为 Sapling 网络升级铺路的共识层改动。
背景:1.1.1-rc2 的定位
1.1.1-rc2 是 Zcash 主网 Blossom 升级之前的过渡版本,其任务清单集中体现了当时的开发重心:
- 为即将到来的Sapling 网络升级做共识层与序列化准备(v4 交易结构、Sapling 零知识证明验证、Sapling Merkle 树与锚点、ZIP 243 签名哈希);
- 从 Bitcoin Core 上游回移植一系列工程质量改进(序列化重构、
bitcoin-cli/zcash-cli功能增强、RPC 输出改进); - 修复内存管理、构建脚本、macOS 参数下载等细节问题。
对普通节点运营者与钱包用户而言,最直接可感知的是两大变更:zcash-cli -stdin与scriptSig 的 SIGHASH 类型解码。下文逐一展开。
一、zcash-cli 参数隐私:-stdin输入模式
为什么需要它
所有支持 RPC 的命令行客户端都有一个通病:命令行参数会暴露在进程表中,同一系统上的任何用户通常都能通过ps、/proc/<pid>/cmdline等手段读到其他进程的命令行参数。如果你在命令行上直接执行:
src/zcash-cli walletpassphrase mysecretcode 120那么mysecretcode就会以明文形式出现在进程表中,任何人都有机会窥探到你的钱包口令、私钥等敏感信息。
为此,1.1.1-rc2 为 RPC 命令行客户端新增了-stdin参数:从标准输入逐行读取额外参数,直到 EOF(或 Ctrl-D)。官方推荐用这种方式输入私钥、口令等敏感信息。
使用方式
以解锁钱包为例,官方发布说明给出的示例:
$ src/zcash-cli -stdin walletpassphrase mysecretcode 120 ^D (Ctrl-D)执行流程为:先运行src/zcash-cli -stdin walletpassphrase,随后在标准输入中逐行输入参数(第一行为口令mysecretcode,第二行为解锁时长120秒),最后以 Ctrl-D(EOF)结束输入,zcash-cli会将读取到的每一行作为后续参数追加并组装出完整的 RPC 调用。
源码级实现细节
该功能位于 src/bitcoin-cli.cpp 的CommandLineRPC中(第 341-370 行):
if (GetBoolArg("-stdin", false)) { bool hide = false; if (args.size() > 0 && args[0] == "walletpassphrase") { if (!SetStdinEcho(false)) throw std::runtime_error("unable to set terminal to non-echoing"); hide = true; } // Read one arg per line from stdin and append std::string line; while (std::getline(std::cin,line)) { args.push_back(line); if (hide) { if (!SetStdinEcho(true)) throw std::runtime_error("unable to set terminal back to echoing"); hide = false; } } }其中值得注意的细节:
- 逐行追加:通过
std::getline(std::cin, line)循环读取标准输入,每行作为一个独立参数追加到args中,直至 EOF。 - 回显抑制:如果第一个参数恰好是
walletpassphrase,客户端会调用SetStdinEcho(false)关闭终端的输入回显,使口令输入不显示在屏幕上(类 Unix 平台通过tcsetattr修改termios的ECHO标志实现,见 src/bitcoin-cli.cpp),并在读取完第一行后立即恢复回显。这同时防止了口令被屏幕记录和肩窥。 - 帮助文本:命令行帮助中对该参数的描述为"Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases). If first extra argument is
walletpassphrasethen the first line(password) will not be echoed."(见 src/bitcoin-cli.cpp)。 - 非终端场景:当标准输入不是终端(如重定向自文件或管道)时,
SetStdinEcho会因ENOTTY直接返回成功——非终端输入本来就不会回显,因此不会报错,这也意味着-stdin天然支持脚本化调用:
# 从文件或管道喂入参数同样可行 echo -e "mysecretcode\n120" | src/zcash-cli -stdin walletpassphrase该功能上游对应 Bitcoin Core 的 "rpc: Input-from-stdin mode for bitcoin-cli"(Wladimir J. van der Laan)提交,并在本版本由同一作者补充了发布说明。
二、scriptSig 的 SIGHASH 类型解码
变更内容
从 1.1.1-rc2 开始,每个 scriptSig 的asm属性会为其中“携带合法已定义哈希类型”的每一条签名追加解码后的签名哈希类型标注。典型示例(引自发布说明):
变更前:
304502207fa7a6d1e0ee81132a269ad84e68d695483745cde8b541e3bf630749894e342a022100c1f7ab20e13e22fb95281a870f3dcf38d782e53023ee313d741ad0cfbc0c509001变更后:
304502207fa7a6d1e0ee81132a269ad84e68d695483745cde8b541e3bf630749894e342a022100c1f7ab20e13e22fb95281a870f3dcf38d782e53023ee313d741ad0cfbc0c5090[ALL]对比可见:DER 编码签名末尾的 SIGHASH 类型字节01(即SIGHASH_ALL)被解码并替换为可读文本[ALL]。签名主体十六进制不变,只是末尾哈希类型字节的展示形式从裸字节变成了语义标注。
受影响的所有输出接口
发布说明明确列出了所有受影响的位置:
| 接口 | 说明 |
|---|---|
RPCgetrawtransaction | 交易详情中的scriptSig.asm |
RPCdecoderawtransaction | 交易解码中的scriptSig.asm |
REST/rest/tx/(JSON 格式) | 交易 JSON 中的scriptSig.asm |
REST/rest/block/(JSON 格式、含扩展交易详情时) | 区块内交易的scriptSig.asm |
zcash-tx -json | JSON 输出的scriptSig.asm |
不受影响:RPCdecodescript的输出没有变化——因为它被明确配置为只处理scriptPubKey而非scriptSig脚本。
底层实现原理
解码逻辑集中在 src/core_write.cpp 的ScriptToAsmStr(第 73-115 行),其签名带有一个关键布尔参数:
std::string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode)实现要点:
- 按需开启解码:
fAttemptSighashDecode默认值为false(见 src/core_io.h)。只有scriptSig的输出路径才显式传入true(如 src/core_write.cpp 的ScriptToAsmStr(txin.scriptSig, true)、src/rpc/rawtransaction.cpp),而scriptPubKey的输出路径(如 src/core_write.cpp、src/rpc/rawtransaction.cpp)保持默认false——这正解释了decodescript不受影响的原因。 - 签名格式校验:对每个大于 4 字节的 pushdata,先通过
CheckSignatureEncoding校验其是否符合 DER 签名格式,避免把公钥或任意数据误判为签名(源码注释明确指出:Pubkey/Multisig 脚本中的公钥因格式与CheckSignatureEncoding检查不兼容,不会被误解码)。 - 哈希类型查表替换:对通过校验的数据,取出最后一个字节作为 SIGHASH 类型,在
mapSigHashTypes中查表(src/core_write.cpp):
| 字节值 | 解码文本 |
|---|---|
01 | [ALL] |
02 | [NONE] |
03 | [SINGLE] |
81 | [ALL|ANYONECANPAY] |
82 | [NONE|ANYONECANPAY] |
83 | [SINGLE|ANYONECANPAY] |
查表命中后弹出原哈希类型字节,改为追加解码文本,即HexStr(vch) + strSigHashDecode。字节常量定义位于 src/script/interpreter.h:SIGHASH_ALL = 1、SIGHASH_NONE = 2、SIGHASH_SINGLE = 3、SIGHASH_ANYONECANPAY = 0x80。
- 只解码已定义类型:对于
00、80等未在表中定义的字节值(例如空签名00或仅带ANYONECANPAY位而无基本类型的值),不进行解码,直接以裸十六进制展示。
测试佐证
ScriptToAsmStr的 SIGHASH 解码行为在 src/test/script_tests.cpp 有完备的单测覆盖。在fAttemptSighashDecode = true时:
BOOST_CHECK_EQUAL(derSig + "[ALL] " + pubKey, ScriptToAsmStr(CScript() << ToByteVector(ParseHex(derSig + "01")) << vchPubKey, true)); BOOST_CHECK_EQUAL(derSig + "[NONE] " + pubKey, ScriptToAsmStr(CScript() << ToByteVector(ParseHex(derSig + "02")) << vchPubKey, true)); BOOST_CHECK_EQUAL(derSig + "[SINGLE] " + pubKey, ScriptToAsmStr(CScript() << ToByteVector(ParseHex(derSig + "03")) << vchPubKey, true)); BOOST_CHECK_EQUAL(derSig + "[ALL|ANYONECANPAY] " + pubKey, ScriptToAsmStr(CScript() << ToByteVector(ParseHex(derSig + "81")) << vchPubKey, true));而在fAttemptSighashDecode = false(默认)时,同一批数据全部保持裸十六进制输出(derSig + "01 " + pubKey等),验证了该开关仅影响显式开启解码的调用方。此外,tt-delin1-out.json、tt-delout1-out.json等 交易解析测试向量 中已出现大量...[ALL] <pubkey>形态的期望输出,说明该格式已固化进全量测试体系。
实用意义
对开发者与运维人员而言,这一变更让getrawtransaction/decoderawtransaction/ REST 接口的调试输出更具可读性:无需再人工对照 DER 签名末尾字节与 SIGHASH 常量表,即可一眼看出交易输入使用的是哪种签名哈希语义(ALL、NONE、SINGLE,以及是否叠加ANYONECANPAY)。这对分析多签交易、部分签名交易(PSBT 前身方案)以及排查签名验证失败问题都很有帮助。
三、同期关键改动速览(为 Sapling 铺路)
除上述两项用户可见变更外,1.1.1-rc2 的 changelog 还透露出大量服务于 Sapling 升级与工程质量的内部改动,摘录如下:
Sapling 共识与钱包准备
- 交易版本与序列化:从 Sapling 激活高度起要求 v4 交易("Adjust consensus rules to require v4 transactions from Sapling activation"),实现 Sapling v4 交易解析器、
SpendDescription/OutputDescription数据结构,并将 Overwinter 交易在 Sapling 激活高度前过期。 - 签名哈希:实现 ZIP 243 Sapling 签名哈希并更新相应 sighash 测试。
- Merkle 树与锚点:新增 Sapling Merkle 树实现与回归测试向量;将
hashAnchor/DB_ANCHOR/GetAnchorAt/PushAnchor等重命名为 Sprout 专属,并新增GetBestAnchor(SAPLING)、PushSaplingAnchor、Sapling 锚点共识校验、mempool 中过期锚点交易的驱逐等。 - nullifier 跟踪:引入 Sapling nullifier 集合与 nullifier 类型枚举,将旧缓存/映射重命名为 Sprout nullifier,并支持在数据库与 mempool 中记录 Sapling nullifiers;同时确保 Coinbase 交易不得包含 shielded spend/output。
- librustzcash 集成:多次升级 librustzcash(含 sapling-crypto 依赖),接入 Sapling 测试网参数加载、Groth16 证明验证与生成、
merkle_hashAPI 测试等;Rust 工具链升至 1.26 Stable。 - 交易大小上限:从 Sapling 激活起将交易大小上限从 100kB 提高到
MAX_BLOCK_SIZE(对应源码 src/consensus/consensus.h 中的MAX_TX_SIZE_AFTER_SAPLING = MAX_BLOCK_SIZE),并新增最大合法 Sapling 交易的基准测试与大小边界测试。 - 地址/密钥编码重构:引入
CBitcoinAddress包装、{Encode,Decode}PaymentAddress等无CZCEncoding的新实现,并以boost::variant表达 shielded 地址与密钥,为后续统一地址格式(Unified Addresses)演进打基础。
上游工程质量回移植
- 序列化重构(Pieter Wuille 等):
prevector类型、流读写返回void、GetSerializeSize包装为CSizeComputer、去除nType/nVersion、unique_ptr/shared_ptr序列化支持等,为后续代码大幅瘦身。 - RPC 增强:
createrawtransaction支持按输入设置nSequence并可选locktime(Tom Harding);getblock回移植 Bitcoin PR #8704 的可选完整交易详情,并增加 verbosity 级别改进(Simon Liu,关闭 issue #3178)。 - uacomment 配置:按 BIP-0014 实现
uacomment配置参数,可为 user agent 添加注释并限制其总长度。 - 健壮性修复:OOM 时不再抛
std::bad_alloc而是立即终止并尝试记录日志;prevector断言成功分配;base58SetString清洗完整向量;修复memcpy传 NULL 指针的未定义行为;统一使用EXIT_*退出码常量。 - 构建与脚本:默认禁用
libzcashconsensus构建;移除废弃的CreateJoinSplit/GenerateParams二进制;macOS 下 fetch-params 改用 curl 与shlock;BDB 备份下载改为 https。
结语
Zcash 1.1.1-rc2 是一个典型的“承上启下”版本:zcash-cli -stdin与 scriptSig SIGHASH 类型解码为使用者提供了更安全的交互方式与更可读的交易解析输出,二者在 src/bitcoin-cli.cpp 与 src/core_write.cpp 中都有清晰的实现与单测支撑;而隐藏在 changelog 中的大量 Sapling 共识改动,则直接铺平了后续 v4 交易与 Sapling 激活的道路。对于需要自行审计或二次开发 Zcash 的工程师,建议以本版本为基线,结合 src/test/script_tests.cpp 与 src/test/data/tt-delin1-out.json 等测试资产验证你对接 RPC 输出格式的解析代码。
【免费下载链接】zcashZcash - Internet Money项目地址: https://gitcode.com/GitHub_Trending/zc/zcash
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考