Contents

P2P

来自 HASEEB QURESHI 的三篇关于P2P网络的博客的翻译

来自这三篇博客

https://nakamoto.com/p2p-networking/

https://nakamoto.com/gnutella/

https://nakamoto.com/bitcoins-p2p-network/

P2P Networking

如果我们想从根本上了解加密货币是如何工作的,我们的工具包里需要的不仅仅是密码学。为了让加密货币发挥作用,它需要的不仅仅是密码学上的安全–它还需要去中心化。中本聪从21世纪初的点对点(P2P)网络历史中学到了很多东西。这些经验为比特币的网络层设计提供了参考。

在本模块中,我们将探讨比特币的网络模型,以及它如何实现其两个主要目标:去中心化和抗审查

网络架构

传统的网络服务是以集中的客户端-服务器模式进行架构的。一个中央服务器提供服务,每个客户端从这个中央服务器请求数据或工作。你在网络上使用的几乎所有应用程序都是这样的结构–Facebook、Google、Wordpress。今天,“中央服务器 “通常是在负载平衡器后面的一个服务器群,但在高层次上,他们从根本上是相同的架构。

https://nakamoto.com/content/images/2020/01/image-90.png

客户端/服务器架构从根本上说是集中式的,并依赖于单一的一方。如果中央服务器关闭,该服务就会暂停(如发生在DigiCash的情况)。

P2P网络是一种分布式的网络模式,其中没有中央服务器。相反,每个对等体承担着网络的部分负荷。这意味着每个对等体可以向网络进行查询,但也必须对查询作出回应。你可以把P2P网络想象成一个 “群(swarm)",它融合了客户和服务器的角色。

https://nakamoto.com/content/images/2020/01/image-91.png

P2P网络是引人注目的,因为它允许我们实现去中心化。一个去中心化的网络是一个不依赖任何单一节点的网络,因此对任何单一节点的关闭或离开网络都具有弹性。

因此,去中心化是很酷的,但是实质性的问题是–去中心化究竟是如何使一个系统变得更好?

去中心化给我们带来两个理想的属性:

  • 第一个属性是崩溃容错。崩溃容错意味着你能够承受单个节点的故障或失效–即使一个节点死了,系统仍然可以运行。这对可扩展性至关重要,因为大型网络一直都有节点故障。

译者注: 平时所说的容错算法, 主要指2种, 崩溃容错 和 拜占庭容错

  • 崩溃容错 CFT: 有节点故障了, 整个系统还能继续正确运行
  • 拜占庭容错 BFT: 有节点数据错误(说谎), 整个系统还能正确运行(达成一致性)
  • 去中心化给我们的第二个属性是抗审查。如果一个节点被审查,但整个网络是去中心化的,那么没有关系–系统的其他部分继续运行。为了使审查制度有效,通常每个节点都必须串通起来执行审查制度,而这在一个大型网络中是很难做到的。如果我在一个去中心化的网络中搜索文件,只要有一个节点愿意为我的查询提供回应,整个事情还是可以的。

我们为什么要关心这些资产,这可能并不明显。毕竟,谁在破坏或审查这些网络?简单回顾一下P2P协议的历史,就会明白为什么中本聪重视比特币的这些特性。

P2P 协议简史

在90年代,在大型分布式系统中协调大规模的计算任务是相当罕见的。当时MapReduceHDFS还没有被发明出来,所以大规模的分布式计算是罕见的,也是昂贵的。P2P协议是第一个在计算机数量上达到大规模的分布式系统(除了互联网本身)。

Napster不是第一个P2P协议,但它是第一个展示P2P模式力量的主流公共成功。

Napster公司由两名大学生Sean Parker和Shawn Fanning于1999年创立。它提供了一个简单的价值主张:它让你从Napster网络中的任何用户那里下载MP3文件。

https://nakamoto.com/content/images/2020/01/image-67.png

Napster的架构很简单。有一个中央Napster服务,这主要是一个巨大的搜索索引,保持跟踪所有的同行和他们的共享内容。它将内容元数据存储为以下三元组: 文件名、IP地址、端口号

每当一个对等体(peer)加入Napster网络时,对等体就会向中央服务器发送一份它愿意分享的文件清单。然后,服务器将更新其搜索索引,以包括这些新共享的文件。

每当用户搜索一个文件时,服务器将查询其搜索索引,并向用户展示所有相关的点击。由于这些点击代表了其他对等体的文件,用户将ping每个相关对等体的IP,以确定他们的下载延迟和线路速度。

https://nakamoto.com/content/images/2020/01/napster-optimized-2.gif

一旦用户选择了要下载的文件,他们的客户端将直接从与该文件相对应的IP上获取该文件。所有的文件传输都直接发生在对等体之间。在这一点上,Napster服务器本身已不再参与。

