自己验证“交易已在链上”:默克尔证明的构造与核验步骤 图 1
自己验证“交易已在链上”:默克尔证明的构造与核验步骤 · 图 1

轻客户端的核心问题:怎么证明”在里面”

不跑全节点的人要确认一笔交易是否被记账,绕不开一个信任缺口:服务器说你收到了,你怎么知道它没骗你?比特币的答案是用区块里的默克尔树给出可验证的包含证明:交易经过逐层哈希被承诺进区块头的默克尔根,只要提供一条从交易到根的路径,任何人重算哈希就能确认这棵树的承诺成立。证明本身只有几百字节,远小于整个区块。全节点把这把钥匙做成了两个 RPC:gettxoutproof 构造证明,verifytxoutproof 校验证明。理解这一对命令,就理解了 SPV 信任模型的最小内核。

自己验证“交易已在链上”:默克尔证明的构造与核验步骤 图 2
自己验证“交易已在链上”:默克尔证明的构造与核验步骤 · 图 2

构造与校验的完整回路

回路的第一步,在持有该交易的节点上执行 gettxoutproof 并传入交易编号,节点在对应区块的默克尔树里提取兄弟哈希链,打包回一个十六进制字符串;若节点没建交易索引,需要同时指定区块哈希。第二步,把这段证明交给任何一台同步完成的节点做 verifytxoutproof,它重算整棵树的哈希路径,对照区块头里的默克尔根,通过后返回所承诺的区块哈希——注意,校验过程不需要重放区块里的任何交易数据,快且轻。回路到这一步只回答了一半问题,剩下的一半在下面。

证明不包含”这条链是正统链”

包含证明的逻辑是”该交易被承诺在某个区块头里”,而区块头本身对不对,取决于它处在谁的链上。因此严谨的核验必须补两个动作:其一,确认返回的区块哈希位于累计工作量最大的链上(getblock 查确认数、比对当前链尖即可间接确认);其二,如果关心金额与脚本,还要取回原始交易本身核对内容——证明只承诺”这条哈希在树里”,不承诺”内容如你所想”。SPV 轻钱包把这条链路的自动化版本做进了协议:下载全部区块头、向对端索取匹配区块的默克尔分支。手动跑 RPC 的价值在于你不必信任任何服务器界面,只要有一台可信节点。

剪枝与索引带来的边界

剪枝节点会丢弃旧区块的完整数据,但区块头始终保留,所以对旧交易能否构造证明取决于节点是否还存有那个区块;没有开启交易索引的节点,则必须显式告知区块哈希,或者先用交易所在区块的信息补齐。运维上常见的误区是把”浏览器显示已确认”当作终态——浏览器同样是查询者之一,它依赖的节点同样受上述条件约束。把默克尔证明当成可交付的审计证据时,连同区块哈希、确认数与校验它的节点版本一起归档,才构成一个自足的证据包。

把证明链讲完整:还差哪些承诺

默克尔包含证明回答的是”交易被承诺进某个区块头”,但一条完整的信任链还有三环:区块头自身的有效性由该链累计的工作量背书,累计工作量是对每个区块由难度目标换算出的工作量逐项求和,而非区块数量或所谓”链上时间”——“最长链”更准确的说法应是”最重链”;区块头与你比较的当前链尖必须来自同一份节点状态,跨两个节点分别取数会拼出一条不存在的链;证明路径挂在区块默克尔树的叶子上,隔离见证之后这条树的叶子取的是对交易全部序列化数据求哈希得到的编号,任何字节的改动都会让路径对不上。审计场景的标准动作是在同一台节点上一次走完闭环:先取当前链尖高度与累计工作量,再校验证明、取区块头、确认确认数,最后单独取回交易原文核对金额与脚本。把五步写进同一个核对脚本、绑定同一台节点,输出的结论才是自洽的。

风险提示:本文为技术核验方法说明,不构成投资建议;链上状态请以你自己的节点复核为准。