理解以太坊的 P2P 网络

此文旨在帮助你理解 P2P 网络,并阐述一些以太坊的实现细节。P2P 技术利用终端设备的丰富资源,能够缓解中心化系统的短板,而且从九十年代开始,这项技术就已经被 eMule,bitTorrent 和 Skype 等知名软件所采用。P2P 技术也是比特币或者以太坊区块链系统的核心组件(我们的项目 Shyft Network 就是从这些区块链中衍生出来的)。很多人都听说过 P2P,但是不知道它到底是什么。那就让我们从了解什么是 P2P 网络开始吧。
什么是 P2P网络?

点对点(P2P)网络是一种网络覆盖层(overlay network)——就是说,它是建立在公开互联网之上的。从数学的角度来说,P2P 网络可以被视作一个有向图 G = (V,E),其中 V 是网络中的对等节点集合,E 是对等节点所连成的边的集合(也即节点间连接的集合)。每个对等节点 p 都有一个独一无二的标识号 pid。集合 E 中的边(p,q)指 p 可通过直接相连的路径向 q 发送消息;也就是说,p 使用 q 的 pid 作为目的地址,在网络之上向 q 发送消息。尽管在底层的 TCP/IP 网络中,相似的 IP 地址可以转译为在地理位置上相互接近,但很少有这么明确的直接关联。

理解以太坊的 P2P 网络

理想情况下,所有的对等节点间都应该有一条路径相连。但因为每个节点对网络拓扑和其他对等节点只有一个不完整的视图,所以网络覆盖层需要中间节点将消息转发至正确目的。图的结构为每对节点提供了多条中间路径,因此就算对等节点改变,也可通过图的连通性提供网络的恢复能力。对每个对等节点来说,图的连通性通过与其他对等节点的邻接关系来反映。当对等节点加入或者离开网络,邻接的对等节点可能会持有不正确的邻接信息。因此使用网络覆盖层维护机制(Overlay maintenance mechanisms)保存更新的邻接信息,使得所有节点间保持连通性。

理解以太坊的 P2P 网络

P2P 网络中的参与者向其他网络参与者提供部分资源。不需要中心化的协调者,每个对等节点都可贡献计算周期(CPU),磁盘存储和网络带宽。传统的 客户端-服务器 模型中,服务器提供资源,客户端使用资源;与之相对的,在 P2P 网络中,对等节点既是网络资源的供应者,也是消费者。因此,P2P 网络可以很好地解决 客户端-服务器 模型下的一些短板,比如可扩展性和单点故障。

理解以太坊的 P2P 网络

一般来说,P2P 网络会有一个门槛,节点的资源贡献高于这个门槛才能加入网络。度量资源贡献的标准应该是公平的,比如说,要求网络中每个对等节点的平均贡献应该在 P2P 系统总体平均值的统计范围内等。资源贡献应该是双方互惠的。付出贡献后可得到的利益,吸引着用户加入 P2P 应用。
以太坊的 P2P 网络是如何工作的?
以太坊的官方客户端节点软件 Geth,基于一种覆盖层维护机制 (称作 Kademlia 分布式哈希表)实现了对等节点发现协议(RLPx 节点发现协议)。虽然 Kademlia 是为了在 P2P 网络中有效地定位和存储内容而设计的,以太坊的 P2P 网络只用它来发现新的对等节点。
Kademlia
以太坊网络中,每个客户端节点都配备有一个 enode ID,之后将此 ID 用 SHA3 算法散列为一个 256 位的值。Kademlia 使用 XOR 操作定义距离,因此两个 256 位的数字之间的距离是他们的按位异或值(bitwise exclusive OR)。每个对等节点都拥有一个包含 256 个不同的桶(buckets)的数据结构,每个桶 i 中存储与本节点距离在 2i-1 到 2i 之间的 16 个节点。为了发现一个新的对等节点,以太坊节点选择自己作为目标 x,从桶中寻找到 16 个与目标 x 最近的节点,之后请求这 16 个节点,让它们从自己的桶中各找出 16 个与目标 x “更近” 的节点并返回,这样以来,会得到至多 16×16 个新发现的节点。之后请求这 16×16 个新发现的节点中离目标 x 最近的 16 个节点,让它们返回与 x 更近的 16 个节点。这个过程持续迭代,直到没有新节点被发现。

理解以太坊的 P2P 网络

对等节点间通信
Geth 使用 UDP 连接交换 P2P 网络的信息。有 4 种类型的 UDP 消息。一条 *ping* 消息请求一条 *pong* 消息作为返回。此对消息用来判断相邻节点是否可响应。一条 *findnode* 消息请求一条 *neighbors* 消息(其中包含 16 个已经被响应节点知晓的节点列表)作为返回。当建立好对等节点的连接之后,Geth 节点通过加密和认证的 TCP 连接来交换区块链信息。

理解以太坊的 P2P 网络

数据结构
Geth 客户端用两种数据结构存储其他节点的信息。第一种是称作 db 的长期数据库,它存储在磁盘内,客户端重启之后数据也是持久存在的。db 中包含客户端交互过的每个节点信息。db 的每条记录包含节点 ID,IP 地址,TCP 端口,UDP 端口,(此客户端)最后一次向(记录中)节点发送 ping 的时间,最后一次从节点收到 pong 的时间,节点响应 findnode 消息的失败次数。如果最后一次从一个节点收到 pong 消息的时间超过了一天,此节点将会被移出 db。
第二种数据结构是称作 table 的短期数据库。当客户端重启时 table 是空的。table 包含 256 个桶,每个桶存储至多 16 条记录。每条记录存储其他以太坊节点的信息——节点的 ID,IP 地址, TCP 端口和 UDP 端口。如果记录中的某个节点对于 findnode 消息连续响应失败,多于 4 次时将被移出 table。
当某个客户端第一次启动时,它的 db 是空的,只知道 6 个硬编码的引导节点。随后,当客户端开始发现对等节点,客户端依据上面描述的机制,将节点加入 db 和 table。
如果你想查阅更多关于以太坊 P2P 网络的内容,可以参见下面一些由以太坊社区成员贡献的文章:
“RLPx Node Discovery Protocol” by Felix Lange, Gustav-Simmonsson, and Roman Mandeleil
“Peer to Peer” by Felix Lange
“Kademlia Peer Selection” by James Ray
参考:
Vasilios Darlagiannis, (2010). P2P Systems and Overlay Networks, [PDF file] Retrieved from: https://www.iti.gr/iti/files/document/seminars/p2p_eketa_090610_v2.pdf
S. Umamaheswari and Dr. V. Leela, (2011, Mar.01). P2P Overlay Maintenance Algorithm, [PDF file] Retrieved from: http://journals.sagepub.com/doi/pdf/10.1260/1748-3018.6.3.555