重構智能合約:平行宇宙與無限擴展

編者按:本文來自「小蟻區塊鏈」,作者 張錚文、達鴻飛;36氪經授權發佈。

1、前言

本文是小蟻的兩位創始人過去兩年中在設計小蟻智能合約時所做的深度思考和技術探索的結果。《重構智能合約》系列文章將分為上、中、下三篇,分別從確定性和資源控制、擴展性和耦合度、通用性和生態兼容三個方面來剖析現有智能合約系統的優缺點,並提出新的智能合約體系的設計思路。

在上一篇《重構智能合約(上):非確定性的幽靈》中我們分析了智能合約對確定性、資源控制和隔離的需求,得出了虛擬機作為智能合約的執行環境在上述三點都相對與容器技術具備相當的優勢。本篇中,我們將從擴展性的角度繼續比較幾種智能合約系統的優劣,並提出改進的思路。

2、執行環境的性能

智能合約的執行環境會對合約的性能起到非常重要的作用。目前主流的區塊鏈架構對智能合約執行環境的設計主要分為兩種:虛擬機和容器(Docker)。無論是虛擬機還是容器,它們的作用都是在一個沙盒中執行合約代碼,並對合約所使用的資源進行隔離和限制。

1) 虛擬機

虛擬機通常是指能夠像真實機器一樣執行程序的計算機的軟件實現。有些虛擬機會模擬出一個完整的物理計算機,比如VMware、Hyper-V等,可以在這些虛擬機上安裝操作系統和應用程序;另一些虛擬機則只提供了硬件的抽象層,而與具體的底層硬件無關,例如Java虛擬機。

區塊鏈智能合約系統的設計中,很少會採用模擬完整物理計算機的模式,因為這種方式會消耗大量的資源並嚴重影響性能,且很難兼容不同的硬件架構。所以絕大多數的區塊鏈會採用更加輕量級的虛擬機架構,例如以太坊開發了EVM,R3 Corda則直接採用了JVM,還有一些區塊鏈採用了V8引擎——Google的JavaScript引擎(虛擬機)。

當我們分析執行環境的性能時,有兩個指標是非常關鍵的:(1)指令的執行速度(2)執行環境本身的啟動速度。對於智能合約而言,執行環境的啟動速度往往要比指令的執行速度更為重要。智能合約中較多是一些甚少涉及IO操作的邏輯判斷指令,這些指令的執行速度很容易得到優化。上一篇《重構智能合約(上):非確定性的幽靈》中,我們提到了出於安全性考慮,智能合約必須在相互隔離的沙盒執行環境中運行。每個智能合約每次被調用,都必須啟動一個新的虛擬機/容器。因此執行環境本身的啟動速度(啟動一個虛擬機/容器)對智能合約系統的性能影響更大。

上述的EVM、JVM、V8引擎這些輕量級的虛擬機架構對智能合約的性能提升有顯著的優勢。它們的啟動速度非常快,佔用資源也很小,適合像智能合約這樣短小的程序。缺點是,這類虛擬機的執行效率會相對略低,好在智能合約一般都比較短小,會更加註重環境加載的速度而非代碼執行的速度。另外,通過JIT(即時編譯器)技術對熱點智能合約進行靜態編譯和緩存可以顯著提升虛擬機的執行效率。

2) 容器(Docker)

與其它主流區塊鏈設計不同的是,超級帳本中的子項目Fabric獨樹一幟地採用Docker作為其智能合約的執行環境。與虛擬機的作用一樣,Docker也進行了資源的隔離,但不如虛擬機那麼隔離充分。Docker本身沒有採用虛擬化技術,而是讓程序直接運行在底層操作系統上,因此代碼執行的效率很高。但由於其相對於輕量級虛擬機而言過於龐大的體型,部署和啟動Docker本身需要消耗大量的時間和資源。當使用在智能合約系統時,Docker的啟動時間成為了制約整體效率的瓶頸。Fabric在性能測試時,即便用上了IBM的大型機LinuxONE這樣的強悍硬件,性能依然不高。

執行環境的代碼執行速度就好比汽車的最高時速,而執行環境的啟動速度則好比汽車的0-100km/h加速度。和一般程序相比,智能合約短小精悍,總是處在「啟動—停止—啟動—停止」的狀態,很少能夠跑到極速。執行環境的啟動速度才是影響智能合約性能的關鍵因素。

3、併發、分片與無限擴展

當談及一個系統的擴展性時,總會涉及到兩個詞Scale Up(垂直擴展)和Scale Out(水平擴展)。最典型的垂直擴展案例是單核時代的CPU——主要靠提高主頻達到性能的提升。垂直擴展很容易就碰上天花板,當CPU製程工藝的提升越來越困難后,通過多核實現水平擴展,對指令進行并行處理,成為了提升CPU性能的重要手段。

