1、P2P原理及协议概述
P2P主要存在四种不同的网络模型,也代表着P2P技术的四个发展阶段:集中式、纯分布式、混合式和结构化模型。不过需要指出的是,这里所说的网络模型主要是指路由查询结构,即不同节点之间如何建立连接通道,两个节点之间一旦建立连接,具体传输什么数据则是两个节点之间的事情了。
最简单的路由方式就是集中式,即存在一个中心节点保存了其他所有节点的索引信息,索引信息一般包括节点IP地址、端口、节点资源等。集中式路由的优点就是结构简单、实现容易。但缺点也很明显,由于中心节点需要存储所有节点的路由信息,当节点规模扩展时,就很容易出现性能瓶颈;而且也存在单点故障问题。
那第二种路由结构则是纯分布式的,移除了中心节点,在P2P节点之间建立随机网络,就是在一个新加入节点和P2P网络中的某个节点间随机建立连接通道,从而形成一个随机拓扑结构。新节点加入该网络的实现方法也有很多种,最简单的就是随机选择一个已经存在的节点并建立邻居关系。像比特币的话,则是使用DNS的方式来查询其他节点,DNS一般是硬编码到代码里的,这些DNS服务器就会提供比特币节点的IP地址列表,从而新节点就可以找到其他节点建立连接通道。新节点与邻居节点建立连接后,还需要进行全网广播,让整个网络知道该节点的存在。全网广播的方式就是,该节点首先向邻居节点广播,邻居节点收到广播消息后,再继续向自己的邻居节点广播,以此类推,从而广播到整个网络。这种广播方法也称为泛洪机制。纯分布式结构不存在集中式结构的单点性能瓶颈问题和单点故障问题,具有较好的可扩展性,但泛洪机制引入了新的问题,主要是可控性差的问题,包括两个较大的问题,一是容易形成泛洪循环,比如节点A发出的消息经过节点B到节点C,节点C再广播到节点A,这就形成了一个循环;另一个棘手问题则是响应消息风暴问题,如果节点A想请求的资源被很多节点所拥有,那么在很短时间内,会出现大量节点同时向节点A发送响应消息,这就可能会让节点A瞬间瘫痪。
Ripple CTO:已将比特币卖到只剩当初持币量的2%:Ripple首席技术官David Schwartz发推说,他现在持币量是最初拥有的比特币总量的2%。早在在2017年,他据卖掉了自己的一些BTC,当时比特币达到了20000美元的历史新高。除了比特币和XRP之外,Schwartz还持有以太坊,ADA,BAT,XLM,EOS,BTG等加密货币。[2021/4/15 20:23:27]
再来看看第三种路由结构:混合式。混合式其实就是混合了集中式和分布式结构,如下图所示,网络中存在多个超级节点组成分布式网络,而每个超级节点则有多个普通节点与它组成局部的集中式网络。一个新的普通节点加入,则先选择一个超级节点进行通信,该超级节点再推送其他超级节点列表给新加入节点,加入节点再根据列表中的超级节点状态决定选择哪个具体的超级节点作为父节点。这种结构的泛洪广播就只是发生在超级节点之间,就可以避免大规模泛洪存在的问题。在实际应用中,混合式结构是相对灵活并且比较有效的组网架构,实现难度也相对较小,因此目前较多系统基于混合式结构进行开发实现。其实,比特币网络如今也是这种结构,后面再细说。
最后一种网络则是结构化P2P网络,它也是一种分布式网络结构,但与纯分布式结构不同。纯分布式网络就是一个随机网络,而结构化网络则将所有节点按照某种结构进行有序组织,比如形成一个环状网络或树状网络。而结构化网络的具体实现上,普遍都是基于DHT(DistributedHashTable,分布式哈希表)算法思想。DHT只是提出一种网络模型,并不涉及具体实现,主要想解决如何在分布式环境下快速而又准确地路由、定位数据的问题。具体的实现方案有Chord、Pastry、CAN、Kademlia等算法,其中Kademlia也是以太坊网络的实现算法,很多常用的P2P应用如BitTorrent、电驴等也是使用Kademlia。因为篇幅有限,就不展开讲这些算法的具体原理了。目前,我们主要理解DHT的核心思想即可。
在P2P网络中,可以抽象出两种空间:资源空间和节点空间。资源空间就是所有节点保存的资源集合,节点空间就是所有节点的集合。对所有资源和节点分别进行编号,如把资源名称或内容用Hash函数变成一个数值,这样,每个资源就有对应的一个ID,每个节点也有一个ID,资源ID和节点ID之间建立起一种映射关系,比如,将资源n的所有索引信息存放到节点n上,那要搜索资源n时,只要找到节点n即可,从而就可以避免泛洪广播,能更快速而又准确地路由和定位数据。当然,在实际应用中,资源ID和节点ID之间是无法做到一一对应的,但因为ID都是数字,就存在大小关系或偏序关系等,基于这些关系就能建立两者的映射关系。这就是DHT的核心思想。DHT算法在资源编号和节点编号上就是使用了分布式哈希表,使得资源空间和节点空间的编号有唯一性、均匀分布式等较好的性质,能够适合结构化分布式网络的要求。
数据:比特币矿工三月收入超过17亿美元,创历史新高:据欧科云链OKLink数据显示,比特币三月的矿工收入达到17.34亿美元,创下历史新高。而同期以太坊矿工在三月的收入则为13.41亿美元。[2021/4/1 19:35:49]
综上,这就是P2P网络的一点理论基础,不同的区块链可能会使用不一样的网络模型,但基本原理是一样的。后面分别讲解下最有代表性的两个区块链的网络:比特币网络和以太坊网络。
2、比特币和以太坊中的P2P网络整体对比分析
2.1通信协议层面
比特币的P2P网络完全基于TCP构建,主网默认通信端口是8333。
以太坊的P2P网络是一个完全加密的网络,提供UDP和TCP两种连接方式,主网默认TCP端口30303,推荐UDP发现端口为30301.
2.2初始节点发现
比特币网络中,初始节点发现有两种方式:
DNS-seed,又称为DNS种子节点,比特币的社区会维护一些域名,通过nslookup该域名可解析出数十个A记录的主机IP。
硬编码一些seed-node,当所有的种子节点全失效时,全节点会尝试连接这些种子节点。
在以太坊中,思路也大致相同,也是在代码中硬编码了一些种子节点做类似的工作。
2.3启动后节点发现
在Bitcoin的网络中,一个节点可以将自己维护的对等节点列表(peerlist)发送给临近节点,所以在初始节点发现之后,你的节点要做的第一件事情就是向对方要列表:“快把你的节点列表给我复制一份。”
所以在每次需要发送协议消息的时候,它会花费固定的时间尝试和已存的节点列表中的节点建立链接,如果有任何一个节点在超时之前可以连接上,就不用去DNSseed获取地址,一般来说,这种可能性很小,尤其是全节点数目非常多的情况下。
而在以太坊网络中,也会维护类似的一个节点列表(NodeTable),但是这个节点列表与比特币的简单维护不同,它采用了P2P网络协议中一个成熟的算法,叫做Kademlia网络,简称KAD网络。
比特币谷歌全球搜索指数创两年新高:金色财经报道,根据谷歌搜索数据,比特币在全球范围内的关注度目前为28,搜索指数创两年新高。此前的第二高峰在2019年6月,搜索指数为26,当时比特币价格约为1.4万美元。[2020/5/15]
它使用了DHT来定位资源,全称DistributedHashTable,中文名为分布式哈希表。KAD网络会维护一个路由表,用于快速定位目标节点。由于KAD网络基于UDP通信协议,所以以太坊节点的节点发现是基于UDP的,如果找到节点以后,数据交互又会切换到TCP协议上。
2.4NAT穿透
局域网中的主机IP与公网IP建立P2P连接的解决方案是做NAT穿透。比特币和以太坊均使用了UPnP协议作为局域网穿透工具,只要局域网中的路由设备支持NAT网关功能、支持UPnP协议,即可将你的区块链节点自动映射到公网上。
UPnP是通用即插即用的缩写,它主要用于设备的智能互联互通,所有在网络上的设备马上就能知道有新设备加入。
2.5节点交互协议
一旦节点建立连接以后,节点之间的交互是遵循一些特定的命令,这些命令写在消息的头部,消息体写的则是消息内容。
命令分为两种,一种是请求命令,一种是数据交互命令。
节点连接完成要做的第一件事情叫做握手操作。这一点在比特币和以太坊上的流程是差不多的,就是相互问候一下,提供一些简要信息。
比如先交换一下版本号,看看是否兼容。只是以太坊为握手过程提供了对称加密,而比特币没有。
握手完毕之后,无论交互什么信息,都是需要保持长连接的,在比特币上有PING/PONG这两种类型的消息,这很明显就是用于保持节点之间长连接的心跳而设计的;而在以太坊的设计中,将PING/PONG协议移到了节点发现的过程中。
请求命令一般分为发起者请求,比如比特币中的getaddr命令是为了获取对方的可用节点列表,inv命令则提供了数据传输,消息体中会包含一个数据向量。
我们说区块链最重要的功能就是同步区块链,而同步区块恰巧是最考验P2P网络能力的。区块同步方式分为两种,第一种叫做HeaderFirst,它提供了区块头先同步,同步完成以后再从其他节点获得区块体。
数据:三月份以来比特币流入量急剧下降,但仍处于历史较高水平:Glassnode最新数据显示,根据bitfinex,Coinbase、火币、Binance等12个主要交易比特币持有数据,在12日至13日崩溃之后,大量比特币被撤出。(与2月份的峰值相比,约为7%)。如图显示,自三月份以来,比特币流入数量急剧下降,但仍处于历史上较高水平。流入最高的时期包括2018年底的“哈希战争”,2017年的“加密货币泡沫”和这次的“新冠病冲击”。(Coinpost.jp)[2020/3/31]
第二种叫做BlockFirst,这种区块同步的方式比较简单粗暴,就是从其他节点获取区块必须是完整的。第一种方案提供了较好的交互过程,减轻了网络负担。这两种同步方式会直接体现在节点交互协议上,他们使用的命令逻辑完全不同。
一般P2P网络技术要解决两个主要问题,第一是资源定位,第二是资源获取,这一篇文章也是主要围绕这两点展开,其中节点发现和局域网穿透是属于资源定位问题,节点交互协议是属于资源获取问题。
3、比特币中的P2P网络详解
比特币网络
首先,比特币网络中的节点主要有四大功能:钱包、挖矿、区块链数据库、网络路由。每个节点都会具备路由功能,但其他功能不一定都具备,不同类型的节点可能只包含部分功能,一般只有比特币核心(bitcoincore)节点才会包含所有四大功能。
所有节点都会参与校验和广播交易及区块信息,且会发现和维持与其他节点的连接。有些节点会包含完整的区块链数据库,包括所有交易数据,这种节点也称为全节点(FullNode)。另外一些节点只存储了区块链数据库的一部分,一般只存储区块头而不存储交易数据,它们会通过“简化交易验证(SPV)”的方式完成交易校验,这样的节点也称为SPV节点或轻节点(LightweightNode)。钱包一般是PC或手机客户端的功能,用户通过钱包查看自己的账户金额、管理钱包地址和私钥、发起交易等。除了比特币核心钱包是全节点之外,大部分钱包都是轻节点。挖矿节点则通过解决工作量证明(PoW)算法问题,与其他挖矿节点相互竞争创建新区块。有些挖矿节点同时也是全节点,即也存储了完整的区块链数据库,这种节点一般都是独立矿工(SoloMiner)。还有一些挖矿节点不是独立挖矿的,而是和其他节点一起连接到矿池,参与集体挖矿,这种节点一般也称为矿池矿工(PoolMiner)。这会形成一个局部的集中式矿池网络,中心节点是一个矿池服务器,其他挖矿节点全部连接到矿池服务器。矿池矿工和矿池服务器之间的通信也不是采用标准的比特币协议,而是使用矿池挖矿协议,而矿池服务器作为一个全节点再与其他比特币节点使用主网络的比特币协议进行通信。
比特币全网未确认交易39983笔:金色财经消息,据btc.com数据显示,目前比特币全网未确认交易数量为39983笔,24小时交易速率为3.79txs/s。目前全网难度为16.55T,预测下次难度上调4.7%至17.33T,距离调整还剩10天9小时。[2020/3/13]
在整个比特币网络中,除了不同节点间使用比特币协议作为通信协议的主网络,也存在很多扩展网络,包括上面提到的矿池网络。不同的矿池网络可能还会使用不同的矿池挖矿协议,目前主流的具体矿池协议应该是Stratum协议,该协议除了支持挖矿节点,也支持瘦客户端钱包。一个包含了比特币协议主网络各种节点和Stratum网络,以及其他矿池网络的扩展比特币网络大概如下图所示:
另外,挖矿这块还有特殊需求。我们知道,矿工创建新区块后,是需要广播给全网所有节点的,当全网都接受了该区块,给矿工的挖矿奖励才算是有效的,这之后才好开始下一个区块Hash的计算。所以矿工必须最大限度缩短新区块的广播和下一个区块Hash计算之间的时间。如果矿工之间传播区块只采用上图所示的比特币协议网络,那无疑会有很高的网络延迟,所以,需要一个专门的传播网络用来加快新区块在矿工之间的同步传播,这个专门网络也叫比特币传播网络或比特币中继网络(BitcoinRelayNetwork)。
4、以太坊中的P2P网络详解
以太坊网络
和比特币一样,以太坊的节点也具备四大功能,也同样存在很多不同类型的节点,除了主网络之外也同样存在很多扩展网络。但与比特币不同的,比特币主网的P2P网络是无结构的,但以太坊的P2P网络是有结构的。前面我们已经提过,以太坊的P2P网络主要采用了Kademlia(简称Kad)算法实现,Kad是一种分布式哈希表(DHT)技术,使用该技术,可以实现在分布式环境下快速而又准确地路由、定位数据的问题。所以,下面主要讲解下以太坊的Kad网络。
在Kad网络中,每个节点都具有一个唯一的节点ID。另外,也会计算不同节点之间的距离,但这个距离不是物理上的距离,而是逻辑上的距离,是通过对两个节点ID进行异或(符号为^)计算得到的,即A、B两节点之间的距离的计算公式为:D(A,B)=A.ID^B.ID。异或有一个重要的性质:假设a、b、c为任意三个数,如果a^b=a^c成立,那就一定b=c。因此,如果给定一个结点a和距离L,那就有且仅有一个结点b,会使得D(a,b)=L。通过这种方式,就能有效度量Kad网络中不同节点之间的逻辑距离。
在异或距离度量的基础上,Kad还可以将整个网络拓扑组织成如下图所示的一个二叉前缀树,每个NodeID会映射到二叉树上的某个叶子。
映射规则主要是:
将NodeID以二进制形式表示,然后从高到低对每一位的0或1依次处理;
二进制的第n位就对应了二叉树的第n层;
如果该位是0,进入左子树,是1则进入右子树;
全部位都处理完后,这个NodeID就对应了二叉树上的某个叶子。
在这种二叉树结构下,对每个节点来说,离它越近的节点异或距离也是越近的。接着,可以按照离自己异或距离的远近,对整颗二叉树进行拆分。拆分规则是:从根节点开始,将不包括自己的那颗子树拆分出来,然后在包含自己的子树中,把不包括自己的下一层子树再拆分出来,以此类推,直到只剩下自己。以上图的110节点为例,从根节点开始,由于110节点在右子树,所以将左边的整颗子树拆分出来,即包含000、001、010这三个节点的这颗子树;接着,到第二层子树,将不包含110节点的左子树再拆分出来,即包含100和101这两个节点的子树;最后,再将111拆分出来。这样,就将110节点之外的整个二叉树拆分出了三颗子树。
完成子树拆分后,只要知道每个子树里面的其中一个节点,就可以进行递归路由实现整颗二叉树所有节点的遍历。但在实际场景下,由于节点是动态变化的,所以一般不会只知道每个子树的一个节点,而是需要知道多个节点。因此,Kad中有一个叫K-桶(K-bucket)的概念,每个桶会记录每颗子树里所知道的多个节点。其实,一个K-桶就是一张路由表,如果拆分出来有m颗子树,那对应节点就需要维护m个路由表。每个节点都会各自维护自己的m个K-桶,每个K-桶里记录的节点信息一般会包括NodeID、IP、Endpoint、与Target节点的异或距离等信息。以太坊中,每个节点维护的K-桶数量为256个,这256个K-桶会根据与Target节点的异或距离进行排序,每个K-桶保存的节点数量上限是16。
在以太坊的Kad网络中,节点之间的通信是基于UDP的,另外设置了4个主要的通信协议:
Ping:用于探测一个节点是否在线
Pong:用于响应Ping命令
FindNode:用于查找与Target节点异或距离最近的其他节点
Neighbours:用于响应FindNode命令,会返回一或多个节点
通过以上4个命令,就可以实现新节点的加入、K-桶的刷新等机制。具体的实现流程就不细讲了,留给大伙自己去思考。
总结
不同结构的P2P网络,会有不同的优点和缺点。比特币网络的结构明显容易理解,实现起来也相对容易得多,而以太坊网络引入了异或距离、二叉前缀树、K-桶等,结构上复杂不少,但在节点路由上的确会比比特币快很多。另外,不管是比特币还是以太坊,其实都只是一种或多种协议的集合,不同节点其实可以用不同的具体实现,比如,比特币就有用C++实现的BitcoinCore,还有用Java实现的BitcoinJ;以太坊也有用Go语言实现的go-ethereum,也有用C++实现的go-ethereum,还有用Java实现的Ethereum(J)。
思考和实践
在以太坊的Kad网络中,新节点的加入和K-桶的刷新流程是怎样的?比特币的新节点加入流程又是怎样的?哈希表有哪些实现方式?
郑重声明: 本文版权归原作者所有, 转载文章仅为传播更多信息之目的, 如作者信息标记有误, 请第一时间联系我们修改或删除, 多谢。