CryptoKitties源码剖析(二)——KittyBase

引言

上一节讲解了OwnableERC721GeneScienceInterfaceKittyAccessControl四个合约,今天来看看接下来的KittyBase这一合约。KittyBase是CryptoKitties的基类/合约。保存所有常见的structs, events 和 base variables。

KittyBase合约

contract KittyBase is KittyAccessControl {
    event Birth(address owner, uint256 kittyId, uint256 matronId, uint256 sireId, uint256 genes);
    event Transfer(address from, address to, uint256 tokenId);

    struct Kitty {
        uint256 genes;
        uint64 birthTime;
        uint64 cooldownEndBlock;
        uint32 matronId;
        uint32 sireId;
        uint32 siringWithId;
        uint16 cooldownIndex;
        uint16 generation;
    }

    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;
    Kitty[] kitties;

    mapping (uint256 => address) public kittyIndexToOwner;
    mapping (address => uint256) ownershipTokenCount;
    mapping (uint256 => address) public kittyIndexToApproved;
    mapping (uint256 => address) public sireAllowedToAddress;

    SaleClockAuction public saleAuction;

    SiringClockAuction public siringAuction;

    function _transfer(address _from, address _to, uint256 _tokenId) internal {
        ownershipTokenCount[_to]++;
        kittyIndexToOwner[_tokenId] = _to;
        if (_from != address(0)) {
            ownershipTokenCount[_from]--;
            delete sireAllowedToAddress[_tokenId];
            delete kittyIndexToApproved[_tokenId];
        }
        Transfer(_from, _to, _tokenId);
    }
    function _createKitty(
        uint256 _matronId,
        uint256 _sireId,
        uint256 _generation,
        uint256 _genes,
        address _owner
    )
        internal
        returns (uint)
    {
        require(_matronId == uint256(uint32(_matronId)));
        require(_sireId == uint256(uint32(_sireId)));
        require(_generation == uint256(uint16(_generation)));

        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)
        });
        uint256 newKittenId = kitties.push(_kitty) - 1;

        require(newKittenId == uint256(uint32(newKittenId)));

        Birth(
            _owner,
            newKittenId,
            uint256(_kitty.matronId),
            uint256(_kitty.sireId),
            _kitty.genes
        );
        _transfer(0, _owner, newKittenId);

        return newKittenId;
    }
    function setSecondsPerBlock(uint256 secs) external onlyCLevel {
        require(secs < cooldowns[0]);
        secondsPerBlock = secs;
    }
}

事件:

  • event Birth(address owner, uint256 kittyId, uint256 matronId, uint256 sireId, uint256 genes):每当一只新的小猫出现时,就会发生Birth事件。这显然包括通过giveBirth方法创建小猫,但在创建新的Gen0猫时也被调用。
  • event Transfer(address from, address to, uint256 tokenId):在ERC721的当前草案中定义的Transfer事件。每次分配猫咪所有权,包括出生时触发。

结构体:

     /// 代表小猫的`struct`。**CryptoKitties**中的每只猫都用一个这种结构的拷贝来表示。
     /// 注意`Kitty`中的成员的顺序,符合**紧密打包**规则。
     struct Kitty {
        // 小猫的DNA被打包成256位,猫的DNA永远不会改变。
        uint256 genes; 
        // 当这只猫出现的时候,来自块的时间戳。
        uint64 birthTime;
        // 这只猫可以重新参与繁殖的最小时间戳。
        // 同样用于怀孕计时器以及siring冷却。
        uint64 cooldownEndBlock;
        // 这只猫的母亲的ID,为GE0猫设定为0。
        uint32 matronId;
        // 这只猫的父亲的ID,为GE0猫设定为0。
        uint32 sireId;
        // 为怀孕的猫设置siring猫的id,未怀孕则为0
        uint32 siringWithId;
        // 作为冷却数组的索引,Gen0猫是从0开始的,其他代猫是(gen/2)
        // 每发生一次繁殖行为则加1
        uint16 cooldownIndex;
        // 这只猫的“世代数”。CK合约出售的猫被称为“GE0”,代号为0。
        // 所有其他猫的世代数是他们父母的两代数中最大的一个加1
        // 即 max(matron.generation, sire.generation) + 1
        uint16 generation;
    }

