给树哈希加速:EIP-7797 想让 hash_tree_root 跑双倍速 图 1
给树哈希加速:EIP-7797 想让 hash_tree_root 跑双倍速 · 图 1

以太坊共识层的每个区块、每份状态都要算 SSZ 哈希——所谓 hash_tree_root:把结构体切成 32 字节的叶子,两两拼接后做 SHA-256 得到父节点,递归到只剩一个根。验证者每秒都在成批地给区块出证明,证明要签在信标状态根上,于是树哈希成了共识层代码里最热的循环。EIP-7797(2024 年 10 月创建,目前 Stagnant)提出了一个外科手术式的提速:树哈希里每个待哈希的输入块经填充后消息长度永远是 512 位——把这个先验知识喂给哈希函数,它就不必再执行完整填充流程,性能大约翻倍。

瓶颈藏在前置处理里

通用 SHA-256 的规矩:收到任意长度的消息后,追加一个比特 1,再补若干个 0 凑到 512 位的整数倍,末尾附上 64 位的大端消息长度,然后一块块进压缩函数。hash_tree_root 的输入恰好是 32 字节;在 SSZ 树结构里每个节点哈希的两个孩子拼起来是 64 字节——即 512 位。512 位消息进通用流程:补 1 个 1、补 447 个 0、写 64 位长度字段,正好凑成 1024 位、两个 512 位分组,于是每次节点哈希都要跑两轮压缩函数。可长度是已知的,填充块的内容因此也是完全固定的——跑第二遍压缩等于在哈希一串没人改得动的比特。

定制哈希的安全边界

EIP-7797 的方案是定义一个名为 SHA256-512 的定制原语:对定长 512 位输入只走一次压缩函数。要注意它的定位——这是一种只在树哈希调用点使用的专用函数,输出与标准 SHA-256 并不相同,二者是对同一 64 字节输入的两种不同承诺构造,协议靠「仅在此处使用此构造」的限定把两者隔开。为什么砍掉一半工作量安全?安全考虑部分的答案靠两层限定。第一层是输入空间:树哈希的输入被钉死为 512 位定长,需要构造超长消息才能施展的经典攻击面在这个调用点上没有立足之地;被缩减的是输入的多样性,不是摘要的位数。第二层是用途划界:提案明确这种定制哈希仅限在 hash_tree_root 的规范位置使用,历史数据签名、跨版本对象等场合继续用标准 SHA-256——「为特定调用点定制原语」和「替换全局哈希函数」是性质完全不同的两件事,前者可以逐点做安全论证,后者牵一发动全身。

一条性能提案的命运曲线

验证者数量的增长曲线把这类提案不断推上议程:证明聚合、状态转换、轻客户端同步,每个环节都被哈希成本卡着。但定制原语的复审成本也随行:每个实现都要新增一套专用代码路径,跨客户端一致性测试要覆盖新旧两种哈希的使用边界,形式化分析要为定长假设重新背书。提案因此需要持续推动者;在共识层哈希路线整体被 SSZ 化改造(EIP-7916 的渐进式结构路线与各类树优化)摊薄后,这单一动作的紧迫性下降,提案在沉寂后被机器规则转为 Stagnant——「至少六个月无活动」的自动挂牌。

一份对照表

把树哈希放进熟悉的东西旁边。比特币区块头哈希:输入 80 字节定长,跑完整 SHA-256(双重),长度填充分毫不省,因为输入本就要进通用哈希。SSZ 节点哈希:输入永远是两个 32 字节孩子拼出的 64 字节,长度先验固定,通用流程里的填充串因此逐比特可预知。差异决定了两种优化的可行域完全不同:对比特币那种把哈希当通用原语的场景,定制哈希等于换地基,没人会动;对 SSZ 树哈希这个调用点极其单一的场景,定制只是一个带先验的专用例程,风险半径小得多。也别忘了反面:定制原语意味着同一份数据结构在两个代码路径上有两个「等价但实现不同」的函数,实现者要保证新旧两条路径在所有边界上都给同一个根——这是新增的一致性负担。性能路线图常在这种「单点收益明确、维护面永久扩大」的取舍上分岔,7797 停在后者赢的那边。

快速问答

问:那它现在还能用吗? 答:这是处于 Stagnant 的提案,共识规则并未采纳;写作时不应当作现行协议行为描述。

问:树哈希和我熟悉的双轮 SHA-256 是一回事吗? 答:不同。双轮 SHA-256 是比特币把哈希当抗碰撞原语用;SSZ 树哈希用单轮 SHA-256 在结构树上做承诺,安全需求不同。

问:性能翻倍有实测吗? 答:提案声称收益来自跳过固定填充块,实际收益取决于各客户端实现与硬件,以基准测试为准。

风险提示:本文描述协议草案与哈希原语,不构成任何投资建议;机制细节以提案原文为准。