作者:sedited
来源:https://thecharlatan.ch/Kernel/
原文出版于 2024 年 10 月,是罕见的讨论
libbitcoinkernal的文献。
我已经在 Bitcoin Core kernal 库(libbitcoinkernel) 中投入两年时间了。这个软件库志在囊括 Bitcoin Core 的验证逻辑,或者说,用来确定给定一个区块是不是延长了已知拥有最多工作量的链的所有逻辑。它有意不包含任何 P2P 联网模块、JSON RPC、REST 或者 ZMQ 的服务端、钱包模块、额外的数据库索引乃至用户界面。这个项目最初是由 Carl Dong 启动的,也一直被 Bitcoin Core 的贡献者和用户期待。Carl 最初说这是为了让 Bitcoin Core 的验证逻辑 “不再像意大利面”,能够形成一种更凝练、更容易维护的秩序,将验证部分与非验证部分清晰地分开。它的 API(应用程序接口)的设计则留给了未来。在这些初步工作的基础上,这个项目已经取得了一些成果:这种分离已经很大程度完成了。轮到当初没有做的事情了。
在这篇文章中,我会说明我对这个项目的愿景。为如此规模的项目公开表达个人的愿望是危险的。它可能会产生过度影响,把现实装扮成看起来取得了我们想要的结果。思想应该凭自身的品质成立,而不是凭其假想中的结果。甚至哪怕是对这些愿景的细节有不同意见,也可能导致教条观念,让我们无法适应新的数据和论证。我的自负无法免疫这一切。我不得不抛弃糟糕的想法、放弃我已经投入了两年的工作,这也让我低落。与此同时,吸引人们加入你的项目需要你分享想法、讲述一个故事。不讨论项目的目标,一样是排外的。这会让新来的人更难以加入,并助长没有明说的议程控制。跟这个项目打交道的人将不得不猜测它的目标到底是什么。
我提到的这项特性可能一个也实现不了。不过,它们也一样可能会过度工程,无法证明对其他开发者足够有用(或者在我看来,最重要的是它们不够有趣),甚至有危险的副作用。这个项目对 Bitcoin Core 来说已经成果了。开发者们从增加的简洁性 —— 分离了验证代码和非验证代码 —— 中得到了好处,这也让代码更加连贯(相比于三年前)。即使停在原地、不再推进,它也已经提供了显著的好处。
到目前为之,这个项目一直集中在清晰地分离 Bitcoin Core 的验证逻辑与非验证逻辑。不过,依然有一些剩余的部分:验证模块用到的功能在多个地方重复。举个例子,一些 RPC 代码可以简化,如果总是跟其它模块的代码一样通过同一个接口来调用逻辑的话。
完全暴露组成这个库的所有逻辑,可能有风险。这会将这个库的用户绑定在 Bitcoin Core的内部开发流程中。只要代码改变,它的应用场景可能就会被破坏,或者导致频繁的重建。这可能反过来会给 Bitcoin Core 的开发者带来更大的压力,让代码更难进化。在未来的软分叉情形中,这个库的用户将不得不细致地检查每一种数据结构和函数,以防止其逻辑发生了变化。这将包括 Bitcoin Core 的代码中可能没有明说的低层变更。如果没有跟上,继续使用这个库可能导致共识错误。 给外部用户使用的稳定和安全的 API 必须解决这个问题。
内部验证代码的许多变化,比如让序列化更加高效、加入新的数据库格式、进一步优化代码的结构,都不需要改变 API 。Bitcoin Core 的开发将保持灵活,只有当 API 变化时,才需要考虑后向兼容性和版本控制。确保一个 C 语言 API 的后向兼容性是一项历史悠久的惯例,几千个软件项目都在做。比如说,如果一个函数在一次软分叉之后需要另一些参数或者更多参数,可以将这个函数的新版本添加到 API 中。
最近,我打开了一项 PR,为这个库添加了一个 API 的初步版本。它提供了一个 C 语言头文件(src/kernel/bitcoinkernel.h),外部项目可以用来直接访问 Bitcoin Core的验证逻辑。具体来说,这意味着,这个 C 语言头文件定义了一些函数,在调用时,与运行 Bitcoin Core 全节点时所执行的代码一模一样。这个头文件不仅允许用户验证区块,还可以做更多底层的任务,例如读取区块数据、验证脚本,并且,在未来的迭代中,还可以验证区块头和交易。关于它的更深入描述,在该 PR 中有述,并且,我已经建立了一个关联到它的 rust-bitcoinkernel 库。
发布一个 C 语言头文件作为接口(而不是常见的 C++ 头文件),有一些缺点。因为大部分Bitcoin Core 的代码都是由 C++ 写的,所以需要一个薄薄的翻译层,将它们翻译成 C 语言代码,这需要额外的审核,而且必然会重复一些代码。使用 C 语言 API 的好处在于,绝大部分其它编程语言都有成熟的 C 语言绑定,但通常它们的 C++ 绑定会更加随意,甚至根本没有。C++ 也不那么稳定。新的特性更快演化,而且每三年都会发布新的标准。这种选择可能会给项目带来压力,让他们更慢采用新特性和新标准,以便让整个生态系统更慢升级、保持一些后向兼容性。
(译者注:这里的 “C 语言绑定” 指的是能在其它编程语言中使用 C 语言编写的代码。)
使用稳定的外部 C 语言 API 的另一个缺点在于,它无法被 Bitcoin Core 现有的内部代码优雅地使用。若要这样做,将需要添加另一个接口,让代码更加复杂。在同一套代码基础中从 C++ 翻译成 C,再翻译成 C++ ,这是无意义的兜圈子。但因为 Bitcoin Core 中没有这套 API 的直接用户,可能会增加维护负担,因为出了问题也不一定会被注意到,或者也不会被认为是紧急的问题。此类问的一个例子可以在现在已经弃用的 libbitcoinconsensu 库中找到,这个库的目的是仅仅验证脚本。在 Taproot 升级激活两年以后,它才在其 API 中支持 taproot。解决这种问题的办法之一是编写一些小型的单元来使用这套 API,比如区块线性化、重索引工具,或者现有比特币链状态案例程序的替代。针对这套 API 的测试也会有所帮助。更加长久的解决方案是将这套 API 所引入的任何逻辑超越仅仅是将 C++ 类型翻译成 C 代码,而变成一套单独的接口,可以被代码的其它部分复用。在你的组织内使用自己开发的产品常被称为 “吃自家狗粮”。
这个 C 语言头文件可以被外部项目用来重新实现一种全节点,同时依然依赖 Bitcoin Core 的验证代码。这将大大降低重写验证代码可能带来的共识故障风险。替代性的比特币全节点验证有过在自己的验证代码中出现 代码/脚本 验证 bug 的先例,例如 btcd 和 libbitcoin (虽然 libbitcoin 的 bug 尚未放出)。还应之处,libbitcoin 已经有选择地支持了现在已经弃用的 libbitcoinconsensu 库(这些项目都使用了令人混淆的相似的名字,算是前车之鉴)。类似地,floresta 也使用了 libbitcoinconsensu 来验证脚本。确定获得成功所需的 API 灵活度,即使对高度优化而且有特定意图的应用场景,也并非易事。为了获得最高的效率,用户将被迫复用这个库所用的相同的数据结构。至少,这个库应该允许用户配置 “assumevalid”、“assumeutxo” 和交易转发规则这些 “bitcoin core 兮兮” 的特性。替代性的节点实现可以使用自己的 P2P、钱包、索引和 UI 功能,使用任何他们认为可行的编程语言。
这个库可能对数据科学项目或者索引生成器(比如 electrs)也是有用的,因为它们需要读取区块,但有了本库就无需重新实现 Bitcoin Core 的内部数据表示。Bitcoin Core 最新的 v28 发行版加入了 XOR 区块数据存储。这打破了许多现有的区块数据解析器,比如 mempool/electrs 内置的那一个。如果他们未来会使用这个 kernel 库,他们就无需被迫发布补丁,而只需更新到这个库的一个新版本,继续操作就可以了。几年前我写自己的论文时,也不得不实现自己的 Bitcoin Core 区块读取器,以收集区块和交易数据。这总的来说是个坏注意:整个生态系统依赖于 Bitcoin Core 的内部数据表示,而不是一个稳定的借口。当前使用 JSON RPC 接口从 Bitcoin Core 读取区块的项目在未来可以使用本 kernal 库。通过本库直接从文件系统读取,会比将数据序列化成 JSON 再通过 socket 来传输,性能好得多。
这个 kernal 库,之所以被称为 “kernal(内核)”,是因为它与许多其它库不同,它会管理资源、写入磁盘,还会生成线程。它会存储区块、为 UTXO 维护索引。将这套 API 拓展成 sans-I/O(将网络逻辑与磁盘操作完全解耦的架构)可能也有好处。这样一来,这个库就无需再内部完成所有的数据库操作,而允许用户定义自己的数据库选择,甚至把所有东西都放在内存中。在代码内使用抽象的接口,可能也方便测试。单元测试和模糊测试将完全不必触及文件系统,抽象的接口可以模拟成测试它们的动作。不过,改变用于共识引擎的数据库,也有出现糟糕后果的先例。当跟踪未花费输出的数据库在 0.8 版本由 Berkeley DB 改成 LevelDB 时,在验证规则中产生了意料之外的变更,导致了区块链分裂和一次重复花费(详情见 BIP 50)。
拥有一个 sans-I/O 接口,可以让这个库进入不提供完整操作系统的平台。这就可以在嵌入式设备中启用比特币区块验证,或者,编译成 WASM 代码,直接在浏览器中运行。在非常有限的存储要求中实现嵌入式验证,对于 带验证的闪电签名器(VLS) 这样的项目来说应该有吸引力。最近,Bitcoin Core 代码库中也有人有兴趣将一些验证文件编译成 32 位的 risc-v 文件。这是 risczero 项目所用的目标架构,用于从任意程序中创建一个零知识电路。能够在零知识证明系统中,使用与 Bitcoin Core 所用的相同逻辑来验证区块、区块头、脚本和交易,可以让人们对 BitVM 这样的桥接合约产生更强的信心。
这个项目,按其本性,只能积跬步以至千里。提出一种适合所有东西的理想结构、然后一次性实现它、提交它,是行不通的,因为这会带来太多风险。相反,走向一套良好 API 的道路只能徐徐前进,我估计哪怕它被合并,也会在很长一段时间里被标记为 “实验性的”。我希望最终能在博客中详细讨论上面提到的一些话题。尤其是 C 语言 API 的设计,曾是一个很难找到参考书的话题(至少对我来说)。相反,我从其他开发者哪里收获了大量的反馈,所以这可能是一个有趣的后续话题。到目前为之,kernel 项目获得了来自现有 Bitcoin Core 贡献者的大量善意和鼓励。这尤其令我惶恐,因为我是从一位前贡献者手中接手了这个项目,TA 有丰富的贡献经历,而我只是偶尔贡献,只有少数几个 PR 有我的署名。我希望这份热情不仅能持续到下一个阶段,也能触动外部开发者来看一看现在提出的 API 。我很期待这个项目的新阶段,看看哪些东西能保留下来将是有趣的事。
(完)