在上一步中你有没有发现一个潜在的安全漏洞?
注意到了吗?setKittyContractAddress 可见性居然申明为“外部的”(external),岂不是任何人都可以调用它! 也就是说,任何调用该函数的人都可以更改 CryptoKitties 合约的地址来搞破坏,比如说改到一个无效的地址。
我们确实是希望这个地址能够在合约中修改,但不希望让每个人去改它!
要处理这样的情况,通常的做法是指定合约的“所有权” - 就是说,给它指定一个合约所有者,只有所有者对它享有一些特定的权利。
Ownable 合约
下面是一个 Ownable 合约的例子: 来自 OpenZeppelin Solidity 库的 Ownable 合约。 OpenZeppelin 是主打安全审计的智能合约库,你可以在自己的代码中引用相关的代码。
阅读下这个合约的代码(如果有不懂的先别担心,我们会在之后解释)
/**
* @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;
}
}下面有没有你没学过的东西?
构造函数:
function Ownable()是一个 constructor (构造函数),构造函数不是必须的,它与合约同名。构造函数只会在合约最初被创建的时候被执行一次。函数修饰符:
modifier onlyOwner()。 函数修饰符跟函数很类似,不过是用来修饰其他已有函数用的, 在其他语句执行前,为它检查下先验条件。 在这个例子中,我们就可以写个修饰符onlyOwner检查下调用者,确保只有合约的所有者才能运行本函数。我们下一节中会详细讲述函数修饰符,以及那个奇怪的_;。
所以Ownable 合约基本都会这么干:
合约创建,构造函数先行,将其
owner设置为msg.sender(合约部署者)为它加上一个修饰符
onlyOwner,它会限制陌生人的访问,将访问某些函数的权限锁定在owner上。允许将合约所有权转让给他人。
onlyOwner 非常有用,大多数人开发自己的智能合约应用,都是从复制/粘贴 Ownable 开始的,从它再继承出的子类,并在之上进行功能开发。
既然我们想把 setKittyContractAddress 限制为 onlyOwner ,我们也要做同样的事情。
实战演习
首先,我们已经将 Ownable 合约的代码复制一份到新文件 ownable.sol 中。 接下来,我们需要创建一个 PetIncubator,并且继承 Ownable,操作步骤如下。
在程序中导入(import)
ownable.sol的内容。 如果你不记得怎么做了,参考下petmating.sol。修改
PetIncubator合约, 让它继承自Ownable。 如果你不记得怎么做了,看看petmating.sol。


