CryptoKitties源码剖析(一)——Ownable、ERC721、GeneScienceInterface、KittyAccessControl

引言

CryptoKitties是第一个基于Etherum的游戏Dapp,2017年下半年风靡一时,一度造成了Etherum网络的堵塞。虽然现在这款游戏的热度已经开始消退,但是作为技术学习而言,CryptoKitties确实是一个很好的案例参考教程。本节内容作为专栏的开篇,先以最基本的几个合约开始入手讲解。

目录

Ownable合约

这是来自OpenZeppelin Solidity 库的 Ownable 合约,提供了基本的权限控制功能。大多数人开发自己的 DApp,都是从复制/粘贴 Ownable 开始的,从它再继承出的子合约,并在之上进行功能开发。源码先贴上:

contract Ownable {
  address public owner; 

  function Ownable() { 
    owner = msg.sender;
  }

  modifier onlyOwner() { 
    require(msg.sender == owner);
    _;
  }

  function transferOwnership(address newOwner) onlyOwner {
    if (newOwner != address(0)) {
      owner = newOwner;
    }
  }
}

成员变量:

  • owneraddress类型,指向合约拥有者的账户地址

构造函数:

  • function Ownable():仅在合约最初被部署时执行一次,并将部署者的账户地址赋值给owner变量

函数修饰符:

  • modifier onlyOwner():修饰符跟函数很类似,不过是用来修饰其他已有函数用的,在其他语句执行前,为它检查下先验条件。

一般函数:

  • function transferOwnership(address newOwner) onlyOwner:该函数的作用是将合约的拥有权转交给另外一个账户地址,由于加上了上述的修饰符onlyOwner,意味着只有合约当前的拥有者才有权限调用此函数。

ERC721合约

这个合约涉及到ERC标准的内容,以及ERC20同质化货币和ERC721非同质化货币的区别。首先同质化货币比较好理解,我们日常生活用的货币就是同质化货币,我手里的一块钱和你手里的一块钱没有任何区别,我钱包账户里的1 ether和任何人钱包里的1 ether 也是完全等同的。而非同质化货币就类似于收藏品了,你的Kitty猫的基因性格外观和我的Kitty猫绝对不会一模一样。另外同质化货币是可分割的,一块钱可以拆成两枚五毛,但是一只kitty就是一只,不可能送给你半只。。。

游戏里的猫其实就是基于ERC721的加密货币,而ERC721作为一个合约标准,提供了在实现ERC721代币时必须要遵守的协议,要求每个ERC721标准合约需要实现ERC721和ECR165接口,接口定义如下:

contract ERC721 {
    function totalSupply() public view returns (uint256 total);
    function balanceOf(address _owner) public view returns (uint256 balance);
    function ownerOf(uint256 _tokenId) external view returns (address owner);
    function approve(address _to, uint256 _tokenId) external;
    function transfer(address _to, uint256 _tokenId) external;
    function transferFrom(address _from, address _to, uint256 _tokenId) external;

    event Transfer(address from, address to, uint256 tokenId);
    event Approval(address owner, address approved, uint256 tokenId);

    function supportsInterface(bytes4 _interfaceID) external view returns (bool);
}
  • totalSupply() returns (uint256 total):返回代币总供应量
  • balanceOf(address _owner) returns (uint256 balance):传入 address 参数,然后返回这个 address 拥有多少代币
  • ownerOf(uint256 _tokenId) returns (address owner):传入一个代币 ID 作为参数 ,然后返回该代币拥有者的 address
  • approve(address _to, uint256 _tokenId):授予地址_to具有_tokenId的控制权,方法成功后需触发Approval 事件。
  • transfer(address _to, uint256 _tokenId):代币的拥有者调用transfer 方法,传入他想转移到的 address 和他想转移的代币的 _tokenId,代币直接传入对方地址。
  • transferFrom(address _from, address _to, uint256 _tokenId):转移代币所有权,一次成功的转移操作必须发起 Transer 事件。函数的实现需要做一下几种检查:调用者msg.sender应该是当前tokenId的所有者或被授权的地址;_from 必须是 _tokenId的所有者;_tokenId 应该是当前合约正在监测的NFTs 中的任何一个;_to 地址不应该为 0。
  • supportsInterface(bytes4 _interfaceID) returns (bool):基于ECR165的自检接口。合约实现了任何标准化接口则返回true

