温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

怎么进行PBFT共识算法分析及Java实现

发布时间:2021-12-18 13:44:26 来源:亿速云 阅读:240 作者:柒染 栏目:互联网科技

怎么进行PBFT共识算法分析及Java实现,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。

PBFT共识算法详细分析及Java实现

为什么写这个

最近研究了区块链相关的一些东西,其实就三大块:

  1. 分布式存储(去中心)

  2. 共识机制

  3. 安全加密 分布式存储,就是一个分布式数据库,每个节点都保存一份副本。通过非对称秘钥,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)

  1. 从全网节点选举出一个主节点(Leader),新区块由主节点负责生成。

  2. 其中一个节点的客户端向主节点发起请求。

  3. Pre-Prepare:主节点分配一个序列号n给收到的请求(顺序的保证!),并向全网广播<<PRE-PREPARE,v,n,d>,m>

  4. Prepare:每个节点接收到交易请求后,模拟执行这些交易,验证交易报文的正确性。验证通过后,存入预备列表,并向全网广播<PREPARE,v,n,d,i>

  5. Commit:如果一个节点收到的2f(f为可容忍的拜占庭节点数)个其它节点发来的PREPARE消息摘要都和自己相等,就向全网广播一条commit消息<COMMIT,v,n,D(m),i>

  6. Reply:如果一个节点收到2f+1条commit消息,即可提交新区块及其交易到本地的区块链和状态数据库(操作确认完成)。

问题分析


整个协议理解起来还算比较简单,但是这里面有好些问题,需要一一的剖析。

  1. 主节点怎么产生?

  2. 主节点失效了怎么办?

  3. 主节点造假怎么办?

  4. 数据怎么重传?

主节点的产生

因为节点的操作都需要通过主节点,所以每个节点启动后的第一件事,找主节点。 主节点由公式p = v mod |R|计算得到,这里v是视图编号,p是副本编号,|R|是副本集合的个数。 所以其实找主节点就是初始化视图view。

  1. 全网广播获取视图协议:

public static final int VIEW = -1;
  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连接正常,也不一定业务处理正常。答案是,超时控制。 当发起请求的节点在超时时间内没有完成操作确认,则可以怀疑主节点失效,于是:

  1. 客户端全网广播超时的请求报文,为什么不用一个专门的包来发起失效提议?主要是防止发起请求的节点作恶,比如循环发起提议。导致不断产生提议检验,导致网络拥堵

  2. 副本节点检查,如果处理过(说明可能是网络问题),重新将处理结果返回即可,如果未处理,则可能主节点宕机,将请求重新转发给主节点,且增加超时校验。这时,如果主节点是正常的,那么就会走正常流程,最终会确认操作请求。如果主节点真的有问题,则设置的超时将触发

  3. 超时后全网广播主节点切换提议

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实现问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注亿速云行业资讯频道了解更多相关知识。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI