=====节点发现算法===== ====核心数据结构==== NodeTable类负责以太坊的节点发现,NodeTable采用kademlia(KAD)算法进行节点发现 * NodeTable维护一个网络节点列表,此列表为当前可用节点,供上层使用 * 由于NodeID经过sha3生成出的Hash为256位。列表有256-1=255项,其中-1是因为刨除了当前节点(本机) * 列表的每一项位一个节点桶(NodeBucket),每个桶中最多放16个节点 * 列表的第i项代表据当前节点(本机)距离为i+1的网络节点集合 ====节点探索算法==== 其中节点间距离定义如下: * 节点NodeID(512位)会先用sha3算法生成一个256位hash。算两个节点的256位hash的XOR值,节点距离定义为此XOR值的1位最高位的位数。(例如:0010 0000 1000 0101 位XOR值的化 那么这两个节点的距离为14) * 此处的NodeID为网络节点公钥(512位) * 注意:这里的节点距离与机器的物理距离无关,这个距离仅仅是逻辑上的一种约定 ====发现算法思路如下==== {{:wiki:p2pjiedian1.png?600|}} - 先随机一个目标节点的NodeID - 在列表中以相对NodeID的“距离”为指标,由近及远查找此待连节点“附近”的节点。并将这些节点放入“附近”节点集合 - 向目标节点的“附近”节点集合中的每个节点发送FindNode消息 - 若在目标节点的”附近”没有搜到节点,则返回步骤1 - 否则等待600ms后跳转到步骤2 ====节点的状态==== {{:wiki:p2pjiedian2.png?600|}} * **Pending:** * 挂起状态,每个新发现的节点或通过代码添加的节点的初始状态 * 在新增节点时会向此节点发送Ping消息,已查看是否在线 * **Alive:**此状态说明Pong消息已收到,此节点在线。 * **Evicted:**由于对当前节点某个距离的桶最多只允许存在16个节点,若在此距离发现的新节点正好超过了限额,则新节点保留,桶中最老的节点会被唤出,进入此状态 ====如何冷启动==== 由于初始化时节点列表为空,所以不可能找到目标节点的所谓附近节点。这就需要一些初始种子节点进行连接。在eth客户端启动时会添加5个种子节点,这些节点的NodeID、ip、端口被硬编码在Host.pocHosts函数中。 std::unordered_map Host::pocHosts() { return { // Mainnet: { Public("a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c"), "52.16.188.185:30303" }, { Public("de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786"), "54.94.239.50:30303" }, { Public("1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082"), "52.74.57.123:30303" }, // Testnet: { Public("6ce05930c72abc632c58e2e4324f7c7ea478cec0ed4fa2528982cf34483094e9cbc9216e7aa349691242576d552a2a56aaeae426c5303ded677ce455ba1acd9d"), "13.84.180.240:30303" }, { Public("20c9ad97c081d63397d7b685a412227a40e23c8bdc6688c6f37e97cfbc22d2b4d1db1510d8f61e6a8866ad7f0e17c02b14182d37ea7c3c8b9c2683aeb6b733a1"), "52.169.14.227:30303" }, }; } ====节点发现协议==== * 协议: * FindNode:节点查询协议,向目标节点询问其临近节点列表 * Neighbours:响应FindNode消息,当某节点接到其它节点发来的FindNode消息时,会回送Neighbours消息,其中携带了此节点的附近节点 * PingNode:用来查看节点是否存活。对于缺失NodeID的节点,也可用来询问其NodeID * Pong:对PingNode消息的响应 * 协议交互: * 在上面的算法描述中,当前节点会向随机选定的节点的附近节点集合中每个节点发送FindNode消息,表示希望查这些节点的附近节点 * 这些节点在接收到FindNode消息会需要向发送节点回送Neighbours消息,并在此消息内含有所在节点的附近节点集合 * 当前节点在收到回送的Neighbours消息后,会将Neighbours中所携带的节点加入到自己的网络节点列表中,并对这些携带节点发送Ping消息 * 等到Pong的消息到达,证明此节点存活,并加入到节点列表中 {{:wiki:p2pjiedian3.png?600|}}