怎么进行PBFT共识算法分析及Java实现,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。
PBFT共识算法详细分析及Java实现
为什么写这个
最近研究了区块链相关的一些东西,其实就三大块:
-
分布式存储(去中心)
-
共识机制
-
安全加密 分布式存储,就是一个分布式数据库,每个节点都保存一份副本。通过非对称秘钥,hash等技术对操作数据进行签名,验证摘要,可追溯,以链式结构存储,互相以hash摘要校验数据,防篡改。以拜占庭容错共识算法解决节点间的通信,达成一致协议。
区块链协议简介
分布式共识算法是分布式系统的核心,常见的有Paxos、pbft、bft、raft、pow等。区块链中常见的是POW、POS、DPOS、pbft等。 其中:
-
POW、POS、DPOS是开放式的共识协议
-
PBFT为半开放式的共识协议
-
Paxos、raft等是封闭式共识协议
区别:
-
开放式,无法确切的知道节点的多少及连接状态,每个节点都可能是恶意的,但是大多数是非恶意的
-
半开放式,可以确定节点的多少及连接状态,每个节点都可能是恶意的,但是有满足一定条件的非恶意节点
-
封闭式,每个节点都是非恶意的,只不过可能断开连接或crash。
性能: 从上往下越来越高
总结:
-
私有链是封闭生态的存储系统,采用Paxos、raft最佳
-
联盟链有半公开半开放特性,因此拜占庭容错的PBFT算法比较合适
-
公有链来说,POW、POS、DPOS是比较适合的高安全性的协议
那么,下面我们要说的就是联盟链性质的共识协议:PBFT算法协议。 协议诞生已经很久了,网上的文章也不少,然而基本都是翻译原论文,稍加一些个人的阅读心得,细致的分析还是比较少,一些关键的衔接点没有说清楚,粗看好像都懂,但是真正要实现起来,还是有比较多坑。于是本人采用demo的方式,以多线程模拟多节点,实现完整的PBFT算法,其中有一些问题,记录下来,供各位参考,讨论。
PBFT算法简介
PBFT协议简单步骤:
主要有三个阶段:预准备(pre-prepare)、准备(prepare)、和确认(commit)
-
从全网节点选举出一个主节点(Leader),新区块由主节点负责生成。
-
其中一个节点的客户端向主节点发起请求。
-
Pre-Prepare:主节点分配一个序列号n给收到的请求(顺序的保证!),并向全网广播
<<PRE-PREPARE,v,n,d>,m>
。 -
Prepare:每个节点接收到交易请求后,模拟执行这些交易,验证交易报文的正确性。验证通过后,存入预备列表,并向全网广播
<PREPARE,v,n,d,i>
-
Commit:如果一个节点收到的2f(f为可容忍的拜占庭节点数)个其它节点发来的PREPARE消息摘要都和自己相等,就向全网广播一条commit消息
<COMMIT,v,n,D(m),i>
-
Reply:如果一个节点收到2f+1条commit消息,即可提交新区块及其交易到本地的区块链和状态数据库(操作确认完成)。
问题分析
整个协议理解起来还算比较简单,但是这里面有好些问题,需要一一的剖析。
-
主节点怎么产生?
-
主节点失效了怎么办?
-
主节点造假怎么办?
-
数据怎么重传?
主节点的产生
因为节点的操作都需要通过主节点,所以每个节点启动后的第一件事,找主节点。 主节点由公式p = v mod |R|计算得到,这里v是视图编号,p是副本编号,|R|是副本集合的个数。 所以其实找主节点就是初始化视图view。
-
全网广播获取视图协议:
public static final int VIEW = -1;
-
超过2f+1的节点回复的view作为初始化的view(q1:如果无法满足怎么办?)
if(this.viewOk)return; long count = vnumAggreCount.incrementAndGet(msg.getVnum()); if(count >= 2*maxf+1){ vnumAggreCount.clear(); this.view = msg.getVnum(); viewOk = true; System.out.println("视图初始化完成["+index+"]:"+ view); }
主节点失效了(这里失效指应用挂掉、或被他人控制作恶)
谁先发现主节点失效了,当然,这里是不能通过连接断开来看,因为即使tcp连接正常,也不一定业务处理正常。答案是,超时控制。 当发起请求的节点在超时时间内没有完成操作确认,则可以怀疑主节点失效,于是:
-
客户端全网广播超时的请求报文,为什么不用一个专门的包来发起失效提议?主要是防止发起请求的节点作恶,比如循环发起提议。导致不断产生提议检验,导致网络拥堵
-
副本节点检查,如果处理过(说明可能是网络问题),重新将处理结果返回即可,如果未处理,则可能主节点宕机,将请求重新转发给主节点,且增加超时校验。这时,如果主节点是正常的,那么就会走正常流程,最终会确认操作请求。如果主节点真的有问题,则设置的超时将触发
-
超时后全网广播主节点切换提议
if(!this.viewOk) return; // 已经开始选举视图,不用重复发起 this.viewOk = false; // 作为副本节点,广播视图变更投票 PbftMsg cv = new PbftMsg(CV, this.index); cv.setVnum(this.view+1); PbftMain.publish(cv);
当每个节点都收到2f+1个对同一个view的提议后,则切换成新的view。且检查是否有请求待发送,一切恢复正常逻辑:
long count = vnumAggreCount.incrementAndGet(msg.getVnum()); if(count >= 2*maxf+1){ vnumAggreCount.clear(); this.view = msg.getVnum(); viewOk = true; System.out.println("视图变更完成["+index+"]:"+ view); // 可以继续发请求 if(curMsg != null){ curMsg.setVnum(this.view); System.out.println("请求重传["+index+"]:"+ curMsg); doSendCurMsg(); } }
提议时,如果有恶意节点重复多次发起,需要检测每个节点只能投票一次。
String vkey = msg.getNode()+"@"+msg.getVnum(); if(votes_vnum.contains(vkey)){ return; } votes_vnum.add(vkey);
清理状态
当commit通过后,即确认了操作,则可以对该消息相关的状态进行清理:
// 清理请求相关状态 private void remove(String it) { votes_pre.remove(it); votes_pare.removeIf((vp)->{ return StringUtils.startsWith(vp, it); }); votes_comm.removeIf((vp)->{ return StringUtils.startsWith(vp, it); }); aggre_pare.remove(it); aggre_comm.remove(it); timeOuts.remove(it); }
其他关键点
PbftMsg消息的DataKey必须是唯一的,可以通过uuid或者其他方式定义
private int type; // 消息类型 private int node; // 节点 private int onode; // 发起请求的节点 private int vnum; // 视图编号 private int no; // 序列号 private long time; // 时间戳 private String data; // 数据,表示数据的hash,必须唯一
关于怎么进行PBFT共识算法分析及Java实现问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注亿速云行业资讯频道了解更多相关知识。
原创文章,作者:745907710,如若转载,请注明出处:https://blog.ytso.com/220030.html