重構(gòu)智能合約(上):非確定性的幽靈
1. 前言
本文是小蟻的兩位創(chuàng)始人過去兩年中在設(shè)計(jì)小蟻智能合約時所做的深度思考和技術(shù)探索的結(jié)果?!吨貥?gòu)智能合約》系列文章將分為上、中、下三篇,分別從確定性和資源控制、擴(kuò)展性和耦合度、通用性和生態(tài)兼容三個方面來剖析現(xiàn)有智能合約系統(tǒng)的優(yōu)缺點(diǎn),并提出新的智能合約體系的設(shè)計(jì)思路。
2. 智能合約與區(qū)塊鏈
自從比特幣、以太坊的相繼誕生,以及區(qū)塊鏈技術(shù)的逐步升溫,智能合約一詞便開始頻繁的出現(xiàn)在金融和科技媒體之中。智能合約是1994年由密碼學(xué)家尼克薩博(Nick Szabo)***提出的理念,幾乎與互聯(lián)網(wǎng)同齡。根據(jù)Nick Szabo的定義:智能合約是指能夠自動執(zhí)行合約條款的計(jì)算機(jī)程序。
傳統(tǒng)意義上的合約的生命周期一般包含:各方協(xié)商、簽名記錄、條款執(zhí)行三個階段,智能合約也類似。但是在區(qū)塊鏈技術(shù)出現(xiàn)之前,單臺計(jì)算機(jī)難以提供安全可靠的簽名記錄和條款執(zhí)行服務(wù),所以智能合約也一直僅僅停留在理論階段。比特幣及區(qū)塊鏈技術(shù)出現(xiàn)后,其多方存儲、多方計(jì)算、規(guī)則透明、不可篡改等特性,恰好為智能合約提供了安全可靠的記錄載體和執(zhí)行環(huán)境。
與普通的計(jì)算機(jī)程序不同,區(qū)塊鏈上的智能合約必須同時具備兩種性質(zhì):確定性和可終止性。
區(qū)塊鏈?zhǔn)且粋€通過多方存儲、多方計(jì)算的方式來實(shí)現(xiàn)數(shù)據(jù)不可篡改、計(jì)算結(jié)果可信的分布式系統(tǒng)。智能合約會在區(qū)塊鏈網(wǎng)絡(luò)的多個節(jié)點(diǎn)中運(yùn)行。如果一個智能合約是非確定性的,那么不同節(jié)點(diǎn)運(yùn)行的結(jié)果就可能不一致,從而導(dǎo)致共識無法達(dá)成,網(wǎng)絡(luò)陷入停滯;如果智能合約是永不停止的,那么節(jié)點(diǎn)將耗費(fèi)無窮多的時間和資源去運(yùn)行合約,同樣導(dǎo)致網(wǎng)絡(luò)進(jìn)入停滯狀態(tài)。這就涉及到了兩個有趣的話題,下面我們分別來探討一下確定性和可終止性問題(停機(jī)問題)。
3. 智能合約的確定性
如果一個程序在不同的計(jì)算機(jī)、或者在同一臺計(jì)算機(jī)上的不同時刻多次運(yùn)行,對于相同的輸入能夠保證產(chǎn)生相同的輸出,則稱該程序的行為是確定性的,反之則稱該程序的行為是非確定性的。使程序產(chǎn)生非確定性的因素有很多,總結(jié)起來有以下幾種:
1) 調(diào)用了非確定性的系統(tǒng)函數(shù)
一般在編寫程序的時候,開發(fā)者或多或少會調(diào)用一些系統(tǒng)提供的函數(shù)和功能以減少開發(fā)的工作量。這些系統(tǒng)函數(shù)中可能會存在一些非確定性的函數(shù),比如生成隨機(jī)數(shù)、獲取系統(tǒng)時間等。一旦程序調(diào)用了另一個非確定性的程序并使用了它們輸出的內(nèi)容,那么該程序自身的行為也可能會變?yōu)榉谴_定性的。
2) 使用了非確定性的數(shù)據(jù)來源
如果一個程序在運(yùn)行時獲取數(shù)據(jù),而數(shù)據(jù)源提供的是非確定性的數(shù)據(jù),那么該程序也可能會變成非確定性的程序。例如,通過搜索引擎來獲取某個關(guān)鍵詞的前10條搜索結(jié)果——搜索引擎針對不同的IP地址來源可能會返回不同的排序結(jié)果。
3) 動態(tài)調(diào)用
動態(tài)調(diào)用是指,一個程序在調(diào)用另一個程序時,如果必須在運(yùn)行時才能確定被調(diào)用的目標(biāo),則稱該調(diào)用為動態(tài)調(diào)用;反之,如果在運(yùn)行前即可確定被調(diào)用的目標(biāo),且在運(yùn)行時無法變更該目標(biāo),則稱該調(diào)用為靜態(tài)調(diào)用。由于動態(tài)調(diào)用的目標(biāo)在運(yùn)行時決定,因此其行為是非確定的。
對于區(qū)塊鏈上的智能合約,我們一般要求它的行為必須是確定性的,因?yàn)榉谴_定性的合約可能會破壞系統(tǒng)的一致性。區(qū)塊鏈的作者必須考慮到這個問題,并在設(shè)計(jì)智能合約系統(tǒng)的時候,就想辦法把非確定性因素排除在外。那么我們來看看現(xiàn)有的各個區(qū)塊鏈?zhǔn)侨绾谓鉀Q這個問題的。
1) 比特幣
比特幣內(nèi)置了一套腳本引擎,用于執(zhí)行鑒權(quán)腳本,它是區(qū)塊鏈智能合約的雛形。開發(fā)者可以基于這套腳本系統(tǒng)來開發(fā)一些簡單的應(yīng)用,但由于其指令集非常簡單且非圖靈完備,能夠?qū)崿F(xiàn)功能相當(dāng)有限。這套系統(tǒng)既沒有提供任何系統(tǒng)函數(shù),也沒有提供任何訪問數(shù)據(jù)的能力,更沒有動態(tài)調(diào)用的功能,甚至連靜態(tài)調(diào)用也沒有提供,因此比特幣的智能合約一定是確定性的。
2) 以太坊
以太坊的主要設(shè)計(jì)思想,就是提供一個圖靈完備的智能合約平臺,讓用戶可以編寫任意邏輯的程序。它專門開發(fā)了一個用于執(zhí)行合約代碼的虛擬機(jī)EVM,并設(shè)計(jì)了一種類似于Java的高級語言Solidity,以方便用戶進(jìn)行開發(fā)。以太坊智能合約沒有提供任何非確定性的系統(tǒng)函數(shù),可訪問的數(shù)據(jù)也僅限于鏈內(nèi)數(shù)據(jù),外部數(shù)據(jù)需要通過交易來發(fā)送到合約。但是,以太坊的CALL和CALLCODE指令的目標(biāo)地址通過棧來傳遞,使得合約可以在運(yùn)行時動態(tài)調(diào)用其它的合約代碼,使合約的調(diào)用路徑變?yōu)榉谴_定性。好在合約可以訪問到的數(shù)據(jù)都是確定性的,使得所有節(jié)點(diǎn)在動態(tài)調(diào)用目標(biāo)代碼時一定會獲得相同的目標(biāo)地址,保證了系統(tǒng)的一致性。但是調(diào)用路徑的非確定性,會導(dǎo)致一個可擴(kuò)展性上的重要性能損失,具體會在《重構(gòu)智能合約(中):平行宇宙與***擴(kuò)展》中詳述。
3) Fabric
Fabric是超級賬本中的一個子項(xiàng)目,它的智能合約采用了重量級的Docker作為執(zhí)行環(huán)境。這可能跟大家的印象有點(diǎn)矛盾——“Docker不是一直被認(rèn)為是一種輕量級的容器技術(shù)嗎?”。實(shí)際上,Docker的“輕”是相對于模擬物理機(jī)架構(gòu)的重量級虛擬化技術(shù)而言。在區(qū)塊鏈應(yīng)用場景下,Docker是一個比較“重”的執(zhí)行環(huán)境,這也是Fabric的性能瓶頸所在,目前只能達(dá)到每秒幾百TPS。由于Docker的特性,智能合約幾乎可以使用物理計(jì)算機(jī)上的所有功能,因此具有極高的非確定性。所以Fabric要求智能合約的開發(fā)者在編寫代碼的時候盡量避免使用到具有非確定性的功能,并計(jì)劃提供一套專門開發(fā)的確定性系統(tǒng)函數(shù)庫供開發(fā)者使用。 然而,由于無法從底層機(jī)制上避免非確定性的產(chǎn)生,寄托于開發(fā)者遵守良好的開發(fā)規(guī)范難免有些一廂情愿。非確定性就像幽靈一般,平時似乎并不存在,在一些邊緣案例(corner case)上就可能會突然冒出來造成難以判斷的故障。
4. 停機(jī)問題與資源控制
停機(jī)問題(halting problem)是邏輯數(shù)學(xué)中可計(jì)算性理論的一個問題。通俗地說,停機(jī)問題就是判斷任意程序是否能在有限的時間之內(nèi)結(jié)束運(yùn)行的問題。該問題等價(jià)于如下的判定問題:是否存在一個程序P,對于任意輸入的程序w,能夠判斷w會在有限時間內(nèi)結(jié)束或者死循環(huán)。
艾倫·圖靈在1936年用對角論證法證明了我們無法編寫出程序P——即不存在解決停機(jī)問題的通用算法。這個證明的關(guān)鍵在于對計(jì)算機(jī)和程序的數(shù)學(xué)定義,這被稱為圖靈機(jī)。停機(jī)問題在圖靈機(jī)上是不可判定問題。這是最早提出的決定性問題之一。
區(qū)塊鏈上的智能合約必須是可終止的,否則將會消耗***的時間和資源。從上面的論證已經(jīng)可以看出,停機(jī)問題是不可解的,我們無法在不運(yùn)行一個程序的情況下,提前判定該程序是否會停機(jī)。因此,區(qū)塊鏈的設(shè)計(jì)者不得不假設(shè)所有的智能合約都可能會進(jìn)入死循環(huán),并對于可能已經(jīng)進(jìn)入死循環(huán)的合約采用異常終止的方式來結(jié)束它。通常會有以下幾種策略:
1) 非圖靈完備
如果一個區(qū)塊鏈的智能合約系統(tǒng),只提供了有限的指令集,而不提供諸如跳轉(zhuǎn)、循環(huán)等指令,以及可以等效實(shí)現(xiàn)類似功能的指令,那么基于這個系統(tǒng)的智能合約就不可能進(jìn)入死循環(huán),因此它們總是可停機(jī)的,比特幣就是其中的一個例子。值得一提的是,比特幣改進(jìn)計(jì)劃BIP12的提案中,有過在比特幣指令集中增加一條OP_EVAL指令的計(jì)劃,這條指令可以加載計(jì)算棧中的腳本并動態(tài)執(zhí)行,從而解決多重簽名算法的問題。但由于該指令會間接地使得比特幣的腳本系統(tǒng)變?yōu)閳D靈完備,因此最終被廢棄了。
2) 計(jì)價(jià)器
既然無法在智能合約運(yùn)行之前就判定其是否會停機(jī),那么也可以在合約運(yùn)行中進(jìn)行計(jì)步——每執(zhí)行一條指令就將計(jì)步器加一。當(dāng)計(jì)步器的數(shù)值超過一定的限制之后,就認(rèn)為合約已經(jīng)進(jìn)入死循環(huán),從而強(qiáng)行終止它的運(yùn)行。這種方法需要區(qū)塊鏈對智能合約的執(zhí)行有較大的控制能力,能夠精確的計(jì)算出合約執(zhí)行的步數(shù)且在各個節(jié)點(diǎn)間保持一致。
計(jì)價(jià)器的方案和計(jì)步器基本上是一致的,但是它采用經(jīng)濟(jì)手段來解決停機(jī)問題:對合約執(zhí)行的每一個指令進(jìn)行收費(fèi),當(dāng)合約的手續(xù)費(fèi)全部用完之后,如果合約還沒有終止,那么就強(qiáng)行終止退出。以太坊采用的就是這個模式,它通過消耗一種被稱為燃料的代幣來對智能合約進(jìn)行計(jì)價(jià)。一旦燃料耗盡,合約就會執(zhí)行失敗,并且不會退回消耗掉的費(fèi)用。
3) 計(jì)時器
計(jì)時器和計(jì)價(jià)器的方案比較類似,但它使用時間作為標(biāo)準(zhǔn)來衡量一個合約是否已經(jīng)進(jìn)入死循環(huán):如果合約在超時時間到達(dá)之前還沒有正常終止,那么就認(rèn)為它已進(jìn)入死循環(huán)并強(qiáng)行終止它。Fabric采用了計(jì)時器的方案,原因是Fabric使用Docker作為其智能合約的執(zhí)行環(huán)境,而Docker中運(yùn)行的程序是無法計(jì)步或計(jì)價(jià)的。雖然計(jì)時器可以部分地解決停機(jī)問題,但是在分布式系統(tǒng)中,每個節(jié)點(diǎn)的執(zhí)行時長未必能夠保證一致,且每個節(jié)點(diǎn)的性能和負(fù)載都不一樣,導(dǎo)致對合約運(yùn)行是否超時這一判斷會出現(xiàn)不一致的情況,從而使得共識算法的失敗率大大的增加,這是計(jì)時器方案的重要缺點(diǎn)。
上述的非圖靈完備、計(jì)價(jià)器、計(jì)時器方案,實(shí)際上都是一種資源控制手段,即通過對代碼量設(shè)定上限,對運(yùn)算資源計(jì)價(jià),或?qū)?zhí)行時間設(shè)定上限等方法來將智能合約占用的資源控制在合理的范圍。
5. 資源隔離
虛擬化的執(zhí)行環(huán)境除了實(shí)現(xiàn)資源控制外這一目的外,更重要的是通過資源隔離來保障系統(tǒng)的安全。在開放的區(qū)塊鏈上,任何參與者都可以編寫并上傳智能合約,圖靈完備的智能合約就意味著可以編寫并執(zhí)行任意的邏輯——包括病毒或故障合約如果智能合約直接在區(qū)塊鏈節(jié)點(diǎn)的宿主系統(tǒng)上運(yùn)行,病毒就能夠自我復(fù)制,故障合約就可能破壞宿主系統(tǒng)的自身數(shù)據(jù)。因此智能合約必須放在一個隔離的沙盒環(huán)境中運(yùn)行——虛擬機(jī)或者容器。
通過沙盒執(zhí)行環(huán)境,合約和合約之間,合約和宿主系統(tǒng)之間進(jìn)行了有效的資源隔離,也就控制住了惡意或故障合約的影響范圍。但在資源隔離度上,Docker所依賴的基于命名空間的隔離要弱于虛擬機(jī)的隔離。正因?yàn)榇?,在公有云上兩個不同用戶的Docker鏡像一般不會被放在一個宿主操作系統(tǒng)中運(yùn)行。因此,基于Docker的方案在資源隔離度的安全性上要弱于虛擬機(jī)方案。
6. 小結(jié)
通過對確定性、可終止性、資源控制、資源隔離的分析,不難發(fā)現(xiàn)區(qū)塊鏈上的智能合約需要具備強(qiáng)確定性,可終止性、精確的資源控制和資源隔離。在這一維度上,為區(qū)塊鏈量身定制虛擬機(jī)的方案相比借用現(xiàn)有容器的方案有著較大的優(yōu)勢。而容器方案的優(yōu)點(diǎn)在于編寫語言的靈活,不要求合約開發(fā)者學(xué)習(xí)新的編程語言,從而更容易融入現(xiàn)有的開發(fā)者生態(tài)。那么是否有辦法兼顧二者呢?我們會在《重構(gòu)智能合約(下):兼容性與生態(tài)》中給出方案。
在此之前,我們會先通過《重構(gòu)智能合約(中):平行宇宙與***擴(kuò)展》來分析一下現(xiàn)有公有鏈、聯(lián)盟鏈的性能為何如此不堪。
關(guān)于作者
本文兩位作者均系小蟻區(qū)塊鏈(www.antshares.org)的創(chuàng)始人。小蟻區(qū)塊鏈?zhǔn)且粋€探索可編程智能經(jīng)濟(jì)的開源公有鏈。小蟻起源于2014年,是***個區(qū)塊鏈項(xiàng)目。2017年,小蟻的前期技術(shù)積累將進(jìn)入兌現(xiàn)期,會在智能合約、跨鏈互操作、共識機(jī)制、密碼學(xué)等多個方向?qū)崿F(xiàn)技術(shù)突破。