不想说话,自闭中..

Solidity函数修改器的基本概念及应用

基本概念

函数修改器可以一定程度上改变函数的行为。可以作为函数执行的先行条件,如果符合函数修改器定义的条件,才可以执行函数体内容。关于函数修改器,可以把理解成if的变相。

函数修改器定义

函数修改器的定义语法如下:

1
2
3
4
5
6
7
8
modifier 修改器名 {
条件体..
_;
}

function a() 修改器名 {
函数体..
}

当要执行a()时,会先去执行修改器,判断条件体,如果符合条件,才会继续执行a();如果不符合条件,a()将不执行。”_;”在这里表示的是a().
通过一个小栗子,直观的感受下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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;
}
}
```
在上述的例子中,我们实现了只有合约所有者才能修改合约归属的权限的功能。

#### 函数修改器参数
函数修改器可以接收上下文中存在的任意变量组成的表达式,直接在函数修改器中传入参数。
```js
modifier 修改器名(uint 参数1, string 参数2) {
if(参数1>10 && 参数2 != "男")
//条件体..
_;
}

函数修改器执行规则

当函数的修改器条件判断不成功,如果函数没有返回值,对应的函数将不执行;如果函数有返回值,那将返回对应类型的默认值。
而函数修改器中的条件体,不论函数是否符合条件,都会继续执行完毕修改器中的后续逻辑。
参考如下实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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.
}
}

这里写图片描述

多函数修改器

当一个函数拥有多个函数修改器时,执行顺序是按照先后顺序依次执行。如果有一个不满足,函数即不能执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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;
}
}

这里写图片描述

函数修改器的继承与重写

子类可以使用父类中的函数修改器,也可以重写父类的函数修改器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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;
}
}

应用

结合现实场景中的应用。在一些敏感操作中,我们需要设定特定的权限才允许执行相关操作;再者我们可以利用函数修改器进行数据的校验;还可以利用简单的函数修改器来进行重入锁的机制。

权限控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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;
}
}

数据校验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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;
_;
}
}

重入锁实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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;
}
}