來自技術的驅動,深度剖析aelf技術核心
市場的波動左右著大眾心理上的起伏,6月至今,底層公鏈間經歷了各方媒體口中的劍拔弩張再到百花齊放。二級市場的回落也給所有的專案敲響警鐘,畢竟價格只是一層糖衣,真正的價值體現註定會迴歸在技術本身。到今天,機遇或是挑戰已然不是大家所爭論的焦點,彎道超車的快捷方式也全然不適應這個行業,解決也漸漸的大過於創新。無數的思考、推翻、重構、與顛覆認知才會讓行業的細流不斷。
公鏈技術的痛點究竟在哪?如何解決?怎樣破局?帶著這些問題,我們聽到了aelf技術副總裁戎朋在UDC2018區塊鏈開發者大會中提出的一些解決方案。
談及區塊鏈技術,我們都認為它具有重塑及顛覆各行業的能力,然而,至今的區塊鏈技術彷彿少了些“靈魂”,那這一個丟失的“靈魂”究竟是何物?結合二三十年來網際網路行業的發展軌跡來看,我們發現,“網路效應”正是我們所尋找的“靈魂”,一個大的網路效應需要支撐起成百上千個Dapp,而一個Dapp上也可能存在著百萬級別的使用者,要支援如此大規模的網路基礎設施,我們必須利用IT行業幾十年來發展出來的技術與工程經驗,這是所有做公鏈的專案方都應該意識到的問題,也是aelf設計中所秉承的原則。
為了推進區塊鏈價值的實現,aelf識別出了區塊鏈目前亟待解決的三個主要問題:
資源隔離問題
雖然因為網路效應,眾多dapp執行在同一個區塊鏈生態裡面更有價值,但是實際上不同的dapp對於儲存、計算以及網路頻寬等資源的需求是各不相同的。所以我們必須實現資源隔離,使得這些dapp可以相安無事的執行在同一平臺,而無需為了共享的資源而競爭。如果因為某款爆紅的遊戲使得你的保險交易無法被處理是有問題的。
效能問題
一個區塊鏈或dapp的使用量是很難預見的,隨著dapp的使用量增加,系統的處理能力也必須要能與實際的需求匹配才行。所以我們需要一個可伸縮的處理能力,從而滿足逐漸增長的需求。
治理問題
區塊鏈是一個開放的去中心化平臺。而且一旦啟動,它就獲得了自己的生命。一個區塊鏈平臺要想能夠長期健康的存在下去。它必須具備自我演化的能力。這個問題的關鍵是要有一種機制使得利益相關方能夠以一種健康協作的方式進行決策,從而使得整個生態系統可以朝著有益的方向演進。而且系統最好也要能夠自動的執行已被批准的更新。
基於以上三個行業痛點,aelf也提出了對應的解決方案:
DPoS共識協議
通過DPoS共識協議,所有利益相關方都可以投票選擇委託節點,委託節點會負責執行交易、打包區塊以及共識產生合法的區塊鏈等事項。所有利益相關方也會通過投票的方式來決定是否更新系統合約。整個鏈的執行規則是被定義在系統合約裡的,通過投票的方式,規則可以通過預先定義好的方式進行更改,而不會發生區塊鏈的硬分叉。DPoS主要是解決治理的問題,一定程度上DPoS也可以提供更好的處理效能,因為通過DPoS公式,委託節點的出塊時間是預先確定好的,而且間隔非常小。
主鏈 多側鏈,一鏈一場景
這種主鏈加多側鏈的結構是aelf獨有的設計。這主要解決資源隔離的問題。設計上,一條側鏈支援一個場景,並且每條側鏈有獨享的一套資源。這也一定程度上增強了整個平臺的處理能力,因為當一條側鏈出現擁堵的情況,不會影響到其他側鏈。主鏈的用途是索引側鏈產生的區塊,促成跨鏈通訊。
並行執行
除此之外,aelf還致力於實現智慧合約的並行執行。一些特別受歡迎的合約可能在同一個區塊內有很多個交易。並行執行使得這些交易可以被同時執行,而不受制於單臺計算機的處理能力。
模組化
在aelf的設計裡,每個構件都是模組化的並且是鬆耦合的。這種設計使得當某個構件成為瓶頸的時候,我們可以獨立的擴充套件此模組的能力。
基於叢集
aelf的構件執行與叢集內,結合模組化的設計,aelf內部各模組的處理能力都可以伸縮與優化。
針對這些解決方案,戎朋在會議上向嘉賓介紹了一些具體的實現細節(灰色部分):
【DPOS機制】
戎朋 :共識機制號稱區塊鏈靈魂,共識機制決定了記賬權,影響了交易速度,所以首先介紹一下aelf的共識機制。
aelf使用的DPOS共識機制來保證高頻穩定的生成區塊。DPOS共識機制主要分為兩部分,首先我們必須通過所有持幣者投票選出生產節點,其次系統需要設計一個機制安排獲選節點的順序。今天我們會著重講一下系統對於獲選節點的排序機制。順序計算的主要目的是為了使得各個節點的挖礦順序是隨機的,從而降低惡意節點對網路的影響。aelf網路一開始會有17個生產節點,之後每年會增加2個節點。每一輪中所有獲選的節點都會被分配一個時間槽,獲選節點需要在分配的時間內生產出區塊。每一輪完成之後,會有一個額外區塊,用來計算下一輪的順序。在每一輪中,所有的生產節點都會產生一個祕密的隨機值並公佈這個隨機值的雜湊,在當前輪結束時,所有的祕密值也會被公佈,這些祕密值被彙總在一起,用來產生下一輪的順序。
【並行執行】
戎朋: 剛才提到AElf要解決效能問題,我們將並行執行的思想引入AElf。在區塊鏈中通常情況下,為了一致性,交易是被嚴格順序執行的,那AElf中的並行是怎樣實現的呢?
為了更好的理解aelf架構的意義,讓我們來看看一個以太坊節點跟一個aelf節點的比較。
在一個以太坊節點裡面,主要構件包括:
a. 包括一個p2p網路模組用來處理網路資訊
b.一個miner模組,用來安排區塊生產
c.一個worker模組,用來完成實際的交易執行
d.一個db模組負責狀態與其他資料的儲存
所有這些構件都存在於一個程序裡面。與此對照,一個aelf的節點,雖然還是被稱為一個節點,實際上它是一個叢集。
其組成部分,除了一個負責處理網路資訊以及安排區塊生產的主程式之外。其他兩個部分,db和worker都各自是一個叢集。這種架構的好處有以下幾點:
a. 首先因為worker並不是一個,所以這些worker能夠同時處理多個交易
b. 其次因為db和worker都是叢集,它們的處理能力都可以很方便的擴充套件
c.因為這樣模組化的架構與部署,我們可以很方便的單獨監控並優化各個模組的效能
接下來,讓我們看看aelf是怎麼實現並行執行的。我們可以想象在aelf的設計裡有兩層並行執行。
a.首先是鏈這一層的。我們可以把這一層看作是設計時的並行。開發者在設計與編寫智慧合約的時候會考慮到哪些合約是屬於同一個應用場景的。他們會把有聯絡的、屬於同一個場景的合約部署到同一條鏈,而沒什麼關係的合約會被部署到不同的鏈上面。
b.另外一層就是鏈內的並行。雖然發往同一條鏈的交易有可能是有聯絡的,但是我們認為這種情況發生的概率是很小的。大部分時候,很多交易都跟別的交易是不相關的,沒必要順序執行的。比如,我們考慮執行在一條側鏈上的一個電商dapp。我們沒必要非等到Alice購買面膜的交易被處理後才去處理Bob購買一本書的交易。所以我們認為,區塊鏈上面的高流量更可能是來源於大量的使用者傳送的少量交易,而不太可能是少量使用者傳送的大量交易。所以,交易這一層的並行是有意義的也是值得去做的。為了實現這一層的並行,我們必須要分析所有的交易,然後決定它們使用的資料是否有衝突。如果它們使用的資料沒有衝突的話,它們被執行的順序並不會影響它們各自的執行結果,所以它們是可以並行被執行的。
那麼,這就引出了一個問題。我們如何確定交易之間是否有資料使用的衝突呢。
我們可以把區塊鏈想象成一個通過合法交易促發的狀態轉移機。那麼,這裡面有三個角色。一個用來儲存狀態的資料庫,一個定義狀態轉移邏輯的智慧合約,以及作為輸入與發起方的交易。因為區塊鏈的狀態資料是在資料庫裡面以鍵值對的形式儲存的,而合約裡面使用的資料形式是合約類的欄位。所以我們需要一種機制來將合約的欄位翻譯為資料庫資料的key。我們定義了一個被稱為data provider的類來負責這個翻譯工作。在合約的欄位裡,有可能有兩種型別的欄位。一種是普通欄位,這種欄位只是簡單的擁有一個值,而這個值可以通過欄位名來定位。另外一種欄位是Map型別的欄位,之中欄位本身也是一個鍵值對的集合。每一個合約例項我們會分配一個root data provider,這種root data provider會負責整個合約所有欄位的翻譯。對於普通欄位,我們可以直接得到一個最終唯一的Key,但是對於map型別的欄位我們辦不到。所以,對於map欄位的翻譯我們得到的不是最終的key,而是一個sub data provider,這個sub data provider會負責在執行時將實際使用的這個map裡的data key翻譯為資料庫的key。被翻譯出的結果,我們稱為path,它指向的資料我們成為資源,這樣方便我們在討論並行執行時沒有歧義。對於一個普通欄位的path,一個標準形式包含chain id,合約地址,data provider名,以及欄位名。而sub data provider,我們可以認為翻譯後的sub data provider名是root data provider名加上map欄位名。完整的path被用來唯一確定交易所使用的資源,普通欄位的path可以通過靜態分析合約程式碼得到。而map對於map欄位,因為實際的data key是未知的,所以我們只能靜態的得到一個partial path,在執行時我們可以通過從交易提供的資料動態的提出使用資源的path。所有使用資源的full path我們稱為resource set,當我們得到所有合約的resource set之後,我們就可以分析交易之間是否有共用的資源。如果兩個交易有共用的資源,那麼他們就會被分到同一組。有共用資源的同組交易只能被順序執行,而不同組之間可以被並行執行。
這裡有一個特殊情況需要考慮,就是當合約方法有呼叫別的合約時。這種情況下,那麼被呼叫的(callee)函式的resource set會被加到caller函式的resource set裡,這樣來構成交易最終的resource set。
1.總結來說,並行執行的整個流程涉及以下幾個概念:
a. Metadata:我們需要從合約裡提取出resource set以及合約之間的呼叫關係
b.Call graph:一條鏈的所有合約呼叫關係會被儲存在記憶體裡的一個全域性的call graph裡。當有新的合約部署或舊的合約程式碼更新時,這個call graph會被更新
c.資源提取:我們通過metadata與call graph通過靜態與動態的方式提取出每個交易使用的資源。對於partial path來說,合約裡所有涉及的地址都會被分別append到partial path上,從而確保不漏掉一個可能的resource。
d.分組執行:交易通過resource set來分組,不同的組被並行的執行
讓我們來看看實際的例子。這是一個最簡單的token合約。它有幾個普通欄位,用來記錄token的一些基本資訊。一個map欄位用來記錄使用者的餘額。這個合約有兩個方法,一個Initialize方法用來初始化合約資訊。一個Transfer方法用來做使用者賬戶之間的轉賬。
我們可以看到Initialize方法會使用所有的普通欄位。這裡列出來了這些普通欄位的Path。我們可以看到,組成Path的各部分:chain id (1234),合約地址(abcd),data provider名,欄位名。
Transfer方法使用到的欄位值有餘額欄位Balances,我們可以看到這個餘額欄位產生的path的形式,這是一個partial path。它的最後一部分user account address由交易動態的提供。
我們可以從下面的示例交易中看出最終產生的path。我們看tx 1,從aaaa使用者轉賬給bbbb使用者的交易。這裡涉及到的是sender和receiver的地址,所以最終的path會由把這兩個地址append到partial path產生。
2. 這是另外一個例子,這個合約是一個眾籌token的合約,它會呼叫native token合約,使用者呼叫這個合約的Contribute方法時,會同時向這個合約轉入native token,並且該使用者在此合約上的餘額會按比例增加。
所以跟Contribute方法相關的metadata除了resource set之外,還包括對於native token的Transfer的呼叫關係。
而對於呼叫Contribute的方法的交易,產生的resource set不僅包括Contribute直接產生的resource set,也包括被呼叫的native token上的Transfer方法產生的resource set。
所以Contribute的完整resource set,是user account address分別在兩個合約上的餘額。
3.這一頁展示了我們是怎麼做分組。我們有4個交易,讓我們來看看當我們一個一個增加交易時,分組是怎麼產生的。
首先,我們有一個交易,從aaaa使用者到bbbb使用者的轉賬。這裡涉及到的資源有兩個,分別是aaaa和bbbb在native token合約a上的餘額,tx 1在這兩個資源之間建立了一個link。如果有別的交易使用這兩個資源的話,它就會跟tx 1分在同一組。
tx 2的交易跟tx 1類似,但是發生在另外兩個使用者之間。因為它們沒有資源衝突,所以這時tx 1與tx 2會分別屬於不同的組。
接下來我們加入一種不同型別的交易。比如,aaaa使用者在同一個區塊裡參與到眾籌token中。他在傳送tx 1的時候同時發了一個tx 3到眾籌合約c的Contribute方法,這個交易會涉及到使用者aaaa在兩個合約上的餘額,所以tx 3在aaaa賬戶在c合約和a合約上的餘額之間建立了一個連線。所以此時tx 3會和tx 1分到同一組。而tx 2還是屬於另一個組。
我們再繼續加入別的交易,假設我們此時發現了一個交易tx 4,此交易是要在cccc和aaaa之間轉賬,這個交易會在這兩個使用者在a合約上的餘額之間建立一個連線。這種情況下,兩個組就被合併為一個組了。所有這4個交易都不能被並行的執行。如果我們沒有交易tx 4的話,那麼tx1、tx 3的組可以與tx 2的組並行執行。
4.分組的概念可能比較簡單。但是,在實際的實現中,有更多的東西需要考慮。
這一頁裡,我們列出了幾點考慮與進行中的改進:
a.首先為了奠定分組的基礎,我們需要從合約程式碼裡提取metadata,而這要求我們不能給與合約開發者很大的自由度。我們在sdk中預先定義一些可以使用的類,開發者必須使用我們提供的類來編寫合約,從而確保metadata可以從程式碼中被準確的提取出來。
b.在區塊生產的過程中,我們必須確保分組所使用的時間很小,從而不會過多影響能被用於實際交易執行的時間。
c.除了交易的並行以外,還有其他可以並行的地方,同樣可以提升執行效率。比如合約的簽名驗證。我們知道簽名與簽名驗證是很費時間的計算,同時簽名驗證不需要什麼state資料。所以如果我們能把簽名驗證並行執行的話,我們可以大大減少用於簽名驗證的時間。
【側鏈】
戎朋:雖然我們用並行執行的思路加速了交易執行速度,但如果在一條鏈上存在多個DAPP,仍然會出現資源爭用的問題,相當於多個DAPP需要分時的使用計算資源,如果一個DAPP消耗資源多勢必影響其他DAPP的執行,為此我們引入了側鏈的思路。
1.每條側鏈完成單一的場景,側鏈擁有獨立的計算資源,側鏈和側鏈之間資源隔離互不影響,比如:一條側鏈用來實現遊戲的應用,另一個鏈實現保險交易應用,兩者互不影響
a.側鏈分為內部側鏈和外部側鏈
i.內部側鏈是基於AElf通過聯合挖礦的形式建立的側鏈
ii.像比特幣和以太坊這樣的鏈也可以作為外部側鏈的形式加入aelf
b.目前我們通過側鏈相互索引可以做到:
i.交易的存在性驗證
ii.交易的時序行驗證
2.下面咱們看看如何實現這兩個目的(聯合挖礦)
a.父鏈將子鏈區塊頭收錄到自己的區塊中
b.子鏈記錄父鏈的區塊頭到自己儲存合約中
c.通過這兩個步驟就可以實現相互驗證
3.非聯合挖礦
思路是:父鏈 和 子鏈 分別構建對方的輕節點
【叢集】
戎朋:上面我們提到的並行執行和側鏈,都是基於叢集,aelf的每一個節點都是基於叢集執行
1.首先看下整個叢集的結構
我們通過使用Kubernetes實現對叢集的管理,叢集裡面最大的管理單元是鏈,有主鏈和側鏈;Pod是Kubernetes中的最小的管理單位,一個鏈中有多組不通的Pod角色組成:Launcher Pod負責啟動鏈的節點
a.Akka Manager Pods是一組伺服器為並行框架提供發現及管理服務
b. Worker Pods是一組伺服器,負責並行執行
c.DB Pods是一組伺服器,儲存鏈上的資料
2.更詳細一些,下面是叢集中的並行執行結構
a.aelf是通過Akka框架實現並行執行
b.通過多個Worker Pod組成並行執行的叢集
c.每一個Worker Pod由多個actor組成,actor為Akka中最小的計算單元,我們的交易就是最終分配給這些actor完成執行的
3.不僅並行執行通過叢集管理,通過聯合挖礦的側鏈也屬於叢集的範疇,這裡展示的是一個主鏈 2個側鏈的結構
a.Kubernetes鏈和鏈之間使用Namspace來做資源隔離
c. 主鏈和側鏈之間通過Merge Mining(聯合挖礦)的形式實現相互索引
以上內容是aelf技術副總裁戎朋在UDC大會上提出的aelf的技術解決方案。
區塊鏈技術的發展,經歷了野蠻生長,也經歷了變革穩重。通過戎朋此次的講解也為我們揭示了區塊鏈其未來發展的路徑,可能對於行業外之人來說,行業已經達到冰點,但筆者認為行業才剛剛開始。