闪电网络的每一条消息都要带一个 channel_id 字段告诉对端”这条消息说的是哪条通道”。但同一个通道在生命周期里其实用过两种编号:开通之前用的是随机拼出来的临时编号,开通之后换的是从资金交易推导的正式编号。BOLT 2 对两种编号的构造规则各有一段专门文字,值得逐句读。
v1 通道:从资金交易里异或出来
正式 channel_id 的规则在 BOLT 2 原文里只有一句话:由资金交易的 funding_txid 与 funding_output_index 用大端异或组合得到——括号里补了一句解释,输出序号只会改动最后两个字节。
这条规则有三个推论。第一,通道还没上链时这个编号不存在:它的一切信息都来自那笔已经写进区块链的资金交易,所以 open_channel 与 accept_channel 阶段没有正式编号可用。第二,同一笔资金交易的不同输出天然是不同通道,最后两字节的差异让同交易多通道自动互不冲突。第三,任何人只要能看到链上资金交易,就能直接算出这条通道的编号——它不是秘密,只是身份。

开通前的临时编号:随机数与撞号问题
正式编号缺席的空窗期,双方用 temporary_channel_id 对话,BOLT 2 的定义是”一个随机 nonce”。随机就有撞号风险,协议的处理方式很冷静:不同对端之间完全可能同时用着同一个临时编号,因此任何在资金交易成形前按裸编号引用通道的 API 都被原文明确标注为本质上不安全(inherently unsafe)。在开通完成之前,协议层唯一可靠的标识是(发起方节点公钥、接收方节点公钥、临时编号)这个三元组——要区分两条”看起来同号”的开通过程,必须连对端是谁一起查。BOLT 2 还提醒:这类按临时编号的 API 引用连持久性都没有,在资金输出的脚本公钥确定之前,没有任何东西阻止同样的编号出现在重复的通道上。
工程后果是直接的:钱包或托管系统如果在开通回调里只回传一个 channel_id 做对账,开通竞态下就可能张冠李戴;正确姿势是开通期间用三元组、链上确认后换正式编号,两段各存各的映射表。
v2 协议:用撤销基点做哈希
协议版本 2(双出资那条线)改了配方:channel_id 变成 SHA256(较小撤销基点拼接较大撤销基点),大小按基点的排序定。发 open_channel2 时对方基点还未知,临时编号按”非发起方的基点置零”算出;发 accept_channel2 时必须原样沿用 open_channel2 里那个临时编号,好让发起方把回复对上自己的请求。
原文给出的动机有两层。撤销基点本来双方就必须记住(惩罚机制依赖它),拿现成的公共信息相混,既消除撞号,又切断了对资金交易 txid 的依赖——v1 配方里”链上可见即编号可推”的性质在这里弱化了,但别误读成隐私功能:基点在开道的明文消息里就来往,这只是把编号的推导源换成了通道参与者之间的共享秘密材料。v2 通道的识别仍然建立在两条公钥消息之上,链下旁观者的推断难度提高,远达不到”编号不可见”的程度。把这段读成身份保护机制的二手资料应当警惕。
排障时怎么区分两套编号
日常运维里这个差别有个实用面:通道日志、RPC 输出与对端消息里出现的都是 32 字节十六进制编号,肉眼无法分辨来源。约定俗成的判断法则是看阶段——资金交易确认之前日志里的编号按临时编号对待,之后按正式编号对待;v2 协议通道则在双方基点交换完成的那一刻起才可能出现正式编号。对账脚本里把两种编号混存一个字段,是”开了三个通道只回来两个”这类幽灵故障的经典成因:同一个临时编号被两条并行开通复用,或正式编号在重组前后被重复推导。稳妥的表结构是给每条通道建一行内部主键,把临时编号、正式编号、资金交易引用三列分开存,消息路由按阶段查对应列,永不直接拿报文里的裸编号当键。
风险提示:本文为闪电协议机制说明,接入闪电开发的请按原文与实现版本复核;本文不构成投资建议。
发表评论
还没有评论,来说两句吧。
评论区为展示样式,提交不会被处理。