10.7 以太坊和智能合約

我們已經介紹了幾種用比特幣的腳本語言寫出有趣的應用的方法,如有托管功能的支付交易。我們也看到比特幣腳本語言的一些瓶頸:只有很少的指令,並不符合圖靈計算的標準。因此,很多新的另類幣增添與應用程序相關的特殊功能。域名幣是第一個嘗試,後來又有許多加密貨幣,類似於比特幣但是支持賭博、股票發行、市場預測等。

設想我們不需要為了每個應用程序,每次都建設一套新的系統,而是創造出一個加密貨幣系統,以支持所有未來可以想像到的任何應用。這就是所謂的圖靈完備——據我們理解,滿足圖靈完備標準的編程語言,可以讓你寫出圖靈機可以完成的任意功能,它可計算的函數和圖靈機可計算的函數是完全相同的。因此,每一圖靈完備的編程語言——包括我們熟悉的Java、Python和Lisp——都是圖靈等價的。如果不考慮實際中的簡單性和表現,圖靈完備是我們在編程語言有關表達能力上需要的最好的性質。

從某種程度上講,今天加密貨幣的發展使人回想起20世紀40年代早期計算機發展的時代:在第二次世界大戰時,建造大量的複雜的只有某種特定功能的計算器(比如用於暴力破解密碼的機器和海軍用於確定發射彈道軌道的機器),這些工作促使研究專家致力於建造第一個可重複編程的通用計算機。任何可預見的應用程序都可以使用該計算機(見圖10.8)。

圖10.8 在布萊切利園(Bletchley Park)博物館重建的炸彈機(Bombe)

註:炸彈機是一個由阿蘭·圖靈(Alan Turing)設計的特殊功能的高級計算機,用於破解德國的英格瑪(Enigma)密碼。通用計算機取代類似炸彈機的精巧裝備,以太幣能否也能像這樣取代特殊功能的另類幣呢?

以太幣(Ethereum)是一種雄心勃勃的另類幣,致力於提供一種滿足圖靈計算要求的可編程語言,用這種語言可以編寫腳本或者合約。雖然有其他方案可以做到這一點,但是以太坊無疑是最引人注目的:它使用了幾個創新的技術,成功地完成了眾籌,在幾個月內籌資兩千萬美元,並且採用激進的參數,比如使用較短的產生區塊的時間參數。以太坊系統本身很複雜,需要再編寫一本新的教材才能完整闡述,本節只做簡要討論。

智能合約編程模式

智能合約最初是用來指使用計算機系統(或者其他自動化方式)來執行合約。例如,你可以把自動售貨機看成一個銷售商品的智能合約,執行的就是你和機器主人之間關於如何購買一個糖果的合約。

在以太坊體系,一個合約就是一個存在區塊鏈裡的程序。任何人支付一點費用,就可以用特定的操作將他的程序上傳,建立一個以太坊合約。這個合約是用字節碼(bytecode)寫的,可以被特殊的以太坊專用虛擬機(Ethereum-specific Virtual Machine,簡稱EVM)執行。一旦合約上傳,便永遠存在在區塊鏈裡。智能合約有它自己的資金賬戶,其他用戶可以調用程序裡面開放的應用程序編程接口(API),合約可以收發款項。

一個簡單的例子:以太坊中的域名幣

我們說以太坊可以用來執行任何特定應用的另類幣功能。舉個簡單的例子,我們可以展示使用一個簡單的以太坊合約,來構建出域名幣形式的功能。

圖10.9 所示就是一個構建的案例。它是以「穩健」語言(Solidity)編寫,「穩健」是以太坊裡用於定義合約的高級編程語言。這個合約產生一個原始的域名/數值(name/value)儲存配對或者註冊名。名字永遠連著數字。這個合約定義了一個數字變量——註冊表(registryTable), 裡面有32比特長字節和公開密鑰的配對關係。初始時期,每個字節都對應著空地址0×0000000…000。這個合約同時定義了單一入口點,叫「用戶名稱」(claimName)。這個入口點只接受名字參數。首先,這個合約確認調用這個合約的人已經支付了至少10個wei。wei是在以太坊裡最小的貨幣單位。如果沒有支付10wei, 合約自動終止並發出錯誤信號(「throw」的編程命令就做這些)。如果足夠的wei已經發出而且這個名字還沒有被註冊,那麼這個名字就和調用的地址永久地聯繫在一起。

