我曾分析了DPOS算法的漏洞而且模擬了一個簡單的攻擊的方法,而後實現了一個簡化的PBFT算法模型試圖去修復該漏洞,而且對比了效果。javascript
隨後在正式的產品中實現了完整版的算法,而且部署了10臺機器進行了測試。測試的結果在安全性方面徹底符合預期,即通過頻繁的重啓、不按常規的廣播區塊、少數受託人聯合做弊的狀況下,整個系統依然不會分叉;可是在性能方面,不太理想,在沒有任何交易的狀況下,網路流量的峯值(廣播區塊的瞬間)達到了1.5Mbps。java
其實這個流量也不算離譜,爲了安全性,付出一些帶寬的代價也算合理,但咱們認爲還有很大的優化空間,並且asch做爲一個開發平臺,平臺底層的效率和穩定性是很重要的,帶寬方面的優化是頗有必要的,咱們馬上着手去作了。截止到8月9號,系統已升級到0.9.5版本,在49個節點組成的testnet中,帶寬的峯值在600kbps左右。
下面咱們會對比下這幾個版本共識算法的差別和基本原理。爲了方便起見,咱們把最初實現的dpos with pbft算法稱爲AC0.5(AC即asch consensus),優化後的版本稱爲AC1.0。git
咱們以前分析過,DPOS的主要問題在於受託人的權力太高,而其餘節點對區塊的有效性驗證過於簡單,這就很容形成一個局面,即不一樣內容、但相同高度的區塊共存於網絡中的不一樣節點,也就是所謂的分叉,進而形成雙重支付。github
PBFT算法的本質則是讓每個節點都儘量知道其餘節點的決策,並以此來肯定本身的決策。算法
所以,PBFT帶來額外的流量消耗就能夠理解了,其根源也找到了。在AC0.5算法中,這個額外的流量或者說「其餘節點的決策」指的就是每一個節點對下一個區塊的投票信息,投票信息主要內容有4個部分,即區塊高度、區塊ID、簽名、公鑰,其中區塊高度和區塊ID的長度能夠忽略不計,簽名的長度爲64字節,公鑰的長度爲32字節,一個區塊達成共識須要所有受託人總數的2/3的投票,在asch系統中受託人總數爲101人,也就是說須要至少67個投票。一個區塊的大小大約是200字節,至關於2個投票的大小,所以AC0.5中流量消耗是原始DPOS的30多倍(在沒有交易的空block狀況下,若是有交易則交易不會消耗額外的流量)。json
AC0.5中服務器之間的消息傳遞使用json格式,二進制字段則是轉化爲hex編碼後再進行傳輸,投票中的二進制字段包括公鑰和簽名,以前咱們算的是100字節,轉化爲hex編碼後則翻1倍,變成200字節了。安全
另外json的字段信息和冗餘的分隔符所佔的字節數也很多。服務器
AC1.0對這一點作了改進,使用了protobuf做爲序列化方法,效果很不錯,帶寬降爲原來的60%左右。網絡
AC0.5的流程爲數據結構
廣播一個待確認區塊
收集選票(以廣播的形式)
收集確認信息(以廣播的形式)
確認區塊
AC1.0的流程爲
propose (廣播一個區塊的元信息及當前generator的ip信息)
collect votes (其餘節點採用一對一的方式直接將選票發送給當前generator)
commit and broadcast(廣播一個已經確認的區塊並攜帶投票信息)
經過對比能夠發現,AC0.5須要三次廣播,AC1.0僅須要兩次廣播,而且在propose環節,只廣播了區塊的元信息,不包括交易信息,只廣播元信息有個好處,能夠防止在區塊沒法達成共識的狀況下白白浪費流量,由於若是連元信息都沒法經過,那就不必廣播交易信息了。
AC0.5使用的廣播協議爲最樸素也最粗暴的gossip算法,即隨機選取必定數量的相鄰節點而後將消息廣播給它們, 這個必定數量固定爲100. 任何一個節點在收到一條信息後,會計算這個消息的hash,若是發現沒有收到過,就會繼續廣播給它的相鄰節點。也就是說一輪廣播須要進行100 * N次通信,在N小於100的狀況下,至關於複雜度爲O(N^2), 在這裏N爲整個網絡的節點個數。
AC1.0把這個固定數量改成sqrt(N), 也就是說假若有100個節點,每一個節點只須要廣播給10個相鄰節點。
這個改動很小,可是參數的設置倒是很是須要經驗的,咱們作過了大量的測試後,認爲sqrt(N)能夠達到比較理想的效果,一次廣播須要的通信次數略高於N * sqrt(N)。
除此以外,咱們還實現了一個基於一致性哈希的廣播算法,性能達到了極致,算法複雜度下降到了O(N), 可是這個算法須要更多的測試,其穩定性和可靠性也不如更簡單的隨機算法。
算法的demo版本在這裏,有興趣的能夠研究下。
關於容錯性,我認爲能夠從內因和外因兩個方面來講。
從內因的角度來講,系統應該能容忍正常節點出錯,這些錯誤主要是指服務器宕機、硬件錯誤、網絡擁塞等。Asch系統可以容忍最多1/3的受託人節點同時出錯,假如某個受託人的節點出錯了,那輪到該受託人生產區塊的時候,就會缺失一個,並順延到下一個10秒。假如超過1/3節點同時出錯,那麼系統將暫停工做,等到足夠的節點恢復正常後,系統就能夠當即恢復正常。
假如1/3以上的節點永遠沒法恢復(這種狀況是存在的,好比他們的密鑰丟了),那麼系統必需要經過一次軟件升級來恢復,而且這個升級不強制全部節點執行,只要部分節點升級,區塊生產恢復正常後,經過受託人投票把那些不正常的節點撤銷掉,系統就恢復如常了。
從外因的角度來講,系統應該可以容忍黑客攻擊、受託人做弊的狀況。這裏的黑客攻擊不是說DDOS,DDOS形成的後果最可能是部分服務器宕機,咱們已經歸到內因裏去了,這裏的黑客攻擊主要是指經過入侵拿到部分受託人密鑰並獲取權限,而後利用這些權限獲利。獲利的手段無非是廣播多個版本的區塊,在短期內形成分叉,而後進行雙重支付。在asch系統中,黑客必需要同時得到1/3以上節點的密鑰,纔可以發動連續攻擊,使網絡的分叉持續下去,不然系統將經過最長鏈同步算法迅速消除分叉,分叉之間的差距不會超過1個區塊高度,也就是說2次確認以上的交易基本上不可能被回滾了。
從現有的使用DPOS算法的系統來看,包括bitshares、crypti、lisk在內,這些系統出現的分叉都是內因形成的,甚至大多數是算法實現上的bug所致使的。黑客攻擊DPOS的案例尚未據說過,雖然存在理論上的漏洞,但要想真正的攻擊,須要高超的技術和昂貴的資源,成本和收益不對等,所以也不會有人去攻擊,當一個DPOS系統的市值慢慢增大,咱們能夠繼續提升受託人節點的數量,進一步提升攻擊的成本,所以外因的風險基本能夠忽略。
最後,我想解釋一下分叉這個詞,分叉來源於英文的fork,fork根據上下文的不一樣,我認爲能夠翻譯成兩種意思,一個是分叉,另外一個是分裂。
分叉指的是在社區成員團結一致的狀況下系統由於bug或被攻擊形成的不一致性,而分裂是指社區成員因觀念分化形成軟件走向不一樣的方向。
分叉強調的是系統的bug和不一致性,強調了物的因素,分叉後系統可能仍是一個系統,而且是極可能被複原的;而分裂則是強調了人爲的因素,一旦社區分裂,則系統一分爲二,變成兩個系統。
從這個角度來講,asch對於分叉能夠作到事前的預防和過後的修復,但沒法應對社區的分裂,任何一個區塊鏈系統也沒法解決分裂的問題,包括比特幣和任何一個聲稱能夠避免分叉的PBFT系統。