从某种意义上说,Napster是客户/服务器模式和P2P模式之间的交叉。服务器基本上是作为MP3下载者的一个匹配引擎。而事实证明,有一大批人想下载MP3。

在推出后不久,Napster就像野火一样起飞了。在其高峰期,该服务有超过8000万用户。2001年,它经常使大学宿舍的高速网络过载,并很快在许多大学被禁止使用。

2000年,Napster被Metallica、Dr.Dre起诉,随后又被A&M唱片公司起诉侵犯版权。这些诉讼成功地为Napster公司带来了更多的新闻报道和宣传。但是,当法官对Napster公司发出禁令,停止受版权保护的音乐交易时,这场演出终于结束了。

Napster公司的教训

虽然我们称Napster为P2P网络,但在文件发现方面,Napster的设计是传统的客户-服务器模式。只有文件传输实际上是P2P。这给他们的架构带来了单点故障,并意味着网络在Napster公司关闭后消失了

在Napster中,也很少考虑安全问题。所有的信息和请求都是以明文形式发送的,所有的IP都是公开的,这使得该系统几乎没有隐私可言。

但最终,Napster公司棺材里的钉子是法律上的,而不是技术上的–Napster公司被认为对其用户的版权侵犯负有法律责任。尽管Napster公司没有直接侵犯任何版权,但法官裁定,Napster公司诱使其用户侵犯版权,因此 “替代性地侵犯 “了版权人的权利。

法院要求Napster公司遏制所有版权侵权行为。Napster公司声称,它可以实施一个解决方案,以阻止99.4%的侵权行为,但法院认为这还不够,除非他们能100%地阻止。

后Naspter世界

Napster公司最终申请了破产。它的资产将被收购并重新命名为Rhapsody。但是,Napster公司的消亡将掀起一场巨大的风暴,以至于掩盖了它的渊源。LimeWire和KaZaA等网络接替了Napster,建立了性能更强、去中心化的P2P协议,比前者的限制更少。

随着时间的推移,所有这些协议最终都将被BitTorrent所取代。到2009年,P2P文件共享–主要是BitTorrent–占了所有互联网流量的70%。快进到今天,BitTorrent是世界上最大的单一P2P网络,也是大多数国家最大的上游流量来源。

值得停下来反思的是:为什么BitTorrent能够成功,而其他大多数协议却逐渐消失?

首先,BitTorrent的带宽共享是以牙还牙的模式,这意味着对等者会给那些对他们慷慨的人提供更多的带宽。这种互惠的系统不鼓励搭便车,鼓励付出。(在现实中,以牙还牙的模式并不特别好用,但与以前的文件共享协议相比,它还是一个进步。) 当涉及到带宽消耗时,BitTorrent也恰好是非常有效的,特别是对于那些需求量大的文件。

但在许多方面,BitTorrent成功的核心真正归结于他们无可挑剔的信息传递。该协议的开发者从未主张将版权文件共享作为该服务的合法用途。他们把BitTorrent说成是 “为你的网站提供更好的服务”,他们的网站只提到无害的使用案例,如Linux发行版和下载魔兽世界的更新。

即使你想这样做,也没有集中的BitTorrent服务可以使用。只有一个由私人经营的BitTorrent追踪器组成的联合网络。因此,当其他P2P网络被执法部门关闭时,BitTorrent只能对个别追踪者采取行动。由于BitTorrent的DHT模式,许多torrents甚至可以在任何单个跟踪器被查封后存活。因此,针对BitTorrent的审查企图往往会演变成一场 “打地鼠 “游戏。

译者注:

DHT: 分布式哈希表, 参考文章:

https://luyuhuang.tech/2020/03/06/dht-and-p2p.html#23-分布式哈希表

https://colobu.com/2018/03/26/distributed-hash-table/

举例:

  • 在区块链中, 区块链本身的存储是不适合进行大量数据存储的, 所以其利用了DHT来进行大量数据存储, 比如IPFS
  • 以太坊节点发现是基于Kademlia协议的, Kademlia是一个基于UDP的分布式哈希表协议

文件共享革命对我们与技术和媒体的关系产生了深刻的社会影响。但它的影响并没有结束–它还引发了对P2P协议的工程兴趣的复苏。这导致了许多学术项目,如Folding@home和SETI@home,个人可以将其计算能力借给世界规模的P2P科学计算项目。甚至还有像Skype这样成功的初创公司,最初也是建立在P2P架构之上。

但随着执法和法律斗争的加剧,P2P协议在公众心目中逐渐成为 “非法文件共享 “的同义词。在经历了足够多的负面头条新闻后,学术界的热情也随之枯竭。同时,分布式系统的改进使工程师能够建立更大规模的集中式系统,使P2P架构在大多数商业应用中没有必要。