成员变量:

  • uint32[14] public cooldowns:一个查找表,指示在任何成功繁殖动作后的冷却时间,称为母体的“怀孕时间”和“siring冷却”。设计时,每次猫被繁殖时,冷却时间大致翻倍,鼓励主人不要再重复饲养同一只猫。在一周内(一只猫可以繁殖无数次,最大的冷却时间是7天)。
  • uint256 public secondsPerBlock:当前块之间有多少秒的近似值,即15秒。
  • Kitty[] kitties:一个包含所有存在的猫的Kitty结构的数组。每个猫的ID实际上是这个数组的索引。
  • mapping (uint256 => address) public kittyIndexToOwner:小猫的ID到拥有者的地址之间的映射
  • mapping (address => uint256) ownershipTokenCount:所有者地址到小猫数量的计数
  • mapping (uint256 => address) public kittyIndexToApproved:从KittyIDs映射到已被批准调用transferFrom()的地址。每个猫咪在任何时候只能有一个批准的地址。零值意味着没有批准。
  • mapping (uint256 => address) public sireAllowedToAddress:一个从KittyIDs到一个地址的映射,它已经被批准使用这个Kitty通过breedWith()来进行siring。每只猫咪在任何时候只能有一个被批准的地址。零值意味着没有批准。
  • SaleClockAuction public saleAuction:处理猫咪销售的ClockAuction合约的地址。
  • SiringClockAuction public siringAuction:一个定制ClockAuction子类合同的地址,处理siring拍卖。需要与saleAuction分开,因为在sales和siring拍卖之后采取的行动是完全不同的。

函数:

/// 将特定小猫的所有权转移给一个地址。
function _transfer(address _from, address _to, uint256 _tokenId) internal {
        // 由于小猫的数量上限为2 ^ 32,不会溢出。
        ownershipTokenCount[_to]++;
        // 转移所有权
        kittyIndexToOwner[_tokenId] = _to;
        // 当创建新的小猫,从0x0,但我们没法表示这个地址。
        if (_from != address(0)) {
            ownershipTokenCount[_from]--;
            // 一旦猫咪被转移,也可以清除sire的补助。
            delete sireAllowedToAddress[_tokenId];
            //清除任何先前批准的所有权交易
            delete kittyIndexToApproved[_tokenId];
        }
        // 触发transfer事件.
        Transfer(_from, _to, _tokenId);
    }
/// 创建新的Kitty并存储它.这个方法不进行任何检查,只应在
/// 输入数据已知是有效的。将同时生成Birth事件和Transfer事件。
/// @param _matronId 这只猫的母亲ID(GE0为零)
/// @param _sireId 这只猫的父亲ID(GE0为零)
/// @param _generation 生成该猫的生成数,必须由调用方计算
/// @param _genes 猫猫的DNA
/// @param _owner 这只猫的拥有者
 function _createKitty(
        uint256 _matronId,
        uint256 _sireId,
        uint256 _generation,
        uint256 _genes,
        address _owner
    )
        internal
        returns (uint)
    {
        require(_matronId == uint256(uint32(_matronId)));
        require(_sireId == uint256(uint32(_sireId)));
        require(_generation == uint256(uint16(_generation)));
        // 新的Kitty以与父母Gen/2相同的冷却开始。
        uint16 cooldownIndex = uint16(_generation / 2);
        if (cooldownIndex > 13) {
            cooldownIndex = 13;
        }
        // 构建kitty
        Kitty memory _kitty = Kitty({
            genes: _genes,
            birthTime: uint64(now),
            cooldownEndBlock: 0,
            matronId: uint32(_matronId),
            sireId: uint32(_sireId),
            siringWithId: 0,
            cooldownIndex: cooldownIndex,
            generation: uint16(_generation)
        });
        // 将kitty放进kitties中,返回得到kittyId
        uint256 newKittenId = kitties.push(_kitty) - 1;

        require(newKittenId == uint256(uint32(newKittenId)));
        // 触发Birth事件
        Birth(
            _owner,
            newKittenId,
            uint256(_kitty.matronId),
            uint256(_kitty.sireId),
            _kitty.genes
        );
        _transfer(0, _owner, newKittenId);

        return newKittenId;
    }
  • function setSecondsPerBlock(uint256 secs) external onlyCLevel:调整区块之间的秒数,只有CXO们可以设置。

猜你喜欢

转载自blog.csdn.net/qq_33829547/article/details/80663031