一、前言
p2p模块总是在区块链的众多子模块中优先启动,它承担将本地消息向外发送,获取外部消息的职责;获取与发送消息之前自身又需要维持一个最低的链接数,维持这个最低链接数就需要获取跟多节点信息。
与区块链的多个子模块交互、消息传输的隐私性与安全性、去中心化等一系列问题,使得设计一个p2p模块自然会涉及到很多问题,然而很多优秀的公链已经为我们提供了一些参考的实例。本文分析主流公链的的p2p模块及实现的一些细节问题,但是限于篇幅的原因仅会给出部分具体实例。
二、 架构分析及实现细节
从职责划分上来讲,p2p模块可分为两块:发现服务、消息传输服务。发现服务主要用来获取更多节点信息,使得消息传输服务在与其他节点建立链接时有更多的选择,节点信息包括IP、端口、协议、节点ID等,链接到更多的节点意味着在以下方面有更大概率:更新的本地状态、同等算力下更多的节点收到本地挖出的block。消息传输服务,顾名思义,就是用来传输消息的,比如共识消息、状态同步消息、心跳包、tx/block广播消息等。消息传输服务从业务深度上可划分为:
- 链接资源申请管理
- 协议/代码版本检测
- 隐私保护
- 消息路由
- 链接资源释放管理
1. 发现服务
主流公链的发现服务一般有三个组成部分:
- 节点信息的持久化与查询
- 引擎部分:维护链接池、调度节点发现
- 节点发现,主要使用pex、dht算法,概括的来讲:
- pex是在已建立链接的俩个节点之间交换各自已知节点信息
- dht解决了p2p网络中心化tracker的问题
1.1 节点信息与链接池维护
节点信息的应该区分为:
- 已经建立过链接的节点
- 未建立过链接的节点,通过节点发现获得
未建立过链接的节点应该额外的保存两条信息:
- 最后一次尝试链接的时间, 用于控制尝试链接的间隔时间
- 尝试的次数,次数超过一定数量,该信息应该被删除
重启后应该优先尝试已经建立链接的节点。go-ipfs采用bitswap算法,该算法通过负债率r调整消息发送概率P,负债率越大消息被丢弃的概率越大。通过累计发送和接受到的消息字节数计算负债率,进一步调整远程节点的消息被处理的概率,所以我们也建议将这这俩个指标持久化,供消息模块使用以及尝试链接的优先级。尝试失败超过一定的数量后,可以认为这条信息已经失效了。
计算负债率、丢弃概率公式如下:
r = bytes_sent / (bytes_recv + 1)
P = 1 − 1 / (1