CITA 是如何達到 15000 TPS 的?

在前兩期中,祕猿小課堂給你們分享了 構建高性能區塊鏈內核 CITA 背後的思考。這一期,咱們深刻研究 CITA 是如何進行性能優化,而且將交易處理的性能達到 15000 TPS量級

祕猿科技區塊鏈小課堂第 6 期
點擊關注祕猿科技在思否的技術社區吧~html


在區塊鏈的設計中,有一個「不可能三角」的說法,即安全、去中心化、性能,這三者只能取其二。Nervos 是用分層設計來解決不可能三角問題。在底層 Layer1 裏,CKB 就選取安全和去中心化,Layer2 選性能。Layer2 追求把性能作到極致,去中心化和安全由 CKB 來解決。git

CITA 做爲支持智能合約的區塊鏈框架,有很是良好的性能表現,交易處理的性能能夠達到 15000 TPS[1],很是適合做爲 Layer2 的高性能區塊鏈解決方案。本文將會簡要討論祕猿科技是如何對 CITA 進行性能優化的。github

圖片描述

微服務架構

傳統的公有區塊鏈每每採用總體式架構。由於須要考慮去中心化,就須要考慮節點能夠在普通硬件上能夠執行,而在架構設計上沒法兼顧性能。CITA 做爲專門面向企業用戶設計的高性能許可鏈(許可鏈能夠是聯盟鏈,也能夠是公有許可鏈),採用微服務架構,能夠更好的利用服務器集羣,而不是使用單一機器運行節點。這樣能夠充分利用硬件的優點,節點再也不是一個物理概念,而是一個邏輯概念。算法

圖片描述

CITA-BFT

傳統的 PBFT 類算法中,通常使用三階段協議 prevote、precommit、commit ,以 Tendermint 爲例。數據庫

圖片描述

Commit 階段主要是爲了 Proposer 向其餘節點再廣播一輪 BlockProof,使得全部節點統一投票。可是實際上在 Precommit 階段,各個節點已經收集足夠的投票,只是投票集合可能不一致。好比對於 ABCD 四個節點,A 可能收到 ABCD 的投票,B 只收到 BCD 的投票。因爲投票屬於 Block 的一部分,也須要共識,爲了保證節點統一投票,因此由 Proposer 再進行一輪廣播。segmentfault

在 CITA-BFT 中,咱們優化了 Commit 階段。在區塊鏈中,Block 的共識是一個連續的共識。因此,咱們能夠將當前 Block 的 Proof 放到下一個塊的 Proposal 中,這樣能夠在下一個塊的 Prevote 階段對上一個 Block 的 Proof 進行統一,再也不廣播共識後的 Block。緩存

圖片描述

這樣作的優勢有兩個:安全

減小了一輪消息的廣播,縮短了共識時間,而且減小了網絡的負擔。
傳統的 PBFT 類共識算法在 Commit 階段,若是 Proposer 發送掉線等狀況,會須要額外的一輪進行共識,而在 CITA-BFT 則不會發生這種狀況。性能優化

Proposal 預處理

在傳統的 PBFT 類共識的區塊鏈中,共識和交易的處理都是串行的。在共識 Block 的過程當中,Executor 是閒置的。共識完成以後,將新的 Block 發送給 Executor 處理,Consensus 等待 Executor 處理完以後才能進行新的高度的共識,此時 Consensus 模塊是閒置的。待 Executor 處理完 Block 以後,發送最新的 Status 以後,Consensus 才進行新高度的共識。服務器

在實際的共識過程當中,節點在 Prevote 階段收到 Proposal 並驗證以後,Proposal 就有很大可能變成最終 Commit 的 Block。在網絡狀況正常的狀況下,一般一輪共識便可完成當前高度的 Block,此時若是提早對 Proposal 中的交易進行處理,則在共識流程中的後半部分則和交易的執行是同時進行的。當 Executor 處理完以後,等待 Consensus 發送已經確認的 Block,此時 Executor 只須要判斷此 Proposal 是不是共識的 Block。若是是,則直接將處理結果進行 Finalize,並通知 Consensus 進行新高度的共識;若是否,則從新處理,這種狀況和沒有預執行的流程是一致的。

圖片描述

