如何以DApp的方式實現匿名版的以太貓

買賣虛擬貨幣
現在區塊鏈開發平臺的隱私保護問題

我們現在基本都知道以太坊是一個區塊鏈應用開發平臺,基於以太坊構建的網路和智慧合約語言,我們可以實現各種各樣的與現實世界接近的分散式應用(DApp)。這種基於智慧合約實現去中心化的分散式應用固然是一種創新,但不覺又引發了市場對資訊保安的擔憂。目前透過以太坊區塊瀏覽器很容易就能追溯一個賬號的所有交易情況,以及該賬戶資金情況等,這基本上沒有任何門檻,只要你能上網就能分析一個賬號的資金流向,以及持幣等情況,這對個人來說毫無隱私可言。

SERO:世界上首個支援智慧合約的匿名公鏈

目前門羅幣(Monero),達世幣(DASH)以及大零幣(Zcash)等熱門的匿名區塊鏈技術。雖然都能匿名起到隱私保護的作用,但是它們都不支援智慧合約,無法用來開發DApp。超零協議(SERO)[白皮書]的公鏈,是個支援圖靈完備智慧合約的匿名公鏈,採用零知識證明實現隱私保護技術,並且已經發布beta版本。SERO似乎是隱私保護的一個完美方案,並且可以在其上開發匿名的DAPP。

以太坊上的以太貓程式概述

以太貓應該是以太坊迄今為止最成熟,最成功的Dapp。它在很短的時間內造成了以太坊的交易擁堵。以太貓是按照ERC721開發的,智慧合約程式碼大約兩千行,原始碼在github上有開源,並且也可以在以太坊區塊瀏覽器中找到。

以太貓程式包括了七個主程式:

1、KittyAccessControl,這個合約管理只能由特定角色執行操作的各種地址和約束。這些角色叫CEO, CFO and COO;
2、KittyBase,這個合約定義在整個核心功能中共享的最基本程式碼的地方。這包括我們的主要資料儲存,常量和資料型別,以及用於管理這些資料的內部函式;
3、KittyOwnership,這提供了遵循ERC-721規範草案的基本不可互換令牌交易所需的方法;
4、KittyBreeding,這個檔案包含了將貓一起繁殖所必需的方法,包括跟蹤繁殖提供者,並依靠外部基因組合合約;
5、KittyAuctions,在這裡,有公開的方法來拍賣貓或招標貓或繁殖貓。實際的拍賣功能是在兩個兄弟合約(一個用於買賣,一個用於繁殖)中處理的,而拍賣的建立和投標主要是透過核心合約;
6、KittyMinting,該合約包含用來建立新的gen0貓的功能;
7、KittyCore,這是主要的CryptoKitties合約,編譯和執行在以太坊區塊鏈上。這份合約把所有東西聯絡在一起。

匿名版以太貓實現

按照SERO的DApp程式設計規範,可以實現一款匿名版以太貓的DApp。目前SERO支援"發行匿名票據"的功能已經發布,這個功能對應的就是以太坊ERC721協議,是實現以太貓的基礎。

票據(Ticket)相關介面定義

SERO團隊部署了一個Remix-ide的站點,其中有一個名為SeroInterface.sol的例子,主要是提供釋出匿名token和ticket的介面,這些應該是系統介面,只要是想實行匿名就必須繼承的。根據SERO團隊提供的例子,在生成、轉移Ticket的介面中必須包含系統定義好的日誌Topic

 /**
  * the follow topics is system topics,can not be changed at will
  */
  bytes32 private topic_sero_send           =  0x868bd6629e7c2e3d2ccf7b9968fad79b448e7a2bfb3ee20ed1acbc695c3c8b23;  bytes32 private topic_sero_allotTicket    =  0xa6a366f1a72e1aef5d8d52ee240a476f619d15be7bc62d3df37496025b83459f;  bytes32 private topic_sero_category       =  0xf1964f6690a0536daa42e5c575091297d2479edcc96f721ad85b95358644d276;  bytes32 private topic_sero_ticket         =  0x9ab0d7c07029f006485cf3468ce7811aa8743b5a108599f6bec9367c50ac6aad;
從上面定義的topic來看每種型別的Ticket都必須有category屬性,類似於ERC721中的syboml。SeroInterface主要提供了以下幾個介面是本次寫匿名版以太貓需要用到的:1.生成ticketId,並將ticketId直接存入到個人賬號中去
/**
   * @dev generate a tickeId and allot to the receiver address
   * @param _receiver receiving address of tickeId
   * @param _value  the seq of tickeId,can be zero. if zero the system ,the system randomly generates
   * @param _category the category of the ticket
   */
  function sero_allotTicket(address _receiver, bytes32 _value, string memory _category) internal returns (bytes32 ticket){
      bytes memory temp = new bytes(96);
      assembly {
          let start := temp          mstore(start, _value)          mstore(add(start, 0x20), _receiver)          mstore(add(start, 0x40), _category)          log1(start, 0x60, sload(topic_sero_allotTicket_slot))
          ticket := mload(add(start, 0x40))
      }      return;
  }

