以太猫CryptoKitties源码(二):猫的属性以及创建

2019-02-22  本文已影响0人  雪飘千里

2、定义猫的属性 以及 创建猫

//猫的基础数据
contract KittyBase is KittyAccessControl {
    /*** EVENTS ***/

    /// 出生事件
    event Birth(address owner, uint256 kittyId, uint256 matronId, uint256 sireId, uint256 genes);

    /// 转账事件
    event Transfer(address from, address to, uint256 tokenId);

    /*** DATA TYPES ***/

    /// 猫的属性
    struct Kitty {
 
        // 猫的基因
        uint256 genes;

        // 出生日期
        uint64 birthTime;

        // 猫可以再次进行繁殖的最小区块,用于已经怀孕的猫
        uint64 cooldownEndBlock;

        // 猫 母亲 的ID
        uint32 matronId;
        // 猫 父亲的 ID
        uint32 sireId;

        // 怀孕期的猫的配偶 ID
        // 通过这个参数可以知道怀孕的猫的配偶是谁
        uint32 siringWithId;

        // 繁殖冷却时间,与上面的cooldownEndBlock构成双重保障
        //这两个参数之所以没放在一起,是因为一个是uint64,一个是uint16
        // 这样可以节省gas
        uint16 cooldownIndex;

        // 猫的代数
        uint16 generation;
    }

    /*** CONSTANTS ***/

    // 数组,cooldownIndex 对应的冷却时间,
    // 随着猫的代数和生育次数的增加,猫进行交配的冷却时间也会逐渐增加
    // 最大冷却时间不会超过7天
    uint32[14] public cooldowns = [
        uint32(1 minutes),
        uint32(2 minutes),
        uint32(5 minutes),
        uint32(10 minutes),
        uint32(30 minutes),
        uint32(1 hours),
        uint32(2 hours),
        uint32(4 hours),
        uint32(8 hours),
        uint32(16 hours),
        uint32(1 days),
        uint32(2 days),
        uint32(4 days),
        uint32(7 days)
    ];

    // 生成一个区块的秒数
    uint256 public secondsPerBlock = 15;

    /*** STORAGE ***/

    // 所有猫的ID
    Kitty[] kitties;

    //cat IDs to the address ;猫ID  对应  地址(用户)
    mapping (uint256 => address) public kittyIndexToOwner;

    // address to count of tokens that address owns
    // 地址(用户) 对应  猫的数量
    mapping (address => uint256) ownershipTokenCount;

    // KittyIDs to an address that has been approved to call transferFrom()
    // 授权 ,把uint256(猫id)  授权给address(用户) 交易 ,先授权,然后才能交易
    mapping (uint256 => address) public kittyIndexToApproved;


    // 授权,把uint256(猫id)  授权给address(用户)交配,先授权,然后才能交配
   // 系统中所有待交配的猫
    mapping (uint256 => address) public sireAllowedToAddress;

    /// 买卖的交易所,实际上存储的是合约的地址
    SaleClockAuction public saleAuction;

    /// 交配的交易所,实际上存储的是合约的地址
    SiringClockAuction public siringAuction;

    // 交易
    // 把 _from 拥有的_tokenId 猫,交易给 _to
    function _transfer(address _from, address _to, uint256 _tokenId) internal {
        // _to 用户拥有猫是数量 加 1 
        ownershipTokenCount[_to]++;
        // _tokenId 猫的所有者 更改为 _to
        kittyIndexToOwner[_tokenId] = _to;
        // 判断 _from 地址是否为空
        if (_from != address(0)) {
             // _from 用户拥有猫是数量 减 1 
            ownershipTokenCount[_from]--;
            // 删除猫的出售信息
            delete sireAllowedToAddress[_tokenId];
            // 删除猫的授权信息
            delete kittyIndexToApproved[_tokenId];
        }
        // Emit the transfer event. 
        Transfer(_from, _to, _tokenId);
    }
    

     // 创建猫
    /// @param _matronId The kitty ID of the matron of this cat (zero for gen0)  母亲ID
    /// @param _sireId The kitty ID of the sire of this cat (zero for gen0) 父亲ID
    /// @param _generation The generation number of this cat, must be computed by caller.   猫的代数
    /// @param _genes The kitty's genetic code.  猫的基因
    /// @param _owner The inital owner of this cat, must be non-zero (except for the unKitty, ID 0)   猫的拥有者
    // 返回 猫的ID
    function _createKitty(
        uint256 _matronId,
        uint256 _sireId,
        uint256 _generation,
        uint256 _genes,
        address _owner
    )
        internal
        returns (uint)
    {
        //是为了限制猫的数量  最多2^32次方只猫
        require(_matronId == uint256(uint32(_matronId)));
        require(_sireId == uint256(uint32(_sireId)));
        // 是为了限制猫的代数 最多2^16 
        require(_generation == uint256(uint16(_generation)));

        // 繁殖冷却时间最多是 7天    cooldowns[13] = 7天
        uint16 cooldownIndex = uint16(_generation / 2);
        if (cooldownIndex > 13) {
            cooldownIndex = 13;
        }
        
        // 生成一个 猫 ,内存中
        Kitty memory _kitty = Kitty({
            genes: _genes,
            birthTime: uint64(now), //时间戳
            cooldownEndBlock: 0,
            matronId: uint32(_matronId),
            sireId: uint32(_sireId),
            siringWithId: 0,
            cooldownIndex: cooldownIndex,
            generation: uint16(_generation)
        });

        // 放入区块链(kitties)中,返回新生成的猫ID
        uint256 newKittenId = kitties.push(_kitty) - 1;

       // 判断,猫的ID不能大于2^32
        require(newKittenId == uint256(uint32(newKittenId)));

        // emit the birth event ,触发事件,发送给前端监听
        Birth(
            _owner,
            newKittenId,
            uint256(_kitty.matronId),
            uint256(_kitty.sireId),
            _kitty.genes
        );

        // 把猫 分配给 拥有者_owner
        _transfer(0, _owner, newKittenId);

        return newKittenId;
    }

    // 修改生成区块的秒数,只有 CEO CFO COO才能执行
    // 权限控制见下面
    function setSecondsPerBlock(uint256 secs) external onlyCLevel {
        require(secs < cooldowns[0]);
        secondsPerBlock = secs;
    }
}

