世界需要闪电网络
目录
让我们来做个约定:我将努力解释什么是闪电网络,但不会说得(太)详细,但你必须给我一个小时的时间。这将是一段漫长而复杂的旅程(但也有快乐)。
如果您想支持我的工作,可以访问 捐款 页面。每一笔捐款,无论多少,都能帮助我花更多时间撰写、修改和更新这些文章。感谢您的支持!
修道院是仁慈的
我们总是从最基本的开始 #
闪电网络(和比特币一样)是一个 无信任的系统,它允许 价值交换,而无需 信任网络中的其他参与者。
但这个系统是什么样子的?它位于何处?它是如何运作的?
假设我是一家披萨店的经理。每隔一天,我的朋友 Kris 都会来买披萨。Kris 喜欢用比特币支付,但用时间链快速消费很累。它的规模不够大,所以支付速度不快,而且还有手续费。
克里斯和我达成一致,并建立了一个假想的沟通渠道,我们都把一些钱(比特币)存到一个保险箱里。保险箱里有一张纸条,上面写着我和 Kris 到底存了多少聪。这使得他可以随心所欲地订购披萨,直到他那份聪币被完全侵蚀为止。
每当克里斯买了一个披萨,这张纸就会更新一次,从他的名字里去掉一些聪币,然后加到我的名字里。既然我和克里斯已经建立了沟通渠道,那么任何与克里斯有直接联系的朋友都可以利用我们的新联系。
因此,“闪电 “的目标是通过利用现有渠道,找到完成支付的最小路径,从而实现高效的支付 “路由”。
路由 是指在任何运输系统(无论是物理运输还是数字运输)中寻找最佳路线的过程。
具体到现实中:当你使用智能手机上的地图搜索最佳路线时,实际上就是在进行路由选择。
披萨的例子非常琐碎,与 “闪电 “的真实运作方式完全不符,也不现实,但这是进入主题的一个很好的开端。
公平协议 #
根据定义,“公平协议 “是一个参与者之间公平的系统。
但是,如果不通过要求信任、施加 法律状态 或假定存在 受信任的各方 等方式,又怎么可能实现公平呢?
愚蠢的问题: 我写过一整篇文章来讨论这个问题:这要归功于博弈论和密码学!
在密码系统中,我们信任协议。协议只不过是一套规则的系统,由于博弈论中的激励机制或抑制机制,我们可以在不需要中间人的情况下 值得信任的协议。但是,只有规则写得好,才能实现公平。
让我们举例说明,并将这一定义引入 现实世界。
午餐时间,两个小兄弟因为不想分享一盘薯片而争吵。很经典。母亲自作主张,权威性地实施 她的法律 ,按照她认为合适的方式分享两个小兄弟的盘子。 她可以自愿或非自愿地多给其中一个弟弟一些薯条。结果呢?系统不公平,我们有了一种完全自主做出决定的 法治国家 形式。
更明智的方法可能是制定规则,比如 两个小兄弟中的一个可以分享盘子里的食物,但另一个可以选择先吃哪个盘子里的食物 。
两人之间的这种新动态会带来什么影响呢?我们引入了 激励 和 惩罚 ,因为如果负责分享盘子的小弟弟试图作弊,他的哥哥可以通过选择更大的盘子来惩罚他。
安全基元 #
为了让这两个小弟弟的例子行得通,它必须依赖于某些 不可或缺的 基本原理,如行为的 “顺序排列 “和有意的 “不否认”。
- 两人中的一人在选择菜肴时,不能 在分菜之前。
- 两人都必须承诺不否认对菜肴的选择。
为什么是 “闪电”? #
正如比萨店的例子所提到的,比特币并不能很好地扩展。但这不是一个问题,而是一个 特点。第一层的比特币不需要扩展,也不需要扩展。
请记住,交易是在全球时间链上记录的,随着交易需求的增加,一个区块很快就会饱和。与此同时,其他所有区块都会被搁置。要想绕过队列, 摆脱等待 ,你可以支付更高的费用,因此每个人的费用都会无差别地上涨。
如果交易需求持续增加,越来越多的交易会被搁置,从而使微交易变得不经济,因为花费的费用比实际交易额还要多。
为了解决这个 “问题”–其实并非如此–一些 “天才 “认为最好是增加区块大小,但是……我们已经在《比特币 101》中讨论过这个问题了。运行并重新阅读 block-size war。
如何在不失去比特币第一层安全性的前提下,实现可扩展的链外交易?
2015 年 2 月, Joseph Poon 和 Thaddeus Dryja 提出了一个可能的解决方案,并发表了白皮书–现已过时–比特币闪电网络: 可扩展的链外即时支付。
闪电网络概念设想了一个新网络的出现,技术上称为第二层,用户可以在其中以 点对点 模式进行支付,而无需在时间链上注册交易。使用时间链的唯一目的是打开一个通信通道,并对该通道进行 结算 ,即同意将比特币从 “闪电 “中取出并返回第 1 层。
除了自然减少比特币节点的负载外,使用闪电支付的交易费用将非常低,并且具有更大的隐私性(我们将看到部分隐私),因为这些支付不像标准交易那样对整个第 1 层可见。
白皮书强调的功能 #
我预计,我们将在接下来的几章中讨论每一点,现在看来,这似乎是一个模糊的清单,但我有必要介绍这些概念,然后对其进行描述。
- 以极低的成本实现实时支付。
- 价值交换无需像 L1 那样等待典型区块确认。
- 支付是最终的、不可逆转的,只有收款人可以退款。
- 整个网络无法看到付款。
- 付款不会像L1那样被永久存储。
- 闪电使用 “离子路由 “概念,因此参与支付传输的中间节点只知道前一个节点和下一个节点。它们不知道发送方和接收方。
- Lightning 上使用的比特币是真正的比特币,这一特性提供了价值托管和完全的余额控制,就像链上比特币一样。
##闪电如何运作
我们从这里开始停止游戏:如果你累了,去煮点咖啡吧,因为你即将发现闪电网络的奇妙之处。
基本技术前提 #
- 数字签名 :是一种用于证明数字文档真实性的方法。
让我们举个例子来解释一下:你需要秘密写信给你的一位好友,你想确保他知道你确实是这封信的作者。你有一把 魔钥 ,它可以让你以数学方式在信上签名,证明只有你才能写这封信。通过使用这个万能钥匙,你创建了一个数字签名。
你的朋友有另一个源自你的魔法密钥,但他只能用它来验证你的签名。他不能以你的名义创建签名。
这就是数字签名。
- 数字密钥 :由一组数字组成,可用于加密和解密信息。
举个例子来说明:你有一把特殊且独一无二的密钥,它只能打开一个特定的保险箱。这把数字密钥非常强大,因为它还能让你签署文件或访问受保护的资源。只有你才能拥有它,当你使用它时,其他人都知道你就是你所说的那个人。
它由两部分组成,一个 公共 和一个 私人 。公共部分可以验证你的身份,但没有说明如何使用,也无法打开保险箱。而私人密码则是你必须保守的秘密。
- 哈希 :是一种数学函数,它能将大小可变的数据转换成长度固定的字符串。
让我们举个例子来解释:想象一下,你必须把一份剂量非常精确的烹饪食谱转换成一个唯一的数字。有一种数学函数可以通过指定一个 指纹 将 ‘某物’ 转换成一个数字。
哪怕改变食谱中的一克配料,并重新应用哈希函数,最终结果都会发生巨大变化,得到的数字也会完全不同。如果有人改变了你的食谱,你就会立即意识到这已经不再是以前的食谱了。总而言之,这是一种确保数字世界 完整性 和 安全性 的方法。
对于更务实的人来说,哈希值可以从这个世界的终端计算出来
$ echo -n "musclesatz" | shasum -a 256
结果如下
fb4584b61ffaee257347cbff270e3c0bc9c504317685b68a72c1a47c400984f3
- 比特币交易 :是一种数据结构,用于编码不同网络参与者之间的价值转移。
一笔交易花费 输入 ,产生 输出 。交易输入就像对之前交易输出的引用,每笔交易又会产生新的输出。
比特币节点会记录所有这些可用和可消耗的输出。这就是为什么它们被称为 “未用交易输出 “或 “UTXO”。所有UTXO的集合构成 “UTXO集”,这个集随着新的UTXO的产生或消耗而增大或缩小。
产生的产出是离散的、不可分割的价值单位,这意味着未消耗的产出必须全部消耗掉。
所以,你是说,如果我必须支付 1 万 satoshi,但我有 3 个比特币的 UTXO,我就得花光?
好消息是,你将支付整个 UTXO 作为输入,但你将得到两个输出:
- 一个是支付 10k Satoshi
- 另一个则将差额作为 resto 返还给您。
说起来不好听,但它的工作原理和你花法定货币时一模一样。如果你有一张 10 欧元的纸币,却要花 0.10 美分才能买到一个 goleador,那么你就必须花光它。
唯一没有输入的交易是 coinbase 交易 ,这是矿工用来维持整个挖矿过程的特殊交易。
每笔交易都有一个标识符,称为 “交易 ID”,简称 “TxID”。该 ID 由交易数据的哈希函数生成。相反,我们使用 “输出点 “来识别 TxID 的特定输出,“输出点 “是放在 TxID 末尾的简单数字,前面用冒号(:)来确定我们所指的输入。
- 比特币脚本 :是本轮定义的结尾,是比特币中用来定义交易中资金释放条件的脚本语言。换句话说,比特币脚本决定了必须满足哪些规则才能使用交易中的资金。
比特币脚本由两部分组成: - 阻塞脚本 :这些脚本被嵌入到交易输出中,并设定花费输出所需的条件。 - 解锁脚本 :这些脚本嵌入到输入中,满足阻塞脚本设定的条件。
举例说明,如果阻塞脚本的阻塞输出是这样的
3 + x = 5
不难看出,我们可以在事务输入中使用解锁脚本 2 。
任何验证此交易的人都会将我们的解锁脚本(2)与阻塞脚本(3 + x = 5)连接起来,并给出肯定的答案,从而允许输出被花费。当然,这些脚本中并不包含基本算术,在实际操作中,它需要证明对某个秘密的了解,这里我们又回到了数字密钥的概念。
既然我们已经分析了锁定和解锁脚本,让我们再做一次披萨的例子,试着接近它的实际工作原理:
- 克里斯向肌肉男支付 1 万个比特币购买披萨。
- 最简单的锁定脚本需要 musclesatz 签名才能解锁资金。
- 该脚本类似于”<签名> <公钥> CHECKSIG”。
CHECKSIG 包含两个元素,一个签名和一个公钥,并验证它是否是我的。公钥已经在块脚本中,缺少的是与公钥相对应的肌肉签名。只有我拥有(或应该拥有)私钥。只有我才能生成一个有效的签名,让我花掉这些聪币。
我必须提供一个包含我的数字签名的解锁脚本。此操作的结果将是 真 !
<musclesatz 签名> <musclesatz 公钥> CHECKSIG
当然还有其他更复杂的脚本类型。这里 有几个例子。
什么是支付通道? #
闪电网络(Lightning Network)是在比特币时间链上以 智能合约 (别想歪了)形式实现的 支付通道 的点对点网络。但这一定义过于简单,因为它也是一个通信协议,定义了网络参与者如何执行这些 智能合约 。
支付通道是闪电上两个节点之间的关系。这种关系允许在这两个闪电节点之间定义余额(以毫聪为单位)。
闪电节点是能够使用LN协议的软件。它们有三个基本特征:
- 它们是通过闪电网络发送和接收付款的钱包。
- 它们必须与其他节点进行点对点通信。
- 它们必须能够访问时间链,以保护用于支付的资金。
通信渠道受 加密协议 保护,该协议通过使用加密技术保证公平性,使其成为事实上的 公平 系统。当参与者双方都向多位2-di-2地址中的共同基金捐款时,该协议就建立了。向 2-2 地址捐款意味着双方必须达成一致。
克里斯和我通过一系列私人交易来消耗这笔余额,但不会将其公布在时间链上。我们渠道中各种序列的最后一笔交易代表了渠道的当前 状态,并定义了我和克里斯之间如何分配余额。
进行闪电交易相当于将余额的一部分转移给我,如果我得到了报酬,则将余额的一部分转移给克里斯,如果我必须向他支付报酬,则将余额的一部分转移给他。
任何向一方或另一方转移资金的行为都是由智能合约处理的,如果通道成员试图发送 旧 状态(属于过去,因此不再有效),则会受到惩罚。
付款转发 #
当网络中的多个参与者拥有多个支付通道时,这些通道可以从一个通道 转发 (还记得路由吗?
我们已经说过,通道是由多标识地址构建的;但我们没有说过的是,通道余额更新交易是预先签名的比特币交易。这意味着,LN 运行所需的信任来自于卓越的去中心化网络的信任:第 1 层,比特币。
重点是什么?
闪电是比特币之上的一个应用程序,它利用了比特币交易及其脚本语言。它是一种创造性的、聪明的方式,能以极低的费用实现任意数量的即时支付,除了比特币本身,无需信任任何人。
回到支付渠道的话题,我们来谈谈可能存在的限制:
- 互联网传输几百个字节所需的时间(可忽略不计)。
- 通道的容量,即打开通道时投入的比特币数量。
- 比特币交易的大小上限:由于每笔闪电支付都由一笔可能仍在进行中的比特币交易支持,因此区块大小会影响单个支付通道上可同时激活的支付数量。
###融资交易
我们所说的通道的基本元素是一个多位 2-2 地址。参与开通支付通道的其中一个对等方可以通过向多位元地址发送中本聪,为通道提供资金。这笔交易被称为 “注资交易”,在时间链上无法与其他交易区分开来。只有在交易 结算 时,即通道关闭时,才能发现这是一个闪电通道。
与融资交易一起存入的金额称为 “通道容量”,它定义了可在该融资通道上发送的最大金额。
退款交易 #
假设我现在与 Kris 建立了一个支付通道。然而,Kris 很调皮。他让我将资金存入一个多 ID 2-of-2 地址,但现在却拒绝与我合作并签署交易。
我如何解锁资金?
为了避免这些麻烦,我需要提前创建一个 退款交易 ,从多位元地址支出,退还我的中本聪。我需要在向该地址传输资金交易之前 签署该退款交易,即在向通道提供资金之前。
现在我受到了保护!
承诺交易 #
该事务是通道中的对等节点之间达成的协议,向每个对等节点支付余额,确保双方无需相互信任。
通过签署承诺交易,您就承诺了 此刻 通道上的余额。如果我想从通道中取回我的资金,我可以随时取回,这正是因为签署了这份合约。
顺便说一下,每当渠道的余额发生 “变化 “时,就会创建这些承诺交易,更新渠道的新状态,并将余额分为应付给我的部分和应付给我的同行的部分。
如果我的渠道合作伙伴消失了?没问题。 如果我的渠道合作伙伴拒绝合作?没问题。 如果我的合作伙伴试图欺骗我?没问题。
现在看来,“没问题 “这个答案似乎烟雾弥漫,毫无意义,稍后我们将详细分析我所说的内容。
你能出轨吗? #
回到我的朋友 Kris,我和他打开了一个通道,在一个多位 2-2 地址中存入了 10 万聪。我们交换签名,我在时间链上传输交易。
我们说过,每次通道余额发生变化时都会创建承诺交易,因此,如果我们假设我向 Kris 发送了 3 万个聪币,那么新的承诺交易将表示该地址向我支付了 7 万个聪币,向 Kris 支付了 3 万个聪币。
但现在我有两个承诺事务,第一个在时间 t0
定义初始状态,支付 10 万萨特,第二个在时间 t1
定义当前状态,支付 7 万萨特。
我想到了一个不健康的想法:但如果我公布了之前的 10 万萨特承诺交易,这是否意味着该地址现在会支付我 10 万萨特?
比特币可以抵制审查制度,没有什么可以阻止我发布不再有效的旧状态。当然,除了加密技术。
为了防止这种盗窃行为,承诺交易是这样做的,如果一个旧的交易被传送,无论是否自愿,我都会受到惩罚。
这就是博弈论中的 抑制 ,就像拿着一盘筹码的小兄弟的例子一样。
我作弊的 抑制 是非常高的,因为在我发布旧频道状态的那一刻,Kris 就可以加入我的俱乐部,并有机会索要该地址的全部存款余额。
我们稍后将分析惩罚是如何运作的,因为我必须介绍 时间锁定延迟 和 重置秘密 的概念。
让我们宣布频道! #
如何通知整个网络我的新支付通道的存在?如何将其公开?
有一个名为 “gossip “的协议,用于向其他节点通报我的通道的存在、容量和佣金。
公布通道的好处是,其他节点可以使用该通道进行支付,甚至会产生一些信用费用。但另一方面, 未宣布的 通道具有一定程度的隐私性,至少在时间链关闭通道之前是如此。
说到关闭通道,您认为什么时候关闭 LN 通道比较好?
不,最好永远不要关闭通道。
关闭通道需要运行链上交易、支付费用、暴露通道的存在,但最重要的是……除非有特殊原因,否则关闭通道没有太大意义。
保持通道开放也有好处,因为当我的发送能力耗尽时,由于 “重新平衡”,我仍然可以接收。
关闭频道有三种不同的方式:
- 协议关闭 ,这是正确的关闭方式:
- 我的 LN 节点通知我的对等 LN 节点我打算关闭。
- 两个节点都致力于关闭。
- 不再接受新的路由尝试,同时解决正在进行的路由尝试。
- 节点准备 关闭交易 :对最后一个状态进行编码,以确定何时将余额分配给两个对等节点。
- 就如何分配时间链上的结算费用达成一致。
- 每个渠道合作伙伴都会收到自己应得的余额。
- 强制关闭 是错误的方法:
- 我试图在未经其他参与者同意的情况下关闭通道。
- 我发布我的节点的最后一笔承诺交易。
- 确认后,将有两个消耗性输出:一个是我的,另一个是同行的。
这就是问题开始变得棘手的地方,因为如果我强行关闭了通道,我的输出就会被 “时锁延迟 “阻断,在未来的某个时间(通常是两周,以时间链上的区块高度来衡量)才能使用我的比特币。
- 违反 协议 ,是一种非常非常糟糕的方式:
当我试图通过发布代表旧通道状态的承诺交易来作弊时,就会发生违反协议的情况。为了让我的对等方注意到这一作弊企图,他们必须在线并关注时间链上的新区块及其上的交易。
虽然我发布了一个旧的状态,但 时间锁 阻止我花费余额,因此我的同伴有时间采取行动惩罚我。通过对我的惩罚,我的同伴可以全额提取存款(你能看到对试图作弊的抑制作用吗?
最后,我的同伴希望惩罚我的交易能尽快在一个区块中被接受,所以他也愿意支付 L1 的最大费用:他可以从我的那部分余额中支付😁。
但如果时间锁定已过期,而他没有注意到我已发布了旧状态,那该怎么办?
不幸的是,他将损失全部资金,或根据我设法发布并完成的最后一个承诺状态损失资金,超过时间锁定延迟。
我们如何检测针对我们的违反协议行为?
使用全天候运行、管理得当的 “闪电 “节点,或使用个人或第三方 “监视塔”。
发票 #
大多数 “闪电 “付款都以收款人(即收款人)开具的发票开始。该发票包含付款的基本信息:
- 付款哈希值。
- 收款人。
- 实际金额和可选描述。
付款哈希值 “由收款人以 安全 和不可预测 的方式选择一个伪随机数据创建,并将其输入哈希函数。我们称这种随机数据为 “前图像”。
H = SHA-256(前图像)
我们得到了一个支付哈希值。根据哈希值的特性,我们知道它不能被反转或强制,因此没有人能从哈希值的结果中找出预图像是什么。预图像是一个秘密,一旦泄露,任何拥有哈希值的人都可以验证预图像确实是秘密。
问题的关键在于,支付哈希值允许支付以原子方式通过多个渠道:要么一直到达目的地,要么失败。没有中间环节。
发票通常是在 Lightning 之外使用任何通信机制发送的。非常流行的一种机制是 QR 码,因为它既方便又小巧。二维码包含上述所有信息。
关于发票的更多信息
- 它们有一个有效期,这样收件人就不必保留所有的预图像。当发票付款或过期时,可以将其删除。
- 它们可以包含 “路由提示”,允许发送方使用未通知的信道构建通往接收方的路径(影子信道,我们将在本文后面讨论)。
寻路、路由 #
这两个术语经常被混淆:
- 寻路 包括寻找从信源到目的地的最佳路径。
- 这条路径的使用被称为 路由 。
闪电使用 “基于源 “协议进行寻路,使用 “离子路由 “协议进行路由。
寻找路由的标准方法是反复 测试 各种路由,直到找到一条具有足够流动性的路由,以便转发付款。这种方法可能无法将路由费用降到最低,但总的来说,效果还不错。
如果我们有每个通道的确切余额,那就太好了(也可能没有,这要看情况而定),因为这样一来,任何一个学过运筹学的大学生都能解决路径选择问题。但事实并非如此,网络参与者并不 也无法 余额。
说到路由,“闪电 “在很大程度上受到了著名网络Tor的启发。Lightning的路由协议名为Sphinx,其工作原理与Tor的洋葱类比完全相同:发送方构建从核心层到最外层的整个 洋葱 。
收件人的付款信息是用密钥加密的,只有收件人才能破译;这些信息是整个路由操作的核心。在路由一章中,我们将从加密的角度逐步分析洋葱的构造。
概括地说,支付是以洋葱的形状构建的,从接收方开始,在找到的路径上向后添加一层新的洋葱,从而从接收方到发送方。洋葱的第一层,也就是最外层,将是发送方接收洋葱数据包的对等信道,该数据包将被转发到网络中。
发送洋葱包时,每个节点只知道它从哪个节点收到洋葱包,以及它将把洋葱包传递给哪个节点,而不知道发送方和接收方是谁。
稍作说明:您可能认为数据包实际上是像洋葱一样被剥开的(洋葱会剥皮吗?)
实际上,每个节点在读取自己负责的部分时,都会添加一个加密 “填充物”,使信息的大小恢复到发送者最初设定的大小(1300 字节)。这个小游戏是为了保护隐私,防止中间节点推断出路径长度或所涉及节点的数量,从而提高整体安全性。
这些小洋葱(小是因为它们可以装进一个 TCP/IP 数据包)在整个路由路径中的长度相同。
洋葱转发 #
我必须转发洋葱。我终于找到了一条可转发的路由。
我将信息转发给我的对等节点,我们说每个节点处理一层洋葱。基本上,每个节点都会收到一条名为 “update_add_htlc “的闪电消息,其中包含付款和洋葱的哈希值。然后,支付转发算法会介入并执行这些操作:
- 解密外层并检查消息的完整性。
- 确认它能满足基于费用和传出容量的路由建议。
- 更新传入通道的状态。
- 添加著名的 填充 数据,以保持洋葱长度不变。
- 反过来,它通过发送包含相同支付哈希值和洋葱的
update_add_htlc
在其输出支付通道上路由洋葱。 - 与通道对等方合作更新通道状态。
但如果在此过程中出现一般错误,会发生什么情况?
会发生的情况是,通信会以相反的方向传播回发送方,并附带一条 “update_fail_htlc “错误信息。参与路由的每个节点也都会看到这条信息。
如果你想知道:什么是 HTLC?为什么每个节点都要知道故障并收到这条错误消息?
你很快就能找到答案。
(可选)如果你想了解更多关于在 Lightning 中加密点对点通信的信息,我建议你访问 Noise Protocol Framework 网站。
通道备份 #
或多或少,每个人都熟悉比特币的 BIP-39,它允许我们通过助记符检索钱包的状态。对于那些不记得的人来说,比特币改进提案 39 允许我们从一个公共列表中生成一串英语单词,作为生成一个确定性钱包的 “种子”,其中包含一个几乎无限的公钥和私钥列表。
但是,如何在闪电中进行备份呢?
闪电钱包也使用 BIP-39 记忆体的备份,但 仅用于链上通信部分 。这一点至关重要。
通道还需要更高级别的备份。这种备份被称为 “静态信道备份”(SCB),每当信道的状态发生变化时就会发挥作用。这应该会让我们产生疑问,因为如果运气不好,一个旧的(已经撤销的)承诺事务被恢复了,我们的通道对应方可能会认为我们在试图欺骗它,从而惩罚我们,请求惩罚事务并清空地址。
另一个需要考虑的问题是,SCB 备份必须加密,以保持高度的隐私性和安全性:如果我错过了一个未加密的备份,任何人不仅可以利用它查看我的支付通道,还可以关闭它们,以便将余额交给我的对应方。
清扫 #
如果我的闪电钱包余额过大,我想降低风险,该怎么办?
我可以进行各种类型的清扫:
- 在区块链上:通过合作关闭通道,将资金从 LN 钱包转移到比特币钱包,我们以前见过这种情况。
- 区块链外:这种模式涉及在网络上运行第二个未公布的 LN 节点。我定期将资金转移到这个 “隐藏 “节点,把它当作储蓄罐使用,我记得这个节点是一个热钱包。
- 潜艇交换:YT 是链上与链下交换。它是原子交换,也就是说,如果我发起潜艇交换并发送一个 LN 通道的余额,对方会给我发送链上比特币作为回报。
潜艇交换 #
我认为值得研究一下 “潜艇交换”,因为它经常会引起很多混淆:
背景:
- musclesatz 在链上有比特币,想在 LN(链下)上获得资金。
- Kris 在 LN(链外)上有资金,想在链上接收比特币。
- Mark 是肌肉z 和 Kris 之间 协调交易的人。
如果 Kris 没有收到 Mark 的付款,Mark 就无法在合同中提出比特币的要求,而由于合同中的一个特殊条款(顺便说一下,该条款被称为 “Hashed Timelock Contract” - HTLC,我们稍后会详细介绍),在一定时间间隔后,我就可以取回我在该合同中的比特币,因为也有一个对我有利的时间锁。
以上就是我对 Bitcoin Script 的解释。
op_size 32 op_equal
OP_IF
OP_HASH160 <ripemd160(swapHash)> OP_EQUALVERIFY
<接收器密钥
OP_ELSE
OP_DROP
<cltv 超时> OP_CHECKLOCKTIMEVERIFY OP_DROP
<发送方密钥> OP_ENDIFY
OP_ENDIF
OP_CHECKSIG
与比特币的一些比较 #
闪电是建立在比特币基础上的,到目前为止一切顺利。它继承了比特币的一些特性和属性,但也有一些重要区别:
- 地址和发票 :比特币地址可以无限次重复使用(从隐私角度考虑不推荐)。LN 发票是一次性使用的,有特定金额。(有一个例外,那就是我们稍后会看到的 “keysend “机制。)
UTXO选择和路径寻找 :在比特币上进行支付,我必须花费 至少 个UTXO,而在LN上支付则不需要消耗 “输出”,因为正如我们所看到的,这是对多位元地址余额的重新平衡。
挖矿和路由费用 :在比特币上,我们向挖矿者支付费用,以便将我们的交易纳入区块链,但在 LN 上,网络用户向其他网络用户支付费用,因为要通过通道进行路由支付。该费用由 “基本费 “和 “费率 “组成。“基本费 “是为路由支付的固定部分(每个通道可以有自己的路由),“费率 “是支付的可变部分,与支付的价值成比例(另一个区别是,在时间链上,费用与价值不成正比)。
比特币公开交易和闪电私人支付 :文章的核心。时间链是公开的,而闪电支付不是。
中本聪和千分之一中本聪 :在时间链上,最小单位是中本聪,而在 LN 上,我们也有千分之一中本聪。在闪电通道的 结算 中,毫子被四舍五入为 接近 的整子。
##闪电网络软件
我一开始的想法是让这一章留白,以免文章过于沉重。如果你有兴趣从技术角度详细了解如何建立一个运行闪电的开发环境,请告诉我,你可以通过X/Twitter或telegram与我联系。
Lightning不是一种产品,也不是一家公司,而是一套定义了通用互操作性的开放标准。与 Bitcoin Core 一样,Lightning 也没有可跟踪的参考实现,但标准是通过一套名为 “Basis of Lightning Technology”(BOLT)的指令定义的,你可以在GitHub上找到这套指令。
由于没有像时间链那样达成共识,因此任何人都可以在核心指令的基础上进行构建,如果这些功能取得成功,它们就可以成为 BOLT 的一部分。
闪电的核心 #
闪电由一系列运行在互联网上的复杂协议组成。我将它们划分为5个不同的层,每一层都利用(和抽象)其下一层。
层 |
---|
网络连接 :定义允许在网络中交互的协议。 |
信息 :定义用于格式化或编码信息的协议。 |
点对点 :定义不同 LN 节点之间的通信协议。 |
路由 :定义路径发现和信息路由协议。 |
支付 :定义发票支付接口 |
支付渠道 #
要理解支付通道背后的工作原理, 重要的是问自己一个问题:
拥有比特币意味着什么?
拥有比特币意味着拥有一个比特币地址的私钥,该地址至少有一个UTXO。这个密钥允许我签署一笔交易,并使我是该余额的所有者这一事实合法化,因为没有其他人知道这件事。
但所有权并不总是掌握在一个人手里。比特币还允许多重签名地址,你需要 多个私钥 来签署一笔交易。一个简单易懂的多重签名方案是 2-of-3:这意味着需要 3 个人中的 2 个人来签署一笔交易并花费该地址的余额。在这种情况下,“2 “被称为 “法定人数”。
但如果在 2 对 2 方案中,有一方不合作呢?
没有法定人数,资金就无法使用。事实上,退款交易可以避免这种情况的发生–文章开头已经分析过了。我们必须将退款交易视为婚前协议。在为一个 2 对 2 地址提供资金之前,我必须确保我有一个退出计划,并将我的资金与对方的资金清楚地分开。
让我们先回到渠道建设上来。要与 Kris 建立通道,我们的两个节点必须建立互联网连接才能开始交易。每个节点都有一个十六进制格式的公钥,该公钥由节点内部的私人根钥生成。但这还不够。我们还需要一个网络地址,在这里我们有两种选择: TCP/IP 或 Tor 。
然后,我们要定义一个 “节点标识符”,其形式为 “ID@Address:Port”,但仍然难以读取。如果能将其全部嵌入二维码中会更好,不是吗?
只要扫描一下,两个节点就连接起来了 😉
既然节点已经连接,我们就可以开始考虑建立支付通道了,这需要在我们各自的节点之间交换六条信息(每个对等节点三条):
- open_channel
/
accept_channel`: 我向 Kris 发送open_channel消息,说明我的能力和期望,如果 Kris 接受我的请求,他将以accept_channel回应。 funding_created
/funding_signed
: 我想避免被骗,所以用 funding_created 同时创建了资金交易和退款交易,以防止可能的欺骗。如果 Kris 同意,他会回复一个 funding_signed。现在我可以放心地传输我的资金交易(链上),以创建和锚定支付通道。这不是即时操作,因为我们是在时间链上操作,所以需要等待锁定确认。funding_locked
/funding_locked
:一旦交易有足够的确认(在初始 accept_channel 消息中定义),Kris 和我将交换一个 funding_locked 消息,开始发送闪电交易。
在通道收缩期间,我做了一件很不寻常的事:我建立了两个连接交易。如果资金交易甚至都没有在时间链上传输,我是如何做到这一点的呢?
多亏了比特币在 2017 年推出的一项名为 “隔离见证”(SegWit)的功能,我才能使用交易哈希值而不是输出 ID 来引用交易输出。这让我可以串联不被传输的交易。
是的,我写的这些听起来像废话,但其实不然,让我来解释一下。
我的意思是,如果退款交易在时间链上传输,并且该交易的输入有我和 Kris 的签名,那么该交易就是有效的。即使我的节点尚未传输资金交易,我也可以通过计算资金交易的哈希值来构建退款交易,并将其作为退款交易的输入。因为我已经计算出了哈希值,所以我事先就知道引用是有效的。
现在,通道已经设定,但所有的流动性都在我这边。这意味着我可以向克里斯发送聪币,但克里斯却没有资金可以发送给我。如果我发送一些聪币,通道的状态就会改变,承诺交易就会出现。通道余额已更新。
假设 Kris 和我开始交易 satoshi,然后我们产生了大量的承诺交易。我们在通道中的余额是这样的:
如果我想通过传输和确认我所拥有的承诺交易来关闭通道,我就不能在 400 个区块内使用余额,而 Kris 可以立即这样做。当然,反之亦然。
为什么会有这种时间锁定延迟,它有什么作用?
我们前面提到过。它的作用是,如果我通过窃取 Sats 来欺骗 Kris,他就可以行使惩罚。这个时间锁定延迟是在通道建立信息中协商确定的。
但是,如果我发布了旧的通道状态,会发生什么?
每次用新提交更新通道状态时,我都会从我的对应方那里得到一个与之前状态相关的加密秘密,称为 “撤销秘密”。如果我试图愚弄克里斯,他就会使用先前状态的撤销秘密,并用密码证明 “状态 “已被撤销,而我违反了规则,因为我正在用无效的状态关闭通道。
给克里斯 惩罚我的 权就是撤销秘密,它给了他我作弊的数学证明。
但我不想欺骗他,而是想合作关闭通道。我协商了一个名为 shutdown 的最终承诺交易,根据每一方的当前状态支付其余额。我指定了一个与我的钱包关闭地址相对应的比特币脚本,并告诉克里斯做一个关闭交易,把我的余额支付给这个钱包。克里斯也会做同样的事情,并接受合作关闭。我们终于可以结算了。我发送最后一条消息 closing_signed,在这条消息中,我提议用我的签名支付链上关闭的交易费用,如果 Kris 同意,他将用自己的签名向我支付同样的费用。如果他不同意,他将提出不同的费用。这样的结算周期会一直持续下去,直到我们找到双方都同意的解决方案。
通道路由 #
要了解路由的广阔世界,我们可以从一个物理现实中的例子开始。
我必须向克里斯发送 10 枚非常稀有的材料硬币,但我与他没有直接联系。但是,我们都认识马克,并且与他有直接联系。我怎样才能说服马克把 10 枚硬币送给克里斯,既不会被骗,又能确保他不会逃跑?最重要的是,我怎么知道这些硬币已经交给了克里斯?
一个可行的办法是,如果马克能向我证明他已经把 10 枚硬币交给了克里斯,我就答应给他 10 枚硬币。
但马克为什么要签署这样一份没有实际利益的合同呢?
这其实不太方便,所以我可以再修改一下,如果马克能向我证明他向克里斯交付了 10 枚金币,我就答应给他 11 枚金币,这样他就能得到一枚金币的佣金,还不错。
不过,信任问题依然存在,因此我们决定使用一种名为 “代收款 “的担保服务。
克里斯需要得到报酬,因此他生成了一个秘密值 R
(为简单起见,我们假设这个值为: 你好世界! ),并使用 SHA-256 哈希算法。我们将 R 的哈希值称为 “支付哈希值”,将解锁支付的秘密称为 “支付前图像”。
此时,克里斯会通过电报向我发送支付哈希值。我不知道这个秘密,但同时我可以用这个支付哈希值重写我和马克的合同,并说
马克,如果你能给我看一条与此支付哈希值匹配的有效信息(预图像),我将补偿你 11 个金币。您可以通过与 Kris 签订合同来获取该信息。为了确保您能得到补偿,我会在您与 Kris 签订合同之前将这些金币锁定在托管账户中。
这份合约可以保护我的钱币,因为在此期间,我将钱币锁定在托管账户中,但最关键的是,如果马克向我出示有效的支付哈希值前图像,我就会向他支付钱币。预图像是 Kris 已收到马克付款的证明。
此时,马克与克里斯签订了一份相同的合约,表示如果克里斯能出示与付款哈希值匹配的有效信息,他将向克里斯偿还 10 个金币。他还警告说,他将把这 10 个金币存入托管账户,在泄露秘密后再偿还。
(但请记住,克里斯才是收款人,是他生成了预图像,因此他可以向马克展示预图像并获得实际付款)。
各方都签订了合同。
克里斯将预图像发送给马克;马克检查该密文是否与支付哈希值相符,确认无误后命令托管机构为克里斯释放 10 个金币。现在,马克将预图像发给我,我检查后命令托管服务为马克发放 11 个金币。
就这样,所有合同都解决了。我总共支付了 11 个金币,其中 1 个由马克作为费用收取,另外 10 个给了我的收款人 Kris。
为什么能成功?
因为有了这样一连串的合同,马克就无法逃脱被托管的命运,他把这些硬币托管了起来。
弱点是什么?
如果克里斯决定不发布他的 “预图像”,那么马克和我就会把这些硬币托管起来。但这个问题很容易解决,只要在合约中加入一个截止日期,即 时锁延迟 。
闪电交易是原子交易,要么通过,要么被KO!
哈希时间锁定合约(HTLC) #
Lightning 上使用的路由公平协议称为 “哈希时间锁定合约”(HTLC),也就是我们刚才在示例中描述的那种。
HTLC 使用支付前图像的哈希值作为解锁支付的秘密。还有另一种路由机制,叫做 “点时间锁定合约”(PTLC),它的效率更高,隐私性更好,因为它直接依赖于比特币在 2021 年新增的一种叫做 “施诺尔签名 “的算法。Here 更多信息。
在阅读并理解了 HTLC 的例子后,我想指出一个小问题:每个合约都可以被任何知道预图像的人解锁。如果克里斯花了马克和我的 HTLC 会怎么样?如果是这样的话,这根本就不是一个无信任的系统。
HTLC 脚本必须附加一个条件,将每个 HTLC 与特定的接收者关联起来:我们要求每个接收者的数字签名与每个接收者的公钥相匹配,以防止其他人使用该 HTLC。
还有一个小问题需要解决:
如果某个节点离线或不合作怎么办?如何 温和地 使支付失败?
- 合作方式:在不改变余额的情况下,将 HTLC 从承诺交易中移除,从而将 HTLC 向后 “回卷”(稍后详述)。
- 时间锁定还款:我们已经讨论过这个问题,每个 HTLC 都包含一个与时间锁定延迟相关联的还款条款。
付款转发 #
HTLC 似乎是多跳支付的特权,但实际上闪电协议也将其用于通道内的 “本地 “支付。这样做的原因是为了在网络中的每个点保持一致性和相同的协议设计。对于收款人来说,通道中的同伴付款与同伴代表他人转发的付款并无区别。
让我们以我、马克和克里斯为例。
- 马克和我有一个频道,双方的余额都是 70k sats。我提醒大家,让我们走到这一步的承诺交易是延迟的,而且是可撤销的。
- 我希望马克接受一个 50k sats HTLC 转发给克里斯。为此,我需要发送 HTLC 的详细信息,如付款哈希值和付款金额。我使用
update_add_htlc
消息来完成这项工作。
从技术上讲,信息交换过程如下
马克收到的信息足以创建一个新的承诺交易,该交易的通道状态(我的和马克的)余额相同,新的输出代表我提供的 HTLC。这个新承诺的 HTLC 输出将有 5 万萨特,这个数额直接来自我,因此我的新余额将是 2 万萨特。
如果信道状态发生变化,就必须有一个新的承诺,在马克创建承诺后,我将用 “commitment_signed “消息签署它。
现在,马克有了一个已签署的承诺,他必须确认它并撤销旧承诺;他会用 “revoke_and_ack “报文来做这件事,这样我就可以建立一个撤销密钥,从而创建一个惩罚事务。现在的情况是,马克不能再发布他新撤销的旧承诺,否则我就有惩罚他的手段(撤销密钥)。从所有意图和目的来看,旧承诺已被撤销。
还缺什么?
嗯,我还没有撤销我的旧承诺,在我目前的状态下,还没有 HTLC!我创建了一个新的镜像承诺,其中包含 HTLC,但仍需要马克签署。和 Mark 一样,我做了一个 revoke_and_ack
并用 commitment_signed
签署了新的承诺。现在,我还承诺不再发布旧状态,并授予了马克撤销旧承诺的密钥。
现在,马克和我有了一个新的渠道承诺,其中也包含额外的 HTLC 输出,但新的预算仍然没有反映出我向马克发送了 5 万的事实。只有向 Kris 提供付款证明,才有可能获得付款!
事实上,我们现在假设马克和克里斯做了完全相同的事情。克里斯是收款人,是唯一知道付款哈希值的预图像的人,因此克里斯可以立即满足与马克的 HTLC。
事实上,他就是这么做的,向马克发送了一条 “更新_满足 HTLC “消息。马克一收到这条信息,就会立即检查这个预图像是否产生了支付哈希值;如果评估结果为 TRUE 条件,这个秘密就可以用来赎回 HTLC。
马克也会向我发送一个 update_fulfill_htlc
信息,预图像会从克里斯传播给我。我将通过评估预图像的哈希值来执行与马克相同的检查。
这正是 Kris 已收到付款的证明!。
马克和我可以从承诺交易中移除 HTLC,并更新我们的通道余额以反映新的资金情况(显然要再次进行承诺和撤销循环 🙂)。
但如果出现错误或最后期限到了怎么办?
在这种情况下,流程将以类似的方式发展,与失败消息 update_fail_htlc
不同。我必须移除 HTLC,然后经过承诺和撤销的事务循环,将通道状态转移到新的承诺,并将余额设置为 HTLC 之前的状态。
在同一通道上的本地支付怎么办?
将示例中的 Mark 替换为 Kris。获利。
洋葱路由 #
付款的第一个发送节点称为 源节点 。最后一个节点即收款人,称为 终端节点 。源节点和目的节点之间的每个中间节点称为 “跳”,每个跳必须为下一跳设置一个出站 HTLC。我,即发送节点,发送的信息可称为跳有效载荷或跳数据,整个信息称为 离子 。
因此,如果我用这个新术语重新表述上一段中的例子,我们可以说我 用跳数有效载荷数据构建了一个洋葱,并告诉每个跳数如何构建出站 HTLC 以向克里斯 发送付款。
我可以使用两种可能的格式向每个跳转点发送信息,一种是传统格式,称为 跳转数据 ,另一种是更灵活的格式,称为 跳转有效载荷 。
我应该从哪里开始构建这条信息?
很明显,应该从将传递给最终节点 Kris 的跳转有效载荷开始。这个字段被称为 “short_channel_id”,是一个信道引用,作为跳转有效载荷的构建者,我需要用它来引用信道。Kris 是我的收件人,所以我将其全部填为 0,因为 Kris 是有效载荷的收件人,所以他不会构建出站 HTLC。
当然,除了克里斯,没有人会知道这些信息,因为这些信息是加密的。
然后,我就可以开始将洋葱报文序列化,其格式称为类型-长度-值,适用于跳转有效载荷。然后,我还将为马克准备跳转有效载荷。
如果amt_to_forward
是 50100 聪币,Mark 将向 Kris 支付 100 聪币的路由费用。时间锁定和路由费用的期望值由两个 HTLC(传入和传出 HTLC)之间的差值决定。
假设在我和 Mark 之间还有一个节点,这将使洋葱信息的路由变得更加复杂。
我有三个有效载荷: 我为 Kris、Mark 和 Lucas 构建了它们。这三个跳转有效载荷将被洋葱包裹!
好了,现在我必须生成几个密钥,以便对洋葱的各层进行加密:
- 我必须确保只有最终节点才能读取它。
- 每个中间节点只能知道上一个节点和下一个节点。
- 没有人需要知道路径的总长度。
- 每个中间人都能验证信息没有 篡改 。
这个构建过程被称为数据包构建。洋葱路由中使用的一个有趣技巧是,让每个节点的路径长度相同,就好像每个节点看到的洋葱仍在其路径的起点,前面还有 19 个可能的跳数(因为洋葱路由路径的最大跳数是 20 跳)。
当每一层被 “消除 “时,垃圾数据也会被添加进来作为填充物,使有效载荷始终保持相同的长度。
All beautiful, but how is this onion created in practice?
首先,洋葱从最终节点(即信息接收者,即 Kris)开始构建。
我首先创建了一个 1300 字节的 “空 “字段,这个字段是用某个密钥伪随机生成的。
我开始插入之前构建的跳转有效载荷,(抽象地)从左侧插入,因此不得不将填充器进一步移到右侧。这一操作会导致文件大小超出 1300 字节,因此一小部分填充物会 “掉出来 “并被删除。
此时,Kris 的数据在洋葱信息中是透明的,所以我必须用 Kris 也知道(也是唯一知道)的名为 rho的密钥对其进行混淆。为了进行混淆,会从这个密钥生成一个伪随机数据流,然后在跳转有效载荷和由 rho 生成的这个数据流之间进行 XOR 运算。
最后,为了保证 Kris 发送的信息的真实性和完整性,我们还使用了另一个名为 mu的密钥来计算 “HMAC”,它只不过是基于整个跳转有效载荷哈希值的校验和。
现在轮到马克的跳转有效载荷了。我执行同样的步骤,不同的是,现在我不再从 1300 字节的填充数据开始,而是从包含克里斯的跳转有效载荷的 1300 字节开始,并对其进行适当混淆 🙂。
从左边插入马克的跳转有效载荷时,我必须非常小心,避免覆盖克里斯的部分数据!在这里,我还使用 “mu “密钥计算了 HMAC,并使用 “rho “密钥混淆了所有内容。不过,这次只有我和马克知道 mu
和 rho
这两个密钥,因为这次跳转只针对马克。
注意两点:
- 我们有两个 HMAC,一个是马克新添加的外部 HMAC,另一个是克里斯的内部 HMAC。
- 当马克收到洋葱信息时,他无法量化信息中有效载荷的数量,对他来说,信息看起来总是可能的 20 个有效载荷中的第一个,这正是因为我们使用了稍后将介绍的技巧来固定信息的长度。
我也为卢卡斯执行了同样的步骤。洋葱有效载荷已经准备就绪,我可以将其发送给卢卡斯,他是我的新渠道合作伙伴。最终,它将由以下部分组成:
- 1 个字节表示洋葱版本,以便在未来协议格式更新时进行区分。
- 33 个字节的公共会话密钥,每个节点都将使用该密钥来计算著名的混淆密钥和校验和(还有第三个我还没告诉你的密钥,叫做 “pad”)。
- 我们的洋葱有效载荷为 1300 字节。
- 最外层的 32 字节 HMAC 将由我的渠道合作伙伴卢卡斯验证。
我已经准备好向 Lucas 发送 update_add_htlc
消息。
首先,它必须用承诺事务更新信道状态,但这部分我们已经详细介绍过了,所以我就跳过不说了。Lucas 需要进行的 “新 “操作包括获取会话密钥、生成 “mu “密钥和验证 “HMAC “校验和。一旦完整性和真实性得到验证,Lucas 将需要检索其跳转有效载荷,然后将洋葱转发到下一跳。
问题:如果卢卡斯删除了他的跳转有效载荷,我们就会失去固定的 1300 字节长度(非常糟糕)。
这就是我提到的窍门:在向下一个节点发送洋葱之前,每个节点都必须生成一个填充器。
卢卡斯的做法是将 1300 字节的跳转有效载荷加倍,然后并排放在一起。现在他有两个 1300 字节的跳转。第二个跳转节点会将其全部设置为 0(零填充)。它根据会话密钥生成 “rho “密钥,并在 2600 字节跳转有效载荷(一个为真,另一个为零填充)与根据 rho 密钥生成的 2600 字节流之间执行 XOR。
由于我们第二次应用了 XOR,所以包含真实跳转有效载荷的前 1300 个字节被去掉了偏移量,这样 Lucas 就能读取跳转有效载荷,而 1300 个零填充字节与由 rho 密钥生成的数据流之间的 XOR 将产生伪随机数据。
这样,卢卡斯将读取它所欠的数据,并将其从跳转有效载荷中移除,所有其他字节向左移动,以覆盖卢卡斯有效载荷中缺失的空间,并使用 XOR 操作中的剩余填充物来保持 1300 字节的长度。不过,为了向 Mark 发送洋葱数据包,必须添加外部 HMAC,以便 Mark 验证完整性和正确性。
Lucas 的跳转有效载荷包含了与 Mark 构建 HTLC 所需的全部信息!
马克会执行与卢卡斯完全相同的步骤,最后克里斯会收到最终的有效载荷。当他收到支付哈希中的 “update_add_htlc “时,他知道这是给他的支付,所以这是最后一跳。它不会做任何不同的事情(或几乎不会):它会从洋葱上移除一层,但不会转发任何东西,因为是时候回复一个 update_fullfil_htlc
给 Mark 以赎回 HTLC 了。这样做的方向相反(克里斯->马克->卢卡斯->肌肉松弛),所有 HTLC 都会被赎回,频道余额也会相应更新。
八卦 #
到目前为止,我们已经了解了如何构建洋葱信息以及如何在节点间传递信息,但现在的问题是: » 如何构建有效的路由?
如何建立通往收件人的有效路由?我如何知道他的信道信息?
我需要一个 “信道图”,它只不过是一组(相互连接的)公开宣布的信道及其连接的节点。
所使用的协议称为 gossip ,它允许每个节点与闪电网络共享信息,并在创建频道时利用它来传播 “新闻”,例如: 我创建了一个新频道: 我创建了一个新频道。我正在通知我的所有其他频道这个新连接。致我的其他同行:请传播这条信息,让它传遍整个网络。
闪电网络(几乎)等同于闪电通道图。
我们谈论的是一个点对点网络,因此 重要的是要有一个初始引导阶段,以便节点可以 看到彼此。第一步是发现至少一个属于该网络并拥有完整通道图的对等节点。
使用无数引导协议中的一种,我可以真正连接到一个对等点,所以现在我需要 下载和验证 通道图。完成后,我也可以开始打开支付通道了。
重要的是要不断更新图表,发现并验证新的渠道,消除已关闭的链上渠道,同时每两周左右消除一次没有’ 引导 ‘的渠道。
如何进行初始引导?
一个简单的方法是利用闪电节点软件中的一些 “硬编码 “对等节点,但由于其脆弱性而不切实际。这种方法被用作 后备 ,以防我们无法通过其他方法(即 “启动 DNS”)找到对等节点。
要使用DNS, BOLT #10 进行对等点发现,我们需要使用三个记录:
- SRV 记录,用于查找节点的一组公钥。
- 将公钥映射到 IPv4 地址的 A 记录。
- AAA 记录,用于将公钥映射到 IPv6 地址。
当你输入 “m musclesatz.com “时,你必须把 DNS 记录 “A “和 “AAA “看作是帮助你找到我的在线网站的地图。
A “记录就像一张地图,可以通过街道(123 Main Street)找到老社区的房屋。实际上,它是一个 IPv4 地址。而 “AAA “记录就像一张地图,可以在一个崭新的、最先进的社区中找到房子,因为它已经非常先进,不再有像 IPv4 那样的标准地址,而更多是一系列数字和字母。
我们需要公钥和 IP 地址才能连接到节点!
所以步骤如下
确定一个支持 Bolt #10 协议的 DNS 服务器。为此,我可以使用由主要闪电网络实现维护的通用种子服务器。
- 我发出 “SRV “查询,获取一组可用于引导的对等节点。每个对等节点都由一个公钥标识,该公钥以一种名为 “bech32 “的格式编码。
我对从 SRV 查询响应中获取的公钥进行解码,以获得节点标识符。
运行 DNS 查询,结合目标节点的标识符(公开密钥)获取目标节点的 IP 地址。
使用 Lightning 客户端连接节点。
我找到我的对等节点并建立第一个连接。
现在,我必须同步并验证定向通道图。之所以称为定向,是因为箭头有相关的方向,这意味着节点 1 与节点 2 之间的关系可能不是对称的,即不一定意味着节点 2 与节点 1 之间的关系是相反的。
通道是UTXO(多位 2 对 2 地址),因此我们可以给通道图下一个新的定义: 是比特币UTXO的子集 。我们将看到,你无法伪造一个 图 。
图中的信息通过三种信息在网络中传播,详见 Bolt #7:
节点公告(node_announcement):执行节点 “欢迎”(welcome),包含标识符、节点的支付路由能力或路由策略等信息。这些信息不会写入时间链,只有在有相应的后续 “channel_announcement “消息时才有效。验证这些消息非常简单:如果消息已经存在,那么新的 “channel_announcement “将有一个更长的时间戳;如果没有消息,那么本地信道图中必然存在一个 channel_announcement(我们稍后会详细讨论)。 它还必须具有
- 有效的签名。
- 根据地址标识符以升序排列的所有包含地址。
- 别名字节必须使用有效的 UTF-8 编码。
channel_announcement:现在我们可以担心宣布新通道的存在了。如果我希望我的节点允许其他洋葱信息被路由,我就必须公开宣布它。理论上,如果一个通道没有被宣布,它就无法接收付款;但实际上,由于路由提示的存在,它可以接收付款,路由提示包括了发送者正确路由洋葱信息的信息。正是因为存在 未公布 的通道,所以实际通道图的大小未知!
一个细节:为了避免垃圾邮件并确保可靠的身份验证,我们使用了比特币的时间链。一个节点要接受 “channel_announcement”(通道公告),就必须在时间链上证明通道开放的存在。
如何在时间链上显示通道引用呢?使用完整的通道输出点是很愚蠢的,因为验证者必须有一份完整的UTXO信息集来进行验证,而通常是完整节点拥有所有这些信息。
使用 短通道 ID 要好得多。要创建这一参考,我们需要依赖该输出在时间链中的位置。我们只需引用某个区块,指向其中的某个交易,然后再指向该交易创建的特定输出。这就是简短的通道 ID(或 scid
)。
- channel_update`: 流言协议中的第三条也是最后一条消息。它用于更新 “闪电 “网络中支付通道的信息,如路由表,或通知其他节点支付通道的变化。这类信息允许节点通过最有效和最新的渠道进行支付。
我们意识到,流言是一种经常性活动,当节点在闪电网络上启动时,它就会开始接收流言,包括它刚刚分析过的信息;有了这些信息,它就会开始构建自己的通道图。收到的信息越多,它的图谱就越准确、越有效,从而找到发送付款的最佳路径。
此外,还可以存储其他信息:各种 “闪电”(Lightning)实施方案可以链接其他元数据,例如评估节点作为支付路径的对等节点的 质量的分数。
###寻路
找路是什么,我们要解决什么问题?
闪电支付主要取决于找到一条连接发送方和接收方的路径。这个过程被称为寻路,但……它不是BOLT标准的一部分!如果在 BOLT 中指定了路由,路由搜索和选择就由付款发送方根据其使用的实现算法来完成。
问题是要在发送方和接收方之间找到 最佳 路径,即在交付成功率、费用低廉和时间短方面最佳的路径。
为了更好地理解我和克里斯之间的中本聪传输问题,需要定义 3 个属性:
- 容量:是多位元地址 2-di-2 中资助的聪币总量(最大值)。
容量(我与 Kris 的通道)= 余额(m musclesatz)+ 余额(Kris)
余额:是通道中每个对等节点持有的金额。
流动性:是实际可以发送的可用余额。我的信道的流动性等于我的余额,但要减去信道储备金和我尚未支付的 HTLC。
capacity(musclesatz) = balance(musclesatz) - channel_reserve(musclesatz) - HTLCs(musclesatz)
对整个网络来说,唯一的公共价值就是通道容量。为了保持高度的可扩展性和隐私性,余额不对外公布。 如果它们是 公开的,我们就可以用任何寻路算法以最小的代价解决寻路问题。
我们必须变得聪明起来,但同时我们可以说,至少我们可以确定信道的流动性在(或等于)“信道储备 “范围内(在最小情况下,这是一个下限),信道容量减去 “信道储备”(上限)
储备 <= 流动性 <= (容量 - 储备)
这就是我们的不确定区间,我们不知道余额,但我们对流动性有一个粗略的估计,流动性必须在区间内。网络不知道这个区间,因为只有 Kris 和我才能知道,但我们可以利用支付尝试失败的 HTLC 来更新我们的流动性估计,从而降低不确定性。
如果 HTLC 失败是因为我们认为它可以处理 N 个聪,而我们发现它只能交付 M(M < N),那么我们就可以将范围的上限改为 M -1
。
简化一下,我们使用 Dijkstra’s [A (A-star)](https://en.wikipedia.org/wiki/A _search_algorithm) 算法,其中我们有节点,并为节点之间的连接分配一个权重。这个权重就是支付的路由费用。
然而,由于发送方不知道信道的流动性,而只是一个理论范围,因此问题很复杂。我们可以 猜测 哪些连接有足够的容量允许付款路由。我可以尝试按权重对这些路由进行排序,并按顺序尝试每条路由,直到付款成功。对于失败的支付,我会利用错误 HTLC 来更新我的图,修复限制并减少不确定性,因为我已经探索了一条路径。
因此,总结如下
- 我构建通道图。
- 根据一些启发式方法进行寻路。
- 尝试付款
- 对于不成功的付款,我会调整我的间隔时间,以减少下一次提交时的不确定性。
矛盾的是,我从错误中学习,因为不成功的付款越多,我就越能减少区间的不确定性,并获得更准确的可能路径图。从错误中获得的知识对我今后的付款非常有用。
但要注意:这并不是 “永恒 “的知识,因为当其他节点发送或路由付款时,这些知识就会过时。除非我是发件人,否则我永远不会看到这些付款。
正如我们所看到的,洋葱路由只允许我看到一跳。我们还必须考虑,在内存中存储这些关于各种路由不确定性的知识变得无用之前,需要存储多长时间。
多部分支付是 2020 年推出的一项功能,相当于一种搜索最佳路径的策略,但会将支付拆分成许多小部分,作为 HTLC 在不同路径上发送,显然保留了支付的原子性。它们必须全部到达收款人,否则交易就会失败。
它们之所以是一种改进,原因很简单:小额支付不必适应通道的流动性,因为 “小部分 “一定会有足够的流动性。这就好比我们的不确定性区间是一个干草堆,而我们的小额支付是一根针。
当然,在统计上并不能确定所有的多部分支付都会通过。
LN 的安全性和攻击 #
“闪电 “是私有的吗?
这取决于我们对隐私的理解。
一个节点可以看到它的后继节点和前继节点。但是,如果节点高度发达、连接超强,它就会产生启发式和联想,就像大型非托管钱包一样。这些提供商的节点负责将很高比例的支付转入或转出该节点。
那么,如果两个或更多节点被恶意对手控制,会发生什么情况呢?如果两个串通的节点在同一条支付路径上,它们会立即意识到它们在为同一笔支付路由 HTLC,因为前图像哈希值是相同的。有哪些风险?
信息安全通常概括为三个方面:
- 责任 :秘密信息是否真的到达了预定的接收者手中?
- 完整性 :信息在传输过程中是否被篡改?
- 可用性 :系统是否正常运行,还是正在遭受某种拒绝服务攻击?
不仅是 “闪电 “系统,任何系统的现实情况都是,无法确定攻击者不会得逞。这是一场零和游戏。当然,你可以降低风险,但永远不可能实现零和。
比特币和闪电的隐私性有区别吗?
乍一看,闪电的隐私性更好。在比特币中,交易不会将数字身份与特定地址联系在一起,但交易以纯文本形式传输并被分析也是事实。近年来,许多以此为生的公司如雨后春笋般出现。
在 “闪电 “上,支付并不会传输到整个网络,最重要的是,也不会像在时间链上那样永久保存。
其中之一是什么?大额支付的路由 “选择 “可能更少。
还有呢?闪电节点保持永久身份,而比特币节点则没有: 我只能从我用来在时间链上打开支付通道的节点接收和发送付款。我不能使用其他节点。我还必须公布我的 IP 地址和 ID,这就在节点 ID 和 IP 地址之间建立了永久链接。IP 是匿名攻击的中间环节,通常与用户在现实世界中的物理位置相关联。
最后,“闪电 “用户可以通过封锁通道或用尽通道来拒绝服务。转发支付需要在 HTLC 中沿途封锁余额(稀缺资源)。攻击者可以在未完成支付的情况下发起多次支付,从而长时间占用资金(但这也取决于所设置的时间锁定延迟)。
总之,我们可以断定,在隐私方面,比特币和闪电没有赢家,它们各有自己的弱点和优势。我们可以说的是,许多研究团队目前正在努力改进两者的安全性和隐私性。
作为本文的最后一部分,我将综述闪电可能遭受或已经遭受的攻击。我们马上就要结束了。
发送方和接收方的连接
在 “闪电 “中,参与者的匿名性是保证支付自由的关键,这一概念也是比特币诞生的支柱之一。但是,恶意用户可能会试图通过发现付款的发件人和收件人来侵犯这种隐私。造成这种情况的原因可能是多种多样的。这不仅会损害支付的安全性,还会鼓励对某些收件人或发件人进行审查。我们可以假定,有两类对手会试图破坏 “闪电 “中的匿名性:“路径外 “对手和 “路径内 “对手。非路径 “对手试图在不直接参与支付路由过程的情况下推断出付款的发件人和收件人,而其他对手则可以利用他们在支付过程中获得的信息来发现发件人和收件人。
前者(非路径攻击)使用一种称为 “探测 “或 “探查 “的技术来推断各种支付渠道中的个人余额。获得这些信息后,攻击者就可以比较 “t1 “和 “t2 “时间的网络快照,找出已经发生的支付,并发现发件人、收件人和支付金额。例如,在 t1 = 21:00 时拍摄网络快照,在 21:01 时拍摄另一张快照。如果只发生了一次付款,那么找出相关方及其金额是轻而易举的事,这也是因为每次余额变化时都必须更新通道状态变化!如果有多笔交易,情况就会变得更加复杂,这就是为什么这些公司开发了启发式方法,将各种付款分离开来。
而路由器则积极参与支付路由,并能观察到支付信息。他们的技能在于利用在支付过程中获得的信息,将某些节点排除在发送方或接收方的匿名集之外。例如,中间节点可以观察任何正在路由的付款金额和时间锁的各种延迟,并将容量小于正在路由的付款金额的任何节点排除在匿名集之外。
拒绝服务
暴露在线资源意味着增加因可能的拒绝服务而导致资源不可用的风险。攻击者如何拒绝为我提供服务?很简单。攻击者会发出大量无用的请求,这些请求与合法请求毫无区别。这些请求不会造成资金损失,其目的只是让 DoS 的目标受害者离线。该网络属于 “闪电 “网络,对公共资源的使用收取费用,但在这种情况下,通道是公共的,费用是路由费。当一个节点为我提交付款时,该节点会使用其数据和带宽更新通道的状态,除其他外,金额会被停放(HTLC),直到结算或过期(时间锁定)。支付失败不会产生任何费用,因此对我们这些可怜的网络合法用户来说是件好事。但对攻击者来说就没那么好了,因为这样他们就可以无偿地向网络发送路由请求。
基于拓扑的攻击是 DoS 的一种概念变体。闪电并不是完全去中心化的,因此要想从支付路由中 “剔除 “节点,攻击者就必须攻击通道图边缘的节点。攻击与许多其他节点相连的中型/大型节点更有意义。
通道中断
每个节点最多可处理 483 个 HTLC。在信道中断攻击中,攻击者通过目标信道正好路由 483 笔付款。可能会发生什么?一个类似的版本涉及流动性阻塞,攻击者使用大量 HTLC 消耗受害者通道的所有可用带宽。这肯定比发送 483 个 HTLC 要昂贵得多。
跨层去匿名化
闪电通道的创建和关闭发生在时间链上,这使得攻击者更容易使用链外和链上数据对用户进行去匿名化。这些攻击者的目标是什么?在第 1 层创建由同一用户拥有的比特币地址群。
- 对策 1:不要重复使用交易资金输出来打开新通道,避免许多启发式方法。
- 对策 2:永远不要使用唯一的外部资金来源,最好各不相同。
在第 2 层创建同一用户拥有的 “闪电 “节点集群。
- 在这里,我想问大家一个问题:使用别名很好,它简化了生活,提高了可用性。这是毋庸置疑的。但如果我使用自己的域名作为别名,你认为这是个好主意还是坏主意?如果你认为这是个坏主意,那么应该采取什么对策?
在时间链中的闪电节点和地址之间建立唯一链接。
- 与上述两种对策相反的做法很容易实现闪电节点与第 1 层管理地址的实体之间的跨层链接。
能否保护?
这并不容易,但可以做到。
- 如果不想进行路由选择,也不导出,就可以使用未通知的通道。
- 如果要进行路由选择:
- 将您愿意接受的最小 HTLC 大小设置为高于默认值。这样,每个插槽就不会被太小的付款占用(避免垃圾邮件)。
- 限制对等方可占用的最大插槽数量。
- 监控出错率,如果某个点对点的出错率超过了一定标准,则限制其出错频率。
- 使用影子通道,它们是与现有通道平行的支付通道。它们可用于路由,但不能公布。
- 尝试在接受和拒绝通道之间找到适当的平衡。当同伴在你的节点上打开一个通道时,你无法知道它是否会被用来攻击你的节点。
- 避免为你的节点和域名使用相同的别名(这就是上面问题的答案😜)。
- 使用 Tor。
- 继续阅读,了解更多信息,不要只依赖这篇文章。