您好,登錄后才能下訂單哦!
怎么進(jìn)行PBFT共識(shí)算法分析及Java實(shí)現(xiàn),針對(duì)這個(gè)問題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問題的小伙伴找到更簡單易行的方法。
最近研究了區(qū)塊鏈相關(guān)的一些東西,其實(shí)就三大塊:
分布式存儲(chǔ)(去中心)
共識(shí)機(jī)制
安全加密 分布式存儲(chǔ),就是一個(gè)分布式數(shù)據(jù)庫,每個(gè)節(jié)點(diǎn)都保存一份副本。通過非對(duì)稱秘鑰,hash等技術(shù)對(duì)操作數(shù)據(jù)進(jìn)行簽名,驗(yàn)證摘要,可追溯,以鏈?zhǔn)浇Y(jié)構(gòu)存儲(chǔ),互相以hash摘要校驗(yàn)數(shù)據(jù),防篡改。以拜占庭容錯(cuò)共識(shí)算法解決節(jié)點(diǎn)間的通信,達(dá)成一致協(xié)議。
分布式共識(shí)算法是分布式系統(tǒng)的核心,常見的有Paxos、pbft、bft、raft、pow等。區(qū)塊鏈中常見的是POW、POS、DPOS、pbft等。 其中:
POW、POS、DPOS是開放式的共識(shí)協(xié)議
PBFT為半開放式的共識(shí)協(xié)議
Paxos、raft等是封閉式共識(shí)協(xié)議
區(qū)別:
開放式,無法確切的知道節(jié)點(diǎn)的多少及連接狀態(tài),每個(gè)節(jié)點(diǎn)都可能是惡意的,但是大多數(shù)是非惡意的
半開放式,可以確定節(jié)點(diǎn)的多少及連接狀態(tài),每個(gè)節(jié)點(diǎn)都可能是惡意的,但是有滿足一定條件的非惡意節(jié)點(diǎn)
封閉式,每個(gè)節(jié)點(diǎn)都是非惡意的,只不過可能斷開連接或crash。
性能: 從上往下越來越高
總結(jié):
私有鏈?zhǔn)欠忾]生態(tài)的存儲(chǔ)系統(tǒng),采用Paxos、raft最佳
聯(lián)盟鏈有半公開半開放特性,因此拜占庭容錯(cuò)的PBFT算法比較合適
公有鏈來說,POW、POS、DPOS是比較適合的高安全性的協(xié)議
那么,下面我們要說的就是聯(lián)盟鏈性質(zhì)的共識(shí)協(xié)議:PBFT算法協(xié)議。 協(xié)議誕生已經(jīng)很久了,網(wǎng)上的文章也不少,然而基本都是翻譯原論文,稍加一些個(gè)人的閱讀心得,細(xì)致的分析還是比較少,一些關(guān)鍵的銜接點(diǎn)沒有說清楚,粗看好像都懂,但是真正要實(shí)現(xiàn)起來,還是有比較多坑。于是本人采用demo的方式,以多線程模擬多節(jié)點(diǎn),實(shí)現(xiàn)完整的PBFT算法,其中有一些問題,記錄下來,供各位參考,討論。
主要有三個(gè)階段:預(yù)準(zhǔn)備(pre-prepare)、準(zhǔn)備(prepare)、和確認(rèn)(commit)
從全網(wǎng)節(jié)點(diǎn)選舉出一個(gè)主節(jié)點(diǎn)(Leader),新區(qū)塊由主節(jié)點(diǎn)負(fù)責(zé)生成。
其中一個(gè)節(jié)點(diǎn)的客戶端向主節(jié)點(diǎn)發(fā)起請(qǐng)求。
Pre-Prepare:主節(jié)點(diǎn)分配一個(gè)序列號(hào)n給收到的請(qǐng)求(順序的保證?。?,并向全網(wǎng)廣播<<PRE-PREPARE,v,n,d>,m>
。
Prepare:每個(gè)節(jié)點(diǎn)接收到交易請(qǐng)求后,模擬執(zhí)行這些交易,驗(yàn)證交易報(bào)文的正確性。驗(yàn)證通過后,存入預(yù)備列表,并向全網(wǎng)廣播<PREPARE,v,n,d,i>
Commit:如果一個(gè)節(jié)點(diǎn)收到的2f(f為可容忍的拜占庭節(jié)點(diǎn)數(shù))個(gè)其它節(jié)點(diǎn)發(fā)來的PREPARE消息摘要都和自己相等,就向全網(wǎng)廣播一條commit消息<COMMIT,v,n,D(m),i>
Reply:如果一個(gè)節(jié)點(diǎn)收到2f+1條commit消息,即可提交新區(qū)塊及其交易到本地的區(qū)塊鏈和狀態(tài)數(shù)據(jù)庫(操作確認(rèn)完成)。
整個(gè)協(xié)議理解起來還算比較簡單,但是這里面有好些問題,需要一一的剖析。
主節(jié)點(diǎn)怎么產(chǎn)生?
主節(jié)點(diǎn)失效了怎么辦?
主節(jié)點(diǎn)造假怎么辦?
數(shù)據(jù)怎么重傳?
因?yàn)楣?jié)點(diǎn)的操作都需要通過主節(jié)點(diǎn),所以每個(gè)節(jié)點(diǎn)啟動(dòng)后的第一件事,找主節(jié)點(diǎn)。 主節(jié)點(diǎn)由公式p = v mod |R|計(jì)算得到,這里v是視圖編號(hào),p是副本編號(hào),|R|是副本集合的個(gè)數(shù)。 所以其實(shí)找主節(jié)點(diǎn)就是初始化視圖view。
全網(wǎng)廣播獲取視圖協(xié)議:
public static final int VIEW = -1;
超過2f+1的節(jié)點(diǎn)回復(fù)的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); }
誰先發(fā)現(xiàn)主節(jié)點(diǎn)失效了,當(dāng)然,這里是不能通過連接斷開來看,因?yàn)榧词箃cp連接正常,也不一定業(yè)務(wù)處理正常。答案是,超時(shí)控制。 當(dāng)發(fā)起請(qǐng)求的節(jié)點(diǎn)在超時(shí)時(shí)間內(nèi)沒有完成操作確認(rèn),則可以懷疑主節(jié)點(diǎn)失效,于是:
客戶端全網(wǎng)廣播超時(shí)的請(qǐng)求報(bào)文,為什么不用一個(gè)專門的包來發(fā)起失效提議?主要是防止發(fā)起請(qǐng)求的節(jié)點(diǎn)作惡,比如循環(huán)發(fā)起提議。導(dǎo)致不斷產(chǎn)生提議檢驗(yàn),導(dǎo)致網(wǎng)絡(luò)擁堵
副本節(jié)點(diǎn)檢查,如果處理過(說明可能是網(wǎng)絡(luò)問題),重新將處理結(jié)果返回即可,如果未處理,則可能主節(jié)點(diǎn)宕機(jī),將請(qǐng)求重新轉(zhuǎn)發(fā)給主節(jié)點(diǎn),且增加超時(shí)校驗(yàn)。這時(shí),如果主節(jié)點(diǎn)是正常的,那么就會(huì)走正常流程,最終會(huì)確認(rèn)操作請(qǐng)求。如果主節(jié)點(diǎn)真的有問題,則設(shè)置的超時(shí)將觸發(fā)
超時(shí)后全網(wǎng)廣播主節(jié)點(diǎn)切換提議
if(!this.viewOk) return; // 已經(jīng)開始選舉視圖,不用重復(fù)發(fā)起 this.viewOk = false; // 作為副本節(jié)點(diǎn),廣播視圖變更投票 PbftMsg cv = new PbftMsg(CV, this.index); cv.setVnum(this.view+1); PbftMain.publish(cv);
當(dāng)每個(gè)節(jié)點(diǎn)都收到2f+1個(gè)對(duì)同一個(gè)view的提議后,則切換成新的view。且檢查是否有請(qǐng)求待發(fā)送,一切恢復(fù)正常邏輯:
long count = vnumAggreCount.incrementAndGet(msg.getVnum()); if(count >= 2*maxf+1){ vnumAggreCount.clear(); this.view = msg.getVnum(); viewOk = true; System.out.println("視圖變更完成["+index+"]:"+ view); // 可以繼續(xù)發(fā)請(qǐng)求 if(curMsg != null){ curMsg.setVnum(this.view); System.out.println("請(qǐng)求重傳["+index+"]:"+ curMsg); doSendCurMsg(); } }
提議時(shí),如果有惡意節(jié)點(diǎn)重復(fù)多次發(fā)起,需要檢測每個(gè)節(jié)點(diǎn)只能投票一次。
String vkey = msg.getNode()+"@"+msg.getVnum(); if(votes_vnum.contains(vkey)){ return; } votes_vnum.add(vkey);
當(dāng)commit通過后,即確認(rèn)了操作,則可以對(duì)該消息相關(guān)的狀態(tài)進(jìn)行清理:
// 清理請(qǐng)求相關(guān)狀態(tài) 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; // 節(jié)點(diǎn) private int onode; // 發(fā)起請(qǐng)求的節(jié)點(diǎn) private int vnum; // 視圖編號(hào) private int no; // 序列號(hào) private long time; // 時(shí)間戳 private String data; // 數(shù)據(jù),表示數(shù)據(jù)的hash,必須唯一
關(guān)于怎么進(jìn)行PBFT共識(shí)算法分析及Java實(shí)現(xiàn)問題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識(shí)。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。