3、权限控制

//再以太猫这个项目中,有三个管理者
//CEO :
//CFO :
//COO :

contract KittyAccessControl {
    
    event ContractUpgrade(address newContract);

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

    // 是否暂停
    bool public paused = false;

    /// 只有CEO才能调用
    modifier onlyCEO() {
        require(msg.sender == ceoAddress);
        _;
    }

    /// 只有CFO才能调用
    modifier onlyCFO() {
        require(msg.sender == cfoAddress);
        _;
    }

    /// 只有COO才能调用
    modifier onlyCOO() {
        require(msg.sender == cooAddress);
        _;
    }
    
    // CEO COO  onlyCFO 都可以调用
    modifier onlyCLevel() {
        require(
            msg.sender == cooAddress ||
            msg.sender == ceoAddress ||
            msg.sender == cfoAddress
        );
        _;
    }

    // 更换CEO ,只有原来的CEO才能更换新的CEO
    function setCEO(address _newCEO) external onlyCEO {
        require(_newCEO != address(0));

        ceoAddress = _newCEO;
    }

    // 更换CFO ,只有原来的CEO才能更换新的CFO
    function setCFO(address _newCFO) external onlyCEO {
        require(_newCFO != address(0));

        cfoAddress = _newCFO;
    }

    // 更换COO ,只有原来的CEO才能更换新的COO 
    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;
    }

    /// 取消暂停,只有CEO才能执行
    function unpause() public onlyCEO whenPaused {
        // can't unpause if contract was upgraded
        paused = false;
    }
}

4、ERC721协议

ERC721代币 :核心是“Non-Fungible Tokens”,非同质代币。
简单的说,就是每一枚代币都是独一无二的,而ERC20 代币中,代币与代币是一样。
“以太猫”为例,每只以太猫拥有独一无二的基因,每只小猫和繁衍的后代也都是独一无二的。从原理上来看,每只以太猫在区块链平台上都是一条独一无二的代码,因此没有两只外表和特性完全相同的小猫。而且,ERC721每个代币都有一个独立唯一的tokenid,例如在cryptokitties里就是猫的id,独一无二。

/// @title Interface for contracts conforming to ERC-721: Non-Fungible Tokens
/// @author Dieter Shirley <dete@axiomzen.co> (https://github.com/dete)
contract ERC721 {
    // Required methods
    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;

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

    // 可选择的实现
    // function name() public view returns (string name);
    // function symbol() public view returns (string symbol);
    // function tokensOfOwner(address _owner) external view returns (uint256[] tokenIds);
    // function tokenMetadata(uint256 _tokenId, string _preferredTransport) public view returns (string infoUrl);

    // ERC165标准,    用于和外部合约交互,
    function supportsInterface(bytes4 _interfaceID) external view returns (bool);
}

