在做区块链项目时,大多落地金融机构,网络开通比较麻烦。现有区块链底层平台组成联盟链要求链上组织机构网络都需要打通,这里相对底层网络进行一些优化,初步想法是通过一些路由节点或是超级节点进行路由,也就是P2P网络中混合式架构。这里调研常见的区块链网络架构。
扩展比特币网络(extended bitcoin network):指代所有包含比特币P2P协议、矿池挖矿协议、Stratum协议以及其他连接比特币系统组件相关协议的整体网络结构。
节点具有钱包、挖矿、路由、完整区块链数据库功能
全节点:这个全指的是拥有完整的区块链副本,全节点可以不借助其他节点就可以完成校验交易
轻量节点:只保留一部分区块链交易(与自己相关的区块链),采用“简易支付验证(SPV)”的方式来完成交易验证,以及网络路由
比特币网络核心客户端:钱包、矿工、完整区块链数据库、网络路由
独立矿工节点:具有完整区块链数据,独立挖矿,网络路由
矿池服务器,一般矿池协议采用stratum协议
1、节点A收到一个block,对其进行验证,验证通过后,会将其广播给其连接的节点;
2、节点A向其连接的节点发送inv的消息,inv消息包含节点A验证过的区块的相关信息;
3、节点B收到inv消息,如果之前没有接收过这个区块,则向节点A发送一个getdata消息,要求得到交易记录,以及区块的具体信息;节点A只有在收到getdata消息后,才会把区块block的具体信息发送给节点B。
当矿工挖到新区块后,区块按上述方法传播到全网中。新区块并不是同时向所有节点广播,而只能向其附近的、建立连接的节点进行广播。比特币网络中,每个节点与至少其他P个节点保持连接,如果连接数低于P,那么节点就会从它已知的地址中随机选择地址并试图与其建立连接。如果连接的数量超过P,后续来自于其他节点的连接请求一般也并不会被拒绝。目前比特币网络中,平均每个节点有32个开放的连接,比特币协议中默认P=8。
新产生的区块必须要使得其被至少50%节点认证并接受。也就是其他节点对新区块进行哈希的过程,对随机数nounce的确可以使得哈希函数小于目标哈希,只有当新区块加入父区块且在此基础上继续产生6个区块后,才认为该新区块是在主链上的。
关于区块分叉问题在之前文章中分析过,因为涉及到交易最终是否被确认,区块是否能上主链,在这里再简单讲一下。两个节点可能同时挖到区块,例如一个节点挖到区块X,同时另一个节点挖到区块Y,并进行全网广播。此时如果节点A先收到区块X并且验证成功,节点A将基于区块X,寻找下一个新的区块。而先收到区块Y的节点B,在验证区块Y也是正确的区块后,将会基于区块Y上面寻找下一个新区块。这时区块链上出现了一个“分叉”,即出现了两条链:一条是X,另一条是Y。
出现两条链后,这时就看两条链挖到下一区块的速度。如果节点A比节点B更快找到下一个新区块Z,由于节点A是基于区块X上寻找新区块的,因此,原来的两条链分别变成X+Z和Y,这时由于X+Z这条链比Y链更长,那些原来在Y基础上挖矿的矿工们,就会放弃Y,并转而在X+Z这条链上继续挖矿,因此X+Z这条链就成为“主链”,而区块Y则成为“孤块”。
尽管有矿工挖到了区块Y,但是由于区块Y是孤块,并没有被加入到主链中去,因此,尽管挖到区块Y的旷工得到了比特币奖励,但是该比特币奖励是"unspendable“的状态,无法使用,也就意味着不会获得比特币奖励,其包含的所有交易记录也不会被确认,将返回到交易池中“待确认”。而挖到区块X的矿工,得到了比特币奖励,经过六个区块的验证后,被确认在主链上了,也就意味着将获得比特币奖励,这时其包含的所有交易记录也将会得到确认,并从交易池Pool中删去。
从前面区块传播的机制可以发现,某节点挖到新区块后,其发现新区块的时间、以及向外面传播该新区块的速度,影响该新区块最终是否能加入主链。
根据相关统计研究,2015年,当时全世界的节点大概为6000个,因此,当一个新挖出的区块被至少3000个节点(50%的节点)接受时,该区块就有较大的可能性加入主链。区块的传播速度与区块的大小有着明显的关系。例如,要传播至3k个节点,一个700Kb+的区块大约要花15秒,而一个200-300Kb的区块则只需要6秒。对于一个8MB大小的区块来说,要传播至网络中3k各节点,需要花100多秒。当矿工新挖出一个区块,当要传播至3k个节点,每个矿工平均花费10秒左右。
一般这个传播速度远小于挖出新区块的速度,一般挖到一个新区块时间为十分钟左右。
比特币主网的P2P网络是无结构的,而以太坊使用P2P网络是有结构的,采用分布式哈希表(DHT)技术,可以实现在分布式环境下快速而又准确地路由、定位数据的问题。以太坊通过Kademlia(简称Kad)算法来实现。
网络节点有一个nodeID,会映射到一个二叉前缀树,
将 nodeID 以二进制形式表示,然后从高到低对每一位的 0 或 1 依次处理;
二进制的第 n 位对应二叉树的第 n 层;
如果该位是 0,进入左子树,是 1 则进入右子树(反过来也可);
全部位都处理完后,这个 nodeID, 就对应了二叉树上的某个叶子节点。
利用异或来计算节点之间的逻辑距离,在这种二叉树结构下,对每个节点来说,离它越近的节点异或距离也是越近的。接着,可以按照离自己异或距离的远近,对整颗二叉树进行拆分。
从根节点开始,将不包括自己的那颗子树拆分出来,然后在包含自己的子树中,把不包括自己的下一层子树再拆分出来,以此类推,直到只剩下自己。以上图的 110 节点为例,可将其拆分成如图阴影所示的除 110 节点之外的三颗子树。
完成子树拆分后,只要知道每个子树里面的其中一个节点,就可以进行递归路由实现整颗二叉树所有节点的遍历。但在实际场景下,由于节点是动态变化的,所以一般不会只知道每个子树的一个节点,而是需要知道多个节点。因此,Kad 中有一个叫 K-桶(K-bucket)的概念,每个桶会记录每颗子树里所知道的多个节点。
其实,一个子树对应一个K-桶也就是就是一张路由表,如果拆分出来有 m 颗子树,那对应节点就需要维护 m 个路由表,所有的K-bucket组合一起就是网络的完整路由表。每个节点都会各自维护自己的 m 个 K-桶,每个 K-桶里记录的节点信息一般会包括 NodeID、IP、Endpoint、与 Target 节点(即维护该 K-桶的节点)的异或距离等信息。
以太坊中的K值是16,也就是说每个K桶包含16个节点,nodeID为256位,所以一共有256个k桶。K桶中记录了节点的NodeId,distance,endpoint,ip等信息,按照与target节点的距离进行排序。也就是每个节点维护256个K桶,也就是256个子路由表,也就是256个划分的子树,所有的k桶合起来也就是一个节点的路由表。
还是以nodeID为110的节点为例,共有3个k桶,如下图:
在以太坊的 Kad 网络中,节点之间的通信是基于 UDP ,有4 个主要的通信协议,用于K-Bucket刷新,新节点加入等操作:
不同于上面两种公链的P2P网络架构,fabric主要用于联盟链,要求同一channel内节点要互通。