PBFT使用拜占庭容错系统

2023-10-08 21:10

本文主要是介绍PBFT使用拜占庭容错系统,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

想知道更多关于区块链技术知识,请百度【链客区块链技术问答社区】链客,有问必答!

在这里插入图片描述
PBFT即实用拜占庭容错系统

英文全称Practical Byzantine Fault Tolerance。它是第一个基于BFT理论并结合实际产生的新的共识算法。但应用场景是私有链和联盟链,如果用于公链,需要和其他共识算法进行灵活的混用。

PBFT算法原理

PBFT是一类状态机拜占庭系统,要求共同维护一个状态,所有节点采用的行动一致。为此,需要运行三类基本协议,包括一致性协议(Agreement)、验证确认协议(Validity)和视图更换协议(Viewchange)。

1.一致性协议:

在PBFT系统中,节点分为两类:主节点(Lead)和从节点(Follow)。主节点就一个,其他剩余节点都是从节点。在实现PBFT系统功能之前,所有节点都是从节点,经过选举,产生一个主节点。每个节点都有被选举权和选举权,选举过程每个节点的概率都是平等的。

一致性协议包含若干个阶段:请求(Request)>序列分配(Pre-prepare)>相互交互(Prepare) >确认(Commit) >响应(Reply)。

注:C为客户端或叫请求节点;0为当前视图下的主节点;1,2,3是从节点,其中3为宕机节点。

该视图满足拜占庭容错,即系统节点总数4 >故障节点个数1x3

1)Request阶段

也就是Lead生成请求阶段。

客户端发送请求到主节点,或者主节点主动从某数据源获取数据作为请求

2)Pre-prepare阶段

也就是Lead给其他所有Follow发送请求阶段。

主节点将收到的请求进行处理,给请求赋值一个序列号n,形成一个带序号的副本,然后分发给其从节点。其中序号n必须在水线(watermark)上下限h和H之间。水线存在的意义在于防止一个失效节点使用一个很大的序号消耗序号空间。发送副本的格式是<,m>,这里v是视图编号,m是客户端发送的请求消息,d是请求消息m的摘要,n是序号。

3)Prepare阶段

也就是Follow收到请求,但不知道其他节点请求是什么,就给收到的请求签名发给其他所有节点,告诉他们我收到了这样一个请求。

从节点接收到副本后,对副本进行校验和确认。检查副本中请求是否签名合法;检查d与m的摘要是否一致;检查当前视图编号是是否与副本v一致。满足这些条件后,从节点将副本存入消息日志中,然后对副本进行签名并转发给其他的所有节点(包括主节点)。如果不满足,则该验证节点就会进入View change阶段。

4)Commit阶段

也就是每个节点收到其他所有节点的签名请求,把这个合法请求跟自己之前保存的请求进行比较,如果超过有2f个请求一样,那么这个请求他会认为是大家共识的请求。然后对这个请求进行处理。

当前阶段所有节点,包括主节点,对接收到的签名副本进行签名验证和确认。签名验证通过后的副本与自己保存的副本进行比较,设和自己一样的副本个数记为s,满足s+1>=2f,故障节点的个数为f,则该节点将会对这个请求进行响应,给予运算结果,并做签名。

5)Reply阶段

也就是将处理结果发送给客户端,客户端收到f+1个一样结果,那么客户端会认为该结果正确。

每个收到请求的节点都会对请求进行响应处理,将结果广播给客户端。当客户端接收到f+1的响应相同时,则会认为该响应就是请求的结果。

2.验证确认协议:

在一致性协议使用时都用到了这个协议。在PBFT系统中,时刻保证数据的准确性,要进行数据校验和签名确认。对Lead的任何行为要进行监视,如果Lead有做越权或错误的事件,其他节点可以对其进行举报。

3.视图更换协议:

此阶段主要是用户在设计PBFT系统时,对Lead节点权限的设定。比如定时多长时间,正常更换Lead,其他Follow节点都有机会成为新的Lead。在Lead任期时期,如果超过2/3的节点反映它有问题,那么也会触发view change。还有Lead在运行中突然挂掉,也会触发view change。我们需要确保在新的view中如何来延续上一个view最终的状态,比如给这时来的新请求的编号,还有如何处理上一个view还没来得及完全处理好的请求。

