欠優化的智能合約吞噬你的財產

欠優化的智能合約吞噬你的財產

摘要

智能合約是在區塊鏈上運行的完整程序(例如以太坊,最受歡迎的區塊鏈之一)。 在以太坊中,Gas(以太幣,比特幣等加密貨幣)是執行費用,用於補償礦工運行智能合約的計算資源。我們發現,未充分優化的智能合約所需的Gas費用超過了必要的數量,因此合約創建者或交易發起用戶將被多收費用。在這項工作中,我們對Solidity(推薦的編譯器)進行了第一次調查,並揭示它無法優化的消耗Gas的編程模式。 特別的是,我們確定了7種消耗Gas的模式,並將它們分為2類。 然後,我們提出並開發GASPER,這是一種通過分析智能合約的字節碼自動定位消耗Gas模式的新工具。 從4,240個真實智能合約中發現3個代表性模式,初步結果顯示,93.5%,90.1%和80%的合同分別受這3種模式的影響。

背景

由於智能合約在礦工的機器上運行,因此Gas被用於從礦工那裡購買計算資源。 Gas可視為具有同等價值的貨幣。 例如,2016年11月11日的平均Gas價格為0.000000024334480804 Ether, 這大約相當於2.5x10^-7美元。

部署和執行智能合約需要支付費用,例如,總計堆棧的前兩項的加法運算需要3個單位的Gas,大約7.5x10^-7美元。 有人可能會說,增加的成本是如此之低,我們不需要優化它。 然而,值得注意的是,真正的智能合約包含大量操作,而某些操作比添加操作消耗更多的Gas,如下表所示,此外,智能合約通常提供公共方法,可由各種客戶和合同無限次地調用。 因此,由於規模效應,優化的智能合約可以比未優化的合約節省明顯的Gas(即貨幣)。

欠優化的智能合約吞噬你的財產

表:不同操作的Gas成本,完整列表可以在以太坊黃皮書中找到。

Gas成本模式

我們確定了7種消耗Gas的模式,可以分為兩類:無用的代碼相關模式和循環相關模式。 由於部署期間字節碼的大小增加以及運行時的可移動字節碼,前者引入了額外的成本。 後者涉及在循環中使用昂貴的操作。 我們使用最新已啟用最優化的Solidity(V 0.4.4)驗證了所有這些模式。 更準確地說,我們將源代碼中消耗Gas的模式提供給Solidity,然後檢查生成的字節碼中是否將消耗Gas的模式轉換為高效模式。 結果表明,這些模式都沒有通過Solidity進行優化。 為了便於說明,我們在源代碼而不是字節碼中呈現模式。

第一類:無用的代碼相關模式

欠優化的智能合約吞噬你的財產

模式1:死代碼,模式2:不透明的條件判斷

1) 死代碼。 上圖(模式1)給出了死代碼的示例,其中不執行第4行,因為在所有情況下第3行的條件判斷“x * x <20”被評估為假。 Solidity不會從生成的字節碼中刪除第3行和第4行,因此會浪費Gas。

2) 不透明的條件判斷。 不知道條件判斷的結果在沒有執行的情況下是真或假。 例如,條件判斷“x> 1” 圖(模式2)是一個不透明的條件判斷。 由於第3行的條件判斷在所有情況下都被評估為真,因此應將其刪除以節省Gas。

第二類:無用的循環相關模式

欠優化的智能合約吞噬你的財產

模式3:循環中的昂貴操作,模式4:循環的常量結果

1) 循環中的昂貴操作。 循環中昂貴的操作值得關注,因為它們可能在一次調用中執行多次。 將昂貴的操作移出循環可以節省Gas。 例如,在上圖(模式3),因為變量sum存儲在存儲器中,第4行涉及一個SLOAD,用於將總和加載到堆棧,SSTORE用於將ADD的結果保存到存儲中。 請注意,與存儲相關的操作非常昂貴。

高級編譯器應該將sum分配給駐留在堆棧中的局部變量(例如,tmp),然後將i添加到循環內的tmp,最後在循環之後將tmp賦值為sum。 這種優化將存儲相關操作從2x減少到2,即一個SLOAD和一個SSTORE。

2) 循環的常數結果。 在某些情況下,循環的結果可以是可以在編譯中推斷的常量。 如上圖(模式4),p4中的存儲變量和等於循環後的5050。 因此,p4的主體應簡化為“return 5050;”。

欠優化的智能合約吞噬你的財產

