1. 程式人生 > >CryptoKittes(加密貓,謎戀貓)智慧合約結構和原始碼解析

CryptoKittes(加密貓,謎戀貓)智慧合約結構和原始碼解析

  CryptoKittes(加密貓、謎戀貓,https://www.cryptokitties.co/)是執行在以太坊上的一個遊戲。 謎戀貓是世界首款架構在區塊鏈技術上的數字貓收集與繁殖遊戲,同樣的技術突破使得比特幣和以太坊的運作的基礎。

  具體可以參考如下兩篇參考資料:

  檢視程式碼兩個地方都可以

  檢視原始碼,我畫了如下的圖,主要結構如下:

  • 貓的屬性
  • 主要邏輯就是從上到下繼承關係
  • 拍賣合約和繁殖合約
  • 遵循ERC721協議和基因科學合約(原始碼未公佈)

   

下面我閱讀原始碼的筆記,沒有貼原始碼,請參照如上地址,閱讀原始碼:

一、合約部分

1、許可權:交易需要本人賬號操作,CEO/CFO/COO有特殊許可權(onlyCLevel指CEO/CFO/COO皆可)
1)onyCLLevel:設定區塊產生速度(多少秒一個區塊);提取拍賣所佣金到CFO賬號;
2)CEO:暫停、啟動整個合約、指定Kitty新合約地址、銷售拍賣和交配權合約地址
3)COO: 生成宣傳貓、初代貓,設定生育費
4)CFO: 提取佣金
2、基因科學合約(繁殖部分)未公佈原始碼

3、主要合約分為:ERC271協議、許可權控制、權屬、繁殖、拍賣(貓交易、交配權拍賣)、創造(宣傳貓、初代貓)
4、下面的程式碼是儲存各種關係的資料結構,Kitty[] kitties是所有貓的陣列,mapping我理解就是key-value的雜湊表

  // An approximation of currently how many seconds are in between blocks.
    uint256 public secondsPerBlock = 15;

    /*** STORAGE ***/

    /// @dev An array containing the Kitty struct for all Kitties in existence. The ID
    ///  of each cat is actually an index into this array. Note that ID 0 is a negacat,
    ///  the unKitty, the mythical beast that is the parent of all gen0 cats. A bizarre
    ///  creature that is both matron and sire... to itself! Has an invalid genetic code.
    ///  In other words, cat ID 0 is invalid... ;-)
    Kitty[] kitties;

    /// @dev A mapping from cat IDs to the address that owns them. All cats have
    ///  some valid owner address, even gen0 cats are created with a non-zero owner.
    mapping (uint256 => address) public kittyIndexToOwner;

    // @dev A mapping from owner address to count of tokens that address owns.
    //  Used internally inside balanceOf() to resolve ownership count.
    mapping (address => uint256) ownershipTokenCount;

    /// @dev A mapping from KittyIDs to an address that has been approved to call
    ///  transferFrom(). Each Kitty can only have one approved address for transfer
    ///  at any time. A zero value means no approval is outstanding.
    mapping (uint256 => address) public kittyIndexToApproved;

    /// @dev A mapping from KittyIDs to an address that has been approved to use
    ///  this Kitty for siring via breedWith(). Each Kitty can only have one approved
    ///  address for siring at any time. A zero value means no approval is outstanding.
    mapping (uint256 => address) public sireAllowedToAddress;

    /// @dev The address of the ClockAuction contract that handles sales of Kitties. This
    ///  same contract handles both peer-to-peer sales as well as the gen0 sales which are
    ///  initiated every 15 minutes.
    SaleClockAuction public saleAuction;

    /// @dev The address of a custom ClockAuction subclassed contract that handles siring
    ///  auctions. Needs to be separate from saleAuction because the actions taken on success
    ///  after a sales and siring auction are quite different.
    SiringClockAuction public siringAuction;

二、貓的屬性

