以太坊源码分析(四 节点发现)

ethereum - build unstoppable applications

节点发现

以太坊的节点发现协议使用的是KAD算法(kademlia)

  1. 数据存储结构
    它在一个数组里维护了256个bucket,每个bucket的数据下标即为其深度,每个bucket最多存储16个节点,所以一个节点可存储的节点数为256 * 16个节点
    这是在NodeTable中定义的


    以太坊源码分析(四 节点发现)_第1张图片
    image.png
  1. 距离算法
    每个节点都有一个nodeId,这个nodeId是根据节点的地址经过哈希算法并使用ECC加密之后,导出的一个512位的公钥
final ECKey generatedNodeKey = ECKey.fromPrivate(sha3(addressOrEnode.getBytes()));


public byte[] getNodeId() {
        if (nodeId == null) {
            nodeId  = pubBytesWithoutFormat(this.pub);
        }
        return nodeId;
    }

两个节点的距离就是两个nodeId异或之后取最高位1所在的位置得来的,举例说明:
假如节点A的nodeId为1011011(二进制表示),节点B的nodeId为1100110,那么二者的距离为6,距离越小那么存储的bucket越浅,也就是越近,本节点就会选取最近的16个节点发起节点查询请求

  1. 消息类型
  • ping 询问节点是否存活
  • pong ping的响应
  • findnode 节点查询,向目标节点询问附近节点列表
  • neighbours findnode消息的响应

节点发现实现

首先节点发现协议使用的Udp,入口是UDPListener,在这个类初始化的时候,它会读取配置文件配置(peer.discovery.ip.list)的初始化的节点发现的IP列表,然后通过start方法启动了3个任务:

  1. 节点服务端监听任务
    使用netty绑定默认为30303的端口,消息的处理器是messageHandler,收到消息的时候会使用NodeManager的handleInbound方法处理各种上面提到的消息类型
while (!shutdown) {
    Bootstrap b = new Bootstrap();
    b.group(group)
            .channel(NioDatagramChannel.class)
            .handler(new ChannelInitializer() {
                @Override
                public void initChannel(NioDatagramChannel ch)
                        throws Exception {
                    ch.pipeline().addLast(stats.udp);
                    ch.pipeline().addLast(new PacketDecoder());
                    MessageHandler messageHandler = new MessageHandler(ch, nodeManager);
                    nodeManager.setMessageSender(messageHandler);
                    ch.pipeline().addLast(messageHandler);
                }
            });

    channel = b.bind(address, port).sync().channel();

    channel.closeFuture().sync();
   ...省略
}
  1. 节点发现任务 每30秒向临近节点发送findnode消息
 discoverer.scheduleWithFixedDelay(
                new DiscoverTask(nodeManager),
                1, KademliaOptions.DISCOVER_CYCLE, TimeUnit.SECONDS);
  1. 刷新任务 每7200毫秒刷新下临近节点列表,其实也是发送findnode消息
refresher.scheduleWithFixedDelay(
                new RefreshTask(nodeManager),
                1, KademliaOptions.BUCKET_REFRESH, TimeUnit.MILLISECONDS);

你可能感兴趣的:(以太坊源码分析(四 节点发现))