此外,事实证明很难围绕P2P协议建立可持续的商业模式。大多数内容业务需要一个中央经纪人或DRM来跟踪使用情况并收取费用。几乎根据定义,P2P协议很难实现集中跟踪。uTorrent的前CEO丹尼尔-艾克(Daniel Ek)放弃了BitTorrent的世界,共同创建了音乐公司Spotify,这就是典型的例子。所有这些因素大体上促成了对P2P协议兴趣的下降。

这就是为什么到了2009年,P2P协议在很大程度上已经落伍了。在文件共享网络之外,采用大规模P2P系统的新应用不多。

尽管如此,中本聪明白,P2P架构是创建一个有弹性的、去中心化的货币协议的唯一可行的方法。中本聪写道: 政府善于砍掉像Napster这样的中央控制网络的脑袋,但像Gnutella和Tor这样的纯P2P网络似乎在坚持自己的观点。

P2P架构的权衡因素是什么?

去中心化并不是免费的。当中本聪为比特币选择P2P架构时,有三个大的障碍不得不接受。

  • 第一个障碍是:在集中式架构中,你通常可以得到全局状态的一个连贯的快照(也就是说,你可以清楚地观察正在发生的一切)。但在P2P协议中,通常不可能得到这种全球快照。P2P节点只存储自己的本地知识,要把网络中正在发生的事情拼凑成一个一致的画面是相当有挑战性的。 例如,在IP路由中就是如此。在IP路由中,没有一个路由器拥有整个互联网的路由表。路由器将数据包传递给他们所知道的下一个最近的节点,相信拥有更多本地信息的节点能够将数据包更接近其目的地。 因此,呈现互联网中活动的全球快照被证明是相当具有挑战性的。
  • P2P协议的第二个缺点是,随着用户的上线和下线,它们的流失率往往很高。这意味着任何P2P协议必须具有高度的容错性才能使用。集中式架构通常不需要像P2P系统那样的容错程度。
  • 但是,P2P协议所面临的最大障碍可能是它们不能轻易地实施质量控制。因为P2P的成员资格通常是完全开放的,任何恶意行为者都可以自由地加入网络并引起骚乱。在一个中心化的服务中,阻止这样的不良行为者是很直接的。但在一个去中心化的P2P网络中,谁来决定谁是坏人,谁是好人?一个设计不良的节制功能可能会让坏的行为者阻止好的用户。这意味着任何P2P网络都必须被精心设计,以便即使有恶意用户存在,网络也无法被颠覆。

尽管有这些权衡,对中本聪来说,P2P网络的特性对去中心化的货币来说是必要的。

至此,我们对P2P协议的历史概述结束。在下一节中,我们将深入研究一个著名但简单的P2P协议,Gnutella。Gnutella将为我们提供一个八卦协议的蓝图,最终将帮助我们理清比特币自己的网络模型。

Gnutella: an Intro to Gossip

Gnutella (/nʊˈtɛlə/) 努特拉 是Napster死后出现的第一批分散式文件共享协议之一。它在文件共享应用程序LimeWire中得到了最广泛的实施。

https://nakamoto.com/content/images/2020/01/image-72.png

译者注: LimeWire重新上线后改为搞NFT了

作为一个相当简单的基于 gossip [/‘gɒsɪp/] 的 P2P 协议,Gnutella 的网络设计是理解比特币的一个很好的蓝图。在本课中,我们将从深入研究 gossip 协议的理论开始。然后我们将介绍 Gnutella 的设计。然后,在作业中,您将构建自己的 Gnutella 式协议。 Gnutella 是 gossip 的一个很好的入口点,因为它是生产中使用的最简单的 gossip 协议之一。但是gossip协议自 90 年代就已经存在,并已用于许多系统,例如无线网络、传感器,当然还有互联网路由。今天,许多分布式数据库,如 Cassandra、Riak 和 Voldemort,都使用 gossip 来传播内部状态更新。 那么为什么选择gossip呢?它有什么作用? 提炼其本质,gossip只是一种进行分散消息传播的方式。

多播(multicast, 组播)问题

你可能很熟悉广播(broadcast)这个词。广播是向网络中的每个节点发送消息多播是指你想向许多方面发送消息,但不是整个网络中的每个人。如果你想在P2P文件共享网络中进行搜索查询,你实际上并不想把它发送给网络中的每个人–如果你这样做,网络会很快过载。只有网络中的一个子集需要对我们的查询做出实际回应。

那么,什么是执行这种多播的最佳方式呢?

你首先想到的可能是遍历你想联系的每个人,然后向他们发送一个点对点的消息。