1、基因是256位標記,貓沒有性別,既可以當母親,也可以當父親
2、貓出生時間戳為程式碼執行系統時間
3、所有貓放在陣列中,宣傳貓和初代貓父母ID為0,懷孕的貓記錄配偶的ID,用來判斷是否懷孕,小貓出生後父親ID就是母親配偶ID
   ID是陣列的索引,貓的ID是uint32,共40億的量,以太坊每年5億的交易量,使用ID的地方都需要溢位檢查
4、後代的代,父母的代中最大的一個加1,比如0代和1代生出的小貓代為2
5、冷卻時間,1分鐘-7天共14檔,出生後初始值是代除以2[ floor(generation/2) ],比如2代出生後冷卻就是1檔,5代就是2檔,以後每交配一次增加一檔,最大是14檔(7d)

    14檔{1m,2m,5m,10m,30m,1h,2h,4h,8h,16h,1d,2d,4d,7d}

    /// @dev A lookup table indicating the cooldown duration after any successful
    ///  breeding action, called "pregnancy time" for matrons and "siring cooldown"
    ///  for sires. Designed such that the cooldown roughly doubles each time a cat
    ///  is bred, encouraging owners not to just keep breeding the same cat over
    ///  and over again. Caps out at one week (a cat can breed an unbounded number
    ///  of times, and the maximum cooldown is always seven days).
    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)
    ];

    冷卻時間確定了之後,會根據冷卻時間除以區塊的生成速度計算冷卻區塊索引
    冷卻時間的區塊索引,這個是計算生貓後小貓記錄區塊的位置,防止溢位

    交配後母貓冷卻時間為懷孕時間,公貓也有相應的冷卻時間

下面程式碼是Kitty的資料結構,好好理解一下

 /// @dev The main Kitty struct. Every cat in CryptoKitties is represented by a copy
    ///  of this structure, so great care was taken to ensure that it fits neatly into
    ///  exactly two 256-bit words. Note that the order of the members in this structure
    ///  is important because of the byte-packing rules used by Ethereum.
    ///  Ref: http://solidity.readthedocs.io/en/develop/miscellaneous.html
    struct Kitty {
        // The Kitty's genetic code is packed into these 256-bits, the format is
        // sooper-sekret! A cat's genes never change.
        uint256 genes;

        // The timestamp from the block when this cat came into existence.
        uint64 birthTime;

        // The minimum timestamp after which this cat can engage in breeding
        // activities again. This same timestamp is used for the pregnancy
        // timer (for matrons) as well as the siring cooldown.
        uint64 cooldownEndBlock;

        // The ID of the parents of this kitty, set to 0 for gen0 cats.
        // Note that using 32-bit unsigned integers limits us to a "mere"
        // 4 billion cats. This number might seem small until you realize
        // that Ethereum currently has a limit of about 500 million
        // transactions per year! So, this definitely won't be a problem
        // for several years (even as Ethereum learns to scale).
        uint32 matronId;
        uint32 sireId;

        // Set to the ID of the sire cat for matrons that are pregnant,
        // zero otherwise. A non-zero value here is how we know a cat
        // is pregnant. Used to retrieve the genetic material for the new
        // kitten when the birth transpires.
        uint32 siringWithId;

        // Set to the index in the cooldown array (see below) that represents
        // the current cooldown duration for this Kitty. This starts at zero
        // for gen0 cats, and is initialized to floor(generation/2) for others.
        // Incremented by one for each successful breeding action, regardless
        // of whether this cat is acting as matron or sire.
        uint16 cooldownIndex;

        // The "generation number" of this cat. Cats minted by the CK contract
        // for sale are called "gen0" and have a generation number of 0. The
        // generation number of all other cats is the larger of the two generation
        // numbers of their parents, plus one.
        // (i.e. max(matron.generation, sire.generation) + 1)
        uint16 generation;
    }

三、繁殖

繁殖的過程分為:
1)出售交配權(公貓),母貓主人拍賣,交配、懷孕、生產
2)相同賬號的兩隻貓可以繁殖,只有後面三個過程