正因為垂直擴展會很快觸及造價、技術的極限,一個不可拆分業務的串行系統的擴展性(或曰性能提升能力)就會很弱——它取決於單台設備的最大處理能力。當我們需要對系統進行擴展時,如果有辦法將串行系統改造成并行系統,那麼理論上我們將可以獲得近乎無限的擴展性。我們在考慮對區塊鏈系統進行擴展時,是否有無限擴展的可能?換言之,區塊鏈能否并行地對業務進行處理?

區塊鏈是一個分佈式的大賬本,裡面記錄了各式各樣的狀態數據,同時也記錄了這些狀態如何變化的規則,智能合約正是用來記錄這些規則的載體。區塊鏈能否并行地對業務進行處理,就取決於多個智能合約能否併發執行——即合約的執行是否是順序無關的。

舉個例子,某賬戶中有10元的餘額,現有兩個合約對該賬戶進行修改,第一個合約在賬戶中增加5元,第二個合約在賬戶中扣除11元。如果先執行前者,則最終賬戶的餘額為4元;如果先執行後者,由於餘額不足,第二個合約將會執行失敗,而第一筆合約會執行成功,最終賬戶餘額為15元。像這樣的兩個合約由於執行的順序不同而導致不同的結果,那麼它們是不可以併發執行的,只能串行處理。

反過來,如果兩個合約分別對兩個不同的賬戶進行修改,那麼它們無論哪一個先執行,結果都不會不同,所以它們是可以併發執行的。

從上面的例子可以看出,兩個合約是否可以并行處理,取決於這兩個合約是否是順序無關的;而是否順序無關,則取決於他們是否能夠對同一條狀態記錄進行修改。

基於上面的分析,我們可以很容易設計出一個具備「無限擴展」能力的智能合約系統。只需要簡單地規定:(1)一個智能合約只能修改屬於該合約自己的狀態記錄;(2)同一個事務批次(區塊)中,一個合約只能被運行一次。這樣一來,所有的智能合約之間都是順序無關可以平行處理了。乾的漂亮!

但是,等等……如果「一個智能合約只能修改屬於該合約自己的狀態記錄」,就意味着合約間無法相互調用,每個合約都是一個孤島;如果「一個區塊中,一個合約只能被運行一次」,就意味着用智能合約發行的某種數字資產在一個區塊里只能處理一筆交易。這顯然和「智能」二字的設計初衷大相徑庭。畢竟合約間的相互調用,同一區塊中多次調用同一個合約,都是我們想要的設計目標。

這樣一來情況就變得複雜多了,特別是像以太坊這種支持動態調用(通過CALL指令)的智能合約系統,不可能在運行前就判斷出合約的行為和調用路徑,也就無法判斷合約會修改哪些狀態記錄。因此,以太坊的擴展性一直是其設計上的一大弊病,其目前的架構設計難以支撐以太坊成為「全球計算平台」的遠大願景。為了解決擴展性問題,以太坊提出了分片(Sharding)方案:

打個比方,分片就類似於戶籍制度。計算一個合約的散列值再對256取模,就可以把合約分配到256個片區中去,這相當於給每個合約分配了一個該片區的「戶口」。江蘇戶口的合約只能調用江蘇的合約,上海戶口的合約就只能調用上海的,不能直接彼此調用。

這樣一來,江蘇、上海等256個片區的合約就可以按片區進行并行處理了,看起來執行效率可以得到256倍的提升。但是在這種設計下,想要跨片區調用,就必須向一個全局賬本(區塊鏈)寫入調用請求,另一片區的合約收到請求后再執行操作,並再次寫入全局賬本來返回調用結果。這導致了跨片區調用無法在同一個業務批次(區塊)中完成,效率顯著降低。在真實的應用場景中,分片的結果很可能是大家都擠到一個「繁華片區」中去,因為這樣才能最高效的進行相互調用,避免跨區操作。在城市郊區修建再多的幹道,也無法解決市中心的擁堵問題。

另外,智能合約代碼的加載方式也會影響到擴展性。目前主流的區塊鏈智能合約系統都會要求將智能合約代碼發佈到鏈上,然後再從鏈上加載代碼執行。有些合約代碼可能只被使用一次就廢棄了,但在區塊鏈中永久性地存在,佔用節點的存儲資源,久而久之這些廢棄代碼會成為區塊鏈的巨大負擔,影響擴展性。

