solidity v0.5.0的重大改變

前言

隨着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:布洛克科技 【閱讀原文】。感謝您的支持!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章