闪电网络中的 “洋葱路由” 及其工作原理_LIC:DINA

作者:LORENZO

一个网络中的计算机依据协议跟彼此交流。在这里,“协议” 指的是一套规则系统,指定了消息应该如何传输和解读。闪电网络协议中的支付消息传输部分由 BOLT#4 描述,也叫 “洋葱路由协议(Onion Rounting Protocol)”。

洋葱路由是一种先于闪电网络 25 年诞生的技术。它也被用在 Tor 中,正是 “Tor” 这个名字(“The Onion Router”)的由来。闪电网络使用的是一个稍微修改之后的版本,叫做 “基于来源的洋葱路由”,缩写是 “SPHINX”。在这篇文章中,我们就要讲讲洋葱路由是怎么工作的。

世界上存在许多不同的通信协议,但因为闪电网络是一个支付网络,选择一个尽可能少揭示正被转发的支付的信息的协议,就是合理的。

如果闪电网络使用跟互联网一样的协议,每一个中间人都会知道谁是支付的发送者、谁是接收者、整条路径上的其他中间人是谁。洋葱路由是一个好的选择,因为其特性保证了中间节点:

只知道自己的上一个节点(谁给自己发来了消息)和下一个节点(要把消息转发到哪里去)。

不知道整条路径的长度;

不知道自己在路径中的位置。

我们用包裹作为类比,解释一下洋葱路由是怎么工作的。

假设 Alice 要给 Dina 支付。首先,Alice 要为自己的支付找出一条可行的路径:

Alice → Bob → Chan → Dina然后,她构造出一个 “洋葱”。她要从 Dina 开始(从路径的末端开始)。她把一个秘密消息(支付内容)放在一个发送给 Dina 的包裹中,并且使用一个只有她和 Dina 知道的密钥来上锁。现在,她把这个包裹放到另一个准备发送给 Chan 的包裹中,并且使用只有她和 Chan 知道的密钥,给这个发送给 Chan 的包裹上锁。对以此类推。

数据:比特币闪电网络节点数为19262个:据1ML数据,当前比特币闪电网络节点数为19262个,过去30天内增加7.58%;通道数量为42268个,过去30天内增加6.8%;网络容量达到1206.82个BTC,过去30天内增加5.65%。[2021/4/17 20:31:05]

Alice 把最终的洋葱(包裹)发给路径上的第一个中间人,Bob。Bob 使用自己的密钥解锁自己的包裹,然后看到下一个包裹是发送给 Chan 的。于是他把包裹转发给 Chan。Chan 也一样,解开包裹之后,把里面那个包裹转发给 Dina。最后,Dina 打开属于自己的包裹,发现其中的支付消息。

在洋葱路由中,像 Bob 和 Chan 这样的中间人,并不知道给 Dina 的信息的内容,也不知道整条支付路径的长度。他们唯一知道的,就是给他们转发这个包裹的人,以及下一个接收包裹的人。这保证了消息的隐私性和路径的机密性。每一个中间人都只能触及专门为 TA 制作的那一层消息。

在闪电网络的基于来源的洋葱路由中,发送者选择支付路径,并为这条路径构造出完整的洋葱,这可以被视为隐私漏洞(译者注:接收者的网络位置必须向发送者曝光)。别的路由方案比如 “盲化路由”(中文译本),通过向发送者混淆部分支付路径来解决这个问题。不过,在这篇文章中,我们专讲 SPHINX。

现在,我们来了解一下洋葱路由的规范。在一开始,我们需要定义这些东西:

发送者是 “最初节点”(Alice);

接收者是 “最终节点”(Dina);

支付路径上的每一个中间节点都是一 “跳”(Bob 和 Chan);

每一跳之间的通信信息,叫做 “跳的负载”。

一旦 Alice 选出了一条支付路径,她就从 gossip 协议中获得每一条支付通道的信息,以创建每一跳的负载,本质上这就是在告诉每一跳,如何为正在转发的支付创建 HTLC(哈希时间锁合约)。

