以太坊的P2P节点入网
P2P节点在启动后是一定要与某个节点相连去加入网络的。
假如Alice在上海起了一个以太坊私有节点,Bob在西安起了一个相同创世文件的以太坊私有节点,全球只有这俩节点使用了这个创世文件(相同hash的创始文件),那么这两个节点要如何发现呢?
全网扫描肯定不现实,下面看下以太坊是怎么实现的。
经过对代码进行跟踪调试,发现以太坊将8个节点的Node信息硬编码进了源代码中,这8个节点被称为BootstrapNodes(本文称其为引导节点)。
如下图,节点默认使用主网的BootstrapNodes,如果在节点启动时声明了具体的BootstrapNodes信息,那么就会使用启动时声明的信息。如果启动时声明了测试网,那么BootstrapNodes会使用对应测试网的引导节点信息。(可以看到,如果既声明了自己私链的BootstrapNodes信息,又声明了连接某一个测试网,那么就会节点会连接测试网,不使用自己声明的私链节点。)
可以看到主网的8个节点信息,观察其中一个,含有nodeID,IP和Port。
查看声明引导节点的地方,如下图,确实是硬编码进去的。主网有8个,Ropsten测试网有4个,Sepolia测试网有2个。
观察主网8个BootstrapNodes的注释,可以看到其使用了不同云服务商的不同地域的云服务来实现异地容灾,降低8个节点全挂的情况。
当节点启动时,会优先连接到主网这8个节点,以加入以太坊的P2P网络,然后我们的节点就可以发现更多的节点,就可以去维护自己的节点列表了。
通过DHT发现节点
DHT,Distributed Hash Table,分布式哈希表。
以太坊使用Kademlia协议发现节点,而Kademlia使用的便是DHT技术。
具体有关Kademlia的内容可以查看我这篇博客:P2P网络-Kademlia协议。
通过DNS发现节点
这个是2018年的EIP-1459提案的内容。如果DHT失效,则会使用通过DNS机制来发现节点。
先看源码,下图的KnownDNSNetwork就根据不同的网络返回了DNS的地址。
可以看到,DNS由前缀+protocol+"."+net+".ethdisco.net"
组成。
前缀在下图中可以看到,是硬编码的。
protocol有两种取值:"all"或"les"(针对全节点和轻节点),等下会看到。
net根据创世文件的哈希值(各个网络的创世文件的哈希值也被硬编码进了源码中)来判断网络类型,不同的网络类型对应不同的net。
下图函数调用了上图函数,可以看到全节点和轻节点各自对应的protocol。
通过调试,可以看到主网的DNS地址为:enrtree://AKA3AM6LPBYEUDMVNU3BSVQJ5AD45Y7YPOHJLEF6W26QOE4VTUDPE@all.mainnet.ethdisco.net
节点访问all.mainnet.ethdisco.net
即可得到所有节点NodeID等数据的列表,这个列表使用 secp256k1 私钥签名,我们需要对应的公钥去验证它。这个公钥就是AKA3AM6LPBYEUDMVNU3BSVQJ5AD45Y7YPOHJLEF6W26QOE4VTUDPE
。将公钥转32进制再压缩,再对其进行base32编码即可得到这串字符串。
具体的DNS列表在其GitHub仓库中可以查看,并且可以看到在不断更新。