闪电付款为什么要带支付密文:防探测与拆分付款的双重角色 图 1
闪电付款为什么要带支付密文:防探测与拆分付款的双重角色 · 图 1

发票里最不起眼的三十二字节

每张 BOLT 11 闪电发票都必须带一个标签为 s 的字段,内容是二百五十六位的支付密文 payment_secret。它不记金额、不表身份,文档里对它的功能定义只有一句话:防止转发节点探测付款接收方。但随着多部分拆分付款的普及,这个字段长出了第二个职责——把同一笔付款的多条碎片缝成一个整体。

没有它会发生什么

闪电路由是洋葱加密的,中途节点本不该知道整条路径,更不该知道谁是终点。但支付哈希是明牌:每个转发节点都看得见。如果最终节点只凭“我有这个哈希对应的预像”来应答,那么任何一跳都可以拿哈希去试探:“假装付款到我这里了”,观察对方是否亮出预像,从而层层逼近终点位置。这种探测会泄露“谁在收这笔款”的商品级隐私。加上支付密文后,规则变了:最终节点对每一笔到达的 HTLC 都核对密文是否与这张发票约定的一致,对不上直接失败。中间节点如果把自己当收款方去试探,因为没有密文而立刻暴露失败,探测成本与收益完全失衡。

拆分付款时的胶水作用

闪电通道的容量上限意味着大额付款常被拆成多条并行路径。接收方怎么知道四条 HTLC 是一笔生意的四分之一而不是四笔无关订单?答案就是密文:所有属于同一支付的 HTLC 携带同一个支付密文加同一个支付哈希,最终节点据此把它们凑成一单,全部凑齐才给出预像收款。规范还允许接收方对先到的部分设一个超时(mpp_timeout),等不齐就把已到部分退回。若付款方不支持多部分能力,接收方应拒发拆分请求;接收方一旦在发票里承诺了能力,就必须对每笔 HTLC 强制校验密文——少了这一步,拆分协议就退化成可被中间节点拼缝的碎片区。

这个密文的保管边界

支付密文泄露不等于币被盗:拿走东西仍然需要支付前像,而前像只在收款方手里。泄露的实际后果是隐私与协议纪律层面的:知道密文的人可以向你发起看起来合法的 HTLC,消耗你节点的通道资源,或者在某些组合支付场景里把你的部分 HTLC 与他处的碎片搅在一起。因此发票应当当作一次性机密对待,别把它截图发在公开论坛;发票公开挂出的场景(例如众筹页)正是规范警告的“公开发票”反模式。需要长期公开收款时,用一次性票加自动重开单的方案,或考虑可复用的报价型协议,让每张实际发票只在付款双方之间短暂存在。本文讨论协议防御机制,不构成任何支付安全承诺或投资建议。

与路由模糊的分工

同样是防探测,闪电网络里有两条思路容易被混为一谈。payment_secret 属于收款方侧的防线:它不改变网络拓扑,只是让“假装付款到我这里”的试探必然失败。另一条是路由模糊(route blinding),用盲路把收款节点的身份从公开图谱里摘出去,付款路径末端是一个临时生成的混淆节点 ID。两者解决的问题不同层次:前者防的是逐跳哈希试探,后者防的是全网图谱比对。收款方两条都可用,也都不改变通道容量与手续费结构。理解分工的意义在于选型:如果你只想让单张发票不被顺手探测,带密文的普通发票已经够用;如果你想让节点长期不出现在公开图谱里,那是另一套盲路工程,代价是路由信息更难自动发现。

排障时的三类现象

付款失败信息里出现与最终节点相关的错误码时,一个常见的自查顺序是:先确认用的发票是不是本次订单现生成的(复用旧票常导致密文与哈希对不上),再确认金额是否等于发票标称值——多付一分钱都会被最终节点判为超额而拒收,少付则会等不齐碎片直到超时。中间节点报“路由失败”而拿不到具体原因,是洋葱错误的正常表现,不必据此判定对方节点故障;只有反复在同一个通道方向失败,才值得怀疑某条通道余额不足。收款方侧则要注意:给自己设一个过短的拆分等待时间,会让大额付款在碎片尚未凑齐时就被退回,表现为“金额越大越容易失败”,此时该调的是自己的等待窗口而不是通道容量。