Skip to content

Latest commit

 

History

History
257 lines (198 loc) · 10.1 KB

02-ownable.md

File metadata and controls

257 lines (198 loc) · 10.1 KB
title actions requireLogin material
Ownable コントラクト
答え合わせ
ヒント
true
editor
language startingCode answer
sol
zombiefactory.sol zombiefeeding.sol ownable.sol
pragma solidity ^0.4.19; // 1. ここにインポートせよ // 2. ここで継承せよ: contract ZombieFactory { event NewZombie(uint zombieId, string name, uint dna); uint dnaDigits = 16; uint dnaModulus = 10 ** dnaDigits; struct Zombie { string name; uint dna; } 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; 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); } }
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; function setKittyContractAddress(address _address) external { kittyContract = KittyInterface(_address); } function feedAndMultiply(uint _zombieId, uint _targetDna, string _species) public { require(msg.sender == zombieToOwner[_zombieId]); Zombie storage myZombie = zombies[_zombieId]; _targetDna = _targetDna % dnaModulus; uint newDna = (myZombie.dna + _targetDna) / 2; if (keccak256(_species) == keccak256("kitty")) { newDna = newDna - newDna % 100 + 99; } _createZombie("NoName", newDna); } function feedOnKitty(uint _zombieId, uint _kittyId) public { uint kittyDna; (,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId); feedAndMultiply(_zombieId, kittyDna, "kitty"); } }
/** * @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; } }
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; struct Zombie { string name; uint dna; } 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; 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); } }

どれ、前のチャプターでセキュリティホールに気がついたか?

setKittyContractAddressexternalだから、だれでも呼び出すことができるのだ!つまり、この関数を呼び出した奴はクリプトキティーズのコントラクトのアドレスを変更することで、我々のアプリをめちゃくちゃにすることが可能なのだ。

もちろん、アドレスの更新ができるようにするつもりだが、誰でも更新できるようにしたいわけでない。

ではどうすれば良いかというと、よくあるやり方としては、コントラクトをOwnable(所有可能)とすることだ。これはコントラクトには特別な権限を持つオーナー(所有者)がいることを意味するものだ。

OpenZeppelinの Ownable コントラクト

一つ例を見せてやろう。これはSolidityのライブラリにある OpenZeppelin というOwnableコントラクトだ。OpenZeppelinは安全でしかもコミュニティで検証を経たスマートコントラクトだ。これをDAppで使うことができる。このレッスンの後で、OpenZeppelinのサイトをチェックしておくように。今後非常に役に立つだろう!

下のコントラクトをよく読むように。まだわからないことがいくつもあるが、あとで教えるから今は気にすることはない。

/**
 * @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;
  }
}

新しいものがいくつかあるから、解説してやるぞ:

  • コンストラクタ: function Ownable()は**コンストラクタ**だ。これは特別な関数で、コントラクトと同じ名前だ。コントラクトが最初に作成された時に、1度だけ実行されるぞ。
  • 関数修飾子:修飾子は半分関数のようなもので、他の関数を編集する際に使うものだ。通常は実行する前に要件をチェックするために使用するぞ。この例で言えば、onlyOwnerowner(オーナー)だけが関数を実行できるように、制限をアクセスするために使用されているのだ。関数修飾子については次のチャプターで詳しく説明してやろう。_; という奇妙なものについてもな。
  • indexed キーワード:これは無視して良い。必要ない。

Ownableコントラクトは基本的には次のようになる。これを覚えておくようにな:

  1. コントラクトが作られた時、コンストラクタがownermsg.sender (実行した人物だ)に設定する。

  2. onlyOwner修飾子を追加して、ownerだけが特定の関数にアクセスできるように設定する。

  3. 新しいownerにコントラクトを譲渡することも可能だ

onlyOwnerは誰もが皆必要としているものだから非常に一般的になった。だからSolidityのDAppを開発するときには、皆がこのOwnableコントラクトをコピーペーストしてから、最初のコントラクトの継承を始めているのだ。 我々もsetKittyContractAddressonlyOwnerに制限したいから、同じ様にするのだぞ。

それではテストだ

新しいファイルの ownable.solにすでにOwnableのコードをコピーしてある。そこでZombieFactory を継承させることにする。

  1. ownable.solimportするように我々のコードを変更せよ。なに、やり方を忘れてしまっただと?その場合はzombiefeeding.solを見て思い出すのだ。

  2. ZombieFactoryコントラクトを編集してOwnableを継承させよ。やり方を忘れていたらzombiefeeding.sol を見て思い出すように。