[CrytoZombie]basic-ゾンビ戦闘システム

37762 ワード

この文章はCrytoZombieを勉強するときに整理した内容です.
CrytoZombie

Ch1. Payable


せいぎょ者

  • アクセス制御者(可視性修飾子):制御関数がいつどこで呼び出されるかを制御する制御者
  • private:契約内の他の関数でのみ
  • を呼び出すことができます.
  • internal:privateと似ていますが、その約束を継承する約束から呼び出すこともできます.
  • external:コーディネータの外部でのみ呼び出すことができます.
  • public:内部から外部まで、どこでも呼び出すことができます.
  • ステータスコントローラ(ステータス修飾子):ブロックチェーンとのインタラクション方法を教えてください.
  • view:この関数を実行すると、データは保存/変更されません.
  • pure:この関数は、ブロックチェーンにデータが格納されず、ブロックチェーンからデータが読み込まれないことをユーザーに伝えます.
    これらはすべてコース外で吹くときにガスを全く消費しません.しかし,他の関数内部で呼び出されるとガスが消費される.
  • カスタムコントロール:onlyOwnerと前のレベル.これらの制御者を使用すると、私たち自身の論理を構成し、これらの制御者が関数にどのように影響するかを決定することができます.
  • 未払管理者


    通常のWebサーバ上でAPI関数を実行する場合、関数呼び出しによってドルを送信することはできません.△ビットコインも送れません.しかし、イーサネットでは、お金(イーサネット)、データ(トランザクション負荷)、契約コード自体がイーサネットに存在するため、関数を実行しながら契約の支払いを行うことができます.
    contract OnlineStore {
      function buySomething() external payable {
        // 함수 실행에 0.001이더가 보내졌는지 확실히 하기 위해 확인:
        require(msg.value == 0.001 ether);
        // 보내졌다면, 함수를 호출한 자에게 디지털 아이템을 전달하기 위한 내용 구성:
        transferThing(msg.sender);
      }
    }
    
    // web3.js
    // `OnlineStore`는 자네의 이더리움 상의 컨트랙트를 가리킨다고 가정하네:
    OnlineStore.buySomething({from: web3.eth.defaultAccount, value: web3.utils.toWei(0.001)})
  • msg.value
    これは、
  • コーディネータがどれだけのデータを送信したかを決定する方法であり、イーサは基本的に含まれる単位である.
  • javascript関数呼び出しで、このフィールドを介して送信されるetherの数を決定します.
  • トランザクションを封筒とし、関数呼び出しに渡されるパラメータをあなたが書いた手紙の内容とすると、価値は現金を封筒に入れることになります.
  • を参照してください.関数が支払可能として表示されていない場合、画像に表示されているようにイーサを送信してみると、関数はトランザクションを拒否します.
  • Ch2. 金を出す


    契約書にエーテルを送ったら、お金を出します.
    contract GetPaid is Ownable {
      function withdraw() external onlyOwner {
        owner.transfer(this.balance);
      }
      
      // 되돌려주는 함수도 만들 수 있다. 
      uint itemFee = 0.001 ether;
      msg.sender.transfer(msg.value - itemFee);
    }
    
  • 伝送関数を使用してイーサを特定のアドレスに送信する.
  • this.balanceはContractに格納されているすべての残高を返します.
  • Ch4. 乱数


    keccak 256を使用して整数を生成

    // Generate a random number between 1 and 100:
    uint randNonce = 0;
    uint random = uint(keccak256(now, msg.sender, randNonce)) % 100;
    randNonce++;
    uint random2 = uint(keccak256(now, msg.sender, randNonce)) % 100;
  • nowのタイムスタンプ値、msg.senderは、増加したnonce(1回の数字、すなわち同じ入力のみを使用し、2回以上同じハッシュ関数を実行できない)を受け入れている.
  • keccakを使用して入力を任意のハッシュ値に変換し、変換後のハッシュ値をuintに変換し、%100を使用して最後の2桁の数字のみを受信します.
  • により、0から99の間の完全な数字が得られる.
  • 不誠実ノードが攻撃を受けやすい理由


    Ethernetでcontractの関数を実行すると、トランザクションとしてネットワーク内の1つ以上のノードに実行通知が発行されます.その後、ネットワーク内のノードは複数のトランザクションを収集し、まず「タスク証明」と呼ばれる計算が非常に複雑な数学的問題を解くことを試みます.その後、対応するトランザクション・グループを、それぞれの作業証明書(PoW)とともにブロックとしてネットワークに配備します.
    あるノードがPoWを解放すると、他のノードはそのPoWの解放を停止し、そのノードが送信したトランザクション・リストが有効であることを確認します.有効な場合は、ブロックを受け入れ、次のブロックの解凍を開始します.
    これは私たちの構造関数を脆弱にします.
    例えば、コイン投げの試合では、正面が現れるとお金が2倍になり、裏面が現れると全員が失う.前後ビューを決定する際には,上に見た乱関数を用いると仮定する.(random>=50はフロントビュー、random<50はバックビュー).
    ノードを実行している場合は、共有せずにノードトランザクションを通知するしかありません.その後、私が勝ったことを確認するために、コイン関数を実行することができます.また、もし私が負けたら、私が解凍している次のブロックにトランザクションを含まないことを選択します.これは無限に繰り返すことができて、私が最終的にコインに勝って、次の街を解いて、私はそこから利益を得ることができます.

    Ch5. 概要

  • ゾンビの中から1つを選び、相手のゾンビを攻撃対象とする.
  • 攻撃側のゾンビなら70%の勝率がある.防御側のゾンビは30%の勝率を持つ.
  • すべてのゾンビ(攻撃、防御はすべて)は戦闘結果とともに増加した利益と損失のカウントがある.
  • 攻撃側のゾンビが勝つとゾンビのレベルが上がり、新しいゾンビが現れます.
  • ゾンビなら何も起こらない.(ゾンビを除くlossCountが増加).
  • ゾンビが勝つかどうかにかかわらず、攻撃側ゾンビの冷却時間がアクティブになります.
  • = zombieattack.sol
    import "./zombiehelper.sol";
    contract ZombieBattle is ZombieHelper {
      uint randNonce = 0;
      uint attackVictoryProbability = 70;
    
      function randMod(uint _modulus) internal returns(uint) {
        randNonce++;
        return uint(keccak256(now, msg.sender, randNonce)) % _modulus;
      }
    
      function attack(uint _zombieId, uint _targetId) external ownerOf(_zombieId) {
        Zombie storage myZombie = zombies[_zombieId];
        Zombie storage enemyZombie = zombies[_targetId];
        uint rand = randMod(100);
        if (rand <= attackVictoryProbability) {
          myZombie.winCount++;
          myZombie.level++;
          enemyZombie.lossCount++;
          feedAndMultiply(_zombieId, enemyZombie.dna, "zombie");
        } else {
          myZombie.lossCount++;
          enemyZombie.winCount++;
        }
        _triggerCooldown(myZombie);
      }
    }
    = zombiehelper.sol
    pragma solidity ^0.4.19;
    
    import "./zombiefeeding.sol";
    
    contract ZombieHelper is ZombieFeeding {
    
      uint levelUpFee = 0.001 ether;
    
      modifier aboveLevel(uint _level, uint _zombieId) {
        require(zombies[_zombieId].level >= _level);
        _;
      }
    
      function withdraw() external onlyOwner {
        owner.transfer(this.balance);
      }
    
      function setLevelUpFee(uint _fee) external onlyOwner {
        levelUpFee = _fee;
      }
    
      function changeName(uint _zombieId, string _newName) external aboveLevel(2, _zombieId) ownerOf(_zombieId) {
        zombies[_zombieId].name = _newName;
      }
    
      function changeDna(uint _zombieId, uint _newDna) external aboveLevel(20, _zombieId) ownerOf(_zombieId) {
        zombies[_zombieId].dna = _newDna;
      }
    
      function getZombiesByOwner(address _owner) external view returns(uint[]) {
        uint[] memory result = new uint[](ownerZombieCount[_owner]);
        uint counter = 0;
        for (uint i = 0; i < zombies.length; i++) {
          if (zombieToOwner[i] == _owner) {
            result[counter] = i;
            counter++;
          }
        }
        return result;
      }
    
    }
    = zombiefeeding.sol
    pragma solidity ^0.4.19;
    
    import "./zombiefactory.sol";
    
    contract KittyInterface {
      function getKitty(uint256 _id) external view returns (
        bool isGestating,
        bool isReady,
        uint256 cooldownIndex,
        uint256 nextActionAt,
        uint256 siringWithId,
        uint256 birthTime,
        uint256 matronId,
        uint256 sireId,
        uint256 generation,
        uint256 genes
      );
    }
    
    contract ZombieFeeding is ZombieFactory {
    
      KittyInterface kittyContract;
    
      modifier ownerOf(uint _zombieId) {
        require(msg.sender == zombieToOwner[_zombieId]);
        _;
      }
    
      function setKittyContractAddress(address _address) external onlyOwner {
        kittyContract = KittyInterface(_address);
      }
    
      function _triggerCooldown(Zombie storage _zombie) internal {
        _zombie.readyTime = uint32(now + cooldownTime);
      }
    
      function _isReady(Zombie storage _zombie) internal view returns (bool) {
          return (_zombie.readyTime <= now);
      }
    
      function feedAndMultiply(uint _zombieId, uint _targetDna, string _species) internal ownerOf(_zombieId) {
        Zombie storage myZombie = zombies[_zombieId];
        require(_isReady(myZombie));
        _targetDna = _targetDna % dnaModulus;
        uint newDna = (myZombie.dna + _targetDna) / 2;
        if (keccak256(_species) == keccak256("kitty")) {
          newDna = newDna - newDna % 100 + 99;
        }
        _createZombie("NoName", newDna);
        _triggerCooldown(myZombie);
      }
    
      function feedOnKitty(uint _zombieId, uint _kittyId) public {
        uint kittyDna;
        (,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);
        feedAndMultiply(_zombieId, kittyDna, "kitty");
      }
    }
    = zombiefactory.sol
    pragma solidity ^0.4.19;
    
    import "./ownable.sol";
    
    contract ZombieFactory is Ownable {
    
        event NewZombie(uint zombieId, string name, uint dna);
    
        uint dnaDigits = 16;
        uint dnaModulus = 10 ** dnaDigits;
        uint cooldownTime = 1 days;
    
        struct Zombie {
          string name;
          uint dna;
          uint32 level;
          uint32 readyTime;
          uint16 winCount;
          uint16 lossCount;
        }
    
        Zombie[] public zombies;
    
        mapping (uint => address) public zombieToOwner;
        mapping (address => uint) ownerZombieCount;
    
        function _createZombie(string _name, uint _dna) internal {
            uint id = zombies.push(Zombie(_name, _dna, 1, uint32(now + cooldownTime), 0, 0)) - 1;
            zombieToOwner[id] = msg.sender;
            ownerZombieCount[msg.sender]++;
            NewZombie(id, _name, _dna);
        }
    
        function _generateRandomDna(string _str) private view returns (uint) {
            uint rand = uint(keccak256(_str));
            return rand % dnaModulus;
        }
    
        function createRandomZombie(string _name) public {
            require(ownerZombieCount[msg.sender] == 0);
            uint randDna = _generateRandomDna(_name);
            randDna = randDna - randDna % 100;
            _createZombie(_name, randDna);
        }
    
    }
    = ownable.sol
    /**
     * @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;
    
      event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    
      /**
       * @dev The Ownable constructor sets the original `owner` of the contract to the sender
       * account.
       */
      function Ownable() public {
        owner = msg.sender;
      }
    
    
      /**
       * @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) public onlyOwner {
        require(newOwner != address(0));
        OwnershipTransferred(owner, newOwner);
        owner = newOwner;
      }
    
    }