原文作者:LORENZO 编译:btcstudy
一个网络中的计算机依据协议跟彼此沟通。在这里,“协议” 指的是一套规矩体系,指定了音讯应该怎么传输和解读。闪电网络协议中的付出音讯传输部分由 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 的包裹上锁。对以此类推。
Alice 把终究的洋葱(包裹)发给途径上的第一个中间人,Bob。Bob 运用自己的密钥解锁自己的包裹,然后看到下一个包裹是发送给 Chan 的。所以他把包裹转发给 Chan。Chan 也相同,解开包裹之后,把里面那个包裹转发给 Dina。终究,Dina 翻开属于自己的包裹,发现其间的付出音讯。
在洋葱路由中,像 Bob 和 Chan 这样的中间人,并不知道给 Dina 的信息的内容,也不知道整条付出途径的长度。他们仅有知道的,便是给他们转发这个包裹的人,以及下一个接纳包裹的人。这确保了音讯的隐私性和途径的机密性。每一个中间人都只能触及专门为 TA 制造的那一层音讯。
在闪电网络的依据来历的洋葱路由中,发送者挑选付出途径,并为这条途径构造出完整的洋葱,这能够被视为隐私缝隙(译者注:接纳者的网络位置必须向发送者曝光)。别的路由方案比如 “盲化路由”(中文译本),经过向发送者混杂部分付出途径来解决这个问题。不过,在这篇文章中,咱们专讲 SPHINX。
组装洋葱
现在,咱们来了解一下洋葱路由的规范。在一开端,咱们需求定义这些东西:
-
发送者是 “最初节点”(Alice);
-
接纳者是 “终究节点”(Dina);
-
付出途径上的每一个中间节点都是一 “跳”(Bob 和 Chan);
-
每一跳之间的通讯信息,叫做 “跳的负载”。
建构跳的负载
一旦 Alice 选出了一条付出途径,她就从 gossip 协议中取得每一条付出通道的信息,以创立每一跳的负载,本质上这便是在奉告每一跳,怎么为正在转发的付出创立 HTLC(哈希时刻锁合约)。
为了建立一个合适的 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。
同享隐秘值与密钥生成
下一笔,Alice 经过为每一跳(包含终究节点)生成一个同享隐秘值(shared secret),预备好洋葱。这个同享隐秘值能够由 Alice 和方针那一跳各自生成出来,办法便是用自己的私钥与对方的公钥相乘。
同享隐秘值对洋葱路由来说是必要的,这让 Alice 和每一跳能够推导出相同的密钥。然后,Alice 运用这些密钥来混杂洋葱的每一层,而那一跳则运用密钥来解开混杂。
为了维护 Alice 的隐私,她会为一个洋葱创立一个一次性的会话密钥,而不是运用自己的节点公钥,以推导同享隐秘值。她给第一跳运用这个会话密钥,然后,对后续的每一跳,Alice 都将最新的密钥乘以一个盲化因子,然后确认性地随机化密钥。这些用来创立同享隐秘值密钥,咱们叫做 “临时密钥” 。
Bob、Chan 和 Dina,都需求跟 Alice 得到相同的隐秘值,因而,他们需求知晓用在自己的会话中的临时密钥。Alice 只将第一个密钥放到洋葱中,以节省音讯的体积。每一跳都计算下一个临时密钥,并将它嵌在给下一个节点的洋葱中。各跳能够运用自己的公钥和同享隐秘值计算出 Alice 所用的盲化因子,然后确认下一个临时密钥。
如前所述,同享隐秘值会被用来生成一些密钥,Alice 和对应跳能够用这些密钥对洋葱做一些操作。咱们来看看每一个密钥的用处。
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 怎么将这些信息转化为终究的洋葱。她先从终究节点开端,然后一步一步往回推。
她先创立一个空的、长为 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 字节的部分。下面这个 GIF 演示了整个进程:https://youtu.be/FzedRXqZDyY
终究,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 只需查看指纹、解开洋葱音讯、揭晓属于自己的负载。这个动图演示了整个进程:https://youtu.be/NhHAE6m9L6A
故障处理
咱们介绍的是一个成功事例,也便是一切都墨守成规的事例,但假如这个进程发生了一些过错,那就必须一路回传一条音讯,以通知一切节点出了问题。这个进程跟惯例的洋葱路由类似。发现一个过错的故节点需求从同享隐秘值中推导出 um key,并运用它生成一个随机字节串,然后运用异或运算来混杂返回的洋葱包裹。
发现过错的节点将给付出途径的上一个节点回传一条音讯。每一跳都运用 um key 和 ammag key 作相同的操作,直到发送者收到这个包裹。终究,发送者别离运用 ammag key 和 um key 解开包裹的混杂并验证。
过错或许由洋葱包裹、节点或通道引起。假如你经常运用闪电网络,你或许遇到过这样的过错,比如 “通道不可用” 或 “手续费不足”。
参考文献
Mastering the Lightning Network
BOLT #4: Onion Routing Protocol
此时快讯
【zkSync Era上TVL突破3.2亿美元】金色财经报道,据 L2BEAT 数据,zkSync Era上的锁仓额(TVL)已突破3.2亿美元,24小时涨幅13.65%,创历史新高。