Solidity函式修改器的基本概念及應用
阿新 • • 發佈:2019-02-14
基本概念
函式修改器可以一定程度上改變函式的行為。可以作為函式執行的先行條件,如果符合函式修改器定義的條件,才可以執行函式體內容。關於函式修改器,可以把理解成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;
}
}