术语表 /闪电网络与 Layer 2
洋葱路由
又称 Sphinx。
- 洋葱路由是什么?
- 洋葱路由是闪电网络用来跨若干个跳点发送支付的分层加密方式,每个节点只能获得与自己那一次转发有关的指令,其余信息一概无法获知。
每笔闪电网络支付都装在一个固定为1,366字节的数据包中传输,数据包的各层逐层剥开,因此转发节点只知道上一跳和下一跳,其他一无所知。数据包在每一跳的大小保持不变,从而隐藏路由长度和您在路由中所处的位置。对于路由中间的陌生人,您的隐私性很强;对于两端的钱包或交易所,则不然。
运作方式
洋葱路由将路由分层加密,每一跳一层,各层按相反顺序包裹,因此任何节点唯一能执行的操作就是剥开最外层。
BOLT 4将这一构造称为 Sphinx,并扩展了每一跳的有效载荷。支付发出之前,您的钱包已知路由上每个节点的公钥,并对每个节点使用 ECDH 推导出共享密钥。每个共享密钥会生成四个密钥:rho 生成用于混淆数据包的字节流,mu 计算完整性 HMAC,um 对返回途中的错误进行认证,pad 为填充数据提供种子。该字节流是对全零数据运行 ChaCha20、并使用96位全零 nonce 得到的,这样做在此处是安全的,只因为没有任何密钥会被重复使用。
数据包有四个字段和一个固定大小:一个版本字节(目前为0x00)、一个作为临时密钥的33字节压缩 secp256k1 点、1,300字节的各跳有效载荷,以及一个32字节的 HMAC。传输时共计1,366字节,无需长度前缀。
组装过程是倒序进行的。1,300字节的区域起初是 ChaCha20 字节流产生的随机字节,从最后一跳到第一跳,每一跳都先将其右移以腾出空间,把末端推出边界并丢弃。对于小于253字节的有效载荷,移位量为该有效载荷的长度加33:一个长度字节和32字节的 HMAC。每次移位之后,都要与该跳的 rho 字节流做异或,这会再次打乱前面的各层。
某一跳读取的是一份带类型的简短字段列表:类型2是以毫聪计的 amt_to_forward,类型4是 outgoing_cltv_value,即该节点接下来提供的 HTLC 必须设置的时间锁,类型6是 short_channel_id,指明要使用的通道,不过节点可以改用通向同一对等节点的另一条通道。最后一个节点收到的不是这个短通道 ID,而是类型8,其中带有一个32字节的支付密钥和总金额。
还有一步使连续的数据包无法相互关联。每一跳在转发之前,都会用一个盲化因子调整临时密钥,因此同一个洋葱包在其经过的每条链路上看起来都是互不相关的字节。BOLT 4坦承了其局限:这种混淆 "does not preclude the possibility of packet association by an attacker via traffic analysis."
购买比特币时为何重要
洋葱路由能保护您免受路由中间陌生人的窥探,却无法保护您免受路由任一端的窥探,而这正是闪电网络隐私中最容易被误解的一点。
第一跳通常是您已有往来关系的服务,它看到的信息远多于随机的转发节点。本站对 Phoenix 的评测原文引用了 ACINQ 自己的常见问题解答,未作任何弱化:"The current version of Phoenix offers no advantage regarding privacy over existing, hosted, custodial wallets. We (ACINQ) know the final destination and amount of payments." Phoenix 在本站的这一项评分为4.0,并且确实是自托管:手机上运行的是真正的节点,由12个单词的助记词保护。托管权与隐私是两个独立的问题,Phoenix 只在其中一个上表现良好。
接收端也好不到哪里去。资金留在应用内时,Strike 属于托管型,并且要求身份验证,因此无论洋葱包经过了多少跳,向您自己的钱包进行闪电网络提币时,Strike 都会知道金额、时间以及它所支付的发票。Wallet of Satoshi 在本站的状态为“需谨慎”,评分为2.7,部分原因在于该公司持有密钥,并且它已经从欧盟撤下了其托管型闪电网络服务。
因此,请不要把洋葱路由当作规避合规要求的手段。它不会改变国家指南中适用于您的合法性部分,不会向出售比特币的交易所隐藏您的购买行为,而当您钱包的运营方就是其中一个端点时,它也帮不上任何忙。它所能提供的仍然值得拥有:您从未听说过的路由节点无法构建谁向谁付款的关系图,无法判断您是发送方还是另一个中继节点,也无法区分短路由和长路由。
通过两个您从未听说过的节点支付 Strike 发票
通过闪电网络支付 Strike 发票,可以说明路由中的每个节点能够得知和无法得知哪些信息。
假设您的钱包选择了一条包含四个节点的路径:您自己的对等节点、两个公共路由节点,然后是 Strike。数据包从 Strike 开始倒推构建。Strike 的有效载荷包含最终金额和支付密钥。每个中间有效载荷包含一个仍然含有下游所有节点手续费的 amt_to_forward、一个按该节点公布的 delta 递减的 outgoing_cltv_value,以及要使用的链路的 short_channel_id。
第二个路由节点只读取一份有效载荷。它得知前一个节点交来了一个 HTLC,并且自己必须向前提供一个略小的 HTLC。它不知道 Strike 位于末端,不知道您位于起点,也无法判断路径是四跳还是八跳,因为它向前传递的区域与它收到的是同样的1,300字节。
如果该节点没有足够的流动性完成转发,它会构建一个返回数据包:包含使用其 um 密钥计算的 HMAC、一个失败代码,以及经过选择的填充(使失败消息加填充至少达到256字节),再将结果与 ammag 密钥生成的字节流做异或。返回途中的每一跳都会再次对其进行混淆。您的钱包逐层解开,然后继续解密,直到循环运行了27次,这是 TLV 有效载荷格式所允许的最长路由,因此出现故障的节点无法通过计时重复尝试来推断自己所处的位置。较新的节点还会附上归因数据:截断为4字节的 HMAC,对于最大规模为20跳的路由共有210个,以及以100毫秒为单位的保持时间。
洋葱路由 vs 盲化路径
洋葱路由对承载支付的节点隐藏路由,而盲化路径则对付款人隐藏路由的尾段。
普通洋葱路由要求发送方知道每个节点的真实公钥,因为共享密钥来自对这些公钥的 ECDH。路由盲化在最后一段上反转了这一点:接收方构建一串经过调整的节点 ID(在 BOLT 4自己的示例中,Bob 变成 Bob prime,Carol 变成 Carol prime),然后交给发送方一个引入点,以及每一跳一个加密数据块。发送方把自己读不懂的数据块包进洋葱包里。两者是组合使用而非相互竞争:BOLT 4要求每条洋葱消息都使用路由盲化,而洋葱消息与支付洋葱包使用相同的数据包格式。
洋葱路由 vs HTLC
洋葱路由携带指令,HTLC 携带资金,闪电网络支付两者都需要,因为两者相互校验。
洋葱包告诉某一跳往哪里转发以及转发多少。HTLC 则是在通道上实际提供的有条件支付。由于有效载荷区域被一个只有发送方和该跳才能生成的整包 HMAC 所覆盖,节点会将收到的 HTLC 与为其写入的 amt_to_forward 和 outgoing_cltv_value 进行比较,两者不一致时即告失败,这样就能发现上游节点悄悄克扣金额的行为。两者也可以分离:类型为513的洋葱消息采用同样的构造但不携带资金,中间跳也不会为其返回任何错误。