The road to iroh 1.0
本文为原文前 6,000 字符的节选翻译,完整内容请查看原文。
The road to iroh 1.0
It is done. After four years of work and various pivots we have released iroh 1.0. A trip down github memory lane We have an expanding web site describing how iroh works under the hood, but we think it is important to also understand why we made the decisions that led to the current iroh. So here is a trip down memory lane / our commit history which highlights major turning points and design decisions. Perhaps more importantly, this post also covers some things we considered but ended up not doing.
大功告成。经过四年的努力和多次转型,我们发布了 iroh 1.0。这是一次 GitHub 记忆之旅。我们有一个不断扩充的网站来描述 iroh 的底层工作原理,但我们认为理解我们为何做出导致当前 iroh 的决策同样重要。因此,这里是一次记忆之旅/提交历史回顾,重点介绍了主要的转折点和设计决策。更重要的是,这篇文章还涵盖了一些我们考虑过但最终没有实施的内容。
The early days Iroh started as a rust implementation of ipfs. We got pretty far implementing various ipfs protocols and had interop with existing ipfs implementations working. Our goal with iroh was to target lightweight and resource constrained devices such as mobile phones. One area where we were unhappy with existing protocols was blob transfer. We started a working group within the ipfs community to come up with a new protocol, called the move the bytes working group.
早期阶段,Iroh 最初是 IPFS 的 Rust 实现。我们在实现各种 IPFS 协议方面取得了很大进展,并实现了与现有 IPFS 实现的互操作。我们开发 iroh 的目标是针对移动电话等轻量级和资源受限的设备。我们对现有协议不满意的一个领域是 blob 传输。我们在 IPFS 社区内成立了一个工作组,旨在提出一种新协议,称为“移动字节”(move the bytes)工作组。
First steps with QUIC For that working group, we worked on a proposal for new blob transfer protocol. We wrote a small library called krakensync. Krakensync used BLAKE3 exclusively for content addressing because BLAKE3 supports verified incremental streaming of large blobs. After a few days we had a bare algorithm, and wanted to add networking to it to make the demo feel more real. We had only two days before the next meeting, and adding networking via rust-libp2p seemed impossible (Remember that this was before LLM coding assistants!). I had a tiny bit of previous experience with QUIC, and remembered from a project of a friend of mine, quinn-noise that quinn is both hackable and has a simple idiomatic rust API. So I tried to add networking via quinn and found it an absolute joy to work with.— Rüdiger
QUIC 的第一步:为了那个工作组,我们起草了一份新的 blob 传输协议提案。我们编写了一个名为 krakensync 的小型库。Krakensync 专门使用 BLAKE3 进行内容寻址,因为 BLAKE3 支持大型 blob 的验证增量流式传输。几天后,我们得到了一个基础算法,并希望为其添加网络功能,使演示看起来更真实。距离下次会议只有两天时间,通过 rust-libp2p 添加网络功能似乎是不可能的(请记住,那是在 LLM 编程助手出现之前!)。我之前有一点 QUIC 的经验,并从我朋友的一个项目 quinn-noise 中记得 quinn 既易于修改,又拥有简单且符合惯例的 Rust API。所以我尝试通过 quinn 添加网络功能,并发现使用它非常愉快。—— Rüdiger
A real world use case In parallel to working on the ipfs compatible iroh, we worked on a side project to implement a single narrow real world use case: transfer of deltachat chat history when provisioning a new device. The technical task was to transfer a single large blob of backup data to a new device. There were hard constraints regarding disk and network usage. And the transfer had to be resumable. We found that we could not implement this very narrow use case with our ipfs based iroh implementation. Doing so would have had less than optimal network performance, and would have pulled in a very large dependency tree. So we started writing a lightweight sync library from scratch, using QUIC as the networking layer and BLAKE3 verified streaming for large blob transfer.
现实世界的用例:在开发兼容 IPFS 的 iroh 的同时,我们还进行了一个副项目,旨在实现一个单一且具体的现实世界用例:在配置新设备时传输 deltachat 聊天记录。技术任务是将单个大型备份数据 blob 传输到新设备。这在磁盘和网络使用方面有严格的限制,且传输必须支持断点续传。我们发现,无法使用基于 IPFS 的 iroh 实现来完成这个非常具体的用例。这样做不仅网络性能不佳,还会引入非常庞大的依赖树。因此,我们开始从零开始编写一个轻量级的同步库,使用 QUIC 作为网络层,并使用 BLAKE3 验证流进行大型 blob 传输。
Abstraction fatigue Despite our best efforts, our main project was still far away from the theoretical limits. Meanwhile our sync side project showed what is possible. So we made the decision to no longer pursue the goal of staying compatible with existing ipfs implementations. Instead of multiaddrs, endpoints were identified by an Ed25519 public key. Instead of multihashes, we had BLAKE3. And instead of multiple transport protocols we had just QUIC. The new iroh was just QUIC based networking using quinn and blob transfer using BLAKE3 verified streaming. Compared to the previous version, the functionality was anemic. But the single thing it did, it did with close to optimal performance. It was a lightweight enough dependency that deltachat could justify including it into their rust code base. And suddenly we had both networking and sync deployed in a real app used by thousands of people across various platforms (windows, linux, iOS, android). The old code lives on in beetle.
抽象疲劳:尽管我们尽了最大努力,我们的主要项目距离理论极限仍然很远。与此同时,我们的同步副项目展示了其可能性。因此,我们决定不再追求与现有 IPFS 实现保持兼容的目标。端点不再使用 multiaddrs,而是通过 Ed25519 公钥进行标识。不再使用 multihashes,而是使用 BLAKE3。不再使用多种传输协议,而只使用 QUIC。新的 iroh 只是使用 quinn 进行基于 QUIC 的网络传输,并使用 BLAKE3 验证流进行 blob 传输。与之前的版本相比,其功能显得苍白,但它所做的唯一一件事却达到了近乎最优的性能。它是一个足够轻量的依赖项,以至于 deltachat 可以将其包含在他们的 Rust 代码库中。突然之间,我们就在一个被数千人跨平台(Windows、Linux、iOS、Android)使用的真实应用程序中部署了网络和同步功能。旧代码在 beetle 中得以延续。
TLS or noise We decided to use just QUIC. But QUIC as specified in RFC 9000 has clear client and server roles, where the server is identified by a certificate chain. At the time we had to add a somewhat Rube Goldberg construction on top to make QUIC work for peer-to-peer connections, a self-signed certificate carrying the real public key in a custom X.509 extension, with a signature binding the two. Also, rustls is a heavy dependency, and its default crypto provider is a C dependency. So there was a temptation to replace the entire TLS encryption scheme with a protocol based on noise that is made for peer-to-peer connections from the start. We ended up sticking with TLS as the industry standard. Without using TLS iroh would not really be QUIC, just reuse some of its parts. But we were still unhappy about the self-signed cert. Thankfully right around the time when we were discussing this raw public keys in TLS support was merged into rustls. So we didn’t waste any time and switched to raw public keys in TLS soon after.
TLS 还是 Noise:我们决定只使用 QUIC。但 RFC 9000 中定义的 QUIC 具有明确的客户端和服务器角色,其中服务器由证书链标识。当时,为了让 QUIC 适用于点对点连接,我们不得不添加一种类似鲁布·戈德堡机械(Rube Goldberg)的复杂结构:一个在自定义 X.509 扩展中携带真实公钥的自签名证书,并带有绑定两者的签名。此外,rustls 是一个沉重的依赖项,其默认加密提供程序是 C 语言依赖。因此,我们曾想过用一种从一开始就为点对点连接设计的基于 Noise 的协议来替换整个 TLS 加密方案。最终,我们还是坚持使用作为行业标准的 TLS。如果不使用 TLS,iroh 就不是真正的 QUIC,而只是重用了它的一部分。但我们仍然对自签名证书感到不满。幸运的是,就在我们讨论这个问题时,TLS 中的原始公钥支持被合并到了 rustls 中。因此,我们没有浪费时间,很快就切换到了 TLS 中的原始公钥。
Hole punching One thing we were not yet happy with: for the deltachat chat history transfer to work, both devices had to be in the same network. Ideally this should work in all cases where the two devices are somehow connected to the internet. We also wanted these transfers to be fast. So what we needed was hole punching. We looked at various systems that implement hole punching. Our goal was to get holepunching to work reliably, even at the expense of compromising p2p purity. If we would need a small server to help, so be it. Of all the existing open source implementations of holepunching, we found the tailscale approach best suited to our requirements. So we implemented something that was initially very similar, with good results. This was a huge effort, but once we merged it we had hole punch rates to be proud of. In most normal circumstances we punched holes almost immediately, and in the few corporate networks where hole punching was impossible, at least we could still transfer data via relays. It just worked.
打洞(Hole punching):有一件事我们还不满意:为了让 deltachat 聊天记录传输正常工作,两台设备必须处于同一网络中。理想情况下,这应该在两台设备以某种方式连接到互联网的所有情况下都能工作。我们还希望这些传输速度很快。所以我们需要的是打洞。我们研究了各种实现打洞的系统。我们的目标是让打洞可靠地工作,即使以牺牲 P2P 的纯粹性为代价。如果需要一个小服务器来提供帮助,那就用吧。在所有现有的开源打洞实现中,我们发现 Tailscale 的方法最符合我们的要求。因此,我们实现了一个最初非常相似的方案,并取得了良好的效果。这是一项巨大的努力,但一旦合并,我们就拥有了令人自豪的打洞成功率。在大多数正常情况下,我们几乎可以立即打洞成功;而在少数无法打洞的企业网络中,至少我们仍然可以通过中继传输数据。它就这样成功了。
Iroh as a batteries included p2p application toolkit We achieved our goal of having a simple p2p library that just worked. But the API was still anemic. It was something that experienced rust teams such as deltachat could include, but not yet attractive to p2p application developers. So we decided to gradually add more functionality. One of the first things we did wa
Iroh 作为内置功能的 P2P 应用工具包:我们实现了拥有一个简单且能正常工作的 P2P 库的目标。但 API 仍然很薄弱。它虽然可以被像 deltachat 这样经验丰富的 Rust 团队所使用,但对 P2P 应用程序开发人员来说还不够有吸引力。因此,我们决定逐步增加更多功能。我们做的第一件事是……