谜恋猫是世界首款区块链游戏。区块链是支持类似比特币这样的加密货币的运作技术基础。尽管谜恋猫不是数字货币,但它也能提供同样的安全保障:每一只谜恋猫都是独一无二的,而且100 %归您所有。它无法被复制、拿走、或销毁。
https://etherscan.io/address/0x06012c8cf97bead5deae237070f9587f8e7a266d#code
先来看看迷恋猫里一共有多少个Contract吧
contract Ownable
contract ERC721
contract GeneScienceInterface
contract KittyAccessControl
contract KittyBase is KittyAccessControl
contract ERC721Metadata
contract KittyOwnership is KittyBase, ERC721
contract KittyBreeding is KittyOwnership
contract ClockAuctionBase
contract Pausable is Ownable
contract ClockAuction is Pausable, ClockAuctionBase
contract SaleClockAuction is ClockAuction
contract KittyAuction is KittyBreeding
contract KittyMinting is KittyAuction
contract KittyCore is KittyMinting
一共2009行代码里,一共包含了15个contract。本系列文章将依次解读每一个contract的功能;了解其编写的原因;分析彼此间继承和调用关系。从而对迷恋猫有一个全面的认识。
每一个只能合约都会编写一个Ownable
,提供给其他contract来继承。Ownable
的功能是为智能合约绑定了一个地址,提供了基础的授权控制修改器onlyOwner()
。Ownable
还可以修改智能合约的拥有者transferOwnership
(),这个方法有一个修饰onlyOwner
,他实现了修改器的功能判断修改拥有者的操作必须是当前智能合约的拥有者。总之Ownable
就是一个用户控制的基类。
4-41行:
/**
* @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; // 地址变量owner
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
function Ownable() { // 构造函数
owner = msg.sender; // owner初始化为智能合约拥有者的地址
}
/**
* @dev Throws if called by any account other than the owner.
*/
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) onlyOwner { //被onlyOwner修饰的修改合约拥有者方法
if (newOwner != address(0)) { // 判断地址newOwner是否合法
owner = newOwner; // 修改合约拥有者为newOwner
}
}
}
先跳过ERC721
和GeneScienceInterface
合约,我们先来分析KittyAccessControl
合约。KittyAccessControl
是一个特殊权限管理合约。他定义了三种管理者的身份,分别为:
KittyCore
合约的构造函数中被初始化,且初始化为当前智能合约的发布者。1915-1928行:
function KittyCore() public {
// Starts paused.
paused = true;
// the creator of the contract is the initial CEO
ceoAddress = msg.sender;
// the creator of the contract is also the initial COO
cooAddress = msg.sender;
// start with the mythical kitten 0 - so we don't have generation-0 parent issues
_createKitty(0, 0, 0, uint256(-1), address(0));
}
KittyCore
的构造函数中(见上面的代码段)。COO角色拥有释放0代小猫以及发布促销猫的功能。0代小猫的数量以及促销猫的数量随着智能合约被部署就被限定,并且只有COO角色可以修改。 // Limits the number of cats the contract owner can ever create.
uint256 public constant PROMO_CREATION_LIMIT = 5000; // 促销猫的个数5000个
uint256 public constant GEN0_CREATION_LIMIT = 45000; // 初代猫的个数45000个
1999-2008行:
// @dev Allows the CFO to capture the balance available to the contract.
function withdrawBalance() external onlyCFO {
uint256 balance = this.balance; //当前剩余的余额
// Subtract all the currently pregnant kittens we have, plus 1 of margin.
uint256 subtractFees = (pregnantKitties + 1) * autoBirthFee; // (怀孕小猫的个数+1) * 生育费
if (balance > subtractFees) { // 判断当前余额是否够所有怀孕的猫都进行生产
cfoAddress.send(balance - subtractFees); // 向cfo角色发送扣除生育费后的余额
}
}
下面是完整的KittyAccessControl
合约的代码:
100-212行:
contract KittyAccessControl {
/// @dev Emited when contract is upgraded - See README.md for updgrade plan
event ContractUpgrade(address newContract); // 监控日志更新的事件
// The addresses of the accounts (or contracts) that can execute actions within each roles.
address public ceoAddress; // 存储ceo的地址变量
address public cfoAddress; // 存储cfo的地址变量
address public cooAddress; // 存储coo的地址变量
// @dev Keeps track whether the contract is paused. When that is true, most actions are blocked
bool public paused = false; //该变量来控制整个合约是否被暂停
/// @dev Access modifier for CEO-only functionality
modifier onlyCEO() { // ceo修改器
require(msg.sender == ceoAddress);
_;
}
/// @dev Access modifier for CFO-only functionality
modifier onlyCFO() { // cfo修改器
require(msg.sender == cfoAddress);
_;
}
/// @dev Access modifier for COO-only functionality
modifier onlyCOO() { // coo修改器
require(msg.sender == cooAddress);
_;
}
modifier onlyCLevel() { // ceo cfo ceo修改器
require(
msg.sender == cooAddress ||
msg.sender == ceoAddress ||
msg.sender == cfoAddress
);
_;
}
/// @dev Assigns a new address to act as the CEO. Only available to the current CEO.
/// @param _newCEO The address of the new CEO
function setCEO(address _newCEO) external onlyCEO { // 更新ceo,被onlyCEO修饰
require(_newCEO != address(0));
ceoAddress = _newCEO;
}
/// @dev Assigns a new address to act as the CFO. Only available to the current CEO.
/// @param _newCFO The address of the new CFO
function setCFO(address _newCFO) external onlyCEO { // 更新cfo,被onlyCEO修饰
require(_newCFO != address(0));
cfoAddress = _newCFO;
}
/// @dev Assigns a new address to act as the COO. Only available to the current CEO.
/// @param _newCOO The address of the new COO
function setCOO(address _newCOO) external onlyCEO { // 更新coo,被onlyCEO修饰
require(_newCOO != address(0));
cooAddress = _newCOO;
}
/*** Pausable functionality adapted from OpenZeppelin ***/
/// @dev Modifier to allow actions only when the contract IS NOT paused
modifier whenNotPaused() { // 修改器,判断合约是否被暂停中止
require(!paused);
_;
}
/// @dev Modifier to allow actions only when the contract IS paused
modifier whenPaused { // 修改器,判断当前合约是否处于暂定状态
require(paused);
_;
}
/// @dev Called by any "C-level" role to pause the contract. Used only when
/// a bug or exploit is detected and we need to limit damage.
function pause() external onlyCLevel whenNotPaused { // 将智能合约暂停中止,智能由CEO/CFO/COO在非暂停状态下调用
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 { // 解除暂停中止状态,只有CEO在暂停状态下进行调用。
// can't unpause if contract was upgraded
paused = false;
}
}
为什么要给智能合约一个暂停的状态呢?因为一个合约一旦部署,就无法再进行更修改。如果没有一个方法可以使得合约暂停下来,那这个合约将会持续运行下去。所以当合约中发现了BUG,就需要将合约暂停下来,尽量限制损失的范围(还记得THE DAO的残局吗?)。所以迷恋猫吸取了其中的教训,他为整个合约的升级和止损提供了很好的办法。
当真的出现无法控制的BUG时,CEO、CFO和COO都可以调用pause()
方法,将整个合约暂停。但是当要恢复合约时,只有CEO可以调用unpause()
方法来恢复合约。这里为什么只有CEO才能调取,而COO和CFO却无能为力呢?按照代码中注释的解释因为当调用pause()
时,可能CFO或COO帐户已经收到了受到损害。
小弟刚刚出来玩,有错多谢大神们及时回复指正,谢谢!