Git是一个分布式版本控制系统,不需要中央服务器。尽管通常将存储库发布在一个众所周知的位置,以便于克隆和获取最新更改,但这实际上不是必需的。每个克隆都可以具有完整的提交历史记录,并且可以独立发展。此外,可以通过电子邮件或其他方式交换代码更改。最后,即使克隆本身也不需要从托管git存储库的知名域中创建(请参阅git-bundle(1))。
鉴于git本身已经完全分散,人们会认为没有进一步的工作要做。我遇到了Radicle项目及其迷幻的网站。该项目除了拥有一个带有狂野色彩方案的网站外,还旨在使用对等网络体系结构提供社交编码实验或git forge功能。根据文档,动机似乎是git的内置功能有效,但不够用户友好以使其无法访问。特别是,它缺乏社交编码功能。
目标是添加git forge功能,例如项目和开发人员发现,问题跟踪器,Wiki等。另外,还涉及明显分散的功能,包括以太坊作为锚定项目元数据,支付贡献者等的方式。Radicle仍然在早期开发,因此尚未实现这些功能。这是我对“工作原理”文档的看法,由于该文档的早期阶段以及某些不完整的句子或错别字,因此有点令人困惑。我不知道我的理解是否实际上对应于今天存在的Radicle实现或其最终愿景,因为我没有研究代码或尝试运行该软件。但是,文档提出的想法本身就很有趣且富有成果,因此我想与大家分享并用自己的语言解释它们,以防您也发现它们值得探索。
让我们快速回顾一下git数据模型,因为它对于理解点对点git伪造很重要。 git存储库包含refs /子目录,该目录为本地分支头(refs / heads /),本地和远程获取的标签(refs / tags /)以及远程获取的分支(refs / remotes /< remote> /)提供名称空间。实际上,此命名空间布局只是日常使用git的惯例,我们可以看到,可以不同地使用refs /命名空间。 git客户端根据将远程引用映射到本地引用的refspec规则从远程获取引用。这使客户端可以仅从服务器获取某些引用。客户端还可以将它们放置在其本地refs /目录中与服务器不同的位置。有关详细信息,请参见git-fetch(1)手册页。
引用文件包含存储在存储库的对象数据库中的对象的提交哈希。对象可以是提交,树(目录),标签或Blob(文件)。分支引用指向最新的提交对象。提交对象指的是一个树对象,该树对象可以指的是子目录的其他树对象,最后指的是构成要存储文件的blob对象。请注意,git存储库支持不共享历史的不相交分支。脱节分支最著名的例子可能是GitHub Pages和GitLab Pages功能,其中这些git forges通过存储库中特定分支上的HTML / CSS / JavaScript / image文件发布静态网站。该分支与其他分支不共享任何版本历史记录,并且目录/文件通常与存储库的主分支没有相似性。
现在,我们已经介绍了足够多的git细节,以讨论点对点git伪造。如果您想了解有关如何实际存储git对象的更多信息,请查看我有关存储库布局和打包文件的文章。
通常情况下,一个git存储库中有一个或多个所有者,这些所有者可以推送参考。没有其他人有权修改refs名称空间。如果我们尝试与全世界共享一个refs名称空间,并且每个人都可以推送该怎么办?命名冲突会造成混乱,恶意用户会删除或更改其他用户。裁判因此,除非有某种方法可以在全局引用名称空间上强制执行结构,否则这似乎是一个不可行的想法。
对等系统可以解决这些问题。首先,可以通过选择具有足够位数的随机数来创建唯一标识,从而避免发生冲突的可能性。该唯一标识可以用作全局ref名称空间中的前缀,以避免意外冲突。其次,需要有一种方法来防止未经授权的用户修改其他用户拥有的全局名称空间部分。
公钥密码学为实现这两个目标提供了原语。公钥或其哈希可以用作提供身份并防止意外冲突的唯一标识符。可以通过验证是否使用与唯一身份相对应的私钥对全局名称空间进行更改来强制实施所有权。
这是基于Radicle文档的简化示例。这里的身份是基于公钥的唯一身份。请记住,世界上没有其他人具有相同的身份,因为不可能生成相同的公共密钥。 head / ref是提交对象的普通git ref-这些是已发布的分支。 signed_refs ref指向一个git对象,该对象包含提交哈希列表和使用公钥生成的签名。可以使用公钥验证签名。
接下来,我们需要验证这些更改,以检查它们是否是使用身份所有者专有的私钥创建的。首先,我们检查一下signed_refs ref指向的对象上的签名。如果签名无效,我们将拒绝这些更改,也不会将其存储在本地存储库中。接下来,我们在heads /中的每个ref相对signed_refs中的列表进行查找。如果列表中缺少参考,那么我们将拒绝这些参考,并且不允许它们进入我们的本地存储库。
该方案适用于点对点系统,因为ref可以在对等点之间传播(复制)并在每个步骤进行验证。身份所有者不需要在每个复制步骤中都在场,因为我们需要他们的加密签名才能确保他们授权了这些引用。因此,我可以从对等端B接收到由身份A最初创建的引用,并且仍然可以确保对等端B没有修改它们,因为身份A的签名是完整的。
现在,我们有了一个分区的全局引用命名空间,以便每个标识都可以发布引用,同级可以验证这些更改是否得到授权。
可能尚不清楚,是否有必要克隆整个全局名称空间。实际上,可能没有任何一个对等实体将拥有整个全局名称空间的完整副本!那是因为这是一个分布式系统。同行仅从同行那里获取他们关心的裁判。同行之间相互取长补短,形成一个网络。网络不需要完全连接,并且有可能在没有完全全局连接的情况下运行多个对等群集。
要引导全局名称空间,有种子存储库。种子是点对点系统中的常见概念。它们为新的同龄人提供了切入点,让他们可以了解并开始与其他同龄人一起参与。在BitTorrent中,这称为" tracker"而不是"种子"。
根据Radicle文档,可以直接从同级获取。这可能意味着需要在公共Internet上访问git-daemon(1)或git-http-backend(1)。由于NAT限制,许多对等节点将没有足够的网络连接。我猜Radicle并不希望每个用户都参与到存储库中。
有趣的是,有一个八卦系统可以通过网络传播裁判。让我们回顾一下全局命名空间中的身份引用:
我们可以发布在remotes /中跟踪的身份。这是一个递归引用布局。这是跟踪我们的裁判的人可以找到有关相关身份及其裁判的方式。
由于git的数据模型,即使我们复制另一个身份发布的引用,也可以共享提交,树和Blob对象。由于git是一个内容可寻址的对象数据库,因此即使多个引用指向该数据,该数据也只存储一次。
现在,我们不仅拥有一个可以在其中发布任何人的git refs的全局名称空间,而且还具有构建对等网络并在整个网络中传播数据的方法。重要的是要注意,仅当对等体有兴趣获取数据时才传播数据。对等方不会被迫存储他们不感兴趣的数据。
让我们将各个部分放在一起,并展示系统如何存储数据。对等方创建一个名为monorepo的本地git存储库,以存储全局名称空间的某些部分。它从种子或直接同伴那里获取引用以开始使用。多亏了远程/引用,它还可以了解网络上其他未直接请求的引用。
该git存储库只是一个数据存储,不适用于常规git工作流。传统的git branch和git tag命令不能与全局名称空间布局和验证要求一起很好地工作。相反,我们可以从monorepo克隆本地file:///存储库,该存储库将ref的子集提取到常规git refs布局中。由于git-clone(1)支持到本地存储库的硬链接,因此可以共享文件。感谢githooks(5)和/或可扩展的git-push(1)远程帮助程序支持,当我们从本地克隆推送到本地monorepo时,可以生成必要的全局名称空间元数据(例如签名)。然后,monorepo可以将最终参考发布给其他同行。
Radicle中有一些简洁的想法,它在支持git forge功能方面的发展还有待观察。需要解决许多挑战:
可用性-Radicle是集中式git伪造和基于电子邮件的分散式开发之间的中间地带。目标是易于使用,如git Forges。对等系统通常面临挑战,即在公钥身份(提供用户名而没有集中用户帐户)的基础上提供人性化的界面。用户可能更喜欢考虑存储库,合并请求,问题和Wiki,而不是同行,八卦,身份等。
安全性-全局名称空间和对等模型是恶意用户攻击的目标,他们试图冒充或窃取身份,向系统中注入垃圾,向游戏信誉系统添加假名,等等。
可伸缩性-对等方只关心某些存储库,并且不希望被全局命名空间中的所有其他引用放慢速度。递归引用布局似乎可能会导致性能问题,也许用户会配置客户端以将深度限制为较低的数字(例如3)。乍一看,Radicle应该能够很好地扩展,因为同级仅需要获取他们感兴趣的引用,但这是使用git refs的一种新颖方法,因此我们可以预期随着系统的增长会出现可伸缩性问题。
数据模型-该数据模型将如何增长以处理Wiki,问题跟踪器等?问题跟踪器注释是需要在分布式系统中解决冲突的数据结构的示例。如果两个用户对一个问题发表评论,将如何解决而不会发生冲突?幸运的是,对分布式数据结构(例如无冲突复制数据类型(CRDT))进行了大量研究,并且可以通过消除线性注释编号之类的概念来避免大多数冲突。
CI / CD-正如我在博客文章中提到的为什么git伪造是冯·诺依曼机器一样,git伪造不仅仅是数据存储。他们也有一个计算模型,最初用于持续集成和持续交付,但实际上是一个通用的无服务器计算平台。很难安全地做到这一点,并且在对等系统中没有不必要的资源使用。也许Radicle将使用以太坊进行计算积分?
Radicle是一个好主意,我期待看到它的发展。 它仍然处于早期阶段,但是已经显示了使用全局refs名称空间和monorepo数据存储的有趣方法。