前言
隨着solidity 0.5.0 nightly build版本的穩步推進,正式版也將在不久的將來與開發者見面.作爲一個大版本更新,新版引入了很多特性,也廢棄了很多關鍵字,比如
-
.call()不僅可以獲知遠程調用執行成功與否,還將獲得遠程調用執行的返回值
-
ABI解碼做了新的處理規範,有效防禦了"短地址攻擊"
-
address地址類型細分成 address和 address payable
-
uintY和 bytesX不能直接轉換
-
回退函數必須顯式聲明爲 external可見性
-
構造函數必須用 constructor關鍵字定義
-
用於拋出異常的 throw關鍵字棄用, 函數狀態可變性修飾符必須用 view,不能混用 constant和 view
-
...
下面我們將對這些改變一一予以介紹,最後給出一個示例代碼,對比展示新舊版solidity代碼寫法的區別,供大家參考.
顯式聲明
函數可見性
-
函數可見性必須顯式聲明. 之前, 函數如果不顯式聲明,將默認 public可見性.
-
public: constructor構造函數必須聲明爲 public可見性,否則編譯報錯.
-
external: 回退函數(fallback function), 接口(interface)的函數必須聲明爲 external可見性,否則編譯報錯.
存儲位置
-
結構體(struct),數組(array),映射(mapping)類型的變量必須顯式聲明存儲位置( storage, memeory, calldata),包括函數參數和返回值變量都必須顯式聲明.
-
external 的函數參數需顯式聲明爲 calldata.
合約與地址
-
contract合約類型不再包括 address類型的成員函數,必須顯式轉換成 address地址類型才能使用 send(), transfer(), balance等與之相關的成員函數/變量成員.
-
address地址類型細分爲 address和 address payable,只有 address payable可以使用 transfer(), send()函數.
-
address payable類型可以直接轉換爲 address類型, 反之不能.
-
但是 address x可以通過 address(uint160(x)),強制轉換成 address payable類型.
-
如果 contract A不具有 payable的回退函數, 那麼 address(A)是 address類型.
-
如果 contract A具有 payable的回退函數, 那麼 address(A)是 address payable類型.
-
msg.sender屬於 address payable類型.
轉換與填充(PADDING)
UINTY與 BYTESX
-
因爲填充(padding)的時候, bytesX是右填充(低比特位補0),而 uintY是左填充(高比特位補0),二者直接轉換可能會導致不符合預期的結果,所以現在當 bytesX和 uintY長度大小不一致(即X*8 != Y)時,不能直接轉換,必須先轉換到相同長度,再轉換到相同類型.
-
10進制數值不能直接轉換成 bytesX類型, 必須先轉換到與 bytesX相同長度的 uintY,再轉換到 bytesX類型
-
16進制數值如果長度與 bytesX不相等,也不能直接轉換成 byteX類型
ABI
-
字面值必須顯式轉換成類型才能使用 abi.encodePacked()
-
ABI編碼器在構造外部函數入參和 abi.encode()會恰當地處理 bytes和 string類型的填充(padding),若不需要進行填充,請使用 abi.encodePacked()
-
ABI解碼器在解析函數入參和 abi.decode()時,如果發現 calldata太短或超長,將直接拋出異常,而不是像之前自動填充(補0)和截斷處理,從而有效地遏制了短地址攻擊.
-
.call()族函數( .call(), .delegatecall(), .staticcall())和 哈希函數( keccak256(),sha256(), ripemd160())只接受一個參數 bytes,且不進行填充(padding)處理.
-
.call()空參數必須寫成 .call("")
-
.call(sig,a,b,c)必須寫成 .call(abi.encodeWithSignature(sig,a,b,c)),其他類推
-
keccak256(a,b,c)必須寫成 keccak256(abi.encodePacked(a,b,c)),其他類推
-
另外, .call()族函數之前只返回函數執行成功是否的 bool, 現在還返回函數執行的返回值,即 (bool,bytes memory). 所以之前 boolresult=.call(sig,a,b,c)現在必須寫成 (boolresult,bytes memory data)=.call(sig,a,b,c).
不允許的寫法
在之前版本的solidity編譯,以下不允許的寫法只會導致 warnings報警,現在將直接 errors報錯.
-
不允許聲明0長度的定長數組類型.
-
不允許聲明0結構體成員的結構體類型.
-
不允許聲明未初始化的 storage變量.
-
不允許定義具有命名返回值的函數類型.
-
不允許定義非編譯期常量的 constant常量. 如 uintconstant time=now;是不允許的.
-
不允許 0X(X大寫)做16進制前綴,只能用 0x.
-
不允許16進制數和單位名稱組合使用. 如 value=0xffether必須寫成 value=0xff*1ether.
-
不允許小數點後不跟數字的數值寫法. 如 value=255.0ether不能寫成 value=255.ether.
-
不允許使用一元運算符 +. 例如 value=1ether不能寫成 value=+1ether.
-
不允許布爾表達式使用算術運算.
-
不允許具有一個或多個返回值的函數使用空返回語句.
-
不允許未實現的函數使用修飾符(modifier).
-
不允許 msg.value用在非 payable函數裏以及此函數的修飾符(modifier)裏.
廢棄的關鍵字/函數
-
years時間單位已棄用,因爲閏年計算容易導致各種問題.
-
var已棄用,請用 uintY精確聲明變量長度.
-
constant函數修飾符已棄用,不能用作修飾函數狀態可變性, 請使用 view關鍵字.
-
throw關鍵字已棄用,請使用 revert(), require(), assert()拋出異常.
-
.callcode()已棄用,請使用 .delegatecall(). 但是注意,在內聯彙編仍可使用.
-
suicide()已棄用, 請使用 selfdestruct().
-
sha3()已棄用,請使用 keccak256().
構造函數
-
構造函數必須用 constructor關鍵字定義. 之前,並未強制要求,既可以用合約同名函數定義構造函數,也可以用 constructor關鍵字定義.
-
不允許調用沒有括號的基類構造函數.
-
不允許在同一繼承層次結構中多次指定基類構造函數參數.
-
不允許調用帶參數但具有錯誤參數計數的構造函數.如果只想在不提供參數的情況下指定繼承關係,請不要提供括號.
其他
-
do...while循環裏的 continue不再跳轉到循環體內,而是跳轉到 while處判斷循環條件,若條件爲假,就退出循環.這一修改更符合一般編程語言的設計風格.
-
實現了C99風格的作用域:
-
變量必須先聲明,後使用.之前,是可以先使用,後聲明,現在會導致編譯報錯.
-
只能在相同或嵌套作用域使用.比如 if(){...}, do{...}while();, for{...}塊內聲明的變量,不能用在塊外.
-
變量和結構體的循環依賴遞歸限制在256層.
-
pure和 view函數在EVM內部採用 staticcall操作碼實現(EVM版本>=拜占庭),而非之前的 call操作碼,這使得狀態不可更改(state changes disallowed)在虛擬機層面得到保證.
來源:降維區塊鏈安全資訊
本文由布洛克專欄作者發佈,代表作者觀點,版權歸作者所有,不代表布洛克科技觀點
——THEEND——
關注“布洛克科技”
布洛克科技
責編內容by:布洛克科技 【閱讀原文】。感謝您的支持!