以太坊是一个由数万个节点组成的全球 P2P 网络。节点如何找到彼此?消息如何在全网传播?执行层和共识层为什么使用两套不同的网络协议栈?本文深入以太坊的网络层。
以太坊 Merge 后,网络层分为两个独立的 P2P 网络:
┌───────────────────────────────────────────────┐
│ 共识层(Beacon Chain) │
│ 使用 libp2p 协议栈 │
│ 节点发现: discv5 消息传播: GossipSub │
├───────────────────────────────────────────────┤
│ 执行层(EL) │
│ 使用 devp2p 协议栈 │
│ 节点发现: discv4 消息传播: RLPx Wire Protocol │
└───────────────────────────────────────────────┘
↑
两层通过本地 Engine API 通信(非 P2P)
两层使用不同协议栈有历史原因:执行层的 devp2p 是以太坊早期自研的;共识层在设计时(2019-2020)选择了更成熟通用的 libp2p。
节点第一次启动时,没有任何已知对等节点。以太坊客户端内置了一批引导节点地址(bootnodes),作为进入网络的"门":
// go-ethereum 内置引导节点(部分)
var MainnetBootnodes = []string{
"enode://d860a01f9722d78051619d1e2351aba3f43f943f6f00718d1b9baa4101932a1f5011f16bb2b1bb35db20d6fe28fa0bf09636d26a87d31de9ec6203eeedb1f666@18.138.108.67:30303",
...
}
通过引导节点进入网络后,节点使用 Kademlia DHT 发现更多节点。
Kademlia 是一种分布式哈希表(DHT)算法,每个节点有一个随机的 Node ID(256 位),节点间的"距离"用 XOR 度量:
distance(A, B) = A.NodeID XOR B.NodeID
XOR 距离的特性:
每个节点维护一张 路由表(k-bucket):