以太坊节点发现协议

本档前部分翻译自以太坊定义的节点发现协议(版本4),后半部分给出了源码实现的大致流程,以帮助理解。

以太坊节点信息的存储采用的是Kademlia分布式哈希表。理解节点发现协议主要是理解分布式哈希表的原理,再加上定义的节点间通信的报文格式,节点ID的定义,距离的计算,加在一起就是以太坊的节点发现协议了。以太坊不同语言版本代码实现上具体细节可能不同但大致流程思想是相同的。

第一部分——节点发现协议定义 节点ID

每个节点都有一个secp256k1椭圆曲线密码学ID。节点的公钥作为标识或节点ID。节点之间的距离为公钥按位异或或者是公钥的哈希值按位异或。计算公式如下:

distance(n₁, n₂) = keccak256(n₁) XOR keccak256(n₂) 节点表

节点表在节点发现协议中用于保存邻节点信息。邻节点被存在一个包含有K桶的路由表中。协议中\(k=16\),即每个K桶至多含有16个节点条目。每项按时间排序——最新发现更新的节点放在前,其他在后。

每当一个新节点\(N_1\)被发现,就可以插入相应的桶中。如果桶中少于\(k\)个条目,\(N_1\)可添加到桶中第一个条目。如果桶中已含有\(k\)项,桶中最早发现的节点\(N_2\),需要通过发送ping包重新检测其有效性。如果没有收到来自\(N_2\)的回复则认为该节点已失效(下线),从路由表中移除并将\(N_1\)添加到桶的前部。

以太坊文档中Node Table一节有部分内容错误, For each 0 ≤ i < 256, every node keeps a k-bucket for nodes of distance between 2i and 2i+1 from itself. ,应该是\([2^i,2^{i+1})\) 。建议阅读论文Kademlia——A Peer-to-peer Information System Based on the XOR Metric。

端点验证

为了预防流量放大攻击,必须验证查询的发送者是否参与了发现协议。如果数据包的发送者在过去12小时内发送了具有匹配ping哈希的有效pong响应,则认为该数据包的发送者已经过验证。

递归查找

一次查找会找到\(k\)个距离目标节点最近的节点。节点查找发起后先选取\(a\)个距离目标节点最近的已知节点。随后同时向这些节点发送FindNode包。其中,\(a\)是一个参数,通常可设为3。发起者继续向先前查询到的节点发送FindNode,如此不断进行递归。对获知的\(k\)个离目标节点最近的节点,选取\(a\)个尚未查询过的节点向其发送FindNode。无法快速响应的节点将被排除在外,除非他们做出响应。

如果一轮FindNode查询失败,即没有返回任何一个比目前节点中更近的节点,那么将会继续向\(k\)个最近节点未被查询过的节点中发送FindNode。

报文协议

节点发现协议报文都是UDP报文,报文中最大的是1280字节。

packet = packet-header || packet-data

数据包头部:

packet-header = hash || signature || packet-type hash = keccak256(signature || packet-type || packet-data) signature = sign(packet-type || packet-data)

当在同一UDP端口上运行多个协议时,hash可使分组格式可识别。除此并无其他目的。每个包都由节点公钥来签名,签名是一个编码长度为65字节数组,签名值r,s,签名验证值v。

消息类型packet-type占单字节。包有效数据在消息类型后面。数据包头部之后的数据用RLP进行编码。根据EIP-8,实现应忽略列表中的任何其他元素以及列表后的任何额外数据。

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]packet-data = [ver

expiration字段是UNIX时间戳,如果一个数据包的时戳过期了可能会无法处理。收到ping数据包后,接收节点应回复pong数据包。并可考虑将发送节点添加到节点表中。

如果在过去12小时内未与发送方进行任何通信,则除了pong之外还应发送ping以验证对端节点。

Pong Packet (0x02)

packet-data = [to, ping-hash, expiration]

Pong是ping的响应。ping-hash须与相应的ping包hash一致。实现时应该忽略那些不含有ping包hash的pong包。

FindNode Packet (0x03)

packet-data = [target, expiration]

FindNode包用于请求距离目的节点近的节点。目标节点ID是一个65字节长度的secp256k1椭圆曲线公钥。当接收到FindNode,接收端需要回复在本地节点表中距离请求目的节点最近的16个节点。

为了对抗流量放大攻击,只有被验证过的FindNode发送者才会被回复邻节点信息。

Neighbors Packet (0x04)

packet-data = [nodes, expiration] nodes = [[ip, udp-port, tcp-port, node-id], ... ]

FindNode包的响应。

存在的问题及建议

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wspxjp.html