Substrate 网络层深度解读:libp2p 助力去中心化点对点高效通信

区块链中需要高效的通信工具来确保节点之间的顺畅交互。而 libp2p 正是开发者在点对点通信中不可或缺的框架,提供了强大的模块化功能,使得去中心化网络中的消息传递变得更加灵活且安全。在 Substrate 中,libp2p 的集成帮助开发者轻松实现各种定制协议,简化了区块链节点之间的通信流程,让复杂的网络交互变得更加直观和高效。本篇文章是由 PaperMoon kaichao 老师所撰写的 Substrate 进阶课程技术文章的六篇,将带你深入了解点对点通信的优势、libp2p 的架构设计,以及 Substrate 如何将这些技术应用于去中心化区块链网络的搭建,帮助你在开发自定义区块链应用时,轻松应对复杂的网络环境。

点对点通信模型的崛起与意义在 Web2.0 时代,绝大多数互联网应用采用了基于 TCP/IP 的“客户端-服务器”通信架构,在客户端采集数据并发送给服务器,服务器存储和处理数据,客户端进而获取并使用这些处理后的数据。这种模式支撑了互联网近三十年的蓬勃发展,在给普通用户提供便利的同时,也出现了各种各样的问题,比如:泄露用户隐私;贩卖用户数据;服务商发布没有底线的广告给不明真相的用户造成不可挽回的损失;不经协商,随意删除用户发布的内容;垄断市场和定价;过度利用用户心理,无节制地占据用户的时间;……客户端-服务器通信模型如下图:在为以上问题寻找解决方案时,点对点(即peer to peer)的通信机制逐渐走进了技术先驱们的视野。在互联网早期的时候,点对点通信主要用于文件共享,如音乐共享服务 Napster 和流媒体下载服务 BitTorrent。点对点服务更加广泛的应用,还需要一定的治理机制,来处理资源的版权问题和现实世界的监管,这些不是本文的重点,不做过多地介绍。点对点通信模型如下图:在点对点的网络里,所有的节点都是对等的,即任何节点都可以存储和处理数据(作为服务端);也可以发送待处理的数据给网络中的其它节点,获取经过网络处理后的数据(作为客户端)。通过这样的通信机制,可以保证,网络具备开放性,节点可以自由加入和退出;不依赖单一服务节点,网络的服务更加可靠、高效;节点运行的程序代码公开可见,规则更加透明。根据网络中传输的数据和提供服务的不同,点对点应用出现了不同的应用场景,包括文件存储和读取、数据计算、内容共享、数据交易等服务。在开发这些应用的过程中,可能涉及到的技术要点有:节点身份,唯一地标识网络中的节点及地址格式;发现机制,在没有中心化的协调服务存在的情况下,如何发现新的节点;路由,本地节点无法存储网络中所有节点的信息,通过路由算法查找需要的节点;多种通信协议比如 TCP、UDP、WebSocket、QUIC 等等;加密和认证,保证消息的可靠和安全;NAT 穿透,解决 NAT 后面的内部IP无法访问的难题;多路复用以节省资源;消息订阅,高效的获取更新而不会给网络造成负担;中继,当需要建立通信的两个节点都无法直接被访问,比如都在 NAT 网络中,需要通过中继节点传递信息;……以上列出的这些技术要点/需求并不会出现在每个点对点应用里,大多数只会使用其中的一部分功能,尽管如此,还是存在严重地重复造轮子的现象。也有一些应用为了避免重复开发,选择了 fork 已有开源应用的功能代码,这种方式引入了原有应用的技术债,难于定制和扩展。复杂多变的网络拓扑和膨胀的应用状态导致了点对点应用的开发、推广和普及都极为困难,出现一个高度模块化的点对点通信开发框架也就不足为奇,也就是接下来我们要介绍的 libp2p。