圖10.9 一個用於實現域名註冊功能的簡單以太坊智能合約

以上就是這個8行代碼的合約能做的事。我們還可以多花點時間,把其他域名幣有的功能都在這個合約裡實現。比如,可以存儲擁有者地址以外的信息,通過存儲上次更新時間來要求域名的主人定期地重新註冊,並且允許其他用戶擁有長期不更新的域名權。

我們還可以加第二個功能,允許錢回撤。按照初始的代碼設計,錢只能不斷堆積在合約裡,也就意味著從流通中消失。當然,在可以回撤錢的程序裡,最好能設定,調用回撤的是合約的主人。任何人在以太坊都可以調用任何方程,但是用戶是指定的,所以能確認誰是真正調用方程的人。

燃料、激勵和安全

和比特幣不一樣的是,以太坊支持循環語句,雖然第一個例子裡並不需要循環。循環語句一聽就容易讓人產生警覺,因為有循環的地方就會有無限死循環。從根本上說,以太坊合約有可能因為種種原因而無限循環。計算機領域裡一個著名的研究結果(難以判斷的終止問題)證明,不存在任何算法,可以根據源代碼去判斷一個程序是否可以無限運行下去。因此,我們如何防止合約無限運行下去呢?

更進一步講,即使條約不會無限運行,也需要某種方式來限制它不會運行太久。以太坊體系通過一種稱作「燃料費用」的機制來實現這一點。簡單地說,每執行一條虛擬機器的指令需要花費一小部分的成本費用,我們稱之為「燃料費用」。不同的操作花費不同。基本的像加減操作只花費1單位燃料費用,而計算SHA-3哈希值(內置函數)需要20單位燃料費用,在永久存儲器上寫256比特長的字符需要100單位燃料費用。每筆交易也需要先支付21 000單位燃料費用。你可以把以太坊體系想像成超級折扣的航空公司。機票只是你支付乘飛機的費用,任何其他需求都要多付錢。完整操作清單和固定的燃料費用都可以從以太坊裡找到。任何清單和費用的變動都需要以太坊產生一個硬分叉,這和比特幣腳本語言的語義改變一樣。

燃料費用是通過以太坊體系內部被稱為以太(ether)的貨幣來支付的。它只是在用來支付合約操作的時候才叫燃料費用。每筆交易都規定了燃料的價格,也就是說,每份燃料需要多少以太。燃料費用就像比特幣的交易費,礦工可以自由公佈交易的燃料費用,每個礦工都可以獨立地決定收費方式。這樣會得出一個反映市場供求關係的燃料市場價格。2016年年初,雖然以太坊網絡體系還是屬於實驗階段,市場已經默認50 gigawei為1單位價格。50gigawei等於5×10-8以太,根據以太幣和比特幣2016年1月的匯兌比例,這也就是大約3×10-10比特幣。

每次調用之前,必須設定燃料費用的最高限,也就是願意支付價格的最大值。當達到這個值(燃料用完了),程序就會終止,發生的所有程序狀態的變化就會被重新設置到原始狀態,但是礦工還是保留燃料。由此可見,不要用完燃料,這一點非常重要。

燃料的使用要求,意味著以太坊不適合很耗費資源的計算。以太坊系統未被設計成像雲計算那樣的服務,即支付一定的費用讓雲服務完成自己無法做到的計算。像亞馬遜的彈性計算雲或者微軟雲計算平台,提供划算百萬倍的計算量。另一方面,以太坊更加適合創建安全邏輯協議。本質上來說,以太坊提供了一種兩個或者多個匿名交易者可以信賴的服務系統。