這樣在多數狀況下區塊都能提早處理,將交易處理的時間提早。即使在較壞狀況下,進行多輪共識時,Proposal 也能夠按照時間戳進行比較,打斷當前正在進行的預處理,執行更新的 Proposal。在最壞狀況下,沒有收到 Proposal 或者收到錯誤的 Proposal,Executor 也和原來的共識流程相同,在收到 CommitedBlock 以後,進行交易的處理,並無任何性能上的損失。

緩存

在性能優化中,緩存是一種常見的手段,一樣在 CITA 中也存在大量緩存,來解決性能問題。

簽名驗證緩存。一般交易簽名的驗證比較耗時,對於已經驗證經過的交易,根據其 Hash 將驗證結果進行緩存。這樣若是節點再收到一樣的交易(多是用戶重複發送或者從其餘節點轉發)時,則能夠命中緩存,減小驗證簽名的時間消耗。
區塊信息緩存。對於在交易處理過程當中,或者用戶查詢操做中,常常會須要查詢 Block 或者 Transaction 等信息,能夠將此類信息緩存,這樣能大大提升查詢的效率。
在交易處理過程當中,須要從數據庫中讀取以前的 State,而 MPT 的查詢路徑比較長,須要屢次 DB 的查詢,很是耗時。在 CITA 中,會將常用到 Account 進行緩存。
經過緩存技術,大大減小了交易驗證和處理的時間。

消息通訊

在微服務架構中,因爲服務的拆分致使各個微服務之間消息通訊比較頻繁,這樣消息中間件很是容易成爲瓶頸。一方面,因爲咱們使用了微服務架構,消息中間件自己能夠單獨部署,能夠經過提升硬件能力進行縱向擴展,也能夠經過集羣的方式進行橫向擴展。

除此以外,咱們還對微服務之間的消息進行優化,來提升微服務間通訊的效率。

消息壓縮。例如在壓力比較大時,Block 中每每有上萬筆交易,這樣消息會很是大。所以咱們採用了消息壓縮技術,在消息超過必定大小時,會對其進行壓縮,這樣減小消息中間件的壓力,同時也減小了傳輸量,提升了傳輸速度。
減小沒必要要的消息。例如,在 Consensus 服務收到 Proposal 時,須要對其合法性進行驗證。因爲其可能包含大量交易,會致使傳輸量很大。所以在 Consensus 能夠先驗證交易的 Hash 是否正確,再將 Proposal 的其餘信息和交易的 Hash 發送給 Auth 模塊便可,而不用將整個交易發送給 Auth 模塊。
打包發送。將消息打包,也是一種比較常見優化手段。好比在 RPC 模塊中,須要將交易發送給 Auth 模塊進行驗證,在壓力比較大的時候單個消息發送則消息數量會很是大,此時 RPC 會將消息進行打包後再發送給 Auth 模塊,能夠大幅度的減小消息的數量,從而減小消息中間件的負載,提升消息的發送速度。

Static Merkle Tree

在 Bitcoin 中,爲了解決輕節點的交易驗證問題,引入了 MerkleTree。可是 Merkle Tree 的每一個節點的產生都要計算一次 Hash,而 Hash 計算很是耗時。

圖片描述

你們注意到最後的葉子節點 Hc 是直接複製了 Hc。這樣是由於 Bitcoin 和 Ethereum 中交易是依次加入到 Merkle Tree 中,能夠遞增地去構造 Merkle Root。好比節點當前的 PendingBlock 中有交易 TxA、TxB、TxC、TxD,當前的 Merkle Root 是 H(ABCD)。新交易來 TxE 來了以後,計算 H(EE),再向上計算,這樣原有的 H(ABCD) 部分不用再計算。

圖片描述
圖片描述

當交易 TxF 來了以後,替換掉最右邊的 TxE,再依次向上計算 root,這樣只計算一部分就能夠了。以前的 H(ABCD) 部分不用再從新計算。

在 CITA 中,交易會先通過 Auth 驗證進入交易池,而後由 Consensus 一次性選取交易打包後共識,最後再由 Executor 處理,同時將處理後的結果 Receipt Root 存入 header 中 。因爲是先共識交易內容而後再計算交易的結果,因此 Block 中的交易的處理結果 Receipt Root 順序已經徹底肯定,不會再發生修改了。由此咱們能夠將全部 Receipts 一次性算出其 Receipt Root,而不用考慮其動態計算的過程。由此咱們能夠優化 Receipt Root 的計算。