模式5:循環融合,模式6:循環中的重複計算

3) 循環融合。 如果可能,可以將幾個循環組合成一個,從而減小字節碼的大小。 特別是,它可以減少循環入口點的操作量,例如條件跳轉和比較等。 如上圖(模式5)可以將兩個循環組合成一個循環,其中m和v都得到更新。

4) 循環中的重複計算。 在某些情況下,可能存在在循環的每次迭代中產生相同結果的表達式。 因此,可以通過計算結果一次然後重新使用該值而不是在後續迭代中重新計算它來節省Gas,尤其是對於涉及昂貴操作數的表達式。 例如,在上圖(模式6),由於重複計算,Gas消耗非常高。 更確切地說,兩個存儲字的總和(即,第6行的“x + y”)非常昂貴,因為x和y應該在加法之前加載到堆棧中(即,SLOAD)。 為了節省Gas,這個求和應該在循環之前完成,然後結果在循環內重複使用。

欠優化的智能合約吞噬你的財產

模式7:與循環中的單側結果比較

5) 與循環中單側結果的比較。 這意味著在循環的每次迭代中執行相同的比較,但即使在編譯中無法確定(即,不是不透明的條件判斷),比較的結果也是相同的。 例如,在上圖中第3行的比較應該移動到循環之前的位置。

總結:如果可以減少智能合約的大小(例如,消除死代碼,刪除不必要的比較),以及合同用戶的成本(如果可以減少智能合約的計算),充分優化可以降低合同創建者的成本(例如,將昂貴的操作移出循環)。

GASPER

我們提出並開發GASPER,以便從智能合約的字節碼中自動發現消耗 Gas量大的編程模式。 GASPER直接處理字節碼而無需源代碼,因為只有少數智能合約才能打開源代碼。 作為早期研究成果,當前版本的GASPER可以找到第一類中的所有模式和第二類中的一個代表性模式(即,循環中的昂貴操作)。其他模式的檢測正在開發中。

GASPER對字節碼運行符號執行以覆蓋所有可到達的代碼塊(塊是一個直線代碼序列,除了條目外沒有分支,除了在出口處沒有分支)。 對於一個智能合約,GASPER首先使用以太坊提供的反彙編器來解析其字節碼。 然後,GASPER構建控制流圖(CFG)。 值得注意的是,如果發現新的控制流轉移,則在符號執行期間將逐漸改進CFG。 符號執行從CFG的根節點開始,並遍歷CFG。 如果GASPER遇到條件跳轉,它會通過查詢Z3求解器來檢查哪些分支(即,true或false)是可行的。如果兩者都可行,GASPER會在深度優先搜索後選擇一個分支。

實驗評估

我們已經基於OYENTE實施了GASPER, 並使用部署在以太坊上的所有智能合約對其進行評估。 更準確地說,我們掃描區塊鏈中的所有地址,因為每個部署的合同必須與唯一的地址相關聯。 截至2016年11月5日,我們發現了566,907個地址,其中539,617個地址不包含字節碼。 因此,我們總共下載了27,290份合約的字節碼。 而且,我們發現許多合約完全相同(即它們的字節碼相同)。 在排除相同合約後,剩下4,669份合約。 在實驗期間,無法檢查429(少於10%)合同,因為OYENTE由於其內部錯誤而崩潰或OYENTE耗盡時間。 最終,成功檢查了4,240份合同。

欠優化的智能合約吞噬你的財產

具有3種消耗Gas模式的智能合約數量如上圖所示。超過70%的合同包含所有這些模式,表明它們的字節碼沒有針對減少Gas消耗進行適當優化。 此外,超過90%的合同都有死代碼或不透明的條件判斷。其他的模式也有廣泛的分佈,證明了GASPER的可用性。

本文主要貢獻

  1. 據我們所知,這是第一次調查顯示,由推薦的編譯器生成的許多智能合約包含消耗Gas量高的字節碼,可以用節省Gas的字節碼來替換以節省資金。
  2. 我們提出並開發了GASPER,這是一種基於符號化執行的新工具,用於自動發現字節碼中消耗Gas量大的模式。 目前的版本涵蓋2個類別中的3個代表性模式,並且正在擴展以支持更多模式。
  3. 我們將GASPER應用於所有部署的智能合約,直到2016年11月5日,並發現93.5%,90.1%和80%的智能合約分別受這3種模式的影響。

致謝

本文由南京大學軟件學院2019級碩士李紫欣翻譯轉述


分享到:


相關文章: