美文网首页
以太猫CryptoKitties源码(二):猫的属性以及创建

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

作者: 雪飘千里 | 来源:发表于2019-02-22 14:30 被阅读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);
        }
    }
    

    相关文章

      网友评论

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

          本文链接:https://www.haomeiwen.com/subject/xlddyqtx.html