5、以太猫(代币)元数据

为了和外部合约交互

contract ERC721Metadata {
    /// @dev Given a token Id, returns a byte array that is supposed to be converted into string.
    function getMetadata(uint256 _tokenId, string) public view returns (bytes32[4] buffer, uint256 count) {
        if (_tokenId == 1) {
            buffer[0] = "Hello World! :D";
            count = 15;
        } else if (_tokenId == 2) {
            buffer[0] = "I would definitely choose a medi";
            buffer[1] = "um length string.";
            count = 49;
        } else if (_tokenId == 3) {
            buffer[0] = "Lorem ipsum dolor sit amet, mi e";
            buffer[1] = "st accumsan dapibus augue lorem,";
            buffer[2] = " tristique vestibulum id, libero";
            buffer[3] = " suscipit varius sapien aliquam.";
            count = 128;
        }
    }
}

6、ERC721实现

contract KittyOwnership is KittyBase, ERC721 {

    /// 代币的名字
    string public constant name = "CryptoKitties";
    // 代币的缩写
    string public constant symbol = "CK";

    // ERC721元数据
    ERC721Metadata public erc721Metadata;
    //验证是否支持 ERC165协议
    bytes4 constant InterfaceSignature_ERC165 =
        bytes4(keccak256('supportsInterface(bytes4)'));

   //验证是否支持 ERC721协议
    bytes4 constant InterfaceSignature_ERC721 =
        bytes4(keccak256('name()')) ^
        bytes4(keccak256('symbol()')) ^
        bytes4(keccak256('totalSupply()')) ^
        bytes4(keccak256('balanceOf(address)')) ^
        bytes4(keccak256('ownerOf(uint256)')) ^
        bytes4(keccak256('approve(address,uint256)')) ^
        bytes4(keccak256('transfer(address,uint256)')) ^
        bytes4(keccak256('transferFrom(address,address,uint256)')) ^
        bytes4(keccak256('tokensOfOwner(address)')) ^
        bytes4(keccak256('tokenMetadata(uint256,string)'));

    /// 实现ERC165 协议,作用是外部账户验证我们的合约是否实现我们想要的函数接口
    function supportsInterface(bytes4 _interfaceID) external view returns (bool)
    {
        // DEBUG ONLY
        //require((InterfaceSignature_ERC165 == 0x01ffc9a7) && (InterfaceSignature_ERC721 == 0x9a20483d));

        return ((_interfaceID == InterfaceSignature_ERC165) || (_interfaceID == InterfaceSignature_ERC721));
    }

    /// 设置代币元数据,只有CEO才能执行
    function setMetadataAddress(address _contractAddress) public onlyCEO {
        erc721Metadata = ERC721Metadata(_contractAddress);
    }

    // 判断猫的主人是否为传递进来的地址 _claimant
    function _owns(address _claimant, uint256 _tokenId) internal view returns (bool) {
        return kittyIndexToOwner[_tokenId] == _claimant;
    }

    /// 判断 _claimant 地址是否已经有了 猫_tokenId 的授权
    function _approvedFor(address _claimant, uint256 _tokenId) internal view returns (bool) {
        return kittyIndexToApproved[_tokenId] == _claimant;
    }

    /// 把_tokenId授权_approved地址
    // 地址可以是买卖交易所合约地址 , 即把 _tokenId猫授权给买卖交易所进行拍卖
    function _approve(uint256 _tokenId, address _approved) internal {
        kittyIndexToApproved[_tokenId] = _approved;
    }

    /// 返回地址(主人) 有多少只猫(代币)
    function balanceOf(address _owner) public view returns (uint256 count) {
        return ownershipTokenCount[_owner];
    }

    /// 转移代币 ,只有当程序 notPaused 没有暂停时才能使用
    function transfer(
        address _to,
        uint256 _tokenId
    )
        external
        whenNotPaused
    {
        // 安全检查,_to 不能为0
        require(_to != address(0));
        // 转账的地址不能为当前账户
        require(_to != address(this));

        // 
        require(_to != address(saleAuction));
        require(_to != address(siringAuction));

        // 当前的合约的调用者 是否 拥有这只猫
        require(_owns(msg.sender, _tokenId));

        // 转账
        _transfer(msg.sender, _to, _tokenId);
    }

    /// 授权代币,同样的,只有当程序 notPaused 没有暂停时才能使用
    function approve(
        address _to,
        uint256 _tokenId
    )
        external
        whenNotPaused
    {
        // 只有拥有者才能授权
        require(_owns(msg.sender, _tokenId));

        // 函数调用,把_tokenId猫授权给_to
        _approve(_tokenId, _to);

        // 触发事件
        Approval(msg.sender, _to, _tokenId);
    }

    /// 将  _from用户  的_tokenId猫  转移给  _to用户
    function transferFrom(
        address _from,
        address _to,
        uint256 _tokenId
    )
        external
        whenNotPaused
    {
        // 安全检查,判断_to 地址是否合法
        require(_to != address(0));
        // 不能转移给本合约地址
        require(_to != address(this));
        // 检查权限,检查msg.sender是否获得了授权转移_tokenId猫
        require(_approvedFor(msg.sender, _tokenId));
        //检查_from 是否拥有 _tokenId猫
        require(_owns(_from, _tokenId));

        // 调用函数_transfer 进行转移
        _transfer(_from, _to, _tokenId);
    }

    /// 返回当前猫的总数
    function totalSupply() public view returns (uint) {
        return kitties.length - 1;
    }

    /// 返回_tokenId猫的所有者
    function ownerOf(uint256 _tokenId)
        external
        view
        returns (address owner)
    {
        owner = kittyIndexToOwner[_tokenId];

        require(owner != address(0));
    }

    /// 返回_owner所有者拥有的所有猫的id 
    function tokensOfOwner(address _owner) external view returns(uint256[] ownerTokens) {
        // 查询_owner 有几只猫
        uint256 tokenCount = balanceOf(_owner);

        if (tokenCount == 0) {
            // Return an empty array
            return new uint256[](0);
        } else {
             // 初始化一个长度为tokenCount的返回值result
            uint256[] memory result = new uint256[](tokenCount);
            uint256 totalCats = totalSupply();
            uint256 resultIndex = 0;

            // 猫的id 从 1 递增到 totalCats 
            uint256 catId;
            // 从1开始循环遍历所有的totalCats 
            for (catId = 1; catId <= totalCats; catId++) {
                 //判断当前catId 的拥有者 是不是 _owner
                if (kittyIndexToOwner[catId] == _owner) {
                    result[resultIndex] = catId;
                    resultIndex++;
                }
            }

            return result;
        }
    }

    /// 拷贝方法
    ///  Ref: https://github.com/Arachnid/solidity-stringutils/blob/2f6ca9accb48ae14c66f1437ec50ed19a0616f78/strings.sol
    function _memcpy(uint _dest, uint _src, uint _len) private view {
     
        for(; _len >= 32; _len -= 32) {
            // 汇编
            assembly {
                mstore(_dest, mload(_src))
            }
            _dest += 32;
            _src += 32;
        }

        // Copy remaining bytes
        uint256 mask = 256 ** (32 - _len) - 1;
        assembly {
            let srcpart := and(mload(_src), not(mask))
            let destpart := and(mload(_dest), mask)
            mstore(_dest, or(destpart, srcpart))
        }
    }

    /// 将_rawBytes中长度为_stringLength 转成string 并返回
    ///  Ref: https://github.com/Arachnid/solidity-stringutils/blob/2f6ca9accb48ae14c66f1437ec50ed19a0616f78/strings.sol
    function _toString(bytes32[4] _rawBytes, uint256 _stringLength) private view returns (string) {
        var outputString = new string(_stringLength);
        uint256 outputPtr;
        uint256 bytesPtr;

        assembly {
            outputPtr := add(outputString, 32)
            bytesPtr := _rawBytes
        }

        _memcpy(outputPtr, bytesPtr, _stringLength);

        return outputString;
    }

    /// 返回指向该元数据包的元数据的URI
    function tokenMetadata(uint256 _tokenId, string _preferredTransport) external view returns (string infoUrl) {
        require(erc721Metadata != address(0));
        bytes32[4] memory buffer;
        uint256 count;
        (buffer, count) = erc721Metadata.getMetadata(_tokenId, _preferredTransport);

        return _toString(buffer, count);
    }
}
上一篇下一篇

猜你喜欢

热点阅读