1
2
3
def simple_multicast(recipients, msg):
    for recipient in recipients:
        recipient.send(msg)

https://nakamoto.com/content/images/2020/03/multicast-optimized-1.gif

这很有效,但效率不高。它需要 O(N)时间来执行完整的组播。这也是不现实的:它要求网络中的每个人都要维护网络中每个其他节点的列表。在一个P2P系统中,这个列表可能是巨大的,并且会因为节点的流失而一直变化。(另外,如果发送者在这个长的组播过程中失败了,整个操作就会失败)。

那么,我们如何才能在此基础上进行改进呢?

优化信息传播的一个方法是建立一个最小生成树。生成树是一棵覆盖图中每个节点的树。最小生成树是可能的最小生成树–换句话说,是用最少的边数(或最小的总边权重)建立的生成树。

下面是这个图的最小生成树。如果我们试图从绿色节点广播一条信息,那么我们所有的信息都将沿着这棵树被传送。(有多个高度为2的生成树,这只是其中之一)。

https://nakamoto.com/content/images/2020/01/minimum-spanning-tree-optimized.gif

这应该是实现了一个广播在只有 O(logN) 跳,因为消息的传播时间将与树的深度成正比。此外,每个节点在该广播中只需要执行少量的操作,而不是由发送者执行一个大规模的 O(N)操作(这对一个大型网络来说是不可行的)。

这很好! 事实上,它是如此之好,以至于在理论上是最优的。最小生成树为我们提供了最大效率的路由,特别是如果生成树是在考虑到基础网络拓扑结构的情况下构建的。

但是生成树有一个很大的问题:它们非常脆弱。如果哪怕只有一个节点发生故障或退出网络,整个树的动脉就会被击断,变得无法到达。在P2P环境中,我们必须假设节点会崩溃,数据包会被丢弃,网络拓扑结构会随着时间而改变。当网络是静态的时候,生成树是很好的,但在P2P网络中(网络节点是动态的),它是一个不可能的事情。

译者注:

最小生成树: https://zh.wikipedia.org/wiki/最小生成树

进入gossip协议

[/‘gɒsɪp/] “流言算法”、“八卦算法”、“瘟疫算法”

如果我们想要生成树的可扩展性,而又没有脆性,我们要使用gossip。gossip协议的原理很简单,你可能已经有了它们如何工作的直觉。

在传统的gossip协议中,每个节点定期向K个随机目标"传染”。这个K被称为感染因子(作为对流行病学的一个点子)。一旦这些 K 目标收到消息后,他们会随机选择另一个 K 的目标进行闲聊。这种情况一直持续到所有可到达的节点都收到消息,或者消息过期。

下图中k=3 (忽略重复)

https://nakamoto.com/content/images/2020/01/gossip-propagation.gif

感染因子越强,信息传播的速度越快,越彻底。另一方面,感染因子越高,网络中产生的噪音就越多,每条消息消耗的带宽就越多。

如果一个节点向他们所有的同伴传递消息,这就被称为泛滥(flooding)。比特币执行的是泛滥而不是随机感染。

像许多随机协议一样,gossip是不完美的,但它最终近似于最小生成树的属性(有很高的概率)。同时,它提供了更高的容错性。

gossip协议对P2P网络来说有几个理想的特性。

  • 可靠性: 只有一小部分目标收件人无法收到你的广播。
  • 低延时: O(logN)
  • 非常高的容错率

这使得gossip成为像比特币这样的系统中信息传播的主要候选人。

所以,在我们掌握了一点gossip理论后,让我们看看Gnutella是如何工作的。

更多的,参考这里

https://managementfromscratch.wordpress.com/2016/04/01/introduction-to-gossip/

Gnutella协议

/nʊˈtɛlə/ 努特拉

Gnutella的设计始于一个简单的想法:让我们采用一个像Napster一样的文件共享系统,但去掉中央服务器。

你会记得,Napster有一个中央服务器(或一组服务器),作为所有可用文件的搜索引擎运行,并允许对等者找到彼此。在Gnutella中,P2P群组将自己处理搜索请求和对等体的发现。

https://nakamoto.com/content/images/2020/01/gnutella-optimized-1.gif

在Gnutella中,每个客户都同时作为客户和服务器(Gnutella称他们为 “服务者”)。客户端通过P2P叠加图(P2P overlay graph)直接连接到对方。

overlay graph是 “覆盖 “在实际基础网络之上的P2P网络。在这种情况下,底层网络是IP本身–这就是节点必须向对方发送数据包的方式。在底层网络中(也称为 “底层”),共享一个大前缀的两个IP将在物理上相互靠近。但P2P overlay graph不一定尊重基础距离。叠加图中的邻近同伴在物理世界中可能很远,而现实世界中的邻居在P2P图中可能很远。

