别再问错在哪:incorrect_or_unknown_payment_details 堵上的探测漏洞 图 1
别再问错在哪:incorrect_or_unknown_payment_details 堵上的探测漏洞 · 图 1

闪电付款失败时,付款方收到的是洋葱加密的错误回包。规范错误码清单里曾经有一族专门由最终节点发出的码,会把”金额不对”和”根本不认识这笔付款”区分开;现在它们被合并成了一个 incorrect_or_unknown_payment_details。合并不是省事,而是堵一个真实存在的探测攻击。本文按 BOLT #4 的错误码小节与相关建议核对这次合并的机制与边界。

旧错误码为什么泄密

BOLT #4 的说明原文点破了原因:旧语义下,收款方节点对”金额或到期与发票不符”和”payment hash 在我这里查无此人”给出可区分的答复,而任何能构造 HTLC 的中间节点都可以利用这一点——拿同一个 payment hash,向多个候选目的地分别试发金额小得多或到期高度偏低的付款,看回来的是”金额错”还是”不认识”,就能把真正的收款人从路由图里差分定位出来。对以匿名身份公开接收付款的节点,这等于每收一笔钱就暴露一次身份指纹。

别再问错在哪:incorrect_or_unknown_payment_details 堵上的探测漏洞 图 2
别再问错在哪:incorrect_or_unknown_payment_details 堵上的探测漏洞 · 图 2

合并后的编码与语义

现在的做法是统一返回 incorrect_or_unknown_payment_details(编号 15 带 PERM 永久标志,不带 NODE 位),载荷里保留一个 htlc_msat 与一个 height。规范明说 htlc_msat 是历史遗留的冗余字段:它的值只要求不低于最后一跳载荷里的数额,对付款方几乎没有信息量——倒数第二跳给出的金额或到期过低的场景另有专门的码(18 与 19 号)处理。height 则由最终节点填自己已知的最新块高,供付款方区分”最后一跳的 CLTV 要求已经过期”和”中途节点拖延导致到期逼近”两种情形。说明里还有一段”最初”的注记,交代合并的正是原来用来区分参数错与哈希未知的 PERM|16 与 17 号旧码,并提醒实现:17 号旧码原本是非永久错误,映射进永久族时要把”以前可重试”的分支处理对,否则会把本可提前的到期当成彻底失败。

失败码的分层没变

合并只动了这一族码,洋葱失败码的框架仍在:高位标志分成 BADONION(洋葱包本身不可解析)、PERM(永久性,换路也没用)、NODE(节点级,不加该位则为通道级)与无标志的瞬态通道错误。按清单读下来:节点级是 2 号瞬态与 3 号永久一对,外加“缺通道特性”的 9 号与“缺节点特性”的 3 号分属两级;洋葱层解析失败是 4、5、6 三个 BADONION 码;通道级瞬态族里 7 号临时故障、11 号金额低于下沿、12 号费用不足、13/14 号 CLTV 相关各管一段,20 号通道停用还带方向标志;永久一侧是 8 号通道级永久、10 号不认识下一跳、22 号载荷无效,24 号盲路径无效则挂 BADONION;18、19、21 与 23 号不带标志,分别管最终节点的 CLTV/金额校验、到期过远与多部分付款凑单超时。付款方收到永久码时,正确姿势不是拿同一组参数重试,而是调整路由或重建 HTLC 参数后再来。

这条防线的前提

合并错误码针对的是”用失败码做探测”这一种手段。洋葱回包本身是尽力而为的诊断数据,规范反复强调其不可靠性——中间跳可能不发错误或乱发,因此合并后的码不构成任何密码学证明,恶意节点可以对一切错误统一返回某个码来假装合规。接收方隐私靠洋葱路由、盲路径、错误码合并与图谱剪枝共同支撑,任何单点都不是保证。顺带一条与回包相关的构造纪律:错误回包的载荷加填充合起来不得低于 256 字节、规范要求尽量恰好等于 256,偏离会让老节点解析失败——统一码配合统一填充,目的都是让”这一跳知道些什么”在回包长相上不留痕迹。机制细节以 lightning/bolts 仓库 BOLT #4 当前文本为准。本文只解释协议,不构成投资建议。