在Solidity中,函数修饰符规定了函数的行为、调用规则。在Solidity语言中预置的修饰符有如下一些:
1、函数和状态变量可见性修饰符
public:在外部和内部均可见(创建存储/状态变量的访问者函数)
private:仅在当前合约中可见
external: 只有外部可见(仅对函数)- 仅仅在消息调用中(通过this.fun)
internal: 只有内部可见
2、状态变量储存位置修饰符
storage:变量储存在区块链中,状态变量默认是storage类型;
memory:变量储临时存在内存中,局部变量默认是memory类型;
3、接受Ether修饰符
payable:允许函数在调用同时接收Ether
4、函数读取状态变量修饰符
pure:不允许修改或访问状态变量-这还没有强制执行
view:不允许修改状态变量-这还没有强制执行
constant(for function):等同于view
constant(for state variables):除了初始化之外,不允许赋值操作,类似JavaScript中的常量
函数修改器(Function Modifiers)
修改器(Modifiers)可以用来轻易的改变一个函数的行为。比如用于在函数执行前检查某种前置条件。
修改器是一种合约属性,可被继承,同时还可被派生的合约重写(override)。
OpenZeppelin库的Ownable 合约
下面是一个 Ownable 合约的例子: 来自 OpenZeppelin Solidity 库的 Ownable 合约。 OpenZeppelin 是主打安保和社区审查的智能合约库,你可以在自己的 DApps中引用。
所以Ownable 合约基本都会这么干:
看一下源码:
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address public owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
function Ownable() public {
owner = msg.sender;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0));
OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
}
我们来看一下函数修改器onlyOwner是怎么定义和使用的?
首先:要定义一个modifier,形式与函数很像,可以有参数,但是不需要返回值;
其次:特殊 _; 是必要的,它表示使用修改符的函数体的替换位置;
第三:使用将modifier置于参数后,返回值前即可。
其他合约如何使用onlyOwner?
继承!函数修改器是可以继承的,只要新建的合约继承Ownable合约,即可使用这个修改器。
########################################
#### 其他合约如何使用onlyOwner? ####
########################################
contract MyContract is Ownable {
event LaughManiacally(string laughter);
//注意! `onlyOwner`上场 :
function likeABoss() external onlyOwner {
LaughManiacally("Muahahahaha");
}
}
带参数的函数修改器
之前我们已经读过一个简单的函数修饰符了:onlyOwner。函数修饰符也可以带参数。例如:
// 存储用户年龄的映射
mapping (uint => uint) public age;
// 限定用户年龄的修饰符
modifier olderThan(uint _age, uint _userId) {
require(age[_userId] >= _age);
_;
}
// 必须年满16周岁才允许开车 (至少在美国是这样的).
// 我们可以用如下参数调用`olderThan` 修饰符:
function driveCar(uint _userId) public olderThan(16, _userId) {
// 其余的程序逻辑
}
使用修改器实现的一个防重复进入的例子。
pragma solidity ^0.4.0;
contract Mutex {
bool locked;
modifier noReentrancy() {
if (locked) throw;
locked = true;
_;
locked = false;
}
/// This function is protected by a mutex, which means that
/// reentrant calls from within msg.sender.call cannot call f again.
/// The `return 7` statement assigns 7 to the return value but still
/// executes the statement `locked = false` in the modifier.
function f() noReentrancy returns (uint) {
if (!msg.sender.call()) throw;
return 7;
}
}
例子中,由于call()方法有可能会调回当前方法,修改器实现了防重入的检查。