数据:比特币闪电网络节点数量一年翻了三倍:在主要的加密货币交易所和支付应用程序采用了闪电网络技术之后,闪电网络节点数不断上涨。Bitcoin Visuals的数据显示,从2019年4月到2020年4月,闪电网络节点的数量仅从4000多个增加到5000多个。但不到一年时间,在3月24日,闪电网络节点突破10000,达到10084个。此后数量仍然一直增加,据1ML数据,截至4月8日,闪电网络节点达到18960个,在过去30天内增加了8.13%,比起去年4月翻了3倍。[2021/4/8 19:57:40]

为了建立一个合适的 HTLC,每一跳都需要:

需要转发的数额;

支付的秘密值;

继续发送洋葱的支付通道的 ID;

时间锁的长度。

这些数据中的大部分,都来自 “通道更新” 消息,这样的消息包含了关于路由手续费、事件所要求、支付通道 ID 的信息。需要转发的总数额,是支付的数额加上后续每一跳所收取的手续费总和;而支付的秘密值则是由 Dina 计算出来并嵌进支付发票中的(由洋葱消息告知路径上的每一跳)。

Alice 从最终节点 Dina 开始。她在包裹中包含转发数额、时间锁时长数值、支付秘密值以及支付数额。注意,她不需要再加入通道 ID,因为 Dina 就是最终节点,不需要再将支付转发给其他人。

乍看起来,提供转发数额是多余的,因为这个数额跟支付数额是一样的,但是,多路径(multipath)支付会将支付总额通过多条路径送达,那时候两个数值就会不一致。

在 Chan 的负载中,Alice 加入 Chan 跟 Dina 的通道 ID。她还添加了转发数额以及时间锁数值。最后,Alice 创建给 Bob 的负载。Chan 为通过自己跟 Dina 的通道的支付收取 100 聪,因此,Alice 需要告诉 Bob 的转发数额是支付额加上手续费。根据 Chan 的通道更新消息,时间锁的数值也提高了 20(以区块为单位)。最后,Alice 也要考虑 Bob 的手续费和时间锁要求,给他一个时间锁长度为 700040、价值为 100200 聪的 HTLC。

闪电网络节点数量已达18859个:金色财经报道,据1ML.com数据,目前,支撑网络的节点数量达到18859个,相较30天前数据,环比上涨8.12%;通道数量为41500,相较30天前数据,环比上涨7.8%;闪电网络承载能力目前为1185.99BTC,约合6887.44万美元。[2021/4/5 19:47:48]

下一笔,Alice 通过为每一跳(包括最终节点)生成一个共享秘密值(shared secret),准备好洋葱。这个共享秘密值可以由 Alice 和目标那一跳各自生成出来,办法就是用自己的私钥与对方的公钥相乘。

共享秘密值对洋葱路由来说是必要的,这让 Alice 和每一跳可以推导出相同的密钥。然后,Alice 使用这些密钥来混淆洋葱的每一层,而那一跳则使用密钥来解开混淆。

为了保护 Alice 的隐私,她会为一个洋葱创建一个一次性的会话密钥,而不是使用自己的节点公钥,以推导共享秘密值。她给第一跳使用这个会话密钥,然后,对后续的每一跳,Alice 都将最新的密钥乘以一个盲化因子,从而确定性地随机化密钥。这些用来创建共享秘密值密钥,我们叫做 “临时密钥” 。

Bob、Chan 和 Dina,都需要跟 Alice 得到相同的秘密值,因此,他们需要知晓用在自己的会话中的临时密钥。Alice 只将第一个密钥放到洋葱中,以节约消息的体积。每一跳都计算下一个临时密钥,并将它嵌在给下一个节点的洋葱中。各跳可以使用自己的公钥和共享秘密值计算出 Alice 所用的盲化因子,从而确定下一个临时密钥。

如前所述,共享秘密值会被用来生成一些密钥,Alice 和对应跳可以用这些密钥对洋葱做一些操作。我们来看看每一个密钥的用途。

闪电网络节点数量已达13434个:金色财经报道,据1ML.com数据,目前,支撑网络的节点数量达到13434个,相较30天前数据,环比上涨2.14%;通道数量为37382,相较30天前数据,环比下降0.3%;闪电网络承载能力目前为1019.22BTC,约合1237.05万美元。[2020/8/18]

