学习区块链(八)--创建僵尸军团进阶Ⅲ之onlyOwner修饰符

在本章之前,不得不说说运行在以太坊的dapp和我们普通的app有着天壤之别,即智能协议的永固性,我们编译的程序会永久的,不可更改的存在在以太网上,不存在像我们中心化的系统上不断的版本迭代的可能。某种程度上,这是对开发者极其不友好的,在我们过去的开发经验中,我们总是在不断迭代我们的系统使其更加稳定健壮,而dapp就只有一次机会,它极大的考验了开发者的代码技巧和细心程度,所以有的人会说区块链某种程度上是和互联网是呈镜像关系的,但正因为如此反而成就了dapp的独一无二的优势,一旦合约被部署确认,任何人将无权更改它,程序会按照原有的代码一丝不苟的执行,如果你的合约产生漏洞,对不起,你只有让你的用户转移到新修复的合约上面去,但是用户大概率是不可能买账开发出漏洞的dapp团队的二次产品,著名的DAO事件还让人历历在目!

还记得之前我们在ZombieFeeding 合约中使用了kittyContract的接口,将address“硬编码”到我们的合约中,假如有天,kittyContract不存在,那么我们的合约也将不能使用,这种将生杀大权交给别人的dapp显然是致命的!

所以一开始的时候我们不会把地址通过变量写入合约,而是通过函数的方式来实现注入!当然,我们不可能让任何人都来修改地址,我们应该只能让合约地址的拥有人可以修改地址。
我们可以通过OpenZeppelin 库的 Ownable 的合约来实现这一目的,我们先来看看ownable.sol的源代码,有了之前的基础,我想看起来还是很简单的:

/** * @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.这是一个构造函数,在合约启动时只运行一次,将合约的地址赋给地址owner */
  function Ownable() public {
    owner = msg.sender;
  }


  /** * @dev Throws if called by any account other than the owner。这里就是modifier onlyOwner的修饰符,用来判定是否是合约的发布者 * */
  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;
  }

}

好,那么我们引入一个函数来指定小猫的地址,在ZombieFactory引用ownable合约,在ZombieFeeding合约中创建setKittyContractAddress函数,并用onlyOwner修饰:

pragma solidity ^0.4.19;

import "./ownable.sol";

contract ZombieFactory is Ownable {

    event NewZombie(uint zombieId, string name, uint dna);

    uint dnaDigits = 16;
    uint dnaModulus = 10 ** dnaDigits;

    struct Zombie {
        string name;
        uint dna;
        // Add new data here
    }

    Zombie[] public zombies;

    mapping (uint => address) public zombieToOwner;
    mapping (address => uint) ownerZombieCount;

    function _createZombie(string _name, uint _dna) internal {
        uint id = zombies.push(Zombie(_name, _dna)) - 1;
        zombieToOwner[id] = msg.sender;
        ownerZombieCount[msg.sender]++;
        NewZombie(id, _name, _dna);
    }

    function _generateRandomDna(string _str) private view returns (uint) {
        uint rand = uint(keccak256(_str));
        return rand % dnaModulus;
    }

    function createRandomZombie(string _name) public {
        require(ownerZombieCount[msg.sender] == 0);
        uint randDna = _generateRandomDna(_name);
        randDna = randDna - randDna % 100;
        _createZombie(_name, randDna);
    }

}
pragma solidity ^0.4.19;

import "./zombiefactory.sol";

contract KittyInterface {
  function getKitty(uint256 _id) external view returns (
    bool isGestating,
    bool isReady,
    uint256 cooldownIndex,
    uint256 nextActionAt,
    uint256 siringWithId,
    uint256 birthTime,
    uint256 matronId,
    uint256 sireId,
    uint256 generation,
    uint256 genes
  );
}

contract ZombieFeeding is ZombieFactory {

  KittyInterface kittyContract;

  function setKittyContractAddress(address _address) external onlyOwner {
    kittyContract = KittyInterface(_address);
  }

  function feedAndMultiply(uint _zombieId, uint _targetDna, string species) public {
    require(msg.sender == zombieToOwner[_zombieId]);
    Zombie storage myZombie = zombies[_zombieId];
    _targetDna = _targetDna % dnaModulus;
    uint newDna = (myZombie.dna + _targetDna) / 2;
    if (keccak256(species) == keccak256("kitty")) {
      newDna = newDna - newDna % 100 + 99;
    }
    _createZombie("NoName", newDna);
  }

  function feedOnKitty(uint _zombieId, uint _kittyId) public {
    uint kittyDna;
    (,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);
    feedAndMultiply(_zombieId, kittyDna, "kitty");
  }

}

不要告诉我你忘了external关键字是用来干什么的,它表示这个函数只能被合约外的合约调用哦!!!

有没有感觉这个onlyOwner是后门的感觉,所以你的只能合约想被大众认可,这些后门都是必要的,而且记得必须在Github上开源他们!!!

阅读更多

更多精彩内容