草庐IT

以太坊和libp2p的dht源码概述(一)

Richelieu_ 2023-07-20 原文

以太坊

对应代码位置
github.com\ethereum\go-ethereum\p2p\discover

概述

以太坊实现了udpv4和udpv6两种节点发现。
他们都包含一个table结构体来存储node信息。

会从table 、discovery两个方面叙述。

table

以太坊的定义是 a Kademlia-like index of neighbor nodes
是一个table但不是哈希table
同样有n个buckets
将网络部分抽象成一个名为transport的接口。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d2TmC6LV-1667273896916)(/tfl/pictures/202210/tapd_47654106_1666333931_51.png)]
每个节点存到哪个bucket:
距离=len-cpl,距离决定了bucket的index,距离小的在0个桶,距离越远,index越大
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OjcEAr4S-1667273896918)(/tfl/pictures/202210/tapd_47654106_1666942953_37.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xHvP1u25-1667273896918)(/tfl/pictures/202210/tapd_47654106_1666942970_74.png)]

本地节点查询

查询节点函数findnodeByID
会返回所有buckets中所有entries节点。
(没有libp2p那种计算距离每次找最近的)

bucket

type bucket struct {
   entries      []*node // live entries, sorted by time of last contact
   replacements []*node // recently seen nodes to be used if revalidation fails
   ips          netutil.DistinctNetSet
}

entries存储节点,每次顺序添加,并记录节点的添加时间。
add节点时,如果entries的长度超过bucketSize,则写入replacements,(不记录写入时间)

节点验证

默认10秒验证一次,每次随机选择一个bucket验证entries中的最后一个节点。只验证entries不验证replacements。
验证方式就是ping/pong
验证成功则移动到队首,且livenessChecks++。验证失败则从replacements中选一个代替这个验证失败的节点。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-py8DqvyZ-1667273896918)(/tfl/pictures/202210/tapd_47654106_1666593649_3.png)]

lookup(即discovery)

doRefresh函数包含了向其他节点查询的功能lookupRandom
每次随机选择3个节点,3是写死的,默认30分钟执行一次。刚启动时会执行一次。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wmIWdbgU-1667273896919)(/tfl/pictures/202210/tapd_47654106_1666593515_78.png)]

每次执行时new一个lookup,并调用run()方法,lookup方法优先从table里找到邻居,这个找邻居的函数就是findnodeByID,然后再向这些邻居发发送find请求,邻居们再调用自己的findnodeByID把结果返回。

入口(如何启动这一切的)

udpv4合udpv5有个new函数,new一个udpv4实例的时候再new一个tab并启动这个tab不断更新。
提供一个函数Resolve来返回查询到的节点。

libp2p的k桶

本文内容源自下面源码版本
go-libp2p-kbucket@v0.4.7
#高频名词解释
cpl:common prefix length两个ID的公共前缀长度。

功能简介

go-libp2p-kbucket实际是实现了一个路由表,他的结构体命名也是RoutingTable
除了正常增删节点之外,主要功能是查找距离一个节点最近的n个节点的peerID
NearestPeers(id ID, count int) []peer.ID
简单来讲是靠计算他们的前缀来实现的。

bucket

bucket是k桶的基础组成,一个k桶包含n个bucket,每个bucket通过一个链表*list.List来维护节点。
bucket提供最基本从增删改查之外,还有一个计算最大公共前缀的函数,
该函数会遍历list中的每个节点,然后找到公共前缀最大的结果并返回。

RoutRoutingTable 路由表(哈希表)

函数NewRoutingTable会初始化一个路由表,强制需要一个localID,并生成一个bucket放入buckets

add节点

add一个节点时,会计算该节点和localID的公共长度,该长度和len(buckets) 取最小值,该最小值就是bucket的index(bucketID)。
用index从buckets拿出对应的bucket,先判断节点是否已经存在,存在则返回false。
如果这个bucket的长度< rt.bucketsize,则正常插入该bucket,并返回true。
如果这是最后一个bucket(也就是最后一个bucket也满了),则扩展bucket个数,然后重新执行获得index,如果bucket的长度< rt.bucketsize则正常插入该bucket,并返回true。
如果这不是最后一个bucket,会从当前bucket踢出一个replaceable==true的节点然后查询这个新节点。
如果没有能replace的,则返回false,插入失败。

获得最近的节点NearestPeers

先计算target和local的cpl,这个cpl作为index,将对应的bucket的全部节点写入结果数组。
如果数量不够,从cpl后面的bucket开始写入结果集。
如果数量还不够,从cpl前面的bucket依次写入结果集。
然后结果集排序并返回。

go-libp2p-kad-dht

IpfsDHT这个结构体实现了很多interface
文本只关心PeerRouting