Rho key

Rho key 被 Alice 用来加密一层洋葱;这样会混淆负载的内容,使外人无法解读。只有 rho key 的主人可以解密负载。这就是收到洋葱的节点要做的事:使用跟 Alice 的共享秘密值推导出 rho key,然后解密洋葱、阅读内容。

Mu key

Alice 使用 mu key 来为每一个负载创建一个校验和。她也会把校验和交给接收洋葱的那一跳。反过来,这一跳会使用 mu key 生成所收到的负载的校验和,检查是否与 Alice 给出的相匹配。这是为了检查负载的完整性,验证它没有被篡改过。

Pad key

这个密钥仅为 Alice 所用,用来生成随机的 “垃圾” 数据。这些数据也是洋葱的一部分,而且它跟支付路径的长度、洋葱已经通过多少跳无关,它让洋葱总是保持相同的体积,即使其某些内容需是无关紧要的。这就是洋葱路由如何隐藏路径长度的,实际上就是在保护发送者和接收者的隐私。

Um key

这个密钥也用来检查洋葱内包含的数据的完整性,但仅在回传错误时使用。没错,它叫做 “um” 是因为这是 “mu” 的倒写。在支付出错的情形中,发现错误的那一跳将使用 um key 创建一个校验和,当前一个节点收到这个报错时,也使用 um key 来验证消息的完整性。

最终的洋葱包裹看起来是这样的:

现在,Alice 拥有了给每一跳的负载,以及给每一跳的共享秘密值。我们来看看 Alice 如何将这些信息转化为最终的洋葱。她先从最终节点开始,然后一步一步往回推。

闪电网络节点数量已达13420个:金色财经报道,据1ML.com数据,目前,支撑网络的节点数量达到13420个,相较30天前数据,环比上涨2.16%;通道数量为37354,相较30天前数据,环比下降0.3%;闪电网络承载能力目前为1019.7BTC,约合1204.63万美元。[2020/8/17]

她先创建一个空的、长为 1300 字节的域,这也是所有洋葱负载的总长。然后,她使用 pad key创建一段长为 1300 字节的随机串,这就是对任何一跳都没用的垃圾。做这一步,是为了确保每一层洋葱看起来都是一样的,所以既无法看出路径的总长(有多少跳),也看不出谁是发送者、谁是接收者。

然后,她给需要使用的负载创建一个校验和,并放在负载的末尾。在给最终节点的消息中,校验和全部为 0,以告知 Dina,她就是这个洋葱的最终接收者。把校验和添加到负载的末尾之后,Alice 就把负载(以及校验和)放到垃圾的开头,并删去整条消息超过 1300 字节的部分,以保证整个消息的长度就是 1300 字节。

然后,Alice 使用 rho key 创建一个随机字节串,并对上一步得到的洋葱负载使用异或(XOR)运算,得到混淆后的负载。负载的原文可以通过对混淆文使用这个随机字节串的 XOR 运算得到(译者注:换言之,这里的 XOR 就是对称加密的算法,而随机字节串就是密钥)。XOR 操作会逐比特对比洋葱负载和(由 rho key 生成的)随机字节串,仅当其中一个数据的比特是 1 时,才会输出 1;这就得出了一个混淆后的负载。XOR 操作巧妙的地方在于,只要你得到了对的那个随机字节串以及混淆后的负载,只需用两者再次运行 XOR 操作,就可以得到混淆之前的负载。

因为收到洋葱的节点可以推导出相同的 rho key,可以他们可以生成跟 Alice 一样的随机字节串。这就是沿路的各个节点可以解开混淆、读到内容的办法。

准备好一跳的混淆洋葱后,Alice 就给下一个节点重复相同的步骤。关键区别在于,完成 Dina 的洋葱之后,她就不再需要生成垃圾了。她只需在有用的负载和校验和之后接上上一步所生成的混淆洋葱,再剪去超过 1300 字节的部分。