PBFT算法在公链中的应用

PBFT机制不需要挖矿,也不需要权益计算,所以通过它达成共识的时间会很快,为了提高公链的tps,将PBFT灵活改进并引入公链,和pow算法或pos相结合形成一套新的混合机制,非常明智。目前这样应用的公链比较多,我下边主要讲解初链(TrueChain)是怎么实现的。

初链使用双链结构,即快链和慢链。运用PBFT和POW混合共识算法,前者生成快链,后者生成慢链。交易确认和挖矿是分开的。交易被打包后,通过PBFT委员会确认,生成fastBlock,此时交易已经得到确认;慢链将快链里的fastBlock打包成为一个snailBlock,经过矿工挖矿确认,实现上链。通过这种混合共识算法,tps得到很大的提升,达到3000左右,而且使用POW挖矿,实现了去中心化的思想。PBFT委员会每两天换届一次,候选委员都是POW挖矿成功后的矿工,保证了公平公正的原则。

虽然PBFT算法的设计初期是服务于私有链和公有链的,但是随着人们对它的不断改进和灵活使用,未来公链它将在共识机制中处于重要地位。

Go语言实现一个网络交互的PBFT工程

工程思路:

1.开启4个终端,分别代表4个小国,开启http监听,监听路径有4个,分别是"/req","/prePrepare", “/prepare”, “/commit”。

2.任意一个节点通过http网页发起请求;

3.该被请求节点会将数据写入自己的数组,然后并通过"/prePrepare"路径向其他节点广播该请求数据;

4.其他节点通过节点监听接收到请求数据,将请求数据写入自己的数组,并通过"/Prepare"路径向其他节点广播该请求数据;

5.所有节点将收到的数据进行校验,如果当前数组的长度大于所有节点个数的2/3,则输出OK,并将OK数据通过路径"/commit"广播给其他所有节点;

6.当节点收到OK数量达到所有节点个数的1/3,则就是最后的结果。

package main

import (

"os""fmt""net/http""io"

)

//声明nodeInfo节点,代表各个小国家

type nodeInfo struct {

//节点名称id string//节点路径path string//http响应writer http.ResponseWriter

}

//创建map,存储各个国家的ip地址,也就是url

var nodeTable = make(map[string]string)

//当http服务器,接收到网络请求并且/req 则回调request

func (node *nodeInfo) request(writer http.ResponseWriter, request *http.Request) {

//该命令允许request请求参数request.ParseForm()//解析完毕,打包返回。所以该方法不需要返回值。if (len(request.Form["warTime"]) > 0) {node.writer = writerfmt.Println("主节点接收到的参数信息为", request.Form["warTime"][0])//fmt.Println(request.Form["warTime"])打印出来是个数组,如果warTime=1111&2222等等,等号后边的是数组,warTime是map的key//激活主节点后,向其他的节点发送广播node.broadcast(request.Form["warTime"][0], "/prePrepare")}

}

//节点发送广播的方法

func (node *nodeInfo) broadcast(msg string, path string) {

fmt.Println("广播", path)//遍历所有的节点for nodeId, url := range nodeTable {if nodeId == node.id {continue}//使当前节点以外的节点做响应http.Get("http://" + url + path + "?warTime=" + msg + "&nodeId=" + node.id)}

}

//处理广播后接收到的数据

func (node *nodeInfo) prePrepare(writer http.ResponseWriter, request *http.Request) {

request.ParseForm()fmt.Println("接收到的广播为", request.Form["warTime"][0])if len(request.Form["warTime"]) > 0 {node.broadcast(request.Form["warTime"][0], "/prepare")}

}

//接收子节点的广播

func (node *nodeInfo) prepare(writer http.ResponseWriter, request *http.Request) {

request.ParseForm()//打印消息fmt.Println("接收到的子节点的广播", request.Form["warTime"][0])//校验if len(request.Form["warTime"]) > 2/3*len(nodeTable) {node.authentication(request)}

}

var authenticationNodeMap = make(map[string]string)

var authenticationSuceess = false

