简介
在以太坊上P2P网络上使用了多种数据要交互,这就会涉及的很多种协议,所以以太坊使用了RPLx协议,它是一个加密的点对点的协议套件,它为在P2P网络上交互的应用提供了一套统一的传输接口,它设计的初衷便是满足去中心化应用。
结构图
概述
RLPx协议建立在tcp以及udp之上,它包含四个组件,分别为Node Disvovery、Handshake、Framing、Flow Control,下面分别就其实现进行分析
Node Disvovery (节点发现):
它在上篇文章节点发现有过分析,它主要是基于UDP实现的,这里就不再赘述。Encrypted Handshake:
连接在建立之初,会有一个连接的握手(区分于tcp的握手),它的基本过程分为两个阶段:
- 第一个阶段为密钥交换,它使用ECIES加密临时密钥传给peer
- 第二阶段为认证和协议协商,协商的协议为是否支持传过来的协议它包含(shh、eth、bzz)
部分代码如下:
public void initiate(ChannelHandlerContext ctx) throws Exception {
loggerNet.debug("RLPX protocol activated");
nodeId = myKey.getNodeId();
handshake = new EncryptionHandshake(ECKey.fromNodeId(this.remoteId).getPubKeyPoint());
Object msg;
if (config.eip8()) {
AuthInitiateMessageV4 initiateMessage = handshake.createAuthInitiateV4(myKey);
initiatePacket = handshake.encryptAuthInitiateV4(initiateMessage);
msg = initiateMessage;
} else {
AuthInitiateMessage initiateMessage = handshake.createAuthInitiate(null, myKey);
initiatePacket = handshake.encryptAuthMessage(initiateMessage);
msg = initiateMessage;
}
final ByteBuf byteBufMsg = ctx.alloc().buffer(initiatePacket.length);
byteBufMsg.writeBytes(initiatePacket);
ctx.writeAndFlush(byteBufMsg).sync();
...省略
}
Fraaming:
协议帧的作用就是在RLPx协议之上能够支持多种协议的传输Flow control:
RLPx协议设置了一个8K的窗口来控制流量。
实现
在握手通过之后,握手处理器被移除,后续增加了p2p的处理器,通过hellomessage的Capability区分出协议类型,根据协议类型激活不同的协议:
public void publicRLPxHandshakeFinished(ChannelHandlerContext ctx, FrameCodec frameCodec,
HelloMessage helloRemote) throws IOException, InterruptedException {
logger.debug("publicRLPxHandshakeFinished with " + ctx.channel().remoteAddress());
messageCodec.setSupportChunkedFrames(false);
FrameCodecHandler frameCodecHandler = new FrameCodecHandler(frameCodec, this);
ctx.pipeline().addLast("medianFrameCodec", frameCodecHandler);
ctx.pipeline().addLast("messageCodec", messageCodec);
ctx.pipeline().addLast(Capability.P2P, p2pHandler);
p2pHandler.setChannel(this);
p2pHandler.setHandshake(helloRemote, ctx);
getNodeStatistics().rlpxHandshake.add();
}
在setHandshake方法中完成了不同协议的激活:
public void setHandshake(HelloMessage msg, ChannelHandlerContext ctx) {
....省略
this.handshakeHelloMessage = msg;
List<Capability> capInCommon = getSupportedCapabilities(msg);
channel.initMessageCodes(capInCommon);
for (Capability capability : capInCommon) {
if (capability.getName().equals(Capability.ETH)) {
// Activate EthHandler for this peer
channel.activateEth(ctx, fromCode(capability.getVersion()));
} else if
(capability.getName().equals(Capability.SHH) &&
capability.getVersion() == ShhHandler.VERSION) {
// Activate ShhHandler for this peer
channel.activateShh(ctx);
} else if
(capability.getName().equals(Capability.BZZ) &&
capability.getVersion() == BzzHandler.VERSION) {
// Activate ShhHandler for this peer
channel.activateBzz(ctx);
}
}
}