以太坊上區塊鏈的安全還沒有像比特幣一樣完善。理論上,以太坊比較複雜,也比較難以用數學推理來論證。實際上,以太坊才剛剛開始發展,其安全性還沒有像比特幣一樣經過很多考驗。尤其是,擔心處理交易的成本會讓類似比特幣的激勵機制失效,我們在共同挖礦的分析中討論過存在這種擔心的情況。當交易成本占礦工的總成本的比重不再能忽略不計的時候,大的礦工有明顯優勢,因為成本和哈希算力相互獨立。更重要的是,燃料只支付給最初包括該交易的區塊的挖工。但是所有的在這之上建立區塊的礦工都必須驗證該區塊,卻得不到任何報酬。這意味著,他們將有動力去跳過該驗證。正如之前所看到的,這種情況不利於區塊鏈體系的健康發展。

第二個例子:以太坊體系中的國際象棋

我們還沒涉及以太坊中新功能如何運用,所以讓我們看第二個案例。假設愛麗絲和鮑勃下國際象棋,賭注是一定數額的金錢。唯一的問題是愛麗絲和鮑勃生活在不同的國家,他們都不相信對方輸了會支付賭注。這個問題可以用以太坊來解決。

愛麗絲寫下以太坊程序,這個程序設定了國際象棋的規則並且被上傳到以太坊網絡。她給這個合約支付一定數量的以太作為賭注。鮑勃可以看到這個合約,如果他答應接受挑戰,他把他的賭注支付給這個合約,就等於開始了這個遊戲。鮑勃在接受挑戰之前應該確認,這個合約是準確無誤地遵守了國際象棋的規則,並且最後會把所有賭注支付給獲勝者。

一旦雙方都支付了賭注,假設他們約定下同樣的賭注,合約會檢查雙方的賭注是否相等。這時候,遊戲就開始了。任何一方除非贏了遊戲,否則無法從合約裡取出錢來。其他人在任何情況下也無法取得這筆錢。

愛麗絲和鮑勃輪流把自己的下棋步驟發給這個合約。這個合約也會檢查輪到誰下確保指令是由愛麗絲或者鮑勃發出,而不是其他人。大家是否還記得調用者需要在每個操作(促使合約執行一個動作)上簽名,因為合約可以根據簽名確認調用者。合約也會根據國際象棋的規則校驗雙方的步驟。如果一方試圖把兵移動3格,合約會拒絕該步驟。

到最後遊戲結束。合約在每一步都會檢測是否有一方被將軍,或者雙方打平,或者滿足其他打平的條件。玩家也可以發送投降的指令。當遊戲結束時,合約終止,並把所有的錢支付給獲勝者,或者平局下平分賭注。

從概念上看,這是一個以太坊的簡單應用,但是有很多微妙的地方值得探討。如果一方快輸了他就放棄了?合約應該設定一個機制,如果一方在規定的時間沒有提交有效的下一步,錢就支付給另一方。

哪個玩家先走呢?白方先走的話,白方就擁有微小的優勢。因此,雙方都想做白方。這就碰到了以太坊合約的一個難題:沒有內置的隨機源。之所以是一個難題,是因為隨機數發生器需要所有礦工的檢驗(因為他們需要檢驗合約是否正確地執行),但是這些隨機數對任何人來說都是不可預測的(否則的話,玩家也許就因為不能先走而拒絕參加這個遊戲)。

隨機數「信號塔」(randomness beacons)可以解決這個問題。正如9.4節討論的,在雙方都加入遊戲後,合約計算區塊鏈下一個區塊的哈希值。對這個特定的遊戲應用而言,這個問題比較容易解決,因為只要讓愛麗絲和鮑勃雙方確信決定誰先誰後是隨機的,這樣就滿足要求,而不需要向所有人證明。所以他們可以採用9.3節的辦法:他們兩個同時提交一個隨機數的哈希值,並且公開他們的輸入值,然後從雙方的輸入總值算出隨機數。實際操作中,以上兩種方法都可以使用。

