# Framework模块 P2P网络 * 节点发现 - 发现对等节点,构建路由表 * 节点连接 - 选择可信的节点,建立连接 ## 节点发现 区块链网络是一种结构化的P2P网络。结构化网络会将所有节点按照某种结构有序的组织起来,比如形成一个环状网络或者树状的网络。 结构化网络的具体实现上普遍都是基于DHT(Distributed Hash Table,分布式散列表) 算法。具体的实现方案有 Chord、Pastry、CAN、Kademlia 等算法。Tron 采用 Kademlia 节点发现算法。 Kademlia 是分布式散列表的一种实现,是去中心化 P2P 网络中最核心的一种路由寻址技术,可以在无中心服务器的情况下,在网络中快速找到目标节点。 * 节点ID 节点 ID 为节点的标识符,占64字节,即512bit。节点在加入TRON网络时,会随机分配一个节点 ID。 * 节点距离 节点距离通过两个节点的ID异或运算得到,公式为`distance(node1, node2) = 256 - 节点ID异或结果的前导0的个数`,如果计算结果为负数,距离等于0。 * K桶 Kademlia 的路由表是通过一些称之为 K 桶的链表构造起来的。根据节点距离的远近,将远端节点划分到不同的桶中,与本节点距离相同的远端节点被记录在相同的桶中,每个桶最多容纳16个节点。根据节点距离的计算公式就可以看出,Tron 实现的 Kademlia 算法一共维护256个桶。 Kademlia 协议包括四种UDP消息:Ping,Pong,FindNode,Neighbors。 * ping - 用于探测⼀个节点是否在线 * pong - 用于响应ping消息 * findnode - 用于查找与⽬标节点距离最近的其它节点 * neighbors - 用于响应findnode消息,会返回一个或者多个节点,最多16个 以上四个消息,就可以实现新节点的加入、K-桶的更新等机制。 节点发现过程中的报文交互:  ### K桶更新 节点发现是区块链节点接入区块链P2P网络的第一步。节点发现实际上就是进行 K 桶更新,将发现的远端节点加入到 K 桶中。 #### 初始化K桶 节点启动后,会读取节点配置文件中配置的种子节点,以及数据库中记录的对等节点,然后向它们发送Ping消息,如果收到回复Pong,在K桶没满的情况下,将相应节点写入K桶;如果相应桶已经达到了16个节点,则向桶中最早的节点发起挑战,挑战成功,将被挑战节点删除,将挑战节点加入K桶。 #### 发现更多节点 - 即发送findnode消息 节点启动时,会开启两个定时节点发现任务(`DiscoverTask`和`RefreshTask`),周期执行节点发现过程来更新k桶。 * `DiscoverTask` 是发现更多距离自己近的节点,每30s执行一次,执行流程如下图所示:(图需要改)  * `RefreshTask` 是通过随机节点ID来扩充本地k桶,每7.2s执行一次,执行流程如下图所示:(图需要改)  `DiscoverTask`和`RefreshTask`中使用的节点发现算法,在一次调用中会执行8轮,每轮向3个节点发送 `FindNode` 消息,执行过程如下: 1. 从K桶中找到距离目标节点 ID 最近的最多16个节点 2. 遍历节点,判断节点是否发送过 FindNode 消息,若是则跳过,若否,则发送 FindNode 消息,并等待50ms 3. 若遍历完列表或者已经向3个节点发送过 FindNode 消息,则退出此轮循环 4. 若此轮中,没有找到一个可以发送 FindNode 消息的节点,则退出调度 #### 接收neighbors消息,更新K桶 接收到neighbors消息后,依次向收到的邻居节点发送 `sendPing()` 消息,如果收到回复消息pong,则判断相应的K桶是否装满,如果K桶未满,则将新节点加入K桶,如果K桶满了,则向其中的某个节点发起挑战,挑战成功,则将旧节点从K桶中删除,将新节点加入K桶。  ### 节点发现过程中节点的状态变更 可以根据节点所处的情形比如未连接,已在K桶中等,对节点进行状态划分。节点的状态包括: * Discovered - 新发现的节点 * Dead - 对其第一次发送ping消息就没有在规定时间内回复的节点 * Alive - 回复了pong消息的节点,并等待被加入到K桶中 * Active - 在K桶中的节点 * EvictCandidate - 之前在K桶中的节点,但是由于被挑战,而从K桶中移出的节点 * NonActive - 回复了pong消息的节点,但是由于K桶满了,还未挑战成功的节点 在节点发现过程中,节点的状态转换图如下:  ## 节点连接 在节点启动后,会创建一个建立TCP连接的定时任务`poolLoopExecutor`,与节点建立连接的过程如下: 1. 选择要与之建立连接的节点 * 把还没有建立连接的activeNodes加入请求列表connectNodes中 * 计算还需选取的节点个数`lackSize` * 如果lackSize大于0,选择节点。首先从邻居发现节点列表里面过滤出满足要求的节点,然后根据[节点打分策略](#)对节点打分,将最高的lackSize个节点加入connectNodes 2. 与请求列表中的节点建立TCP连接 ### 节点打分策略 打分维度包括: * 丢包率:丢包率越低,说明数据通信质量越好。分数与丢包率成反比,最高得分为100,最低为0 * 网络延迟:网络延时越小,说明网络质量越好。分数与平均网络延时成反比,最高得分为20,最低为0 * TCP流量:TCP流量越大,说明通信比较活跃。分数与TCP流量成正比,最高得分为20,最低为0 * 断开连接次数:断开连接次数越少,说明节点越稳定。断开连接分数为负数,且与断开连接次数为正比,值为断开连接次数的10倍 * Handshake:曾经handshake成功过的节点,表示有相同的区块链信息,因此优先选择和他们建立连接。Handshake成功次数大于0时,Handshake得分为20,否则得分为0 * 分数处罚:处于处罚状态的节点,分数为0,不参与其它维度打分,包含以下几种情况: * 节点断开连接时间不到60s * 节点在badNodes列表中(badNodes:当收到异常的协议报文时,会将节点加入到badNodes,生效时长1个小时,当收到 badNodes 的连接请求,会直接拒绝请求) * 区块链信息不一致 计算节点分数时,首先判断节点是否为处罚状态,如果是,则分数计为0,否则,节点分数为各个维度的得分之和。 ### 握手 握手相关的TCP报文:HelloMessage。 TCP 连接建立成功后,主动发起TCP连接请求的节点,会向邻居节点发起握手过程,确认节点间的链路信息是否一致,以及是否需要发起区块同步。握手消息格式如下: ``` message HelloMessage { message BlockId { bytes hash = 1; int64 number = 2; } Endpoint from = 1; int32 version = 2; //p2p版本信息 int64 timestamp = 3; //消息时间戳 BlockId genesisBlockId = 4; //创世块id BlockId solidBlockId = 5; //固化块id BlockId headBlockId = 6; //头块id bytes address = 7; //节点地址 bytes signature = 8; //消息签名 int32 nodeType = 9; //节点类型 0全节点 1轻节点 int64 lowestBlockNum = 10; //最低块高度 全节点-0 轻节点-最低块 } ``` 当收到 Hello message 后,会进行如下检查 1. 本地头块高度是否小于握手消息中的最低块高度,是则断开连接 2. p2p version 检查,若版本号不一致,则断开连接 3. 创世块检查,若创世块不一致,则断开连接 4. 固化块检查,若本地区块链的固化块高度大于邻居节点的固化块高度,且邻居节点的固化块不在区块链中,则断开连接 5. 判断是否为可信节点(trustNodes),若不是可信节点,需要进一步检查,若满足以下任何一种情况,则拒绝请求 a. 已经达到了最大连接数 b. 节点在recentlyDisconnectedNodes列表中 c. 节点在badNodes列表中 d. 此ip已经建立了maxNodesWithSameIp个连接 6. 判断是否为重复连接,若是则断开后面建立的那条连接,通过节点发起连接请求的时间来判断 7. 如果本地节点为中心转发节点,则判断对方节点是否为非SR节点,是则断开连接;或者是SR节点但消息签名验证失败也会断开连接 ### 信道保活 信道保活是通过ping、pong TCP报文来完成的,与节点建立TCP连接并握手成功后,系统会为连接开启一个线程pingTask定期发送PING_MESSAGE消息,每10s调度一次。如果在超时时间内未收到节点回复的PONG_MESSAGE消息,则断开连接。
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up