文档章节

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

andylo25
 andylo25
发布于 2018/06/15 16:46
字数 1738
阅读 648
收藏 6

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 PBFT算法

© 著作权归作者所有

共有 人打赏支持
andylo25
粉丝 0
博文 2
码字总数 2894
作品 0
厦门
高级程序员
私信 提问
加载中

评论(2)

虫洞社区
您好,我是虫洞社区的运营Yang,这个是我们社区的网址:www.chongdongshequ.com 看到您的关于区块链技术的文章质量非常高,尤其是关于共识算法方面,想邀请您成为虫洞社区的首批优质内容签约作者。

虫洞社区是专业的区块链技术学习社区。虫洞社区鼓励内容生产者产生高质量内容,并给予合理的回报,也希望能帮助内容消费者获得高质量的区块链内容,并让数字货币投资者获得有价值的投资洞见。最主要的是希望志同道合者可以在虫洞社区里学的开心,玩的开心,交到更多的朋友。

同时,虫洞社区已经积累了大量的区块链深度从业者,便于作者建立个人品牌。不知道是否方便加您邮箱细聊?邮箱:yang.yu@chongdongshequ.com 期待您的回复。
梨涡浅笑崇明了
梨涡浅笑崇明了
您好,看到您的文章质量非常高,想邀请您成为虫洞社区的首批优质内容签约作者。虫洞社区是专业的区块链技术学习社区。虫洞社区鼓励内容生产者产生高质量内容,并给予合理的回报,也希望能帮助内容消费者获得高质量的区块链内容,并让数字货币投资者获得有价值的投资洞见。同时,虫洞社区已经积累了大量的区块链深度从业者,便于作者建立个人品牌。不知道是否方便加您微信细聊?
Java程序员必读书单,家族又添新成员

点击关注异步图书,置顶公众号 每天与你分享IT好书 技术干货 职场知识 参与文末话题讨论,每日赠送异步图书。 ——异步小编 有些革命出其不意地吸引了全世界的眼球。Twitter、Linux操作系统和...

异步社区
2018/05/09
0
0
如何在面试时搞定 Java 虚拟机?

作者 | 倪升武 责编 | 胡巍巍 笔者最近抽空看了一点《深入理解Java虚拟机》,本篇文章主要来总结一下Java虚拟机内存的各个区域,以及这些区域的作用、服务对象以及其中可能产生的问题,作为大...

CSDN资讯
2018/12/05
0
0
区块链常用架构是什么?它和保险业又如何结合?

源于比特币社区的区块链技术,不仅为金融机构所重视,也逐渐为世界主要经济体及重要国际组织所关注。本文作者尝试对区块链常见架构进行分析,并根据其技术特点提出了一些在保险业可能的应用场...

雪花又一年
2018/04/19
0
0
JDK 11 特性抢先看:5 月新增三个 JEP

一周前(2018年5月7日),JDK11 新增了三个 JEP 。在 jdk-dev 邮件列表中出现了三封邮件,Mark Reinhold 发表了以下公告: JDK 11 实现了 JEP:324:关于 Curve25519 和 Curve448 的重要协议...

oschina
2018/05/16
3.2K
5
Java SE 6 Update 21 发布-下载

有点便扭,但还是得说:Oracle 刚刚发布了 Java SE 6 Update 21 版本。 该版本新增对下面几个系统的兼容: Oracle Enterprise Linux 5.5, 5.4, 4.8 Red Hat Enterprise Linux 5.5, 5.4 Orac...

红薯
2010/07/09
3.3K
9

没有更多内容

加载失败,请刷新页面

加载更多

【结构型】- 享元模式

享元模式 作用:利用共享技术有效地支持大量细粒度对象的复用 享元模式状态 内部状态:在享元对象内部不随外界环境改变而改变的共享部分,存储于享元对象内部 外部状态:随着环境的改变而改变...

ZeroneLove
昨天
1
0
Vue 中使用UEditor富文本编辑器-亲测可用-vue-ueditor-wrap

一、Vue中在使用Vue CLI开发中默认没法使用UEditor 其中UEditor中也存在不少错误,再引用过程中。 但是UEditor相对还是比较好用的一个富文本编辑器。 vue-ueditor-wrap说明 Vue + UEditor + ...

tianma3798
昨天
4
0
php-fpm配置

php-fpm配置 修改bbs.wangzb.cc.conf配置文件,将端口9000改为9001,重新访问网站是失败的 修改配置文件 # vim /etc/nginx/conf.d/bbs.wangzb.cc.conf# nginx -s reloadfastcgi_pass 1...

wzb88
昨天
2
0
配置方案:Redis持久化RDB和AOF

Redis持久化方案 Redis是内存数据库,数据都是存储在内存中,为了避免进程退出导致数据的永久丢失,需要定期将Redis中的数据以某种形式(数据或命令)从内存保存到硬盘。当下次Redis重启时,...

linuxprobe16
昨天
6
0
介绍NoSQL最受欢迎的产品

MongoDB MongoDB是一个基于分布式文件存储的数据库。由C++语言编写。主要解决的是海量数据的访问效率问题,为WEB应用提供可扩展的高性能数据存储解决方案。当数据量达到50GB以上的时候,Mon...

问题终结者
昨天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部