https://nakamoto.com/content/images/2020/01/p2p-overlay-optimized.gif

只要覆盖网络对底层网络拓扑不敏感,你就会得到次优的路由,因为信息不是走现实世界的最短路径。更先进的P2P系统试图使用更聪明的路由模型,将底层网络考虑在内,但最简单的方法是建立一个非结构化的网络,它产生自己的随机拓扑结构,并在信息路由时遵循该结构。Gnutella是一个非结构化的网络,比特币也是如此(有一些注意事项我们将在后面讨论)。

协议简述

现在你已经对Gnutella的工作原理有了一个高层次的概念。让我们深入了解一下更多的细节。

在Gnutella中,每个节点都跟踪一个对等体的列表(为简单起见,让我们说每个对等体不超过5个)。每个节点都会定期对其对等体进行ping,以确保它们仍然在线。如果一个节点注意到它的任何一个对等体已经消失了很久,该节点将取消与他们的对等体,并找到其他的对等体。

在Gnutella协议中,有五种消息类型。

Message type Description
Query 搜索某个文件名
QueryHit 对搜索的肯定回复,说明“嘿,我有一个与该查询匹配的文件”
Ping 探查peer以查看他们是否还活着
Pong A reply to a Ping
Push 要求向请求者发送一个文件(如果文件所有者在防火墙后面,阻止了传入的连接)。

这就是全部。仅仅是这五种信息类型,你就可以做很多事情了!

Ping和Pong是用来发现对等人和心跳的,所以我们现在先不考虑这些。

Push消息只用于协调文件所有者在防火墙后面的文件下载,所以也忽略它。

该协议的主要内容发生在Query和QueryHit消息中。

比方说,你在寻找一首Metallica的歌曲,所以你构建了一个Query消息。你想广泛地传播你的查询,所以你向随机的3个对等体gossip这个查询。这些同伴中的每一个人也会向他们的同伴中的随机3人gossip这个查询,以此类推。

请注意,如果这个转发过程无限期地继续下去,我们就会有一个问题:你的信息会在网络中永远循环。节点A会向B发送,B会向C发送,C会向A发送,以此类推。这显然不是我们想要的–我们需要某种形式的内存,这样一个节点就可以丢弃它已经看过的信息。

为了解决这个问题,我们将给每个消息一个UUID。通过简单地跟踪你已经转发的UUID,你可以忽略重复的消息,从而防止任何无限的消息循环。

但仍有一个问题:每条信息都会在P2P网络中传播,直到它实际上到达每个人手中。如果你想这样做,这很好,但对于文件共享来说,这就太过分了。这将迅速成为一个可扩展性瓶颈(每个节点都必须处理整个网络中的每一个搜索)。

为了解决这个问题,我们可以在每个消息上添加一个TTL(生存时间)。TTL是一个整数,每次转发消息时都会递减,一旦TTL为0,消息就会被丢弃。这意味着每条信息在消失之前只会传播这么远,就像一个衰落的波浪。

有了这些东西的实现,我们应该得到一些很好的类似gossip的消息传播,并在网络内进行搜索。

下图为 TTL 为 4 的gossip传播

https://nakamoto.com/content/images/2020/01/gossip-propagation.gif

路由查询命中

现在让我们来看看另一面:假设我们收到一个关于metallica的查询,我们有一个匹配的文件。我们想给他们返回一个QueryHit响应。我们应该如何将响应传回给原发件人?

最明显的做法是让搜索者在他们的查询中包括他们的IP,这样我们就可以直接响应他们,有点像一个返回地址。这有什么错呢?(真的想一想吧)。问题是这让被动的观察者知道到底是哪个IP在请求哪个文件。这将是一个巨大的隐私泄漏! 我们应该努力做得更谨慎一些,而不是仅仅公布每次搜索的源IP。

也许回应者可以把他们的IP说出来,这将有望让发件人知道应该联系谁。但是,这将侵犯响应者的隐私!这将导致网络中出现许多不必要的噪音。这也会导致网络中出现许多不必要的噪音,因为其他人都要八卦回响应者的IP,尽管只有发送者真正关心。

Gnutella使用的解决方案是相当巧妙的:递归地路由响应

假设你有一个文件符合 “metallica “的查询。你向转发该信息的人发回一个QueryHit响应,并指定你所响应的UUID。节点C看到该UUID的QueryHit后,会将其发回给转发该查询的人,即节点B,然后节点B会将其发回给节点A,即原发件人。通过简单地让每个节点记住谁转发了他们的每个消息,QueryHits可以被递归地送回发件人。这最大限度地减少了网络中的隐私泄漏和不必要的噪音。

Query和QueryHit消息之间的这种功能是Gnutella文件发现的核心。然后通过直接的HTTP连接进行实际的下载。

正如你所看到的,Gnutella是一个优雅、简单的文件共享协议,在没有中心方的情况下工作。

Gnutella的一些问题

尽管有其独创性,Gnutella也有其问题。第一个问题是带宽:Gnutella产生了大量的网络流量,其中大约50%是ping。有些问题可以通过积极的缓存和整合ping信息来缓解,但该协议的早期版本是非常耗费带宽的。

第二个问题是,70%的Gnutella用户是自由职业者,他们只下载文件,从不上传自己的文件。这使得下载者和上传者之间形成了不健康的平衡

最后,Gnutella实际上不是这种基于gossip的网络设计的最佳人选。在Gnutella中,一半以上的网络收到每一个查询。这对文件共享来说是矫枉过正的,你不需要那么大一部分网络来接收每个消息–大多数搜索都是针对常见的文件,许多附近的节点都可以为你服务。

另一方面,比特币要求每个节点都知道每个区块,而交易是为了传播给每个人。可以说,这使得比特币比Gnutella更适合于基于gossip的协议。

这些问题由于Gnutella的扁平结构而变得更加复杂,它在网络拓扑结构中把每个节点都视为平等。但是,当涉及到带宽时,节点并不都是平等的–一些节点更稳定地保持在线,并有更多的带宽可以提供。如果一个系统绕过脆弱的节点并围绕高可靠性的节点进行自我组织,那么它的性能就会立即变得更强。

后来的文件共享客户端,如KaZaAeMule,将使用更多的分层拓扑结构和更智能的路由。在这些系统中,表现良好的节点最终会成为 “超级节点”,在系统中承担更多的负载。相对于像Gnutella这样的扁平结构,这大大提高了它们的整体性能。

在下一课中,我们将看看gossip在比特币本身中是如何运作的,我们还将挖掘隐私在其网络层中的作用。

作业

在这项作业中,你将实现一个简单的P2P协议 这项作业相当复杂,但也是对你推理P2P协议在实践中如何运作的能力的一次大考验。 前往repl.it上的作业,查看README.md。一旦你读完它,fork工作区,开始工作。这个任务没有测试套件;你只需要建立系统,直到协议正常工作。

一旦你完成了(而且只有在你完成之后!),你可以对照解决方案检查你的工作,你可以在Github repo的一个单独的分支中找到。一旦你完成了这项任务,你就可以继续前进了。

比特币的P2P网络

我们已经抽象地看了gossip协议。现在是时候将这些抽象概念应用于比特币自己的P2P网络了。

在高层次上,几乎所有的加密货币都从比特币继承了相同的P2P网络设计。有了Gnutella作为背景,你现在应该完全有能力理解比特币的网络层。它与Gnutella真的很相似,只是在本课中我们会讲到一些增强的部分。

进入网络

到目前为止,我们已经分析了稳定状态下的gossip网络。但是,一个人实际上是如何加入一个八卦网络的呢?你只是在互联网上随机查询节点,直到找到运行正确软件的人吗?(谢天谢地,不是的)。

每个P2P协议都需要一个引导节点来引导你进入网络,并帮助你初始化你的同伴列表。这个引导节点是你进入P2P网络的入口,然后你可以从这里有机地找到新的对等人

https://nakamoto.com/content/images/2020/01/introducer-optimized-1.gif

当然,引导节点的危险在于,如果它没有经过认证,它可能是恶意的,并执行中间人eclipse attack(日蚀攻击)。

在Bitcoin Core,即典型的比特币实现中,这些引导节点被硬编码为可信的DNS服务器,由核心开发者维护

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// From: https://github.com/bitcoin/bitcoin/blob/master/src/chainparams.cpp

vSeeds.emplace_back("seed.bitcoin.sipa.be"); // Pieter Wuille, only supports x1, x5, x9, and xd
vSeeds.emplace_back("dnsseed.bluematt.me"); // Matt Corallo, only supports x9
vSeeds.emplace_back("dnsseed.bitcoin.dashjr.org"); // Luke Dashjr
vSeeds.emplace_back("seed.bitcoinstats.com"); // Christian Decker, supports x1 - xf
vSeeds.emplace_back("seed.bitcoin.jonasschnelli.ch"); // Jonas Schnelli, only supports x1, x5, x9, and xd
vSeeds.emplace_back("seed.btc.petertodd.org"); // Peter Todd, only supports x1, x5, x9, and xd
vSeeds.emplace_back("seed.bitcoin.sprovoost.nl"); // Sjors Provoost
vSeeds.emplace_back("dnsseed.emzy.de"); // Stephan Oeste

你可以通过UNIX命令行使用dig命令对这些引导节点之一进行DNS查询,从而自己检索到一个初始对等体列表。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# dig seed.bitcoin.sipa.be                                            32s

