CKB 节点发现协议
| Number | Category | Status | Author | Organization | Created |
|---|---|---|---|---|---|
| 0012 | Standards Track | Proposal | Linfeng Qian, JinYang Jiang | Nervos Foundation | 2018-11-28 |
CKB 节点发现协议
CKB 节点发现协议主要参考了比特币的协议。主要不同点如下:
- 节点版本号包含在
GetNodes消息中 - 通过
Nodes消息来定时广播当前连接的所有节点 - 我们使用
multiaddr作为节点地址的格式 (不允许出现/p2p/段,如果违反会被认为是不良行为并被打低分)
每次客户端启动时,如果 PeerStore 中的地址列表为空就会尝试通过 DNS 的方式获取初始地址,如果 DNS 的方式失败了就使用硬编码的种子地址来初始化地址列表。
节点发现的手段
DNS 获取地址
第一次启动的时候(引导阶段),如果需要节点发现服务,客户端会尝试向内置的 DNS 服务器发送 DNS 请求来获取种子服务器地址。
硬编码的「种子」地址
客户端会硬编码一些「种子」节点地址,这些地址只有在 DNS 获取地址失败的时候被使用。当通过这些种子节点获取了足够多的地址后需要断开这些连接,防止它们过载。这些「种子」地址的时间戳被设置为 0 所以不会加入到 GetNodes 请求的返回值中。
「种子」节点是那些在线时间较长而且和很多其它节点互连的节点。
协议消息
GetNodes 消息
当满足所有以下条件时,节点会发送一个 GetNodes 请求:
- 这个连接是自己主动发起的 (防御指纹攻击)
- 对方的版本号大于一个预设的值
- 当前存储的地址数量小于
ADDRESSES_THRESHOLD(默认 1000) 个
Nodes 消息
当客户端收到一个 GetNodes 请求时,如果是第一次收到 GetNodes 消息而且这个连接是对方主动发起的就会返回一个 Nodes 消息,该 Nodes 消息的 announce 字段为 false。每隔一定时间当前节点会将当前连接的节点信息以及本节点信息以 Nodes 消息广播给当前连接的所有节点,announce 字段为 true。当前收到 announce 字段为 true 的 Nodes 消息时会对地址可路由的那些节点地址进行转发。
这里 announce 字段的目的是为了区分 Nodes 消息是作为 GetNodes 消息的返回值还是广播消息,可以方便应用不同的规则来对节点的恶意行为做相应的处罚。涉及到的规则主要有:
- 一个节点只能有一个
Nodes消息 (announce=false) 作为GetNodes消息的返回值。 - 一个节点的广播消息中只能第一个
Nodes消息 (announce=true) 包含的节点信息数量超过ANNOUNCE_THRESHOLD(默认 10) 个,这是为了防止其它节点发送过多的Node信息。
所有 Nodes 消息中的每个 Node 中的 addresses 的数量不能超过 MAX_NODE_ADDRESSES (默认 3) 个。
对主要攻击方式的处理
指纹攻击 (fingerprinting attack)
GetNodes 消息只能通过 outbound 连接发送出去。
相关数据结构
我们使用 Molecule 作为数据序列化格式,以下为相关数据结构的 schema:
table DiscoveryMessage { payload: DiscoveryPayload;}
union DiscoveryPayload { GetNodes, Nodes,}
table GetNodes { version: uint32; count: uint32;}
table Nodes { announce: bool; items: [Node];}
table Node { node_id: Bytes; addresses: [Bytes];}流程图
节点 Bootstrap

发送 GetNodes 消息

广播当前连接的节点信息

CKB Docs