最后,Alice 拿到最终的混淆洋葱并添加一个校验和,这样 Bob 就可以验证这个洋葱的完整性。然后,Alice 加入会话公钥,这样 Bob 就可以使用这个公钥来计算共享秘密值。最后,她还要加上一个表示版本的字节,告知其它节点如何解读其中的数据。对 BOLT#4 所描述的版本来说,版本字节应为 0。

转发洋葱

为了发送这个洋葱包裹,发送者创建一条 update_add_htlc 消息,包含下列字段:

通道 ID:这个消息所关乎的具体通道。

ID:这个 HTLC 的标识符。

数额:这个 HTLC 的价值。

支付哈希值:由支付的接收方创建。

过期时间:这个 HTLC 将在一定区块之后过期。

洋葱包裹:为这笔支付创建的洋葱,也就是上面讲到的东西。

额外的数据:用来指定额外的数据。

准备好消息后,Alice 就把消息发送个 Bob。收到消息后,Bob 就可以开始解码属于自己的洋葱了。他先从洋葱包裹中获得会话密钥,然后使用它推导出跟 Alice 的共享秘密值。

有了共享秘密值,Bob 生成 mu key,以验证嵌在洋葱包裹中、负载的校验和。如果负载没有被篡改过,校验和应该能匹配上。

为了防止路径中的其他节点知道路径有多长,Bob 会在洋葱包裹内增加一个 1300 字节长、充满了 0 的字段。然后,Bob 从 rho key 中生成一个 2600 字节长的随机字节串。Bob 使用这个随机字节串,对填充了 0 的洋葱负载作 “异或” 运算。

还记得我怎么跟你说混淆洋葱负载的吗?使用混淆后的洋葱负载作为输入,跟相同的字节串运行 “异或” 操作,就能得到混淆前的洋葱负载。因为 Alice 和 Bob 使用相同的共享秘密值,生成了相同的 rho key,Bob 可以解开混淆。这样做的额外好处是,它又将 1300 字节长的填充字符变成了随机字节。

Bob 解开混淆的负载中包括了他这一跳的负载数据以及一个指纹。Bob 保存这个指纹,以便将它添加到发送给 Chan 的洋葱包裹中。在 Bob 将属于自己的负载从洋葱消息中分离出来后,他将洋葱包裹转回 1300 字节的原始大小,并跟 Alice 一样随机化自己的会话密钥。最后,Bob 加上版本字节、会话密钥以及他准备放在洋葱负载中的指纹,就通过 update_add_htlc 消息将洋葱包裹转发给 Chan。

这个过程会一直持续,直至消息送到最终节点,Dina。当 Dina 收到 update_add_htlc 消息时,她可以揣进到自己所生成的秘密值的哈希值,这说明这个 HTLC 就是要发给她的。因此,Dina 只需检查指纹、解开洋葱消息、揭晓属于自己的负载。

我们介绍的是一个成功案例,也就是一切都按部就班的案例,但如果这个过程发生了一些错误,那就必须一路回传一条消息,以通知所有节点出了问题。这个过程跟常规的洋葱路由类似。发现一个错误的故节点需要从共享秘密值中推导出 um key,并使用它生成一个随机字节串,然后使用异或运算来混淆返回的洋葱包裹。

发现错误的节点将给支付路径的上一个节点回传一条消息。每一跳都使用 um key 和 ammag key 作相同的操作,直到发送者收到这个包裹。最后,发送者分别使用 ammag key 和 um key 解开包裹的混淆并验证。

错误可能由洋葱包裹、节点或通道引起。如果你经常使用闪电网络,你可能遇到过这样的错误,比如 “通道不可用” 或 “手续费不足”。

BTCStudy

个人专栏

阅读更多

金色荐读

金色财经 善欧巴

迪新财讯

Chainlink预言机

区块律动BlockBeats

白话区块链

金色早8点

Arcane Labs

欧科云链

深潮TechFlow

郑重声明: 本文版权归原作者所有, 转载文章仅为传播更多信息之目的, 如作者信息标记有误, 请第一时间联系我们修改或删除, 多谢。

金宝趣谈

[0:15ms0-6:405ms