; <<>> DiG 9.10.6 <<>> seed.bitcoin.sipa.be
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 47846
;; flags: qr rd ra; QUERY: 1, ANSWER: 25, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;seed.bitcoin.sipa.be.		IN	A

;; ANSWER SECTION:
seed.bitcoin.sipa.be.	4502	IN	A	173.249.18.129
seed.bitcoin.sipa.be.	4502	IN	A	46.6.8.230
seed.bitcoin.sipa.be.	4502	IN	A	18.134.242.0
seed.bitcoin.sipa.be.	4502	IN	A	52.59.216.190
seed.bitcoin.sipa.be.	4502	IN	A	37.59.68.8
seed.bitcoin.sipa.be.	4502	IN	A	220.133.39.61
seed.bitcoin.sipa.be.	4502	IN	A	50.53.190.92
seed.bitcoin.sipa.be.	4502	IN	A	135.181.109.135
seed.bitcoin.sipa.be.	4502	IN	A	72.82.9.98
seed.bitcoin.sipa.be.	4502	IN	A	71.204.231.91
seed.bitcoin.sipa.be.	4502	IN	A	47.54.186.223
seed.bitcoin.sipa.be.	4502	IN	A	3.37.231.206
seed.bitcoin.sipa.be.	4502	IN	A	104.233.207.37
seed.bitcoin.sipa.be.	4502	IN	A	162.250.191.222
seed.bitcoin.sipa.be.	4502	IN	A	162.227.162.49
seed.bitcoin.sipa.be.	4502	IN	A	136.32.173.8
seed.bitcoin.sipa.be.	4502	IN	A	88.198.7.110
seed.bitcoin.sipa.be.	4502	IN	A	124.176.25.123
seed.bitcoin.sipa.be.	4502	IN	A	23.93.18.185
seed.bitcoin.sipa.be.	4502	IN	A	85.10.206.119
seed.bitcoin.sipa.be.	4502	IN	A	18.191.42.193
seed.bitcoin.sipa.be.	4502	IN	A	194.118.88.162
seed.bitcoin.sipa.be.	4502	IN	A	15.161.99.250
seed.bitcoin.sipa.be.	4502	IN	A	168.119.104.7
seed.bitcoin.sipa.be.	4502	IN	A	72.10.171.42

;; Query time: 1834 msec
;; SERVER: fe80::5c70:17ff:fe74:3a64%19#53(fe80::5c70:17ff:fe74:3a64%19)
;; WHEN: Tue Aug 30 09:41:24 CST 2022
;; MSG SIZE  rcvd: 449

作为一个有趣的历史点,第一个版本的比特币通过IRC频道寻找对等者来引导其对等者名单。每个比特币节点都捆绑了一个小的IRC客户端,在第一次启动时,它会加入bitcoin00和bitcoin99之间的一个随机频道。如果它在这些频道中发现了其他IP,它就会尝试连接它们,直到填满它的初始对等体列表。

在IRC服务器(LFNet)关闭后,这种形式的引导最终被放弃了,这使比特币的对等体发现程序暂时失效。从那时起,比特币一直依赖这个基于DNS的系统来引导。

最初的对等体发现是所有P2P网络的一个固有的阻塞点。但在这个阶段之后,一个节点可以自由地用它选择的任何对等体来填充它的对等体表。

比特币的垃圾信息保护

你会记得,P2P协议的一个弱点是质量控制。你如何阻止坏的行为者把网络上的垃圾信息搞死?我们早些时候对这个问题做了手脚。原来,比特币使用一个信誉系统来处理这个问题,由Gavin Andresen在2011年实施。

假设你是一个比特币节点,你刚刚启动了你的peer列表。你开始给你的每个对等人分配一个0分的垃圾信息。想想看,这就像一个P2P犯罪记录–在没有中央司法系统的情况下,网络中的每个人都必须对其他人的行为保持警惕。

小的违规行为,如在初始握手时没有发送版本信息,将使你的分数增加1分。更严重的DoS企图,如在INV(inventory)消息中发送超过50,000个ID,将使你被扣20分。

一旦一个peer积累了100分,你的客户端就会自动对他们进行24小时的影子禁言(shadowbans),并停止对他们的gossip。这些垃圾信息的分数实际上没有在协议中跨对等体传播。(为什么你认为它们不是呢?)但即使如此,这个系统也是对行为不端的节点的一种适当的防御。

网络层面的隐私

P2P网络有其内在的隐私权衡。想一想:网络中的每条信息都是公开喊出来的,任何人都可以自由地观察所有经过的信息。对于像比特币这样的金融网络,这种缺乏隐私的情况并不理想。

