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