其他應用

下棋也許很有趣,但是真正激動人心的是以太坊在金融領域的應用。我們在課本裡討論的大部分應用,包括市場預計、智能資產、托管支付、微支付渠道和混合服務,都可以在以太坊體系裡實現。這些應用都有其細微的區別,但是相對比特幣死板的協議,大多數情況下,這些應用都能相對容易地在以太坊體系內完成。

以太坊的狀態和賬戶餘額。第3章中,我們討論了賬本的兩種方法: 基於賬戶和基於交易。在一個基於交易的賬本中,如比特幣,區塊鏈只存儲交易(加上一些少量的轉載標題的設置數據)。為了方便驗證交易,比特幣的幣值是無法分割的,即交易的結果必須整體被消費,可以自己消費,或者如果需要的話,換地址消費。交易實際上是在全球狀態表上操作的,這個表稱為「未花費交易輸出列表」。但是比特幣的協議並沒有明確規定這個全球狀態表。全球狀態表的產生純粹是礦工為了加快驗證過程而創造出來的。

另一方面,以太坊則是基於賬戶的模式。由於以太坊已經存儲了合約地址和狀態的對照表的數據結構,很自然地也同時存儲每個普通地址(或者叫擁有者的地址)的賬戶餘額。這意味著,與非閉環式的交易支付模式必須有輸入和輸出不同,以太坊存儲每個地址的賬戶餘額,這一點,與銀行存儲每個賬戶餘額的方式類似。

以太坊的數據結構。在第3章,我們提到基於賬戶的賬本需要精心設計的數據結構來存儲記錄。以太坊就有這樣的數據結構。具體來說,每個區塊包含每個地址的目前狀態(賬戶餘額和交易數)的摘要,同時也包含每個合約的狀態(餘額和存儲空間)。每個合約的存儲樹結構映射256比特的地址和256比特的字節。這樣可以存儲巨量的(2256×256=2264 )信息。當然,這只不過是理論上的可能空間,我們不會用到這麼大的存儲空間。數據結構裡面提供的摘要,使驗證一個地址有多少餘額或者空間變得相對容易。比如,不需要鮑勃從頭到尾掃瞄整個區塊鏈,愛麗絲就可以向鮑勃證明她有多少餘額。

此時,比特幣用簡單的二項梅克爾樹的結構可以派得上用場。因為它可以把有效的證明數據存在該區塊裡(要求礦工確信對於相同的地址,每個樹狀數據結構都要求該地址相同的狀態)。但是我們也希望能夠更快地查詢地址並且能夠有效更新地址的數值。為了達到這個目的,以太坊使用比較複雜的樹狀結構,叫帕特裡夏樹(Patricia tree)、前綴樹(prefix tree)、字典樹(trie)或基數樹(radix tree)。每個以太坊區塊包含梅克爾-帕特裡夏樹(Merkle Patricia tree)的樹根,它保存每個地址的狀態,也包含合約地址。每個合約的狀態,包含一個樹狀數據結構用來保存合約的存儲狀態。

基於賬戶賬本的另一個不易處理的問題是防止重複攻擊。在比特幣裡,每個交易都使用「未花費交易輸出列表」輸入,因此,任何相同簽名認證過的交易,不可能被重複使用兩次。但是在以太坊設計裡,需要確保當愛麗絲簽下支付給鮑勃1以太交易的時候,鮑勃不能一次又一次地對外廣播並重複使用這個1以太,直到把愛麗絲的賬戶用光。這樣的交易不能重複,因為一旦使用了,愛麗絲的交易計數會增加一次,而這個交易計數是一個全局的狀態參數。

總的來說,以太坊使用比比特幣更加強大的數據結構來管理它的賬本。雖然我們沒有深入研究它的數據結構,但我們知道,這個數據結構使得賬戶、合約,以及交易相關聲明的有效驗證變成可能。