條件
1)母貓主人生育費是否夠2 Finney(1 ETH=1000 Finney)
2)需要公貓的使用者授權(拍賣)
3)母貓未懷孕,且雙方過了冷卻時間
4)是否可以交配,父母和子女,兄妹之間都不可以

懷孕
1)懷孕後,母貓和公貓都增加一檔冷卻時間,母貓的冷卻時間就是懷孕時間

出生
1)檢查懷孕時間超過冷卻時間
2)新生貓的代是最大的父母代+1
3)基因由父母基因遺傳+突變

4)出生的小貓歸母貓主人所有

繁殖的邏輯比較複雜,我這裡就不貼程式碼了

四、拍賣貓

價格單位
//1: wei  Wei  Dai  戴偉 密碼學家 ,發表 B-money
//10^3: lovelace   Ada Lovelace 洛夫萊斯 世界上第一位程式設計師、詩人拜倫之女
//10^6: babbage  Charles Babbage 巴貝奇  英國數學家、發明家兼機械工程師,提出了差分機與分析機的設計概念,被視為計算機先驅。
//10^9: shannon  Claude Elwood Shannon 夏農  美國數學家、電子工程師和密碼學家,被譽為資訊理論的創始人
//10^12: szabo    Nick Szabo  尼克薩博  密碼學家、智慧合約的提出者
//10^15: finney   Hal Finney   芬尼  密碼學家、工作量證明機制(POW)提出
//10^18: ether     以太
1)條件:貓沒有在賣貓/交配權的拍賣中,沒有懷孕,
2)有交易費率,交易稅率以當前價格乘以稅率,拍賣價格以wei為單位
3)新增拍賣後,貓的所屬就交給拍賣合約了,貓不能做其他事情了
4)拍賣持續時間大於1分鐘,起始價格一般(也可以低)比最後價格高,價格線性下降
5)買家出價不低於當前價格就能買下(比如初始100,最後50,100秒,在第20秒時,價格20*(100-50)/100+50=60,出價大於等於60即可)
    真正扣費時是以交易時價格為準,剩餘的金額會返回給買家
6)會記錄0代貓的最新5只拍賣價格

五、拍賣交配權

1)條件:貓沒有在貓交易/交配權的拍賣中,可以交配;符合交配規則
2)固定交易費
3)拍賣成功了直接繁殖

六、宣傳貓和初代貓(COO有許可權)

1)宣傳貓直接給賬號,初代貓放到市場拍賣;宣傳貓5000只,初代貓45000只
2)初代貓預設價格10 Finney,預設拍賣時間1天

3)下一隻初代貓開始價格為前五隻貓的平均拍賣價格的1.5倍,如果小於預設價格,設為預設價格,最後價格為0,開始拍賣!

    /// @dev we can create promo kittens, up to a limit. Only callable by COO
    /// @param _genes the encoded genes of the kitten to be created, any value is accepted
    /// @param _owner the future owner of the created kittens. Default to contract COO
    function createPromoKitty(uint256 _genes, address _owner) external onlyCOO {
        address kittyOwner = _owner;
        if (kittyOwner == address(0)) {
             kittyOwner = cooAddress;
        }
        require(promoCreatedCount < PROMO_CREATION_LIMIT);

        promoCreatedCount++;
        _createKitty(0, 0, 0, _genes, kittyOwner);
    }

    /// @dev Creates a new gen0 kitty with the given genes and
    ///  creates an auction for it.
    function createGen0Auction(uint256 _genes) external onlyCOO {
        require(gen0CreatedCount < GEN0_CREATION_LIMIT);

        uint256 kittyId = _createKitty(0, 0, 0, _genes, address(this));
        _approve(kittyId, saleAuction);

        saleAuction.createAuction(
            kittyId,
            _computeNextGen0Price(),
            0,
            GEN0_AUCTION_DURATION,
            address(this)
        );

        gen0CreatedCount++;
    }