另一種方案,是將智能合約的散列值記錄在鏈上,用IPFS等以散列值為索引的新型分佈式存儲網絡來存儲完整合約代碼。在執行合約的時候,再從鏈外加載代碼。由於合約的散列值已經在鏈上記錄,即使從鏈外加載代碼也不用擔心合約的內容被篡改,這樣可以為節點節省大量的存儲空間。同時也能對智能合約的內容進行一定程度的隱私保護。

4、耦合度

耦合是指兩個或兩個以上的實體相互依賴於對方的一個量度。在區塊鏈與智能合約系統的設計中,對於耦合度的控制有兩個非常極端的例子:

1) 以太坊

以太坊在智能合約系統的設計中是高耦合的典型,區塊鏈與EVM之間到處充斥着相互依賴的關係,例如:

  • 費用的計算混雜在虛擬機的實現邏輯中;

  • 虛擬機指令集中包含大量用於訪問賬本數據的指令;

  • 虛擬機直接提供以區塊鏈賬本作為載體的持久化存儲指令。

將區塊鏈的業務邏輯與虛擬機混在一起,並不是一個良好的設計。這會造成一系列的問題,一旦區塊鏈的功能需要改進或者升級,勢必就要對EVM也進行相應的修改,這種修改多數情況都會體現在增加新的指令上;而EVM幾乎沒有辦法移植到其它區塊鏈系統中,除非另一個鏈的底層架構與以太坊高度一致,或者專門針對EVM開發一個對接層。這種模式會對以太坊的生態應用造成很大的局限性,關於這一點我們將會在《重構智能合約(下):兼容性與生態》中詳述。

2) Fabric

與以太坊的設計模式相反,Fabric的智能合約系統採用了低耦合的設計,區塊鏈賬本與Docker之間幾乎沒有任何依賴關係,因為Docker本身就被廣泛應用於區塊鏈以外的大量場景之中。在Docker中運行的智能合約程序只能通過gRPC協議與節點進行通信,協議中包含了訪問賬本和持久化存儲的功能。當區塊鏈的功能需要改進或者升級時,只需要對gRPC協議進行改動即可。這種超低耦合度的設計模式值得其它區塊鏈的開發者學習參考。

高內聚、低耦合是設計系統架構時所常常追求的目標。Fabric的設計目標是打造通用的許可型區塊鏈的技術框架,因此一開始就採用了高度模塊化的設計思想;而以太坊最初的設計目標是一個具體的公有鏈實例,而非技術框架。因此以太坊中存在着系統耦合度過高的問題。這將會妨礙以太坊作為一種通用技術被使用在聯盟鏈、私有鏈上。

5、小結

在本篇中,我們分析了智能合約系統理想的執行環境性能、併發處理能力、耦合度和代碼加載方式,發現了以太坊的一些高度抽象,無灰度的設計帶來的擴展性問題,提出了可以做到理論上「無限擴展」的高度并行化智能合約系統的設計思路。我們認為一個可以良好進行併發執行的智能合約系統,應該具有以下特徵:

  • 輕量級的執行環境:快速的啟動時間和較高的執行效率。

  • 可插拔的執行環境架構:默認的執行環境應該不提供持久化存儲,從而讓合約默認是一種類似於微服務的無狀態函數,從而可以直接併發處理。僅在需要存儲狀態時,才提供可插拔的持久化存儲模塊。這樣的虛擬機默認只有一個CPU和棧,僅在需要時才提供「硬盤」和其他IO設備。

  • 明示化的調用關係:即只提供靜態調用的功能,從而使得程序的調用關係可以在運行它之前就整理清楚。一旦調用路徑明確了,那麼合約可能會修改到的狀態數據也就明確了,依據這些明示的調用路徑就可以進行即時的動態分片提高合約執行的并行能力。

  • 可鏈外存儲的合約代碼:通過鏈上存儲散列值,鏈外存儲合約代碼實現存儲空間的擴展性。

  • 低耦合度的設計:合約語言、執行環境、區塊鏈之間的低耦合度,提高智能合約系統的通用性。

在本系列最後一篇《重構智能合約(下):兼容性與生態》中,我們將會分析現有智能合約系統對編程語言的選擇,並提出一種讓非區塊鏈開發者也能立即編寫智能合約的新模式。

參考閱讀:《重構智能合約(上):非確定性的幽靈》

關於作者:

本文兩位作者均系小蟻區塊鏈(www.antshares.org)的創始人。小蟻區塊鏈是一個探索可編程智能經濟的開源公有鏈。小蟻起源於2014年,是中國第一個區塊鏈項目。2017年,小蟻的前期技術積累將進入兌現期,會在智能合約、跨鏈互操作、共識機制、密碼學等多個方向實現技術突破。


想在手機閱讀更多網站設計及開發資訊?下載【香港矽谷】Android應用
分享到Facebook
技術平台: Nasthon Systems