devP2P是以太坊的P2P实现库,包含两个模块:
- rlpx
- discv4
本文翻译自以太坊官方文档,原文请参考:
https://github.com/ethereum/devp2p/blob/d3f99e3fbcfeb17399e6e97ee738f89ea441b317/discv4.md
节点发现协议(discv4)
节点发现协议(Node Discovery Protocol v4),包括:
- 发现 RLPx 节点并连接
- 基于UDP的RPC协议
- k桶(kademlia)结构
- 定义四种报文类型:ping, pong, findnode and neighbors
使用k桶来存储以太坊节点,因为k桶结构采用了一种短路径的拓扑图。
节点标识(Node Identities)
每个节点都有一个密码标识,使用椭圆曲线secp256k1算法生成的公钥,这个公钥作为节点的唯一标识,也叫node ID。
节点表(Node Table)
每个节点根据节点之间的距离,将邻居节点存储在一个由k-buckets(k桶)组成的路由表中,这个路由表就是节点表。
因为两个节点最大距离是256,所以节点表一共有256个k桶,每个桶中最多放k=16个节点。
终端证明(Endpoint Proof)
如果两个节点之间超过12h没有通讯,节点应该主动向对方节点发送ping来验证对方节点是否活跃,通过是否收到pong来得到一个终端证明。
递归查找(Recursive Lookup)
节点使用递归查找的方式来获取邻居节点。
开始时,查找发起节点从Node Table中提取α(比如3)个距离最closest节点,并发向这α个节点发送FindNode请求, 一次请求最多将返回k(比如16个)个距离发起节点最closest的节点。
收到Neighbors响应后,发起节点又从这些返回节点中抽取α个最closest节点(没有发送过请求的),重发FindNode请求,就这样递归下去(递归次数8次)。
在递归查找中,没有响应FindNode请求的节点,将会从Node Table删除。
报文协议(wire protocol)
发现节点协议使用udp发送数据包,并限定数据包最大大小为1280bytes。
- udp报文格式
packet = packet-header || packet-data
- 报文头部格式
packet-header = hash || signature || packet-type
hash = keccak256(signature || packet-type || packet-data)
signature = sign(packet-type || packet-data)
hash
:摘要,用于标识对应的ping和pong包。
signature
:签名,被编码成一个65bytes数组,包括(r,s,v)
packet-type
:包类型,占1个字节,有四种类型Ping Packet (0x01),Pong Packet (0x02),FindNode Packet (0x03),Neighbors Packet (0x04);
packet-data
:包数据,不同的packet-type数据不一样,统一被编码成rlp格式列表;
Ping Packet (0x01)
packet-data = [version, from, to, expiration]
version = 4
from = [sender-ip, sender-udp-port, sender-tcp-port]
to = [recipient-ip, recipient-udp-port, 0]
expiration
:有效期,是一个unix时间戳,用于定义过期的ping报文不再处理;
节点接收到ping报文,应该向发送节点回复pong,同时,考虑将发送节点加入路由表。
Pong Packet (0x02)
packet-data = [to, ping-hash, expiration]
pong是对ping的应答。
ping-hash
:与ping包的hash对应,如果节点从pong包找不到对应hash的ping包,将忽略pong包;
FindNode Packet (0x03)
packet-data = [target, expiration]
target
:目标节点的公钥, 占65个字节。
FindNode包用于发送请求,查找最接近target的节点信息。
接收者收到FindNode后,应该从本地Node Table中找出距离目标节点最closest的16个节点,回复Neighbors包。
注意:为了防止流量放大攻击,接收者只对通过endpoint proof的节点回复Neighbors包。
Neighbors Packet (0x04)
packet-data = [nodes, expiration]
nodes = [[ip, udp-port, tcp-port, node-id], ... ]
Neighbors是 FindNode 的应答。
已知问题和实施建议
expiration
:容易出错,依赖节点时钟的准确性。
endpoint proof
:不精确,因为FindNode的发送节点永远无法确定接收节点是否收到“pong”响应。Geth处理如下:如果没有与接收人沟通发生超过12 h,先发送一个ping过去,然后等待接收节点发送ping过来,回复pong,然后发送FindNode。
疑问:
framed
framing 帧 组装 分段 框架
Multiplexing 多路复用
网友评论