2.獲取交易引數中的category

  /**
  * @dev the get category from the tx params
  */
  function sero_msg_category() internal returns (string) {
      bytes memory tmp = new bytes(32);
      bytes32 b32;
      assembly {
          log1(tmp, 0x20, sload(topic_sero_category_slot))
          b32 := mload(tmp)
      }    return bytes32ToString(b32);
  }
3.獲取交易引數中的ticketId
 /**
  * @dev the get ticketId from the tx params
  */
  function sero_msg_ticket() internal returns (bytes32 value) {
      bytes memory tmp = new bytes(32);
      assembly {
          log1(tmp, 0x20, sload(topic_sero_ticket_slot))          value := mload(tmp)
      }      return;
  }

4.將交易中的ticketId存入到接收方的個人賬號

 /**
   * @dev transfer the tickeId to the receiver
   * @param _receiver the address of receiver
   * @param _category the category of ticket
   * @param _ticket the tickeId
   */
  function sero_send_ticket(address _receiver, string memory _category, bytes32 _ticket)internal returns (bool success){
      return sero_send(_receiver,"",0,_category,_ticket);
  }  /**
  * @dev transfer the token or ticket to the receiver
  * @param _receiver the address of receiver
  * @param _currency the currency of token
  * @param _amount the amount of token
  * @param _category the category of the ticket
  * @param _ticket the Id of the ticket
  */
  function sero_send(address _receiver, string memory _currency, uint256 _amount, string memory _category, bytes32 _ticket) internal returns (bool success){
    bytes memory temp = new bytes(160);
    assembly {
      mstore(temp, _receiver)
      mstore(add(temp, 0x20), _currency)
      mstore(add(temp, 0x40), _amount)
      mstore(add(temp, 0x60), _category)
      mstore(add(temp, 0x80), _ticket)
      log1(temp, 0xa0, sload(topic_sero_send_slot))      success := mload(add(temp, 0x80))
    }    return;
}
繼承SeroInterface的智慧合約生成的ticketId將直接儲存到個人賬號中去,智慧合約無需管理個人賬號資產,因此當轉移ticket的時候,ticketId和category必須透過交易引數傳遞,無法透過智慧合約方法的引數傳遞,轉移的ticketId最終也將直接儲存到接收方的個人賬號中去。弄清楚上面這幾個SERO提供的介面後,以太貓的改動就很簡單了,某種程度上在SERO上寫以太貓會更簡單,而且程式碼量應該會更少。KittyBase
由於SERO上ticekId的資料型別全部變為bytes32,一次需要將KittyBase中所有ticketId的資料型別全部有uint32變成bytes32
  struct Kitty {
      bytes32 kittyId;
      uint256 genes;
      uint64 birthTime;
      uint64 cooldownEndBlock;
      bytes32 matronId;
      bytes32 sireId;
      bytes32 siringWithId;
      uint16 cooldownIndex;
      uint16 generation;
      address owner;
  }
kittyId的資料型別轉變後,相應的一些資料介面也需要做調整,需要將KittyBase中的kittys由陣列型別改成map mapping(bytes32 => Kitty) kittys;從SERO的原始碼來看,做匿名交易的第一步就是匿名賬號地址,所有交易中涉及的賬號地址都被一次性地址給替換,所以之前的KittyBase中的sireAllowedToAddress修改為sireAllowedToTokenId。由tickeId到賬號的對映修改為tickeId到ticketId的對映。
  mapping (bytes32 => bytes32) public sireAllowedToTokenId;刪除kittyIndexToOwner、ownershipTokenCount兩個屬性,因為所有的ticekId最終都儲存在個人賬號中,因此無需智慧合約來儲存ticketId和賬號之間的關係。生成Kitty的時候在createKitty方法中直接呼叫SeroInterface.sol中的seroallotTicket方法生成kittyId並且將kittyId儲存到owner的個人賬號中去,無需呼叫一次_transfer方法。KittyOwnership由於透過智慧合約建立的kittyId建立後就直接傳送到個人賬號中去了,因此該檔案中無需ownerOf、tokensOfOwner這樣的方法,只要你能夠在你的個人賬戶中查詢到並且能夠透過交易引數將kittyId傳遞到智慧合約中,就證明你這個賬號擁有這個kittyId。因此該智慧合約最終只有transfer和totalSupply兩個方法。因為本次是簡單的嘗試,很多地方從簡了。刪除了approve相關的方法,需要加上的話,我想思路就是將kittyId到賬號的對映修改為kittyId到kittyId的對映,弱化地址。只要你能將kittyId透過交易傳送出去,就能證明你有擁有這個kittyId,反過來推理就能證明kittyId擁有者就是你所需要授信的賬號地址。在最終的實現過程中發現approve方法在匿名版的以太貓中可以不需要。