// PeerRouting is a way to find address information about certain peers.
// This can be implemented by a simple lookup table, a tracking server,
// or even a DHT.
type PeerRouting interface {
   // FindPeer searches for a peer with given ID, returns a peer.AddrInfo
   // with relevant addresses.
   FindPeer(context.Context, peer.ID) (peer.AddrInfo, error)
}

FindPeer函数会调用dht的runLookupWithFollowup去执行一个lookup操作,
runLookupWithFollowup的参数里有一个回调函数queryFn

具体流程是:
dht.host.Network().Connectedness(id)里找是否已经包含,有就直接返回,没有会启动一个runLookupWithFollowup
runLookupWithFollowup先从自己的routingTable里找到最近的节点(dht.routingTable.NearestPeers
如果结果为空则返回失败。
不为空则run一个query,这个query的结果就是最终结果。这个query包含了哈希表全部的最近节点,对每个节点依次遍历。
遍历的过程是先q.dht.dialPeer,失败则返回,成功则调用回调函数queryFn,通过queryFn拿到新的节点就写入自己的哈希表。无论失败返回还是成功拿到新节点,都会把结果写入一个chan,run出来的query函数里创建的这个chan并一直等待读这个chan,读到则这个节点状态为PeerQueried且将这个节点加入结果集:q.queryPeers.TryAdd(p, up.cause)。最后返回就是读这个结果集q.queryPeers.GetState(p)

queryFn里面的逻辑是
发送一个类型为Message_FIND_NODE的消息
对方收到消息会调用函数 handleFindPeer,该函数会执行dht.routingTable.NearestPeers拿到最近的节点,通过pstore.PeerInfos拿到这些节点的地址并封装成返回值。返回值最终写入一个addrsCh ,从addrsCh中收到的地址写入数组 newAddrs
FullRTdht.h.Connect这些地址
IpfsDHT结构体会返回这些节点的信息

有关以太坊和libp2p的dht源码概述(一)的更多相关文章

  1. UE4 源码阅读:从引擎启动到Receive Begin Play - 2

    一、引擎主循环UE版本:4.27一、引擎主循环的位置:Launch.cpp:GuardedMain函数二、、GuardedMain函数执行逻辑:1、EnginePreInit:加载大多数模块int32ErrorLevel=EnginePreInit(CmdLine);PreInit模块加载顺序:模块加载过程:(1)注册模块中定义的UObject,同时为每个类构造一个类默认对象(CDO,记录类的默认状态,作为模板用于子类实例创建)(2)调用模块的StartUpModule方法2、FEngineLoop::Init()1、检查Engine的配置文件找出使用了哪一个GameEngine类(UGame

  2. 阿里云RDS——产品系列概述 - 2

    基础版云数据库RDS的产品系列包括基础版、高可用版、集群版、三节点企业版,本文介绍基础版实例的相关信息。RDS基础版实例也称为单机版实例,只有单个数据库节点,计算与存储分离,性价比超高。说明RDS基础版实例只有一个数据库节点,没有备节点作为热备份,因此当该节点意外宕机或者执行重启实例、变更配置、版本升级等任务时,会出现较长时间的不可用。如果业务对数据库的可用性要求较高,不建议使用基础版实例,可选择其他系列(如高可用版),部分基础版实例也支持升级为高可用版。基础版与高可用版的对比拓扑图如下所示。优势 性能由于不提供备节点,主节点不会因为实时的数据库复制而产生额外的性能开销,因此基础版的性能相对于

  3. 玩以太坊链上项目的必备技能(初识智能合约语言-Solidity之旅一) - 2

    前面一篇关于智能合约翻译文讲到了,是一种计算机程序,既然是程序,那就可以使用程序语言去编写智能合约了。而若想玩区块链上的项目,大部分区块链项目都是开源的,能看得懂智能合约代码,或找出其中的漏洞,那么,学习Solidity这门高级的智能合约语言是有必要的,当然,这都得在公链``````以太坊上,毕竟国内的联盟链有些是不兼容Solidity。Solidity是一种面向对象的高级语言,用于实现智能合约。智能合约是管理以太坊状态下的账户行为的程序。Solidity是运行在以太坊(Ethereum)虚拟机(EVM)上,其语法受到了c++、python、javascript影响。Solidity是静态类型

  4. elasticsearch源码关于TransportSearchAction【阶段三】 - 2

    1.回顾.TransportServicepublicclassTransportServiceextendsAbstractLifecycleComponentTransportService:方法:1publicfinalTextendsTransportResponse>voidsendRequest(finalTransport.Connectionconnection,finalStringaction,finalTransportRequestrequest,finalTransportRequestOptionsoptions,TransportResponseHandlerT>

  5. (附源码)vue3.0+.NET6实现聊天室(实时聊天SignalR) - 2

    参考文章搭建文章gitte源码在线体验可以注册两个号来测试演示图:一.整体介绍  介绍SignalR一种通讯模型Hub(中心模型,或者叫集线器模型),调用这个模型写好的方法,去发送消息。  内容有:    ①:Hub模型的方法介绍    ②:服务器端代码介绍    ③:前端vue3安装并调用后端方法    ④:聊天室样例整体流程:1、进入网站->调用连接SignalR的方法2、与好友发送消息->调用SignalR的自定义方法 前端通过,signalR内置方法.invoke()  去请求接口3、监听接受方法(渲染消息)通过new signalR.HubConnectionBuilder().on

  6. Cesium源码解析一(terrain文件的加载、解析与渲染全过程梳理) - 2

    快速导航(持续更新中…)Cesium源码解析一(terrain文件的加载、解析与渲染全过程梳理)Cesium源码解析二(metadataAvailability的含义)Cesium源码解析三(metadata元数据拓展中行列号的分块规则解析)Cesium源码解析四(Quantized-Mesh(.terrain)格式文件在CesiumJS和UE中加载情况的对比)目录1.前言2.本篇的由来3.terrain文件的加载3.1更新环境3.2更新和执行渲染命令3.3数据优化3.4结束当前帧4.总结1.前言  目前市场上三维比较火的实现方案主要有两种,b/s的方案主要是Cesium,c/s的方案主要是u

  7. 停车系统源码-基于springboot+uniapp开源项目 - 2

    Iparking停车收费管理系统-可商用介绍Iparking是一款基于springBoot的停车收费管理系统,支持封闭车场和路边车场,支持微信支付宝多种支付渠道,支持多种硬件,涵盖了停车场管理系统的所有基础功能。技术栈Springboot,MybatisPlus,Beetl,Mysql,Redis,RabbitMQ,UniApp功能云端功能序号模块功能描述1系统管理菜单管理配置系统菜单2系统管理组织管理管理组织机构3系统管理角色管理配置系统角色,包含数据权限和功能权限配置4系统管理用户管理管理后台用户5系统管理租户管理多租户管理6系统管理公众号配置租户公众号配置7系统管理操作日志审计日志8系统

  8. 打通源码,高效定位代码问题|云效工程师指北 - 2

    大家好,我叫胡飞虎,花名虎仔,目前负责云效旗下产品Codeup代码托管的设计与开发。代码作为企业最核心的数据资产,除了被构建、部署之外还有更大的价值。为了帮助企业和团队挖掘更多源代码价值以赋能日常代码研发、运维等工作,云效代码团队在大数据和智能化方向进行了一系列的探索和实践(例如代码搜索与推荐),本文主要介绍我们如何通过直接打通源代码来提高研发与运维效率。随着微服务架构的流行,一个业务流程需要多个微服务共同完成。一旦出现问题,运维人员在面对数量多、调用链路复杂的情况下,很难快速锁定导致问题发生的罪魁祸首:代码。为了提高排查效率,目前常见的解决方案是:链路跟踪+日志分析工具相结合。即通过链路跟踪

  9. Android Studio开发之使用内容组件Content获取通讯信息讲解及实战(附源码 包括添加手机联系人和发短信) - 2

    运行有问题或需要源码请点赞关注收藏后评论区留言一、利用ContentResolver读写联系人在实际开发中,普通App很少会开放数据接口给其他应用访问。内容组件能够派上用场的情况往往是App想要访问系统应用的通讯数据,比如查看联系人,短信,通话记录等等,以及对这些通讯数据及逆行增删改查。首先要给AndroidMaifest.xml中添加响应的权限配置 下面是往手机通讯录添加联系人信息的例子效果如下分成三个步骤先查出联系人的基本信息,然后查询联系人号码,再查询联系人邮箱代码 ContactAddActivity类packagecom.example.chapter07;importandroid

  10. java 版本企业电子招投标采购系统源码之登录页面 - 2

    ​ 信息数智化招采系统服务框架:SpringCloud、SpringBoot2、Mybatis、OAuth2、Security前端架构:VUE、Uniapp、Layui、Bootstrap、H5、CSS3涉及技术:Eureka、Config、Zuul、OAuth2、Security、OSS、Turbine、Zipkin、Feign、Monitor、Stream、ElasticSearch等企业电子化采购系统企业电子化采购系统是明理公司在多家大、中、小型企业采购需求的分析与实际应用的基础上,结合企业采购流程优化再造理念开发的一体化电子招标采购平台,对于招标项目提供交易过程的全流程电子化、规范化管

随机推荐