//校验拜占庭

func (node *nodeInfo) authentication(request *http.Request) {

if !authenticationSuceess {if len(request.Form["nodeId"]) > 0 {authenticationNodeMap[request.Form["nodeId"][0]] = "OK"//如果有两个国家节点正确的返回了结果if len(authenticationNodeMap) > len(nodeTable)/3 {authenticationSuceess = truenode.broadcast(request.Form["warTime"][0], "/commit")}}}

}

//返回成功响应

func (node *nodeInfo) commit(writer http.ResponseWriter, request *http.Request) {

if writer != nil {fmt.Println("拜占庭校验成功")//在网页上显示okio.WriteString(node.writer, "ok")}

}

func main() {

//接受终端参数userId := os.Args[1]fmt.Println(userId)//存储4个国家的IP地址nodeTable = map[string]string{"Apple":  "localhost:1111","MS":    "localhost:1112","Google": "localhost:1113","IBM":    "localhost:1114",}//创建国家对象node := nodeInfo{id: userId, path: nodeTable[userId]}//http协议的回调函数http.HandleFunc("/req", node.request)http.HandleFunc("/prePrepare", node.prePrepare)http.HandleFunc("/prepare", node.prepare)http.HandleFunc("/commit", node.commit)//启动服务器if err := http.ListenAndServe(node.path, nil); err != nil {fmt.Println(err)}

}

这篇关于PBFT使用拜占庭容错系统的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/167988

相关文章

Linux使用cut进行文本提取的操作方法

《Linux使用cut进行文本提取的操作方法》Linux中的cut命令是一个命令行实用程序,用于从文件或标准输入中提取文本行的部分,本文给大家介绍了Linux使用cut进行文本提取的操作方法,文中有详... 目录简介基础语法常用选项范围选择示例用法-f:字段选择-d:分隔符-c:字符选择-b:字节选择--c

使用Go语言开发一个命令行文件管理工具

《使用Go语言开发一个命令行文件管理工具》这篇文章主要为大家详细介绍了如何使用Go语言开发一款命令行文件管理工具,支持批量重命名,删除,创建,移动文件,需要的小伙伴可以了解下... 目录一、工具功能一览二、核心代码解析1. 主程序结构2. 批量重命名3. 批量删除4. 创建文件/目录5. 批量移动三、如何安

springboot的调度服务与异步服务使用详解

《springboot的调度服务与异步服务使用详解》本文主要介绍了Java的ScheduledExecutorService接口和SpringBoot中如何使用调度线程池,包括核心参数、创建方式、自定... 目录1.调度服务1.1.JDK之ScheduledExecutorService1.2.spring

Java使用Tesseract-OCR实战教程

《Java使用Tesseract-OCR实战教程》本文介绍了如何在Java中使用Tesseract-OCR进行文本提取,包括Tesseract-OCR的安装、中文训练库的配置、依赖库的引入以及具体的代... 目录Java使用Tesseract-OCRTesseract-OCR安装配置中文训练库引入依赖代码实

Python使用Pandas对比两列数据取最大值的五种方法

《Python使用Pandas对比两列数据取最大值的五种方法》本文主要介绍使用Pandas对比两列数据取最大值的五种方法,包括使用max方法、apply方法结合lambda函数、函数、clip方法、w... 目录引言一、使用max方法二、使用apply方法结合lambda函数三、使用np.maximum函数

Qt 中集成mqtt协议的使用方法

《Qt中集成mqtt协议的使用方法》文章介绍了如何在工程中引入qmqtt库,并通过声明一个单例类来暴露订阅到的主题数据,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 目录一,引入qmqtt 库二,使用一,引入qmqtt 库我是将整个头文件/源文件都添加到了工程中进行编译,这样 跨平台

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Python使用国内镜像加速pip安装的方法讲解

《Python使用国内镜像加速pip安装的方法讲解》在Python开发中,pip是一个非常重要的工具,用于安装和管理Python的第三方库,然而,在国内使用pip安装依赖时,往往会因为网络问题而导致速... 目录一、pip 工具简介1. 什么是 pip?2. 什么是 -i 参数?二、国内镜像源的选择三、如何

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定