1. 程式人生 > >Solidity函式修改器的基本概念及應用

Solidity函式修改器的基本概念及應用

基本概念

函式修改器可以一定程度上改變函式的行為。可以作為函式執行的先行條件,如果符合函式修改器定義的條件,才可以執行函式體內容。關於函式修改器,可以把理解成if的變相。

函式修改器定義

函式修改器的定義語法如下:

    modifier 修改器名 {
        條件體..
        _;
    }

    function a() 修改器名 {
        函式體..
    }

當要執行a()時,會先去執行修改器,判斷條件體,如果符合條件,才會繼續執行a();如果不符合條件,a()將不執行。”_;”在這裡表示的是a().
通過一個小栗子,直觀的感受下:

pragma solidity ^0.4.0;

/**
 * 許可權控制
 */
contract Ownable {
  address public owner = msg.sender;

  // @notice 檢查必須是合約的所有者
  modifier onlyOwner {
    if (msg.sender != owner) throw;
    _;
  }

  // @notice 改變合約的擁有者身份
  // @param _newOwner 新所有者的地址
  function changeOwner(address _newOwner) onlyOwner {
    if
(_newOwner == 0x0) throw; owner = _newOwner; } }

在上述的例子中,我們實現了只有合約所有者才能修改合約歸屬的許可權的功能。

函式修改器引數

函式修改器可以接收上下文中存在的任意變數組成的表示式,直接在函式修改器中傳入引數。

    modifier 修改器名(uint 引數1, string 引數2) {
        if(引數1>10 && 引數2 != "男")
        //條件體..
        _;
    }

函式修改器執行規則

當函式的修改器條件判斷不成功,如果函式沒有返回值,對應的函式將不執行;如果函式有返回值,那將返回對應型別的預設值。
而函式修改器中的條件體,不論函式是否符合條件,都會繼續執行完畢修改器中的後續邏輯。
參考如下例項:

pragma solidity ^0.4.0;

contract Test{
  mapping(bool => uint) public mapp;

  modifier A(mapping(bool => uint) mapp) {
    if(mapp[true] == 0) {
      mapp[true]= 1;
      _;
      mapp[true]= 3;//這句將會最後執行,可以在偵錯程式中檢視mapp的值為3。
    }
  }

  function f1() A(mapp) returns(uint) {
    mapp[true] = 2;
    return mapp[true];//函式結束時,mapp值為2.
  }

  modifier B(mapping(bool => uint) mapp) {
    if(mapp[true] == 1) { //條件體判斷不成功,導致函式f2講不會執行。
      mapp[true]= 1;
      _;
      mapp[true]= 3;
    }
    mapp[true]=7;//秉著一站到底的原則,這句將會被最後執行,mapp值為7。
  }

  function f2() B(mapp) returns(uint) {
      mapp[true] = 2;
    return mapp[true];//有返回值的函式,將返回對應型別的預設值,即為0.
  }
}

這裡寫圖片描述

多函式修改器

當一個函式擁有多個函式修改器時,執行順序是按照先後順序依次執行。如果有一個不滿足,函式即不能執行。

pragma solidity ^0.4.0;

contract Test{


  modifier A(uint a) {
    if(a<10) throw;
    _;
  }

  modifier B(uint b) {
    if(b<10) throw;
    _;
  }

  //必須同時滿足A、B,才能執行f()
  function f(uint a, uint b) A(a) B(b) returns(uint) {
      return 777;
  }
}

這裡寫圖片描述

函式修改器的繼承與重寫

子類可以使用父類中的函式修改器,也可以重寫父類的函式修改器。

pragma solidity ^0.4.0;

contract Father{
  modifier A(uint a) {
    if(a > 100) throw;
    _;
  }
}

contract Son is Father{
  //重寫父類中的函式修改器
  modifier A(uint a) {
    if(a > 50) throw;
    _;
  }

  function f(uint a) A(a) returns(uint) {
    return 777;
  }
}

應用

結合現實場景中的應用。在一些敏感操作中,我們需要設定特定的許可權才允許執行相關操作;再者我們可以利用函式修改器進行資料的校驗;還可以利用簡單的函式修改器來進行重入鎖的機制。

許可權控制

pragma solidity ^0.4.0;

contract Ownable {
  address public owner = msg.sender;

  /// @notice 檢查必須是合約的所有者
  modifier onlyOwner {
    if (msg.sender != owner) throw;
    _;
  }

  /// @notice 改變合約的擁有者身份
  /// @param _newOwner 新所有者的地址
  function changeOwner(address _newOwner)
  onlyOwner
  {
    if(_newOwner == 0x0) throw;
    owner = _newOwner;
  }
}

資料校驗

contract DataVerifiable {

  /// @notice throws if ether was sent accidentally
  modifier refundEtherSentByAccident() {
    if(msg.value > 0) throw;
    _;
  }

  /// @notice throw if an address is invalid
  /// @param _target the address to check
  modifier throwIfAddressIsInvalid(address _target) {
    if(_target == 0x0) throw;
    _;
  }

  /// @notice throw if the id is invalid
  /// @param _id the ID to validate
  modifier throwIfIsEmptyString(string _id) {
    if(bytes(_id).length == 0) throw;
    _;
  }

  /// @notice throw if the uint is equal to zero
  /// @param _id the ID to validate
  modifier throwIfEqualToZero(uint _id) {
    if(_id == 0) throw;
    _;
  }

  /// @notice throw if the id is invalid
  /// @param _id the ID to validate
  modifier throwIfIsEmptyBytes32(bytes32 _id) {
    if(_id == "") throw;
    _;
  }
}

重入鎖實現

pragma solidity ^0.4.0;

contract Mutex {
    bool locked;
    modifier noReentrancy() {
        require(!locked);
        locked = true;
        _;
        locked = false;
    }

    // 這個函式使用了noReentrancy修改器,這保證了在f函式內部無法再次使用呼叫f函式
    // 在執行return 7時也執行了函式修改器中的locked = false
    function f() noReentrancy returns (uint) {
        require(msg.sender.call());
        return 7;
    }
}