比特币由于其假名性,提供了一些金融隐私。如果全世界都知道账户1G9HFbCRikgPpQboURsdqszy9HbKtvceZ5在一家简陋的海外药店花了0.25BTC,也许我就没事。但是,如果有人能找出最初发送签名交易的我的IP,突然间我的隐私就被泄露了。

这很糟糕。P2P网络对审查制度有抵抗力,但对监视却一点也不抵抗

https://nakamoto.com/content/images/2020/01/image-94.png

让我们勾勒出一个复杂的窃听者如何监视网络的过程。

默认情况下,一个正常的比特币节点会与其他对等体建立8个出站连接。然而,比特币客户端是开源的,所以任何人都可以自由地修改他们的客户端,以连接到他们想要的更多对等体。现在比特币网络中大约有10,000个实时对等体,原则上,一个人可以连接到所有的对等体。这将使你成为一个超级节点。

有了超级节点,你就可以跟踪网络中任何地方发送的每一条信息,并从上帝的视角重建所有信息的历史。这基本上可以让你弄清楚哪些地址对应哪些IP,并对比特币交易进行匿名化处理。

https://nakamoto.com/content/images/2020/01/gossip-propagation-1.gif

gossip传播的设计不是为了维护隐私。请记住,在收到消息后的那一刻,每个节点都会立即将其淹没到所有的出站peer。这就留下了每个消息开始的明显痕迹。即使一个被动的观察者只连接到网络的50%,他们也会清楚地看到从发件人那里发出的 “信息波”。

2015年,比特币改变了它传播gossip信息的方式,以实现更好的隐私。它现在使用一种叫做扩散(diffusion)的方法。在扩散过程中,客户端不是立即向每个对等体泛滥,而是在向每个对等体说闲话之前等待一个随机指数延迟。这有掩盖P2P消息图的作用,使人们更难观察到 “消息波(message wave) “的来源。

你可以用这样的代码来勾勒它。

1
2
3
def gossip(msg):
	for peer in peers:
		schedule_send(peer, msg, wait=np.random.exponential(1.0 / theta))

https://nakamoto.com/content/images/2020/01/diffusion-optimized-1.gif

然而,即使如此,比特币网络层的隐私也远非完美。扩散(diffusion)仍然向被动的对手泄露了相当多的信息。此外,P2P信息不是双边加密的,所以被动的数据包嗅探器可以很容易地窥探到任何比特币流量的明文。

在改善比特币的网络级隐私方面,还有很多工作要做。一些有隐私意识的用户更喜欢比特币而不是Tor的网络隐私,但即使这样也有问题

最近,CMU的研究人员想出了一个比扩散(diffusion)更完善的方案,称为蒲公英协议(Dandelion Protocol),提供了更好的网络级隐私保证。

https://nakamoto.com/content/images/2020/03/dandelion.gif

在蒲公英协议中,每一个交易广播都是从一个秘密的电话游戏开始的。发起人会把他们的交易悄悄告诉一个对等人,后者再把它悄悄告诉另一个对等人,如此循环。经过随机数的跳转,最后一个对等体将会像比特币一样把交易说出来。但这个对等体与发起人相距甚远,对于任何观察者来说,都不可能分辨出这条链是从谁开始的。

这对于混淆发起人的IP更为有效,但它的代价是消息的传播速度更慢。蒲公英现在被认为是对比特币gossip机制的一种可能的增强,并且已经在其他加密货币中实施。我们在补充阅读中提供了更多关于蒲公英的资源。

P2P网络中的生活

你现在应该对信息在加密货币网络中的传播方式有一个更好的心理模型。由于其gossip架构,加密货币是 “最终一致”–它们不提供任何关于你的消息何时会被网络的其他部分看到的硬性保证。此外,并非所有节点都会在同一时间看到相同的状态。

下图: 50%(橙色)或 90%(蓝色)的网络需要多长时间才能收到最新的比特币区块

https://nakamoto.com/content/images/2020/01/image-96.png

当这些网络达到一定规模时,信息的传播速度会相当缓慢。对于比特币来说,过去90%的网络需要30多秒才能收到最新的区块!现在,只需要几秒钟就能让整个网络同步到最新的区块。今天,它需要几秒钟,直到整个网络同步到最新的区块上。(矿工看到新区块的速度要快得多,这一点我们将在后面探讨比特币采矿时讨论)。

我们对比特币网络层的探索到此结束。现在你应该对流言协议的工作原理有了直观的认识,以及是什么让它们在规模上如此强大。你也应该明白为什么中本聪选择用P2P架构设计比特币网络。

有了这个基础,在下一个模块中,我们将探索共识–首先,我们将研究它的经典起源,导致中本聪的基本突破,使比特币成为可能。

附加阅读