以太坊項目

以太坊最早於2013年年末開始討論,並於2015年第一次發佈,代號先行者(Frontier)。以太坊採用預售的方式,以固定比特幣價格公開出售,並把所有的預售款投入以太坊基金會。

和其他另類幣相比,以太坊發展比較緩慢,這也反映了以太幣是一個比較複雜的系統。與比特幣相比,以太坊增加以太坊專用虛擬機(EVM),一個全新編程模式,一個全新的數據結構。此外,以太坊還對比特幣的共識模式做了大的修改。每個區塊產生的時間不是10分鐘,而是12秒。在以太坊體系裡,過時區塊的比例高於比特幣體系,為了減少過時的區塊對系統的影響,以太坊採用另一個叫「精靈」(GHOST)的協議來計算共識分支。同時,以太坊採用不同的工作量證明。目前採用的是一個混合的哈希方程,被設計成只能用記憶體計算。未來以太坊計劃轉為通過權益證明份額證明的體系。

以太坊呈現出和比特幣在設計理念上的巨大差異。以太坊項目由非營利機構主導並且在規劃和決策上相對比較集中,它們根據歷史經驗對以太坊協議進行修改,並且都有一個公開的時間表。按規劃,將來也會有硬分叉。而且,所有以太坊合約都要在版本更新前銷毀。所以,以太坊還是一個未來會有很多變更的實驗性體系。截至2015年,投入大量精力在以太坊上並構建真正有用的應用,現在看來是有點太早。但是以太坊無疑是一個非常有潛力的系統。也許這本書未來的版本將會命名為「以太坊和加密貨幣技術」。

本章主要討論了比特幣如何成為其他加密貨幣和另類幣的重要組成部分。它們在各方面相互競爭、合作並且相互影響,有些相輔相成,有些是相互阻礙。在未來,可能會出現新的技術,使得在一種區塊鏈可以直接引用另一個區塊鏈的交易。

但截至目前,有些問題仍然懸而未決。另類幣會演變並相互合併,最後成為少數另類幣主宰的生態系統嗎?還是和現在一樣多樣化?特定功能的另類幣會蓬勃發展,還是以以太坊為標誌的通用編程平台最後成為主流?比特幣和另類幣之間的相互影響是有益的嗎?每個另類幣之間應該相互獨立,比如用不同的挖礦謎題而不是去共同挖礦?我們現在無法回答上述這些問題,但我們已經討論了所有這些你需要理解和評估的重要概念。

延展閱讀

側鏈白皮書:

Back,Adam,Matt Corallo,Luke Dashjr,Mark Friedenbach,Gregory Maxwell,Andrew Miller, Andrew Poelstra,Jorge Timon,and Pieter Wuille.「Enabling Blockchain Innovations with Pegged Sidechains.」2014.

您可以通過如下網址閱讀:

https://blockstream.com/sidechains.pdf.

關於域名幣和使用其他用加密幣設計域名/價值儲存方法的論文:

Kalodner,Harry,Miles Carlsten,Paul Ellenbogen,Joseph Bonneau,and Arvind Narayanan.「An Empirical Study of Namecoin and Lessonsf or Decentralized Namespace Design. Presented at the Workshop on the Economics of Information Security,2015.

您可以通過如下網址閱讀:

http://randomwalker.info/publications/namespaces.pdf.

以太坊白皮書:

Various authors.「A Next-Generation Smart Contract and Decentralized Application Platform.」

您可以通過以下網址閱讀:

http://github.com/ethereum/wiki/wiki/white-paper.

分析以太坊激勵機制錯配的學術論文:

Luu,Loi,Jason Teutsch,Raghav Kulkarni,and Prateek Saxena.「Demystifying Incentives in the Consensus Computer.」 Proceeding of the 22nd ACM SIGSAC Conference on Computer and Communications Security ,New York:ACM, 2015.

《區塊鏈:技術驅動金融》