投資有風險 入市需謹慎
APP
下載火星財經客戶端

掃描下載APP

微信公眾號
火星財經二維碼 火星財經

solidity v0.5.0穩步推進,正式版將在不久后與開發者見面

降維安全實驗室 ·

10月17日

熱度: 35705

隨著solidity 0.5.0 nightly build版本的穩步推進,正式版也將在不久的將來與開發者見面。作為一個大版本更新,新版引入了很多特性,也廢棄了很多關鍵字。

作者: 隔壁老王(Old Wang NExt Door)

前言

隨著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)在虛擬機層面得到保證.

示例代碼

其中 // Error...注釋掉的代碼在solidity版本<0.5.0均可以編譯通過,但是在>=0.5.0均得換成 // Right ...的代碼才能編譯通過.

  1. pragma solidity >0.4.99 <0.6;
  2. contract A {
  3. function () external payable {}
  4. }
  5. contract B {
  6. function () external {}
  7. }
  8. interface ERC20 {
  9. // function transferFrom(address _from, address _to, uint256 _value) public; // Error, `interface` must declare with `external`
  10. function transfer(address _to, uint256 _value) external; // Right
  11. }
  12. contract F
  13. {
  14. address payable to_pay;
  15. address to;
  16. address owner;
  17. // uint constant time = now; // Error, value should be compile time constant.
  18. string constant time = "johnwick.io"; // Right
  19. modifier onlyOwner {
  20. require (msg.sender == owner);
  21. _;
  22. }
  23. // function () { // Error, fallback function MUST be `external`
  24. function () external { // Right
  25. }
  26. // function F() { // Error, consturctor function should use `constructor` keyword
  27. // constructor() { // Error, `constructor` should be `public`
  28. constructor () public { // Right
  29. }
  30. function payable_or_not() public {
  31. bool result;
  32. /* `address payable` VS `address` */
  33. // to_pay = new A(); // Error, `contract` should explicitly convert to `address`
  34. to_pay = address(new A()); // Right, fallback function of contract A is `payable`
  35. to = address(new A()); // Right
  36. to_pay.transfer(1); // Right, `transfer()` is member of `address payable`
  37. result = to_pay.send(1); // Right, `send()` is member of `address payable`
  38. to = address(new B()); // Right
  39. // to_pay = address(new B()); // Error, fallback function of contract B is not `payable`.
  40. // to.transfer(1 ether); // Error, `transfer()` is not member of `address`
  41. // result = to.send(1 ether); // Error, `send()` is not member of `address`
  42. bool success;
  43. bytes memory data;
  44. (success, data)= to.call.gas(0).value(1 ether)(""); // However, you can use `call("")` to send ether
  45. address john;
  46. john = to_pay; // Right, `address payable` can directly convert to `address`
  47. address payable wick;
  48. // wick = to; // Error, `address` can't directly convert to `address payable`
  49. wick = address(uint160(to)); // Right, `address` can forcibly convert to `address payable`
  50. wick.transfer(1 ether); // `wick` is `address payable` now
  51. }
  52. // struct dummy {} // Error, empty struct is not allowed.
  53. struct yummy {string food;} // Right
  54. // `external` function input parameter should explicitly declare `calldata` location.
  55. function transfer(address _to, uint _value, bytes calldata _data) pure external returns (bool success){
  56. // return; // Error, empty return statements for functions with one or more return values are now disallowed.
  57. }
  58. // `public` function input parameter should explicitly declare `memory` location.
  59. function try_some(bytes memory _data) public view returns(bool) {
  60. if(to == to_pay)
  61. {
  62. // throw; // Error, `throw` is deprecated.
  63. revert(); // Right, you should use `assert(),require(),revert()` to raise an exception.
  64. }
  65. bytes32 _hash;
  66. // _hash = sha3(_data); // Error, `sha3()` is deprecated.
  67. _hash = keccak256(_data); // Right
  68. uint256 _time;
  69. // _time = 1 years; // Error, `years` is deprecated, due to the leap year problem.
  70. _time = 366 days; // Right
  71. // var secs = _time * 3600; // Error, `var` is deprecated.
  72. uint secs; secs = _time * 0x3600; // Right
  73. int256 _value;
  74. // _value = 0xff wei; // Error, hex number with unit is not allowed now.
  75. _value = 0xff*1 wei; // Right
  76. // _value = 0Xff*1 wei; // Error, hex number prefix `0X` is not allowed now.
  77. // _value = 1. ether; // Error, dot followed without numbers is not allowed now.
  78. _value = 1.0 ether; // Right
  79. // _value = +1; // Error
  80. _value = -1;
  81. // bytes storage not_initial_bytes; // Error, `storage` without initialization is not allowed now.
  82. // uint[0] zero_array; // Error, fixed-size array of zero length is not allowed now.
  83. bytes32 b32;
  84. bytes20 b20;
  85. bytes1 b1;
  86. uint256 u256;
  87. uint160 u160;
  88. // b32 = bytes32(u160); // Error, can't directly convert `uintX` to `bytesY` of different size.
  89. b32 = bytes32(uint256(u160)); // Right, convert to the same size, then convert to the same type.
  90. u160 = uint160(bytes20(b32)); // Right
  91. b32 = bytes32(u256); // Right, both are the same size, then convert to the same type.
  92. // b1 = 255; // Error
  93. b1 = bytes1(uint8(255+360)); // Right, decimal number like `uintX` should convert to the same size, then the same type.
  94. // b16 = 0xff; // Error, hex number and `bytesX` are different size.
  95. b20 = bytes20(uint160(0xff)); // Right, same size, then same type.
  96. b1 = 0xff; // Right, hex number and `bytesX` are the same size
  97. }
  98. function im_not_payable() public returns (uint256 _value){
  99. // _value = msg.value; // Error, `msg.value` MUST be used in `payable` function.
  100. }
  101. function im_payable() public payable returns (uint256 _value){
  102. _value = msg.value; // Right, `msg.value` CAN be used in `payable` function.
  103. }
  104. // function not_impletement() public onlyOwner; // Error, function without implementation can't use `modifier`
  105. function kill(address payable _to) public {
  106. // suicide(_to); // Error, `suicide` is deprecated.
  107. selfdestruct(_to); // Right
  108. }
  109. function scope_() view public {
  110. uint count = 0 ;
  111. for (uint i=1 ; i<100; i++){
  112. count += i;
  113. }
  114. // i = now; // Error, not C99-Style scope
  115. uint i; i = now; // Right, but this is error in solidity < 0.5.0 due to variable redefinition.
  116. }
  117. event ShowCount(uint);
  118. function inf_loop() public {
  119. uint count = 0;
  120. do {
  121. if (count!=0) {
  122. continue;
  123. }
  124. count = 1; // <0.5.0, `continue` jump to this loop body, which results in infinite loop !!!
  125. }while((count++)<10); // >=0.5.0 `continue` jump to this condition check.
  126. emit ShowCount(count);
  127. }
  128. }


文章聲明:本文為火星財經專欄作者作品,不代表火星財經觀點,版權歸作者所有,如需轉載,請提前聯系作者或注明出處。

關鍵字: solidity 合約

推廣
相關新聞

漲幅榜

你可能感興趣的內容
下一篇

正本清源,區塊鏈技術應用走向坦途

新疆18选7开奖结果