模块化点对点通信框架的诞生——libp2pLibp2p 是一个开发点对点应用的框架,它最早源于去中心的文件共享服务 IPFS,把网络通信相关的内容抽离并重新设计,形成了现在的 libp2p,目前比较成熟的几个语言版本包括 js-libp2p、go-libp2p、rust-libp2p,并且定义了一套参考规范,不同语言的实现版本只要符合这一规范,就可以实现互通信。Libp2p 提供的核心功能包括,在节点之间建立安全可复用的网络连接;可验证的节点身份和可连接的地址。安全可复用的连接Libp2p 支持的底层(传输层)协议包括 TCP/IP、UDP、WebSocket、QUIC等,不同语言版本的实现完成度不尽相同。连接的安全性是通过对传输内容进行加密来保证的,节点的身份也会进行相应的验证。为了提升连接的利用率以及应对复杂的网络场景如各种形式的防火墙和 NAT,对建立的底层连接进行多路复用十分有必要,stream 就是可实现复用的一种上层连接形式,它可以是双向的,也可以是单向的。QUIC 协议有内置的安全和复用组件,对于没有此类功能的协议,使用 libp2p 可以对原始连接进行 upgrade,添加所需的安全和可复用的套件,安全套件有 secio、Noise,可复用套件有 yamux 和 mplex。Upgrade 协议的流程如下图:在 stream 里可以传输各种各样的 libp2p 内置或用户自定义的应用层协议,这些协议定义了节点间交换信息的方式和内容,比如:ping,用来定时检查节点是否在线;identity,用于节点间交换信息如节点的 public key 和网络中的地址;kad-dht,基于 Kademlia 算法的分布哈希表,用于节点间路由;……以 identity 协议为例,它的协议 id(具有路径格式的字符串)为 /ipfs/id/1.0.0,消息的表示和序列化使用的是 protocol buffer,节点身份节点启动时需要提供一个 private key(也可以随机生成),主要用于将节点双方的公钥通过 Diffie-Helman key exchange 对消息进行加解密;对节点的 public key 进行哈希,生成 PeerId 即节点身份。Libp2p 支持的公钥加密算法包括 RSA、Ed25519、Secp256k1 等。PeerId 的生成采用了 multihashes 的形式,即支持多种哈希算法,经过 base 58 编码后的格式如 QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N。将 PeerId 与 multiaddr 结合可以用来在网络中定位节点和验证身份,例如 IP 地址为 7.7.7.7、监听在 4242 端口、拥有上述 PeerId 的节点的 multiaddr(多层次地址)为:以上只列出了 libp2p 提供的部分功能,更多内容例如消息订阅、中继、NAT 穿透等等可以参考相关文档,使用 libp2p 开发点对点应用可以解决以上提到的大部分难题和技术点,节约大量的开发时间,增加系统的可维护性和可扩展性。接下来,我们看一下如何使用 rust-libp2p 实现简单的自定义应用协议。简单应用这里我们基于 rust-libp2p,编写一个简单的点对点应用,可以完成回声(echo)的功能,即其中一个节点发送一个字符串,另一个节点接收该字符串并回复相同的字符,这里我们需要自定义一个应用层的协议 EchoProtocol,需要实现 libp2p 提供的 UpgradeInfo 接口。这里的 protocol_info 方法返回了协议的名字和格式。接着实现 InboundUpgrade 和 OutboundUpgrade,这两个接口都继承自 UpgradeInfo。NegotiatedSubstream 表示协商好的某个协议将会使用的 I/O 流。当远端的节点支持当前协议时,调用 upgrade_inbound 和 upgrade_outbound 分别在 listener 和 dialer 端开启握手信号。之后,定义处理连接请求的 handler,也就是我们这里的结构体 EchoHandler,它保存了处理过程中所使用的状态信息。还需要一个自定义的枚举 event 枚举类型。接着就可以实现 libp2p::swarm 里所提供的 ProtocolsHandler 接口了,当节点为 dialer,handler 在轮询(ProtocolsHandler::poll())时,需要返回包含 EchoProtocol 实例的 ProtocolsHandlerEvent::OutboundSubstreamRequest,用于发起并协商连接使用的协议。如果协商成功,调用 ProtocolsHandler::inject_fully_negotiated_outbound,在这里我们将 handler 保存的 outbount 状态由 None 更新为 Some(send_echo(stream).boxed()),其中 send_echo 接收协商好的 IO stream,无错误发生时返回该 stream。我们接着看 ProtocolsHandler::poll 里的实现,当 outbound为Some,send_echo 返回的 future 轮询的结果为 Poll::Pending 时,更新 outbound 为 self.outbound = Some(send_echo_future),保证下次轮询时依然有效,当结果为 Poll::Ready 时返回相应的事件信息。当节点为 listener,连接中出现新的请求流时,自动调用 ProtocolsHandler::listen_protocol 返回一个 InboundUpgrade 的实例来协商流使用的协议。协商成功之后,调用 inject_fully_negotiated_inbound,其中一个参数为协商好的 stream,在该方法内,将 handler 的 inbound 属性状态更新为 Some(recv_echo(stream).boxed()),recv_echo 方法的实现为。这里泛型 S 需要满足 futures_io 提供的 AsyncRead 和 AsyncWrite 约束。点对点网络就像一个蜂群(Swarm),而蜂群的整体行为是由单一个体的行为所组成的,单一个体的行为由一系列的规则所制定,此类的规则可以组合使用,在 rust-libp2p 中,规定的定义需要实现 NetworkBehaviour 接口,这里我们先定义一个结构体,对规则的状态进行保存。本结构体包含了与 Swarm 沟通的消息 events,行为定义所需要的初始配置。接着,就可以实现 NetworkBehaviour 接口了,当连接建立或者尝试去呼叫节点时会调用 new_handler,返回我们之前定义的 handler 即 EchoHandler,作为该连接的后台处理线程,behaviour 和 handler 通过消息传递的机制进行通信,inject_event 可以把 handler 的消息传给 behaviour,behaviour 在 poll 的时候返回 SendEvent 将消息传递给 handler。到这里,我们已经完成了一个简单的 echo 点对点通信协议,现在我们看一下 main 函数里如何使用。代码的简单说明如下:通过 Keypair::generate_ed25519 生成用于节点间通信加密的密钥,其中的公钥可以派生出节点的 PeerId。libp2p::build_development_transport 构建了开发常用的传输层,支持 TCP/IP、WebSocket,使用 noise 协议作为加密层,yamux 和 mplex 多路复用协议。解析传入参数,如果包含呼叫的节点信息,则是 dialer(客户端),将构造 behaviour 的初始参数 init_echo 设置为 true。使用上面构造的传输层、behaviour、节点 id,调用 Swarm::new(transport, behaviour, peer_id)构造模拟网络的 swarm。当节点为 dialer 时,呼叫传入的远端节点 Swarm::dial_addr(&mut swarm, remote)?,将该节点加入 swarm 节点池中。对 swarm 进行轮询 swarm.poll_next_unpin(cx),如果有 behaviour 触发的消息,处理对应的消息。小结,libp2p 对点对点通信进行了高度的抽象,在开始接触这些概念时,容易摸不着头脑,需要不断去熟悉划分的层次和常用的协议;rust-libp2p 的实现,针对 libp2p 定义的层次和协议,封装出了不同的接口,在开发自定义协议的同时,需要深入去了解这些抽象的接口及接口间通信的方式。总体来说,点对点通信开发的难度比传统的客户端-服务器通信形式高很多,libp2p 的设计在于弥合这其中的一些痛点,但也还有很长的路要走,应用开发者需要更多地了解底层的机制才能更好的开发应用协议。目前,使用 libp2p 的应用包括 IPFS,Substrate/Polkadot,Libra,Ethereum 2.0 等等,接下来我们来了解下 Substrate 如何使用的 libp2p。

