作者:Gloria Zhao
来源:https://btctranscripts.com/adopting-bitcoin/2021/2021-11-16-gloria-zhao-transaction-relay-policy/
本文为 Gloria Zhao 在 Adopting Bitcoin 2021 大会上的演讲,由 Michael Folkson 转写为文字稿。
演讲文档:[github.com/glozow/bitcoin-notes](https://github.com/glozow/bitcoin-notes/blob/master/Tx Relay Policy for L2 Devs – Adopting Bitcoin 2021.pdf)
引言
哈咯我是 Gloria,我在 Brink 公司为 Bitcoin Core
开发。今天我准备聊聊 “交易池规则(mempool policy)” 以及为什么你不能理所当然觉得你的交易会被广播、你的手续费追加方法要如何才能生效。我们正在做的事情就是要解决这些难题。如果你发现交易池有点难懂、不稳定、不可预期,你不确定自己的开发应用时能否放心使用交易池这个接口,这个演讲就是为你准备的。我猜大部分内容都对你有用。希望今天我们能让交易池变得更容易理解。
我的目标是让大家尽可能理解为交易的传播设置的各种限制。而我尝试的视角是作为一名比特币协议开发者的视角 —— 协议开发者如何思考交易池规则和交易转发策略。我接下来要定义什么是 “交易池规则”、我们如何分析它、问什么我们需要特定的策略并定义 “钉死攻击(pinning)”。我会从一些已知的限制开始,可能会清理一些关于攻击界面的误解。我还会提到一种对闪电通道的攻击。
我的目标是,在大家听完演讲之后,就能成为朋友,然后开始讨论如何在 Layer 1 和 Layer 2 之间开发通畅的桥梁。
任何人都能发送比特币支付
我想从头开始,确认我们对比特币的理解是一致的。我们想要比特币这样的系统,是为了让任何人都能把钱发给另一个人。这就是比特币的全部。
人们经常使用 “免准入(permissionless)” 和 “去中心化(decentralized)”、“免信任(trustless)” 这样的词来指称比特币。但可能一个更准确的框架是这样的。我们想要的第一个特性,是非常便宜就能运行一个节点。假如说 —— 我们举个例子哈 —— bitcoind
无法运行在 MacOS 上,或者它需要 32 GB 的内存、需要每月 2000 美元的代价才能运行一个全节点,那这对我们来说是不可接受的。我们希望它能运行在你的树莓派(Raspberry Pi)上,也就是很便宜的硬件就能运行。我们想要的另一个特性是 “抗审查性(censorship resistance)”,因为我们在尝试开发一个系统、帮助那些无法获得传统金融系统服务的人。如果我们开发出来的支付系统,很容易就能被某一些政府、富有的大银行、大公司任意审查他们不喜欢的交易,那我们就失败了。还有一种特性,显然,是安全性。在这个领域,我们不能依赖于某一些政府,由他们来确定 “这个节点在现实生活中就是这个人,他做了一些坏事,我们去起诉他”,我们没有这样的选择。我们希望每个人都能运行自己的节点,这是比特币的设计哲学中属于免信任性和去中心化的一部分。因此,安全也不是一个可选项,而是我们设计交易转发策略时候的首要考量。第四,我们不能强迫任何人运行比特币节点的一个新补丁,所以,当我们说某一个升级会优化用户的隐私性、但矿工的成本会变得更高、损失 50% 的收入或交易手续费会变得更高时,我们无法期待其他人会升级自己的节点。在讨论交易转发时,激励兼容也是一个始终需要考虑的东西。这就是我们思考这些事物的基本框架。
点对点的交易转发网络
希望这些对你来说并不意外,但是,要知道,在比特币中,我们需要在一个分布式的点对点网络中实现这些特性 —— 理想情况下,每个人都运行一个比特币节点、连接到比特币网络。这样的连接有许多形式:可能是你在自己的笔记本后台运行一个 bitcoind
程序、你安装了 Umbrel
或者 Raspiblitz
的树莓派小电脑,也可能是一个服务几千个智能手机钱包用户的大公司的服务器。所有这些都是一个比特币节点。它们本身非常不一样,但在点对点网络中,它们都是一个比特币节点。在你尝试给某人支付的时候,你要做的就是连接你的节点、提交你的交易、将交易广播给你的对等节点,希望这些对等节点会继续广播你的交易、最终将交易传播给某一个矿工。矿工会将交易打包到某个区块。
交易池
交易转发策略的真正关键的地方在于:每个参与交易转发的人都维护着一个交易池(mempool)。如果你想知道 “交易池” 的定义,我这里有一个:它是 “未确认的交易(unconfirmed transactions)” 的缓存(cache),并且是为了选出最为激励兼容(也即手续费率最高)的交易集合而专门优化的。这个优化方向不论对矿工还是非矿工都有意义,它帮助我们优雅地重组(re-org)、允许我们以更私密的方式设计交易转发策略,因为我们希望它比 “只懂 接受-转发” 更聪明一些。这是非常有用的,我在这个主题上写了许多东西。
交易池规则
你可能听过这样一句话,“本就没有唯一的交易池(there’s no such thing as the mempool)”,这是对的。因为每个节点都维护着一个自己的交易池,而且每个节点都可以使用不同的软件。他们可以随心所欲配置其交易池规则,因为比特币是自由的,对吧?不同节点的交易池可能看起来大相径庭,你的一代树莓派可能无法为交易池分配那么大的内存,所以你的交易池的策略可能更加严格。相反,一个矿工可能维护着一个非常大的交易池,因为他们希望有一个很长的备用交易队列,在区块还未满载的时候就可以从中选择交易、打包到区块中。我们将交易池规则称为 “共识规则之上,节点各自设立的交易验证规则” —— 共识规则是所有未确认交易都必须遵守的规则,否则就一定无法进入任何交易池。这些是未确认的交易,跟已经进入区块的交易无关。限制哪些交易能进入自己的交易池是各个节点的特权。
作为协议开发者,我们专注于保护 bitcoind 的用户
我真正想强调的是,从一个比特币协议开发者的角度看,我们想做的是保护运行比特币节点的用户。我跟应用开发者有过许多争执,他们认为我们应该保护正在广播交易的用户 —— 在某些情况下,这两者是不一致的。在交易池验证中,我们可用的资源是非常受限的。一般来说,只有 300 MB 的内存,而且我们不希望(比如说)花半秒钟来验证一笔交易,这太费时了。再强调一次,即使我们希望支持诚实的用户、吹哨人、社会运动人士、在传统世界中被审查的人,我们也需要注意,从设计上来说,我们连接的是一个个对等节点,这些对等节点也可能是攻击者。我们分析的最常见的情形是耗尽 CPU 资源。如果要花费半秒才能验证一笔交易,那真的是太慢了,因为一秒钟就可能用几百笔交易进入交易池。我们的节点是开源的,所以,如果我们的交易池代码中有 bug,那么(比如说),用户就有可能因为使用某种脚本的交易而耗尽内存,我们需要预想到它会被利用。还有一件事情是,在挖矿的竞争环境中,某个矿工用垃圾填埋他人的交易池、或者让对手的交易池变得无用、阻止高手续费交易进入对手的交易池,有时候会给 TA 带来好处。或者,在他们想发动长程攻击(long range attack)的时候,他们可以一边发送交易,让每个人都必须花半秒钟来验证一笔交易,另一边广播新区块。不论如何,他们让整个网络停顿了半秒,这给了他们提前挖掘下一个区块的优势 —— 这可不是件小事。或者,他们可以启用别的一些攻击。
另一个我今天不会多谈的顾虑是隐私性,我主要会谈闪电通道中的对手方,但我们也应该意识到,具有相互冲突的交易的闪电通道对手方可能会在手续费追加竞赛(或者说钉死攻击)中来回拉扯。我们希望做到的事情是:只要诚实的用户广播了一些激励兼容的东西,就总是能够赢得竞赛;至少是大部分时候吧。当然,交易转发策略也是一个我们高度在乎隐私性的领域。如果你能够发现发出某一笔交易的最初节点,那么你就能将一个 IP 地址与一个钱包地址关联起来,不论你如何在链上隐藏信息,这笔交易都会被去匿名化。这是一个对隐私性非常敏感的领域。这方面的另一个例子是,也许发起交易是最便宜的分析网络拓扑图的方法。在优化隐私性的时候,我们一直在努力保证不要搬起石头砸自己的脚。我今天不准备过多讨论隐私性,但这确实是顾虑之一。
策略的光谱
我们可以将策略想象成一个光谱,一端是 “理想的” 完美的交易池,我们有无限的资源,可以在受到交易或者需要组装区块时冻结时间,因此我们有无限多的时间来验证交易、组装区块。在这种情况下,也许我们会接受所有在共识上有效的交易。而在光谱的另一端,是完全防御性的、保守的交易池规则:我们不验证任何东西,不花费任何验证资源在来自我们所不知道的人的任何东西上。不幸的是,从网络层面看,为了让这种策略可持续,我们将需要一个中心身份注册表,将节点与钱包地址对应起来。这时候我们就变成了一个银行。
这两个端点都没有达到我们想要的设计目标。我们想要的是某个在中间的东西。
DoS 抗性并不是全部
但是,如果(DoS 抗性)是唯一的顾虑,那又太简单了。DoS 抗性并不是我们考虑的唯一东西,这是一种常见的误解。我们回到上面说的光谱的两端。我们会看到,线性的方法并不是全部。即使我们拥有无限的资源,我们真的想要接受所有在共识上有效的交易吗?答案也是否定的。我能提供的最好的例子是软分叉。虽然现在所有在共识上有效的交易所构成的空间是这么大,但这个范围也有可能会缩小。比如在最近的 Taproot 激活中,我们注意到,挖出激活区块的 F2Pool 实际上并没有升级节点、部署 Taproot 的验证逻辑。问题在于,如果某人给他们发送了一笔无效的 Taproot 交易,那会怎么样?如果 F2Pool 的交易池接受了这笔交易,然后 F2Pool 将它打包到了一个区块中,那么 F2Pool、AntPool 和所有使用未升级节点的用户就会被分叉出去。我们将经历一次网络分裂,未升级的节点和实际上执行了 Taproot 验证规则的节点将被割裂。这种情况很危险,但只要 51% 的哈希率部署了 Taproot 验证规则,那我们就没问题;但这是一种非常糟糕的情况,是我们希望避免的。所以,假设所有矿工都运行了 0.13 版本以上的节点,那就执行了包含隔离见证的规则。隔离见证设置了 v0 见证脚本的语义,它会反激励所有使用 v1 见证脚本和传统脚本的交易。这就是为什么 F2Pool 不执行 Taproot 规则,也不接受 v1 见证脚本的交易进入他们的交易池、不将它们打包到区块中。这说明了何以线性视角并不完美。
光谱的另一端是完全防御型的策略。问题在于,这里是否有一些策略,是我们尝试保护节点免受 DoS 攻击但伤害了用户的?这就要回到我们前面说的,我跟应用开发者的讨论,他们认为有一些策略伤害了他们的用户。例子之一是 “交易后代(descendant)” 的数量限制。在 Bitcoin Core
的标准化规则中,为一笔交易及其后代设置的默认限制是,在交易池中,这样的一串交易整体不能超过 101 k vB。这是一个有意义的规则,(如果没有这样的限制)你可以想象某个矿工会广播一连串的交易,使得每个人的交易池都被一笔交易及其后代占满;他们只要发布一个与某一笔交易冲突的区块,所有人的交易池都必须清空。其他人要花费几秒来更新目录,然后得到一个空的交易池、没有任何东西能打包到区块中,而这个矿工就可以先人一步,开始挖掘下一个区块。所以说,这个规则可以可保护我们的资源有限的交易池。但是,对闪电网络的开发者来说(可能你也注意到了),在提议为闪电通道中的承诺交易加上 “锚点输出(anchor outputs)” 时,其想法正是,让每一方都可以立即发送携带更高手续费的子交易,来为承诺交易追加手续费。这是锚点输出背后的想法,是个好主意。但是,他们也想到了:“要是其中一方发送了一笔体积为 100 k vb 的子交易,这就触发了交易池默认策略的限制,那另一方还怎么追加手续费呢?” 这就是人们所知的 “钉死攻击” 中的一种。钉死攻击是一种审查攻击,攻击者利用了交易池的策略来阻止某一笔交易进入节点的交易池,或者阻止已经在交易池中的一笔交易被挖出。在这种情况下,他们就是在阻止这笔承诺交易被挖出。
于是,这就有必要提到 CPFP carve out 例外规则了,它是被硬塞到交易池规则追随的,就是为了解决这种对闪电用户的攻击。这个规则有点复杂:假设额外的后代交易最多只有两个祖先交易,而它自身又小于 10 k vb,等等,那么,它可以得到豁免,不会被交易池规则拒之门外。这导致了许许多多的测试 bug,而且是一种丑陋的补丁。我在这里并不是想抱怨它,我是想说,一般的比特币节点用户,如果完全不关心闪电网络的话,那就没有理由在自己的交易池规则中启用这一条 —— 它可能不是完全激励兼容的。我后面会回到这一点。
为能够追加手续费而设计激励兼容的策略
关于手续费追加(fee bumping),我想指出一些人们可能会喜欢的交易池规则案例。这些手续费追加方法全部都是由交易池规则实现的。
第一种是 “RBF(手续费替换)”,它来自于交易池的一种行为;当我们收到一笔新交易、跟已经在交易池中的某一笔交易相冲突时,我们不会直接拒绝这笔交易,而是检查这笔新交易是否支付了高得多的手续费,从而可以考虑替换掉交易池中的那笔旧交易。这对用户来说是好事,因为它是激励兼容的,它允许用户为自己的交易追加手续费。
另一种方法则基于交易池知道祖先交易和后代交易所组成的一个交易包,在我们为区块打包交易的时候,我们会把后代交易的手续费率也考虑进去。这使得子交易可以为父交易支付手续费,也叫 “CPFP(子为父偿)”。而且,当我们要从交易池中驱逐交易的时候,我们也不会驱逐自身手续费率较低、但后代手续费率较高的交易。它也是激励兼容的,也能帮助用户。
交易池规则
总结一下我们迄今为止的理解。我们定义了 “交易池规则” 作为 “共识规则之上、节点为尚未得到确认的交易设置的验证规则”。我们也列举了一些我们需要交易池规则的理由。最显著的理由之一是 DoS 抗性。我们也尝试了设计一种激励兼容的交易接受逻辑,它让我们可以安全地更新网络的共识规则。我还应该提到第四种分类,网络最佳时间或者说标准化规则(例如粉尘限制),但我们今天没有时间了。那是我认为更复杂的一种东西。
我将这个演讲命名为 “交易转发策略”,是因为我们注意到了一个事实:比特币网络上的大部分节点都运行 Bitcoin Core
。而在推特上的无差别调查显示,在交易池规则上,大部分人都使用默认设置。所以,当你要为交易池规则变更开启一个 PR 时,这有点令人畏惧,但也是一个好事,因为它让我们对一笔交易是否能传遍网络由一个预期,所以可以称为 “交易转发策略”。
策略可以认为是随意的、不透明的,而且让交易转发可以预测
现在我准备讲讲 Bitcoin Core
的默认交易池规则中已知的问题。我已经讲过,许多策略似乎是随意的,比如粉尘限制。我感同身受,所以我做了这样一个表情包来表示同情。但根本上,作为一个应用开发者,你没有广播交易的稳定接口、可预期的接口。尤其是在闪电网络或者说链下合约中,我们依赖于广播交易、及时让交易得到确认的能力来保证对手无法偷走我们的钱;没有这样的接口,就很头疼。我注意到一些交易标准化规则,其中最大的一个就是粉尘限制(译者注:对一个 UTXO 的最小面额的限制),还有交易需要遵顼的许多复杂而不透明的脚本规则。另一类是交易在一个交易池中的求值。我已经提到了一些非常小众的、在计算祖先、后代的数量限制时候的例外。我也提到了 BIP 125 RBF 规则让很多人感到痛苦,因为有时候使用 RBF 是非常昂贵的,甚至完全不划算。当然,最大的问题之一是,在交易量高涨的时候,你永远不知道接下来会发生什么,你也不可能总是为自己的交易追加手续费。我在底部放了最糟糕的一种情况,就是每个交易池都有不同的策略。我自己认为,另一种情况也可以说是很糟糕的,就是应用开发者无法很好地理解交易池规则。这也是我在这里演讲的理由。
承诺交易无法相互替换
现在,我准备介绍一种具体的闪电通道攻击,它基于一个事实:一个闪电通道内的承诺交易是无法相互替换的。在闪电通道中, 你有这些条件的组合,你可以提前商量手续费率,但这个商量发生在广播这笔交易的很久以前(当时你并不准备广播这笔交易)。而且你不能使用 RBF。如果你的对手尝试欺诈你,或者你要单方面关闭通道(因为对方不响应你),显然他们不会跟你签名一笔新的承诺交易。但也因为交易池一次只看一笔替换交易,而你的承诺交易将跟对手的承诺交易有相同的费率,所以它们不能相互替换。即使这里的 Tx2A 比 Tx1A 有更高的费率,也不能替换 Tx1B。
一个小体现,我注意到这里并没有完全封装闪电交易的所有脚本逻辑,请原谅我,我没有加入撤销密钥等等的东西。就把它们想成简化版的闪电交易就好。
闪电攻击
我们这里介绍一种发生在 Alice、Bob 和 Mallory 之间的攻击。Alice 和 Bob 有一条通道,Bob 和 Mallory 有一条通道。Alice 准备通过 Bob 给 Mallory 支付。如果闪电网络工作顺利,那么,要么 Bob 和 Mallory 都可以得到支付,要么都得不到支付。这就是我们希望的情形。当 Alice 给 Bob 一个 HTLC,而 Bob 又给 Mallory 一个 HTLC 时,两条通道内都发生了一笔交易。所以两条通道内都出现了一对承诺交易。因为 LN-Panelty,结成一对的两笔承诺交易是不对称的,这样我们才能对广播交易的一方实施惩罚。所以说,它们是相互冲突的交易。Tx1A 和 Tx1B 是相互冲突的。Tx2B 和 Tx2M 也是相互长途的。只有其中一笔可以得到广播、在链上得到确认。我们构造了这些交易,使得 Alice 给 Bob 支付,同时 Bob 也给 Mallory 支付。Mallory 可以揭示原像,然后拿走 Bob 所支付的资金;要么,她不揭晓原像,Bob 可以在 t5
之后拿回自己所支付的资金。另一方面,如果 Bob 拿到了 Mallory 的原像,他也可以向 Alice 揭晓并拿走 Alice 所支付的资金;要么,Alice 将在 t6
之后拿回自己的资金。Bob 在 Mallory 不支付和 Alice 赎回之间有一段小小的缓冲时间。但剧透一下,这次攻击的结果是 Bob 给 Mallory 支付,但 Alice 不会给 Bob 支付。对应的是,Mallory 广播交易,然后通过原像获得支付;而 Alice 也广播交易,然后拿回给 Bob 的支付。也就是说,在 t6
之后,Alice 获得返回的资金,而 Bob 无法让资金返回。
发生了什么事情呢?Mallory 在 t4
广播了她的承诺交易,并带上了一笔体积巨大的钉死交易。这两笔交易进入了每一个节点的交易池。这里就分两种情况了:或者,Bob 也有一个交易池,并且观察到了发生的所有事,他知道 Mallory 广播了这些交易;或者,他并不知情。
我们先分析第一种情况。我们需要 CPFP carve out 规则,但就像我前面说的,我并不知道为什么每个节点都会将 CPFP carve out 作为自己的交易池规则的一部分。在这种情况下,Bob 可以拿回自己的资金,他可以为承诺交易追加手续费并拿回自己的资金。但这种情况说明 CPFP carve out 对闪电网络的安全性很关键。
第二种情况,Bob 没有交易池。我认为,这是对闪电节点更合理的安全假设;对我来说,没有理由要求闪电节点需要一个比特币交易池或需要监控比特币交易池。Bob 没有监控某个交易池,所以到 t5
的时候,他说,“Mallory 没有揭晓原像,而且她也不理我,所以我准备单方面关闭通道,我要广播我的承诺交易,再追加一些手续费,然后我就能在 to_self-delay
之后拿回我的资金。”问题在于,因为 Mallory 的交易已经在交易池中了,所以即使 Bob 广播了自己的那一笔,也没用,他会疑惑 “为什么我的交易无法得到确认?”他查看区块链,看不到自己的交易得到确认,于是非常迷茫。这是因为 Mallory 已经先一笔广播了交易,而他不能替换掉 Mallory 的交易。这个问题的解决方案是 “交易包转发”。这时候,交易包转发就变成了闪电网络安全性的重要部分。
不论在哪种情况中,t5
过去了,但 Bob 还是不能成功拿回自己的资金。到了 t6
,Alice 拿回了自己的资金,但 Bob 还没有。假定 Alice 和 Mallory 实际上是同一个人,她成功地从 Bob 处偷到了这个 HTLC 的价值。希望大家都搞明白了这个例子。
声明一下,如果承诺交易商讨了手续费率,使其可以立即在 t4
或 t5
的下一个区块得到确认,那这种情况是很容易避免的。这就引向了我希望给 L2 和应用开发者的建议。
我们做朋友吧?
我的建议是暂时倾向于高估手续费,而不是低估手续费。这些钉死攻击只有在目标交易游荡在交易池底部的时候才能奏效。另一个建议是,永远不要依赖于零确认交易,因为你无法控制。你的交易池里的东西,跟矿工交易池里的东西不一定相同;甚至于网络中其他所有节点的交易池里都有一笔跟你的交易池中的交易相冲突的交易。所以,如果你无法完全控制那笔交易,就不应该依赖它(假设它还没有得到确认的话)。还有一个建议是,Bitcoin Core
有一个非常棒的 RPC 叫做 testmempoolaccept
。它会同时测试共识规则和默认的 Bitcoin Core
标准化规则;因为网络中的大部分节点都使用这些策略,所以这是很好的测试。尤其是你的应用依赖于手续费追加方法的话,我建议你们使用不同的 mempool 内容做不同的测试,甚至是相互冲突的内容、一批节点,来测试你的假设。每次放出新版本的时候都测试一下,甚至每当软件库的 Master 分支有更新的时候就测试一下。我不想给你太大的压力,但这就是我的建议,这样可以确保你的应用(在交易传播时)不会依赖于不靠谱的假设。
我也希望应用开发者能说出你的不满,不论是对我们的标准交易池规则,还是对测试用例(不足以覆盖你的应用场景)。请出面抱怨,讲出来。希望这个演讲可以说服你,虽然我们因为某些原因施加了许多限制,但我们也希望支持闪电应用。请给发送到 bitcoin-dev 邮件组列表中的提议发送反馈。
另一方面,我认为我们都同意,闪电交易也是比特币交易,所以至少在 Bitcoin Core
里面,我们的重要工作是为当前的交易转发策略形成说明书,并提供一个稳定的测试接口,来确保交易可以通过策略的检查。我们也在为当前的交易池规则开发多种多样的优化和简化。当前,我们已经同意,不应该不在 bitcoin-dev 里面讨论就限制交易池规则。但是,如果你们都不看 bitcoin-dev、或者看了也不给我们反馈,那就什么都没有用。让我们一起,让 L2 应用获得隐私性、可扩展和所有其它有用的特性,且不必牺牲安全性。
问答
问:使用 testmempoolaccept
的时候,它是硬编码了默认的交易池规则,还是它会基于我的节点正在使用的交易池规则?
答:如果你在自己的节点上调用 testmempoolaccept
,它就会使用你的节点的策略。但假如你启用了一些 regtest 节点,或者你自己编译了 bitcoind
,那应该是默认的策略。
问:所以说,如果不是默认策略,那就是我正是使用的策略。如果我想在我的节点测试其它策略的话,我可以变更我的策略,然后它就会传导到 testmempoolaccept
?
答:是的。
问:交易包转发策略有没有可能使我们能够安全地移除 CPFP carve out 规则,而不会伤害现有的闪电通道和其它应用的用户?
答:非常好的问题。我认为可以,但我还没有通盘思考过这个问题。我直觉认为可以。
问:一个关于监控交易池的闪电节点的问题。你说,假设一个闪电节点总能监控交易池是不合理的,因为它没必要。但如果你在转发 HTLC 的话,有人可能会把承诺交易发到链上,你可能会在那里看到原像。你必须接受使用相同哈希值的 HTLC。为什么我们应该等待交易得到确认,而不是在它还在交易池中的时候就采取行动呢?你不应该假设自己总能在交易池中看到它,因为确实可能收不到,但如果你能够在交易池中看到它,你就可以更快地行动。
答:我是这么理解你的问题的。在许多这样的攻击中,如果诚实的一方的监控了交易池,他们是能够缓解攻击、作出响应的。我认为这就是拥有交易池的区别,也是为什么闪电节点需要交易池来保护自己的交易。所以,一方面来说,我同意你说的。拥有交易池会带来好处。但我不认为每个人都希望闪电网络的安全性依赖于交易池。
问:而且,享受这种好处需要能够监控整个网络的交易池,这是不可能做到的。
答:是的。
问:你跟 Lisa 的 “death to the mempool” 是怎么回事?
答:我很开心能在线下讨论这个事。Lisa 和我讨论了我们有分歧的地方。她在自己的 bitcoin-dev 帖子里点名了我。人们以为我们是一致的,但实际上我们有分歧 —— 我认为这就是大家觉得困惑的地方。
问:她的意思是,交易池是不可依赖的,所以我们应该完全抛开它?
答:如他们所说,今天的交易池规则是不完美的。对于应用开发者来说,这是一个非常不稳定的接口。我已经听到许多闪电网络开发者说 “对我们来说,一个好得多的接口就是让我们公司跟某个矿工变成合作伙伴,从而让我们总是能把交易发送给他们”。比如说,所有的 LND 节点或者所有的 rust-lightning 节点都总是可以把交易发送给 F2Pool,或者别的矿池。这就解决了让我们头疼的因素,确保你的交易总是标准的、总能够通过策略检查。我理解为什么这道闪电网络开发者来说更舒服,但我感觉,它只是将 “我们需要交易池来让闪电通道变得安全” 换成了 “我们需要跟某个矿工的合作关系,才能让闪电通道变得安全”。我不认为它具有抗审查性、具有隐私性,我也不认为这是我们想要的比特币和闪电网络的发展方向。这是我个人的意见,也是为什么今天我要讲这个话题,因为我理解大家的不满,但我认为我们可以合作,创造出可靠的接口。
darosior:最终来说,这跟手续费追加方法很像,你总是有一些捷径,但这些捷径通常都会推动中心化。失去了抗审查性,我们才会意识到它的重要性。这就是为什么我们应该在弄丢它之前慎重考虑。
问:(丢失)
答:我无法提供一个具体的路线图,但我可以告诉你已经实现的东西,以及还没有实现的东西。所有的交易池验证代码都已经实现了。但还未全部合并。这部分也已经写好了。下一部分是定义 P2P 协议,这个我们每天都在推进。写出规范、形成 BIP、实现它,在审核之后合并。这是已经实现的和还没有实现的东西。你可以自己估计一下我们还需要多长时间。
问:你一直在开发交易包转发策略,你学到了什么让你惊喜的东西吗?比如说意料之外的、比你想象中更难或者更简单得东西?
答:我的惊喜在于它非常有趣。每天醒来就开始思考图论(graph theory)、激励兼容和 DoS 抗性是我的幸运。真的非常有趣。我从没想过它这么有趣。次一等的惊讶在于,并不是每个人都对交易池规则感兴趣。
问:你能不能为可能引入、不能明显看出的 DDoS 攻击界面举个例子?在我们开发交易包转发策略的时候。
答:举个例子,节点的 UTXO 集大约是几 GB。一部分数据存在硬盘上,而且我们使用分层的缓存。我们希望尽可能不让网络上的对等节点影响我们的缓存。但这会影响区块验证的性能。比如说,基于我们对一笔交易的体积限制,我们可以预期 UTXO 缓存系统会出现混乱。我们不希望交易的体积太大,因为我们希望一次性验证 25 笔交易,或者 2 笔交易,乃至 n 笔交易。这就是一种 DoS 攻击的例子。同样的道理,如果一个交易包对比单笔交易,其签名数量是呈指数级增长的,我们并不希望需要通入的验证资源也呈指数级增长。
问:闪电通道是一个非常特别的例子,它的交易包有两笔交易。这是为了先解决这个问题,然后再考虑一般的情形吗?
答:是的。单个子交易和单个父交易,对比单个子交易和多个父交易。我认为,它基本上是一种权衡,在实现的复杂性和它对 L1 及 L2 协议的有用性中取舍。基于我们知道的事实,大量的批处理手续费追加在比特币链上发生,多个父交易和单个子交易的组合是非常有用的。然后,我们对比 “单个子交易、单个父交易” 和 “单个子交易、多个父交易” 时,会发现后者的复杂性没有那么糟糕。所以两相权衡之下,单个子交易、多个父交易的功能更好。
darosior:将交易池规则的用场限制成为闪电网络提供帮助,在我看来是很奇怪的。我部分同意的对 CPFP carve out 的批评是,为多个父交易追加手续费、CPFP,对其它应用也是非常有用的。
问:我同意你的说法,交易池是一个非常有趣的研究领域。纵观整个区块链领域,许多东西都因为交易池规则而崩溃,这不一定发生在比特币上,也可能发生在其它区块链上。你有没有花时间研究过其它链尝试过的方法,失败的和成功到可以引入比特币交易池中的方法?
答:没有,我从没这样做过。并不是因为我不觉得别的地方也有好想法。我不是唯一一个思考过交易池的人,比特币也不是唯一一个关注交易池的地方。我只是注意到,我们在交易池规则中遇到的大量问题,都根源于一个事实:比特币采用了 UTXO 模式。它很棒,因为我们不会遭遇无法执行交易排序的问题;父交易一定要在子交易之前得到确认,这很清楚。但这也意味着,在我们创建的交易池中,75% 的交易池动态内存使用,都分配给了元数据,而不是交易本身。我们需要跟踪交易包的整体,而不是把它们当作一个又一个的原子。
问:你说的是在交易包转发的世界中,还是在 Bitcoin Core
里面?
答:在 Bitcoin Core
里面。
问:现在?
答:是的,75% 的数字,总是能吓到很多人。我认为,这在很大程度上是因为我们使用了 UTXO 模型。我不认为这是个坏事,只是一个我们必须处理而别人不必处理的东西。就像我说的,这也带来了别人无法获得的一些好处。
问:我在哪里可以找到更多关于交易包转发的资料?
答:Brink 最近放出了一档播客,叫做 “The Bitcoin Development Podcast”。我们已经录了 5 期,都是关于交易池规则、钉死攻击、交易包转发和挖矿优化的。我推荐你去听听看:brink.dev/podcast。
问:你可以讲讲粉尘限制和交易池规则吗?有没有计划为交易池规则降低粉尘限制?我们应该有粉尘限制吗?还是不应该设置呢?是否不同客户端对粉尘限制有不同的想法?我认为这会影响闪电节点在有分歧的时候的关闭通道的操作。
答:解释一下,“粉尘输出” 就是 “不经济的输出”。假设你有一个价值 20 聪的输出,即使当前网络的手续费率是 1 聪/vb,你也无法从花费它中得到任何好处,因为用来证明你可以花费这个输出的数据就将要求你支付 20 聪。在比特币的交易池规则中,我们会拒绝接受任何产生粉尘输出的交易。这不仅仅是为了把关,也是因为,如果在全局的 UTXO 集中产生了粉尘输出,没有任何人愿意花费它,但网络中的每个人都必须保存它。因为可扩展性本质上受限于 UTXO 集的规模,所以 Bitcoin Core
的开发者武断地选择了一个限制:我们不允许粉尘通过 Bitcoin Core
的节点来传播。但它就是我在演讲中提到的最佳习惯的一个例子:粉尘交易在共识上是有效的,但它不属于某一种的 DoS 保护措施,但在某种程度上它又是一种 DoS:你是在要求整个网络为你保存粉尘输出。这也是一个能够体现应用开发者和协议开发者的内在张力的例子(在交易池规则上)。举个例子,在闪电通道中,如果你有一个输出,比如是一个正在服役的 HTLC 输出,或者是表示通道余额的输出,它的价值太低了,成了一个粉尘,那么这笔交易是不会被广播的。所以,我认为,在闪电通道中,如果你有一个输出会成为粉尘,那你应该把它变成手续费。这被认为是最佳的做法。但这样是不便利的,因为必须在实现闪电通道的软件中增加额外的东西。当然,我一直在尝试共情,我也理解为什么这让人难受。但设置粉尘限额是有理由的。这里面是有矛盾的。我并不是说这些东西永远不会改变,我只是把理由说出来。
(完)