論文作者:
Ilya Sergey1and Aquinas Hobor2
1 University College London, United Kingdomi.sergey@ucl.ac.uk
2 Yale-NUS College and School of Computing, National University of Singaporehobor@comp.nus.edu.sg
翻譯:渡鴉
「讓國內外的區塊鏈技術沒有時差」。
寫在【新欄目】前面:
翻譯論文無比痛苦。
但是我們的目標是「讓國內外的區塊鏈技術沒有時差」。
帶給大家更多區塊鏈技術乾貨。
能力有限,還請各路大神多多監督、指教。
歡迎投稿、中英文均可。
摘要:
在本文中,我們探討了諸如Ethereum等智能合約的多事務行為與共用記憶體並發的經典問題之間的顯著相似之處。我們從Ethereum區塊鏈中檢查兩個真實世界的例子,並分析它們是如何容易受到與傳統並發程式中經常出現的錯誤的影響。然後,我們詳細闡述可觀察的合約行為與已深入研究的並發主題之間的關係,如原子性,幹擾,同步和資源所有權。描述的並發對象類似的類比可以更深入地瞭解智能合約的潛在威脅,指導實踐,並使用現有的最先進的形式驗證技術。
1、介紹:
智能合約是儲存在區塊鏈上,拜占庭容錯資料庫的程式。智能合約可以通過區塊鏈交易觸發,並在其塊上讀取和寫入資料[38] 。雖然智能合約以分布式方式運行和驗證,但是儘管存在許多複雜的互動模式,包括例如重入和遞迴調用,但它們的語義表明可以將它們視為順序程式。這種心理模型簡化了關於合約的正式和非正式推理,使得可以立即重用現有的通用架構來進行程式驗證[5,16,31,32] ,可用於驗證例如寫入的智能合約。固化[15] 只有微小的調整。
雖然區塊上的所有計算都是確定的[1],但由於交易本身之間的競爭(如為某個給定的區塊選擇了哪些交易),所以仍然會發生一些非確定性計算。我們將展示,非決定論可以被敵對方利用,並對合約行為進行推理,特別微妙,讓人想起傳統並行編程中涉及的已知挑戰。
在本文中,我們概述了智能合約的並發執行屬性。這種執行可以跨越多個區塊鏈交易(在同一個塊內或多個塊中),從而違反了僅使用合約的實施和本地狀態而不能指定的所需安全屬性,正是現有驗證方法所關注的[5, 32] 。為了便於共同編程的重用,我們提出以下類比:
在區塊鏈中使用智能合約的帳戶就像在共用記憶體中使用並發對象的線程。
在共用記憶體中使用並發對象的線程。通過並發對象,我們意味著用於在同時啟動並執行多個線程(進程)之間交換資料和管理互動的大量資料結構[20] 。並發對象的典型樣本是區塊,隊列和原子計數器 - 通常通過諸如java,util,concurrent的資料庫使用。同時,在運行時,這些並發對象被分配到正在啟動並執行線程可訪問的共用記憶體塊中。由線程同時訪問對象而產生的行為 - 即幹擾是難以預測的,因此極難理解。
其不利用適當同步的並發對象(例如,具有鎖定或障礙物)可以在幹擾下會出現資料競爭[2]行為,導致記憶體完整性的喪失。即使對於無競爭對象,在一個或多個用戶端的觀點下,觀察到的幹擾行為可能是錯誤的。例如,特定的線程可能不會“預見”具有共用對象的其他線程採取的動作,因此可能不會期望該對象以幹擾方式改變。
帳戶使用智能合約塊。智能合約類似於並發對象。他們儲存在塊狀物中而不是一個共用的記憶中; 不是由線程使用,而是由帳戶(使用者或其他合約)調用。像並發對象一樣,它們具有內部可變狀態,管理資源(例如資金),並且可以在塊內和多個塊中的多方訪問。與傳統並發對象不同,由於計算的事務模型,智能合約的方法而是原子的。也就是說,合約的單一調用(或一系列調用一系列相互調用的合約)是有序執行 - 沒有中斷 - 並且在成功更新區塊鏈之後終止或者中止並且回到之前的配置。
然而,“原子性自由”的概念是詐騙,因為在區塊鏈的水平上仍然可以觀察到並發行為:
- 在交易執行時,包含在交易中的交易的順序並不確定,因此,結果可以在很大程度上取決於其他交易的排序[27].
- 幾個編程任務需要將合約邏輯分散在幾個區塊鏈交易中(例如,當合約與塊之外的世界進行“通訊”時),從而實現真正的並發行為。
- 調用其他合約可以被認為是一種多任務合作。通過協同多任務,多個線程可以運行,但不要中斷,除非它們明確地“產生”。也就是說,從合約A到合約B的呼叫可以被認為是從合約A的角度來看的收益,合約B在返回時收益。智能合約的關鍵在於,合約B可以運行合約A的設計者無意識到的代碼,這使得情況比典型的順序裝置更接近並發設定。[3]特別地,合約B可以在調用期間修改合約A可能承擔的狀態。這是DAO錯誤[9],的精髓,合約B在返回[27]之前調用合約A來修改A的本地狀態。然而,重入並不是表現出來的唯一錯誤,因為:
- 不難想象某種合約被用作其他區塊(使用者和合約),管理對共用資源的訪問以及在某種意義上作為並發庫的情境。隨著多重交易變得越來越普遍,各種觀察到的幹涉模式,也應該考慮在內。
我們的目標和動機。幸運的是,在過去三十年中進行的並行和分布式編程的研究為大量的理論和應用程式框架提供了代碼,指定,理由和正式驗證並發對象及其實現的架構。因此,本文的目標是雙重的。首先,我們將簡要概述在智能合約中可能發生的一些已知的並發問題,在更傳統的並發抽象方面表徵問題。第二,我們的目標是建立一個直觀的“良好”和“不良”的合約行為,可以相應地識別和驗證/檢測,使用現有的正式方法推理並發。
2.並發行為樣本
在這裡,我們討論了已經部署在Ethereum塊上的兩個合約,每個合并都說明了並發類型行為的不同方面。BlockKing的合約,像今天的Ethereum集團許多其他人一樣,實現了一個簡單的賭博遊戲[2]。雖然BlockKing使用不廣泛,但我們研究它是因為它展示了Oraclize服務的潛在用途[4],這是一種允許合約與塊之外的世界進行通訊的服務,從而瞭解真正的並發性。由於Oraclize服務的早期採用者將其作為該服務的示範,並將其原始碼免費提供,希望使用Oraclize的許多其他合約可能會在其實現中反映出來。
我們討論的第二個例子是DAO中廣泛研究的錯誤[1]。 DAO與18,000多名投資者建立了一個業主管理的風險投資基金;它吸引了當時存在的14%的以太幣。隨後的攻擊使投資者花費了大約360萬Ether,當時價值約5000萬美元。 DAO採用了我們所說的“不協調的多任務”,因為當DAO向收件者發送錢時,那個接收者能夠運行代碼,通過DAO的合約狀態來幹擾DAO的合約狀態,假設DAO在調用期間不會改變。
2.1BlockKing合約
BlockKing的賭博如下。在任何時候,都有一個指定的“塊王”(最初是合約的編者)。當發送方將貨幣發送到合約時,在1到9之間產生隨機數j。如果當前塊號10等於j,則s成為新的塊王。之後,BlockKing在合約中收到了一定比例的資金(根據不同的參數,從50%到90%),合約的編者將收到餘額。
在確定性系統中,產生優質隨機數是很困難,特別是在所有資料被公開儲存的情況下,而且對於攻擊者來說,都有經濟激勵。因此,BlockKing利用信任方WolframAlpha的服務,使用Oraclize服務產生其隨機數。假設Oraclize是很好的行為,這個隨機數選擇的策略應該是攻擊者的預測。
BlockKing的代碼有365行,但是在圖1中給出的代碼十分有趣; 這裡的行號是由Etherscan[2]給出的合約的實際原始碼。當貨幣發送到合約時,輸入函數被調用。它設定一些合約變數(行 299-301)),然後發送查詢到Oraclize服務(行 303)。
圖1.BlockKing程式碼片段[2]
oraclize_query函數引發一個事件在“真實世界”中可見,然後返回到其調用者,然後退出(行 304)。在現實世界中,Oraclize伺服器監視事件記錄,服務要求(在這種情況下通過聯絡WolframAlpha Web服務),然後在指定的回調點(BlockKing中的行 306 )對原始合約進行新的調用。在事件和其回調之間,可能會發生許多事情,在這種意義上,塊調用可以在調用oraclize_query之前推出幾個塊,並在回調時恢複控制。在此期間,塊狀態,甚至BlockKing合約本身的狀態可能會發生巨大變化。換句話說,這是塊上的真正並發行為。
什麼可以出錯。假設多個賭徒希望在短時間內嘗試運氣(甚至在同一個區塊內)。合約沒有嘗試追蹤這種行為。因此,每個新的參賽者都將覆蓋第s 299-301行中的上一個資料(關鍵warrior塊和warrior變數)。當回調最終發生時,批次中的最後一名參賽者將享有多次機會,以贏得該批次中參加其他回調的參賽者的寶座。罪魁禍首是來自process_payment函數的第 339-347 行,稱為行309中的回呼函數的最後一行。
每次process_支付函數被稱為warrior區塊的最低有效數字被計算並儲存到變數singleDigitBlock中.[4]。每次調用process_支付函數時,他都有一個新機會匹配第339 行中的隨機數。如果數字匹配,則最後的參賽者在第345行加冠。
2.2DAO合約
DAO的原始碼有1,239行,比BlockKing更複雜[[23]由於這個bug已經寫得很多了(例如[9,27])),所以我們僅在圖2中給出了關鍵行。問題是第1012行的順序,(通過一系列進一步的函數調用)將Ether發送到msg.sender,在1014 行,將零餘數的msg.sender的帳戶為零。
圖.2. DAO代買片段[23]
在順序程式中,重新排序兩個獨立的操作對程式的最終行為沒有影響。然而,在並發程式中,順序無害的重新排序的效果可以具有顯著的效果,因為操作發生的順序可以影響線程如何幹擾。在DAO中,在第1012 行中發送Ether,在某些多任務的意義上,產生“位於msg.sender的任意(潛在的惡意)合約的控制。
不幸的是,DAO內部狀態仍然表明該賬戶由於其賬戶餘額在第1014 行尚未清零而得到資助。因此,惡意msg.sender可以通過回調DAO合約來啟動第二次撤回,該協議將在當控制再次到達1012行時,轉一次付款。事實上,惡意的msg.sender可以啟動第三,第四等撤回,所有這些將導致付款。只有在支付原始餘額的許多倍數之後,最終他的賬戶被清除。
以前對此bug的分析表明,問題是在於遞迴或意外的重入。在狹義上,這是真的,但在更廣泛的意義上,正在發生的是順序代碼在並發環境中啟動並執行意義。
3幹預和同步
3.1共用記憶體並發中的原子更新
圖3描述了錯誤使用的並發對象的樣本(以類似Java8的虛擬碼呈現),該執行個體應該使用get和set方法實現“原子”計數器。由於使用了同步原語,左邊的並發計數器的實現顯然是安全執行緒的(即無資料競爭)[17] 。然而,有問題的是,在右側的多線程用戶端代碼中如何使用Counter類的執行個體。