圖片描述

對比 Bitcoin 和 Ethereum 中的 Merkle Tree,會發現因爲沒有奇數節點的複製,節點 E 這裏的 Hash 計算會減小。咱們稱這棵樹爲 Static Merkle Tree。

另外,在 Ethereum 中每筆交易都會產生新的 State Root,而 State Root 的計算又是很是耗時的。所以在 CITA 設計之初,交易計算完以後,只會將其狀態更新到 Account Model 中,而不會將其變動更新到 State 的 MPT 中。只有在整個 Block 計算完成以後,纔會將全部的計算結果提交到 State 的 MPT 中並計算 State Root,這樣大大減小了 MPT 的操做和計算。在 Ethereum 最新的設計中也採用了一樣的方案。

簽名驗證

當前 Bitcoin 和 Ethereum 都採用了 secp256k1 的簽名算法,在交易驗證中,簽名的驗證尤爲消耗 CPU 資源而且耗時。CITA 支持多種簽名算法,默認採用 secp256k1。在筆者的電腦(Thinkpad 470p i7-7820HQ)上簡單的轉帳交易的簽名驗證速度大約爲 3000 多每秒。Auth 模塊提供了交易的並行驗籤,這樣能夠充分發揮硬件的優點,提升系統的驗籤速度。

除此以外,CITA還實現了 Ed25519 簽名,相比 secp256k1 性能更好,而且在安全性方面也更有優點,用戶能夠根據我的需求選擇本身想要的簽名算法。咱們性能測試的 15000 TPS 的數據指的是 secp256k1。

異步處理

在軟件設計中,異步處理一般也是比較好的性能優化手段。除了前文提到的交易預處理,在 CITA 中的其餘模塊中也存在一些這樣的設計。好比在 Executor 中,Block 執行完成以後,須要將最新的 State 保存到 DB,而一旦 State 狀態變動比較多時,此操做將比較耗時。由此,Executor 提早將 Status 發送給其餘模塊,而後再到 DB 進行存儲。固然可能會在存儲失敗時致使異常狀況,所以在 Conseneus 會保存最新的幾個 Block,來防止其餘微服務內存儲失敗的特殊狀況發生。

批量交易

另外,CITA提供了批量交易的接口,用戶能夠組裝多個交易數據,共享同一個簽名。這樣原來的多個交易就變成一個交易,減小交易存儲和簽名驗證,加快交易的處理,同時也下降了用戶發送交易的手續費。例如用戶 A 須要調用 N 個不一樣的合約,原來須要發送 N 筆交易,經過批量交易的方式,能夠將合約調用的 data 按照既定格式拼裝在一塊兒,而後再進行一次簽名發送到批量交易合約,合約再將 data 解析成多個合約調用。

固然批量交易只能算做一筆複雜的組合交易,只完成了一次正常交易的處理流程,所以在性能測試中只能算做一筆交易。CITA 在性能測試中並未採用此手段。

Rust

CITA 中絕大部分代碼是用 Rust 語言實現。Rust 語言的極小運行時,與 C 語言媲美的優異性能,也是 CITA 良好性能的一大保證。做爲國內最先使用 Rust 的團隊,從 2016 年開始至今也在和 Rust 一同成長。 Rust 語言的其餘特性:保證內存安全,基於 trait 的泛型,模式匹配,類型推斷,高效 C 綁定等等,也極大的提升了咱們的開發效率。

將來咱們還會在微服務架構改進,網絡層,Block/Transaction 廣播,狀態存儲,硬件加速,VM,並行計算等等各個方面作更多研究,將 CITA 性能提上一個更高的水平。

參考文獻

CITA技術白皮書:https://github.com/cryptape/c...

Ed25519: https://exonum.com/blog/09-27...

eip-658:https://github.com/ethereum/E...

batch_tx:https://docs.nervos.org/cita/...

Rust versus C gcc fastest programs: https://benchmarksgame-team.p...

相關文章
相關標籤/搜索