KittyBreeding這個檔案應該不需要怎麼改動,由於將sireAllowedToAddress修改為sireAllowedToTokenId,因此只需要修改下approveSiring方法。
function approveSiring(bytes32 _matronId)    external
    whenNotPaused
    {
        //get _sireId A Kitty that you own from the tx param
        bytes32 _sireId = sero_msg_ticket();
        sireAllowedToKittyId[_sireId] = _matronId;
        //Re-save _sireId to your personal account after approval
        sero_send_ticket(msg.sender,symbol,_sireId);
    }
sireId就是透過交易引數傳遞給智慧合約的,因此需要呼叫SeroInterface的seromsg_ticket的方法獲取交易引數的中的ticekId。因為sireId是透過交易引數傳遞,SERO鏈上會將sireId從個人賬號中標記為已經使用掉,你將不再擁有該sireId。但此處只是實現授權,並不需要發生資產轉移,因此在方法的最後還需要將該sireId再次存入到個人賬號中去。KittyAuction刪除createSaleAuction、createSiringAuction方法中的kittyId引數,所有該引數的獲取都呼叫SeroInterface的seromsg_ticket的方法從交易引數中獲取。此處和以太坊的實現稍微有些區別,以太坊之前的做法是先要對saleAuction、siringAuction進行授信,然後再透過nonFungibleContract將_kitttyId轉移到自己賬號中進行託管。由於SERO鏈上的kittyId都存在個人賬號中,並且建立拍賣的時候kittyId都是透過交易引數傳遞,一旦交易成功,kittyId不可能再次使用這個kittyId,因此無需託管這一步,等bid方法被呼叫後直接將kittyId存入到winner的個人賬號中去。
刪除bidOnSiringAuction方法中的matronId的引數,呼叫者透過交易引數傳遞,然後在方法體中透過seromsg_ticket方法獲取。此處和KittyBreeding中的approveSiring有類似的情況,matronId透過交易引數傳遞很重要的一點就是為做一個身份校驗,並不反生資產的轉移,因此在方法的最後還需要將該matronId再次存入到個人賬號中去至此,以太貓的核心部分就都修改完成,由於GeneScienceInterface的智慧合約未公佈,因此mixGenes方法用生成隨機數的方法替代。有興趣的可以去網上找破解版的。總結以太貓核心部分就是基於以太坊的ERC721(非同質代幣協議),其次就是核心的業務邏輯。本次實現匿名版的以太貓主要是調整ERC721介面實現部分,根據SERO團隊在SeroInterface.sol中提供的介面完全能夠實現以太坊ERC721的功能,因此以上的調整主要是針對kittyId的生成,儲存,鑑權以及引數的傳遞。因為SERO上生成的kittyId無論是建立還是發生交易最終都會直接存執到個人賬號中去,因此智慧合約無需管理kittyId的歸屬。在呼叫智慧合約方法的時候,凡是涉及到需要傳遞自己擁有的kittyId的地方都必須透過交易引數傳遞到智慧合約中去。需要特別注意的地方是,SERO鏈上會將交易引數中的kittyId從個人賬號中標記為已經使用掉,你將不再擁有該kittyId。如果透過交易引數傳遞的kittyId僅作為鑑權使用而不需要發生資產轉移,切記在智慧合約的方法中還需要將該kittyId再次存入到個人賬號中去。

SERO實現匿名的第一步就是對地址作文章,無論是交易中的地址還是傳入到智慧合約中地址都將轉換成一次性地址,最終只有擁有該地址私有的人才能查詢到自己的資產情況並且使用這些資產,這無形當中弱化了地址的功效。在以太貓中存在將kittyId授信給一個地址的情況,在SERO中顯然這一點無法做到這一點,因為每次傳入到智慧合約中的地址可能都不一樣了,即使是同一個地址。雖然無法實現對一個地址直接授信,但SERO同樣可以實現類似的功能,只需要我們轉換下思路就可以了。因為SERO的所有資產都儲存到個人賬戶中去了,因此對於擁有非同質代幣的人,在某種情況下一個tickeId就能證明一個地址的身份,因此以太貓中類似將kittyId授信給一個地址的時候,都可以替換為將一個kittyId授信給另外一個kiettyId這樣的方式來實現,授信方的kittyId必須透過交易引數傳遞到智慧合約中。搞清楚以上一些主要的變化後,在不變動以太貓的業務邏輯的情況下,就能很容易將其匿名化。對比原版程式碼量會更少,而且資產的管理邏輯也會減少,實現起來主要的重心就在業務邏輯上了。

透過SERO,不但可以實現以太貓匿名化,而且SERO之於智慧合約,在支援匿名的基礎上還簡化了資產管理邏輯,使所有的DApp開發者可以將重心放到業務邏輯開發上來。某種情況下SERO降低了透過智慧合約釋出代幣的門檻,使傳統App開發者可以更加容易上手開發自己的DApp。

匿名版以太貓的Github原始碼:
https://github.com/kusun/Sero-CryptoKitties

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读

;