GeneScienceInterface合约

这个合约很简单,直接贴代码:

contract GeneScienceInterface {
    function isGeneScience() public pure returns (bool);
    function mixGenes(uint256 genes1, uint256 genes2, uint256 targetBlock) public returns (uint256);
}
  • isGeneScience() returns (bool):单纯返回一个bool值,用来判断这个合约不是期望的那个。注:该合约即基因算法的实现部署在另一个地址,这样实现了业务逻辑的解耦,方便更新业务逻辑。
  • mixGenes(uint256 genes1, uint256 genes2, uint256 targetBlock) returns (uint256):传入妈妈的基因genes1、爸爸的基因genes2以及目标区块的targetBlock来计算生成后代的基因组。

KittyAccessControl合约

KittyAccessControl合约定义了访问控制权限,共有CEO、CFO、COO三种角色权限,代码如下:

contract KittyAccessControl {
    event ContractUpgrade(address newContract);

    address public ceoAddress;
    address public cfoAddress;
    address public cooAddress;

    bool public paused = false;

    modifier onlyCEO() {
        require(msg.sender == ceoAddress);
        _;
    }

    modifier onlyCFO() {
        require(msg.sender == cfoAddress);
        _;
    }

    modifier onlyCOO() {
        require(msg.sender == cooAddress);
        _;
    }

    modifier onlyCLevel() {
        require(
            msg.sender == cooAddress ||
            msg.sender == ceoAddress ||
            msg.sender == cfoAddress
        );
        _;
    }

    function setCEO(address _newCEO) external onlyCEO {
        require(_newCEO != address(0));

        ceoAddress = _newCEO;
    }

    function setCFO(address _newCFO) external onlyCEO {
        require(_newCFO != address(0));

        cfoAddress = _newCFO;
    }

    function setCOO(address _newCOO) external onlyCEO {
        require(_newCOO != address(0));

        cooAddress = _newCOO;
    }

    modifier whenNotPaused() {
        require(!paused);
        _;
    }

    modifier whenPaused {
        require(paused);
        _;
    }

    function pause() external onlyCLevel whenNotPaused {
        paused = true;
    }

    /// @dev Unpauses the smart contract. Can only be called by the CEO, since
    ///  one reason we may pause the contract is when CFO or COO accounts are
    ///  compromised.
    /// @notice This is public rather than external so it can be called by
    ///  derived contracts.
    function unpause() public onlyCEO whenPaused {
        // can't unpause if contract was upgraded
        paused = false;
    }
}

成员变量:

  • address ceoAddress:CEO账户的address
  • address cfoAddress:CFO账户的address
  • address cooAddress:COO账户的address
  • bool paused:合约是否被暂停,如果为true,大部分的功能将不可用

函数修饰符:

  • modifier onlyCEO()CEO账户专属的修饰符,被该修饰符修饰的函数只有CEO才能访问
  • modifier onlyCFO()CFO账户专属的修饰符,被该修饰符修饰的函数只有CFO才能访问
  • modifier onlyCOO()COO账户专属的修饰符,被该修饰符修饰的函数只有COO才能访问
  • modifier onlyCLevel()CEOCFOCOO的修饰符,被该修饰符修饰的函数只有是CXO们才可以访问
  • modifier whenNotPaused():修饰符,被修饰的函数只有成员变量pausefalse才能调用
  • modifier whenPaused():修饰符,被修饰的函数只有成员变量pausetrue才能调用

一般函数:

  • setCEO(address _newCEO) external onlyCEO:设置新的CEO,只有当前CEO才能调用
  • setCFO(address _newCFO) external onlyCEO:设置新的CFO,只有当前CEO才能调用
  • setCOO(address _newCOO) external onlyCEO:设置新的COO,只有当前CEO才能调用
  • pause() external onlyCLevel whenNotPaused:合约状态不是暂停时,将合约状态设为暂停,即成员变量pause变为true,只有CXO们可以调用。一般只在系统出现bug时使用来减少损失
  • unpause() public onlyCEO whenPaused:解除合约的暂停状态,只有CEO才能调用,因为暂停合约的一个可能的原因就是CFOCOO账户被泄露

猜你喜欢

转载自blog.csdn.net/qq_33829547/article/details/80614707
今日推荐