Substrate 网络层架构多协议支持与节点发现机制的实现区块链网络是由去中心(或者说是点对点)的节点所组成,节点之间通过网络连接传递消息,Substrate 作为通用的区块链开发框架,它的网络层使用了 rust-libp2p,可以很容易地使用、扩展一系列的通信协议,例如:传输层支持 TCP/IP(地址格式为 /ip4/1.2.3.4/tcp/5)、WebSocket(地址格式为 /ip4/1.2.3.4/tcp/5/ws)、DNS(地址格式 /dns/example.com/tcp/5或/dns/example.com/tcp/5/ws),以及对应的 IPv6 格式;在传输层之上应用了加密协议 Noise;支持多路复用协议 Yamux 和 Mplex,其中mplex会逐步废弃;使用 libp2p 标准的 ping 协议(/ipfs/ping/1.0.0),周期性的检查节点间的网络连接是否还活着,如果检查失败会断开连接;使用 libp2p 标准的 id 协议(/ipfs/id/1.0.0),节点之间通过该协议周期性地交换节点各自的信息;libp2p 标准的 Kademlia 协议(/<protocol_id>/kad),执行 Kademlia random walk查询,其中 protocol_id 可以用来区分不同的链,在 Substrate chain spec 中进行定义;自定义的 sync 协议(//sync/2),用来同步区块信息,请求和返回结果的数据格式定义在 api.v1.proto 文件中;自定义的 light 协议(//light/2),轻客户端用此协议同步链上的状态信息,数据格式定义在 light.v1.proto 文件中;自定义的 transactions 协议(//transactions/1),用来广播节点接收到的交易信息,它的格式是交易集合的 SCALE 编码结果;自定义的 区块广播协议(//block-announces/1),当节点产出或者接收到区块时,将此区块向其它节点进行广播;自定义的 gossip 协议(/paritytech/grandpa/1),GRANDPA 用来通知其它节点相关的投票信息;自定义的 Substrate legacy 协议(/substrate//),是一个即将被弃用的协议,它也可以同步、广播区块信息,处理轻客户端请求,Gossiping(被 GRANDPA 使用)等等。结合以上的底层和应用层通信协议,Substrate 的节点之间可以通过三种发现机制建立起连接,启动节点(bootstrap nodes),它的地址和 PeerId 都是固定的,适用于网络的冷启动和某节点刚加入网络时,通过启动节点进入到网络中;mDNS,在本地网络通过广播 UDP 数据包,如果有节点响应,则可以建立起连接;Kademlia random walk,当连接建立后,当前节点可以通过 FIND_NODE  请求远端节点,获取远端节点关于当前网络中节点组成的视角。以上的协议共同构成了 Substrate 的通用网络层,而这一网络层的使用是通过 NetworkWorker 和 NetworkService 结构体来实现的,在 node template 节点程序中的使用示例如下:去中心的通信模型为互联网应用开辟了新的范式,也带来了相当大的挑战,libp2p 规范的出现,逐步减轻了开发者在开发点对点应用所遇到的痛点。Substrate 借助 libp2p 的优良特性,在区块链这一细分的去中心应用领域,可以很方便的让普通开发者无需过多的关注底层的通信机制,就可以完成复杂的自定义区块链应用。

倒计时开启!波卡黑客松曼谷站即将迎来最终冲刺为推动开发者深入探索 Polkadot 生态与 Web3,OneBlock+ 社区自 2024 年 7 月 11 日起举办的波卡黑客松大赛现已进入最后阶段。曼谷站的 Demo Day 将于 11 月 16 日精彩呈现,距离代码提交截止(10 月 23 日中午 12:00 UTC+8)仅剩不多时间!作为本次大赛的第二场,曼谷站吸引了众多创新团队,共同竞争总奖金池高达 63 万美元的丰厚奖项。抓住最后机会,加入波卡生态,让你的项目在舞台上闪耀!🏄♂️ 立即报名:https://forms.gle/4pNpmp92pnX2wWSZ8🧺 参赛指南:曼谷站:https://dorahacks.io/zh/hackathon/polkadot-2024-bangkok/detail🛠️ Github 代码库:https://github.com/OneBlockPlus/polkadot-hackathon-2024🗳️ 技术资源库:https://github.com/OneBlockPlus/Technical-docs/blob/main/Substrate-technical-docs.md

第八期 Substrate 开发进阶课程深度解析与项目实战,助你领跑区块链想要快速掌握区块链技术的核心,构建属于自己的应用?OneBlock+ 联合 Polkadot 推出第八期《Substrate 开发进阶与项目实战》课程,邀请了业内资深专家——王大锤、周俊和孙凯超,为学员提供专业指导。课程将全面解析 Substrate 的关键技术,帮助你掌握前沿开发技巧,并通过项目实战提升动手能力。不论你是想在区块链行业崭露头角,还是期待实现职业突破,本次课程将为你打开成功的道路。🪅立即加入吧:https://wj.qq.com/s2/14825200/0zv4/添加小助手 Emma ( 🆔 oneblockEmma) 获取更多资讯!👇

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/55879.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从0开始深度学习(12)——多层感知机的逐步实现

依然以Fashion-MNIST图像分类数据集为例&#xff0c;手动实现多层感知机和激活函数的编写&#xff0c;大部分代码均在从0开始深度学习&#xff08;9&#xff09;——softmax回归的逐步实现中实现过 1 读取数据 import torch from torchvision import transforms import torchv…

查找与排序-交换排序

交换排序是基于“比较”和“交换”两种操作来实现的排序方法 。 由于选择“比较”的基准元素不同&#xff0c;可将交换排序分为以下两种&#xff1a; 冒泡排序快速排序 一、冒泡排序 1.冒泡排序基本思想 因为其实现与气泡从水中往上冒的过程类似而得名。 每一趟的…

基于SpringBoot+Vue+uniapp微信小程序的垃圾分类系统的详细设计和实现(源码+lw+部署文档+讲解等)

项目运行截图 技术框架 后端采用SpringBoot框架 Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的开源框架。它采用约定大于配置的理念&#xff0c;提供了一套默认的配置&#xff0c;让开发者可以更专注于业务逻辑而不是配置文件。Spring Boot 通过自动化配置和约…

Redux (八) 路由React-router、嵌套路由、路由传参、路由懒加载

文章目录 一、React-Router的基本使用1. 安装及基本使用(路由映射配置)2. 路由跳转Link与NavLink3. Navigate导航4. 处理路径不存在的情况 二、嵌套路由三、手动跳转 (类似编程式路由导航)1. 函数式组件2. 类组件实现手动跳转 四、路由传参1. 路径设置占位符(params)2. search传…

Java面试指南:Java基础介绍

这是《Java面试指南》系列的第1篇&#xff0c;本篇主要是介绍Java的一些基础内容&#xff1a; 1、Java语言的起源 2、Java EE、Java SE、Java ME介绍 3、Java语言的特点 4、Java和C的区别和联系&#xff1f; 5、面向对象和面向过程的比较 6、Java面向对象的三大特性&#xff1a…

leetcode30:串联所有单词的字串

给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。 s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。 例如&#xff0c;如果 words ["ab","cd","ef"]&#xff0c; 那么 "abcdef…

1. 解读DLT698.45-2017通信规约--预连接响应

国家电网有限公司企业标准&#xff0c;面向对象的用电信息数据交换协议DLT698.45-2017 为提高用电信息采集系统的业务适应性、采集效率、安全性和数据溯源性&#xff0c;规范用电信息数据交换协议的通信架构、数据链路层、应用层、接口类与对象标识&#xff0c;制定本标准。 …

Linux系统:(Linux系统概述与安装)

硬件计算机硬件是指计算机系统中所有物理部件的总称。包括计算机主机、显示器、键盘、鼠标、内存、硬盘、处理器、主板等等。这些硬件部件是计算机系统运行的基础 不管是电脑系统(个人电脑、服务器等)、还是移动端操作系统(手机、平板等)。它的功能就是做为用户和硬件之间的桥梁…

前端求职简历-待补充

当然可以&#xff0c;针对大厂的前端岗位&#xff0c;一个吸引人的简历应该突出你的技术能力、项目经验、教育背景以及任何能体现你学习能力和团队协作能力的证明。以下是一个简历大纲示例&#xff0c;你可以根据自己的实际情况进行调整&#xff1a; 个人信息 姓名联系方式&a…

图文深入介绍oracle资源管理(续)

1. 引言&#xff1a; 本文将承接上篇继续深入介绍oracle资源管理。本文重点介绍如何使用oracle资源管理器管理好DB。 2. 资源管理器&#xff1a; 可以使用图形界面 OEM$或命令行调用 DBMS RESOURCE MANAGER 程序包的过程进行数据库资源管理。 调用资源管理器的先决条件&…

瑞数后缀加密怎么处理

前言&#xff1a; 瑞数我们经常补环境通过&#xff0c;但是遇到瑞数后缀不知道怎么处理 就拿瑞数4来讲 解决方法&#xff1a; &#xff08;1&#xff09;传明文加密参数 一般情况&#xff0c;我们传明文加密参数也能访问 &#xff08;2&#xff09;再补环境基础调用open …

基于stm32的4G模块点灯实验

led模块功能封装 #include "led.h" #include "sys.h"//初始化GPIO函数 void led_init(void) {GPIO_InitTypeDef gpio_initstruct;//打开时钟__HAL_RCC_GPIOB_CLK_ENABLE();//调用GPIO初始化函数gpio_initstruct.Pin GPIO_PIN_8 | GPIO_PIN_9;gpio_inits…

排序算法 —— 直接插入排序

目录 1.直接插入排序的思想 2.直接插入排序的实现 实现分析 实现代码 3.直接插入排序的分析 时间复杂度分析 空间复杂度分析 稳定性 1.直接插入排序的思想 直接插入排序的思想就是把待排序的元素按其关键码值的大小依次插入到一个已经排好序的有序序列中&#xff0c…

pycharm调试带参数命令行的程序

点击 run configuration 点击加号&#xff0c;选择python name填写程序名字&#xff0c;script填写程序路径&#xff0c;下一行填写传入的参数 点击apply&#xff0c;再点ok&#xff0c;即可debug 参考&#xff1a; pycharm 调试模式下命令行参数的传递_pycharm debug传参 ya…

项目实战:构建 effet.js 人脸识别交互系统的实战之路

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀构建 effet.js &#x1f4d2;1. 什么是effet.js&#x1f4dc;2. 为什么需要使用effet.js&#x1f4dd;3. effet.js的功能&#x1f4da;4. 使用…

【项目案例】-音乐播放器-Android前端实现-Java后端实现

精品专题&#xff1a; 01.C语言从不挂科到高绩点 https://blog.csdn.net/yueyehuguang/category_12753294.html?spm1001.2014.3001.5482https://blog.csdn.net/yueyehuguang/category_12753294.html?spm1001.2014.3001.5482 02. SpringBoot详细教程 https://blog.csdn.ne…

HTML之表单设计

1、HTML表单 HTML表单是用于收集用户输入的信息&#xff0c;并将用户输入的内容信息传到后台服务器中。 表单是通过form标签实现。 特别注意&#xff1a;如果一些内容提交后&#xff0c;没有将内容提交给后台服务器&#xff0c;那么需要添加一个name属性&#xff0c;语法&am…

NC 单据模板自定义项 设置参照(自定义参照)

NC 单据模板自定义项 设置参照&#xff08;自定义参照&#xff09; 如图下图&#xff0c;NC 单据模板自定义项 设置参照&#xff1a; 1、选择需要设置参照的自定义字段&#xff0c;选择高级属性页签&#xff0c;在类型设置中&#xff0c;数据类型选择参照信息&#xff0c;即bd…

【热门主题】000004 案例 Vue.js组件开发

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 【热…

JavaWeb合集11-Maven高级

十一、Maven高级 1、分模块设计与开发 为什么?将项目按照功能拆分成若干个子模块,方便项目的管理维护、扩展,也方便模块间的相互调用&#xff0c;资源共享。 分模块开发需要先针对模块功能进行设计&#xff0c;再进行编码。不会先将工程开发完毕,然后进行拆分。 实现步骤&…