洋葱回包的 32 KiB 预算:失败载荷、截断兼容与归属数据 图 1
洋葱回包的 32 KiB 预算:失败载荷、截断兼容与归属数据 · 图 1

闪电付款失败时沿路径原路返回的洋葱错误回包,规范给它定了一条硬性体积上限:32768 字节,也就是 32 KiB。给出理由的原文不谈性能,谈的是给这条失败消息的携带通道——update_fail_htlc 的 reason 字段——留出归属数据与未来扩展的位置。本文把回包构造、超长兼容与观测通道三件事按规范原文拆开。

回包为什么必须限高

洋葱错误回包的结构是:一个 HMAC、失败载荷长度、失败载荷、再加填充长度与填充字节。每经过一跳,中间节点用自己那轮的共享密钥派生伪随机字节流对整包做一次混淆,使回包内容对沿途节点不透明、只有发起方能逐层剥开。这个设计有个先天约束:回包在途长度要保持可预期,否则长度变化本身会泄露路径信息。规范要求出错节点构造的回包总长不超过 32768 字节,失败载荷加填充至少 256 字节、并且最好恰好 256——说明里写明偏离这个值可能让较旧的节点解析不了。限高的同时,update_fail_htlc 的 reason 字段还要装得下另一样东西:归属数据(attribution_data),一个记录各跳 HTLC 滞留时长与校验链的结构。回包限高就是给这个可选观测通道让位。

洋葱回包的 32 KiB 预算:失败载荷、截断兼容与归属数据 图 2
洋葱回包的 32 KiB 预算:失败载荷、截断兼容与归属数据 · 图 2

超长回包的兼容处理

规范清楚交代了历史:早期版本允许更大的回包,因此中间节点遇到超过 32768 字节的回包时,必须截取其前 32768 字节再继续处理,而不是拒收断开。这条”截断不拒收”让新旧实现共存时失败路径仍然可用——代价是超长回包的尾部(可能正是失败原因的关键字段)在穿越路径时被静默丢弃,付款方拿到的解释力可能更弱。排查链路故障时,若怀疑回包被截断,应沿路径看各节点的失败日志,而不是只信付款方收到的那一层解释。

归属数据:100 毫秒粒度的计时账本

attribution_data 里的 htlc_hold_times 以 100 毫秒为单位记录每一跳持有 HTLC 的时长——规范例子写得很直白:值 3 表示 300 毫秒;测不准时的节点被允许报 0,这让计时无力的嵌入式实现也能合规参与。数组长度按路由支持的最大跳数设定,出错节点把自己的时长放在开头、其余清零。配套的是一串截断 HMAC:每个 HMAC 覆盖回包本身、前 y+1 个停留时长与下游各跳的 HMAC,谁都无法在不被下游察觉的情况下篡改自己之前各跳的计时记录。各跳转发时把已有的时长记录整体右移一格、更新 HMAC,再用自己的归属扩展密钥派生的字节流做一层 XOR 扰动。对盲路径场景规范另有一句限定:盲路由里的节点通过 update_fail_malformed_htlc 返回失败,因此不提供这类计时信息。

与”错误不可靠”原则的关系

洋葱回包是尽力而为的诊断面:中间跳可以不发错误、可以乱发,资金安全靠的是链上承诺与 HTLC 逻辑,回包只帮付款方决定换路还是改参数。理解 32 KiB 上限的正确角度是:它是失败诊断通道的容量预算,并且同一份体积约束也用在成功路径的 fulfillment 载荷上——成功付款的取证回包同样限高 32 KiB,说明这套预算是整条观测面的统一设计而非失败路径的补丁。发起方收到回包后还要先拿每跳的 um 密钥逐个验 HMAC:HMAC 对不上说明有人篡改了回包或路径记录,此时付款结果以链上 HTLC 结局为准,回包内容只做线索参考。规范文本以 lightning/bolts 仓库当前版本为准。本文只解释协议机制,不构成投资建议。