初心者文系主婦がブロックチェーンを学ぶブログ

IT革命のビッグウェーブに乗り遅れた主婦が、ブロックチェーン革命の波にのるべく、ブロックチェーン技術を学ぶブログです。

【CryptZombies】レッスン5チャプター14:まとめ

このレッスンで学んだこと:

トークン、ERC721規格、トレード可能な資産/ゾンビ
・ライブラリとその使い方
・SafeMath関数を使ったオーバーフローとアンダーフロー対策
・コード中のコメントとnatspecスタンダード

このレッスンでゾンビゲームのSolidityコードは終わりです!(H30.12.29現在、将来さらにレッスンが追加される可能性あり)

【CryptZombies】レッスン5チャプター14:まとめ

このレッスンで学んだこと:

トークン、ERC721規格、トレード可能な資産/ゾンビ
・ライブラリとその使い方
・SafeMath関数を使ったオーバーフローとアンダーフロー対策
・コード中のコメントとnatspecスタンダード

このレッスンでゾンビゲームのSolidityコードは終わりです!(H30.12.29現在、将来さらにレッスンが追加される可能性あり)

【CryptZombies】レッスン5チャプター13:コメント

コメントのシンタックス(構文)

SolidityにおけるコメントはJavaScriptのものと似ています。

// これは1行コメントだ。自分への(または他者へ向けた)注意書きのようなものだ。

ただ//をコメントする箇所に加えれば良い。非常に簡単です。


こんな複数行のコメントもできます:

contract CryptoZombies {
  /* This is a multi-lined comment. I'd like to thank all of you
    who have taken your time to try this programming course.
    I know it's free to all of you, and it will stay free
    forever, but we still put our heart and soul into making
    this as good as it can be.

    Know that this is still the beginning of Blockchain development.
    We've come very far but there are so many ways to make this
    community better. If we made a mistake somewhere, you can
    help us out and open a pull request here:
    https://github.com/loomnetwork/cryptozombie-lessons

    Or if you have some ideas, comments, or just want to say
    hi - drop by our Telegram community at https://t.me/loomnetwork
  */
}

特に、コントラクト中の各関数に期待する働きをコメントして説明するのは良いことです。こうして他の開発者が、コード自体を読まずに、コードの働きの概要をさっと理解することができます。


Solidityのコミュニティでは、 natspec というフォーマットを用いることがスタンダードとなっています。

/// @title A contract for basic math operations
/// @author H4XF13LD MORRIS 💯💯😎💯💯
/// @notice For now, this contract just adds a multiply function
contract Math {
  /// @notice Multiplies 2 numbers together
  /// @param x the first uint.
  /// @param y the second uint.
  /// @return z the product of (x * y)
  /// @dev This function does not currently check for overflows
  function multiply(uint x, uint y) returns (uint z) {
    // This is just a normal comment, and won't get picked up by natspec
    z = x * y;
  }
}

@title @authorはそのままの意味です。

@notice は ユーザー に、コントラクトや関数が何を行うか説明します。
@devは開発者向けのさらなる詳細の説明です。

@param@returnでは、関数の各パラメーターが何であり、どんな値を返すのかを記述します。

すべてのタグはオプショナルなので、各関数にこれらすべてのタグを使用すべきというわけではありません。しかし最低でも@devタグで各関数の働きを説明することはしておきましょう

テストの実行

今まで気づかなかったかもしれないが、クリプトゾンビの答え合わせの際コメントは無視されている。なのでこのチャプターではnatspecコードの答え合わせができない ;)
だがこれまでお主はSolidityの達人だったから、これもできると仮定しよう!
どちらにせよ挑戦してみるのだ。ZombieOwnershipにnatspecタグを加えてみよう:

①@title — 例:ゾンビ所有権の移転を管理するコントラクト

/// @title ゾンビ所有権の移転を管理するコントラクト

②@author — お主の名前だ!

/// @author nomadomama

③@dev — 例:OpenZeppelinのERC721ドラフト実装に準拠

/// @dev OpenZeppelinのERC721ドラフト実装に準拠


お疲れさまでした!


<参考>
CryptoZombies - イーサリアム上でゲームを開発する方法を学習。Powered by Loom Network

【CryptZombies】レッスン5チャプター11:SafeMathパート3

winCountやlossCount(uint16)、level(uint32)については、これらの引数でSafeMathのaddメソッドを使用すると、これらのタイプはuint256に変換されるのでオーバーフローを防げません。

function add(uint256 a, uint256 b) internal pure returns (uint256) {
  uint256 c = a + b;
  assert(c >= a);
  return c;
}

// If we call `.add` on a `uint8`, it gets converted to a `uint256`.
// So then it won't overflow at 2^8, since 256 is a valid `uint256`.

つまりuint16とuint32でオーバーフロー/アンダーフローを回避するには、さらに2つのライブラリを実装することが必要です。それらライブラリを、SafeMath16、SafeMath32と呼ぶことにします。

このコードは、uint256の全インスタンスがuint32やuint16で置きかえられること以外SafeMathと全く同じとなります。

テストの実行

①uint32にSafeMath32を使用することを宣言せよ。

using SafeMath32 for uint32; 

②uint16にSafeMath16を使用することを宣言せよ。

using SafeMath32 for uint32; 

③ZombieFactoryには、SafeMathメソッドを使うべき箇所がもう1行ある。どこかわかるようにコメントしておいたからな。

ownerZombieCount[msg.sender] = ownerZombieCount[msg.sender].add(1);


お疲れさまでした!


<参考>
CryptoZombies - イーサリアム上でゲームを開発する方法を学習。Powered by Loom Network

【CryptZombies】レッスン5チャプター10:SafeMathパート2

SafeMathライブラリの中のコード

library SafeMath {

  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
    if (a == 0) {
      return 0;
    }
    uint256 c = a * b;
    assert(c / a == b);
    return c;
  }

  function div(uint256 a, uint256 b) internal pure returns (uint256) {
    // assert(b > 0); // Solidity automatically throws when dividing by 0
    uint256 c = a / b;
    // assert(a == b * c + a % b); // There is no case in which this doesn't hold
    return c;
  }

  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
    assert(b <= a);
    return a - b;
  }

  function add(uint256 a, uint256 b) internal pure returns (uint256) {
    uint256 c = a + b;
    assert(c >= a);
    return c;
  }
}

初めに、libraryというキーワードが出てきました。ライブラリはcontractに似ていますが、少し違いがあります。
ここでの目的だと、ライブラリはusingというキーワードを使えるようにしますが、これで自動的にライブラリの全メソッドを別のデータ型に追加することができます

using SafeMath for uint;
// now we can use these methods on any uint
uint test = 2;
test = test.mul(3); // test now equals 6
test = test.add(5); // test now equals 11

mulとadd関数はそれぞれ2つの引数を必要としますが、using SafeMath for uintを宣言する際、関数上で呼び出すuint(test)は自動的に一つ目の引数として渡されることに気をつけましょう。

SafeMathが行っていること(addの中身のコードを見てみよう)

function add(uint256 a, uint256 b) internal pure returns (uint256) {
  uint256 c = a + b;
  assert(c >= a);
  return c;
}

基本的にaddは2つのuintを+のようにただ足していますが、その合計がaより大きいことを確認するassertステートメントを含んでいます。
こうしてオーバーフローから守ってくれるのです。

assertはrequireと同じようなものですが、偽の場合はエラーを投げます。
assertとrequireの違いは、requireは関数呼び出しが失敗した場合にユーザーにガスの残りを返却しますが、このときassertはそうしません。 なのでコード中ではrequireを使いたいところです。assertはコードにひどい間違いがおこった場合に一般的に使用されます(uintのオーバーフローのように)。

なのでSafeMathのadd、sub、mulそしてdiv関数をシンプルに使えば、基本的な四則演算を行うし、オーバーフローやアンダーフローの際にはエラーを投げてくれます。

SafeMathをコード中で使ってみよう

オーバーフローやアンダーフローを回避するため、コード中で+、-、*そして/を使った箇所を探し、add、sub、mul、divで置きかえましょう。

例) こうする代わりに:

myUint++;

こうしていこう:

myUint = myUint.add(1);


テストの実行

ZombieOwnershipの中で演算を行なった箇所が2つある。そこをSafeMathメソッドに換えてみよう。

①++をSafeMathメソッドで置きかえよ。

ownerZombieCount[_to] = ownerZombieCount[_to].add(1);

②--をSafeMathメソッドで置きかえよ。

ownerZombieCount[_from] = ownerZombieCount[_from].sub(1);


お疲れさまでした!


<参考>
CryptoZombies - イーサリアム上でゲームを開発する方法を学習。Powered by Loom Network

【CryptZombies】レッスン5チャプター9:オーバーフローとアンダーフロー

オーバーフロー&アンダーフロー とは?

例として、uint8は8ビットのみを持ちますが、つまりここに格納できる最大数値はバイナリ(2進数)の11111111ということです。
(またはデシマル(10進数)だと 2^8 - 1 = 255となる※)。
※0を含むため、「-1」をしています。(256個の数字がある。)
次のコードを見てください。最後はnumberはどうなるでしょうか?

uint8 number = 255;
number++;

この場合、オーバーフローの原因となってしまいます。つまりnumberを増やしても、0になるという通常の感覚に反することが起きます。
(もしバイナリの11111111に1を足すと、00000000に戻ってしまいます。時計が23:59から00:00になってしまうような感じ)。

アンダーフローも同様に、0と等しいuint8から1を引くと、255となります(uintは符号なしであるので、マイナスとなれない)。

SafeMathの使用

これらの問題をデフォルトで回避するために、OpenZeppelinはSafeMathという ライブラリ を作成しています。

ライブラリとは

library とは、Solidityにおける特別なタイプのコントラクトです。便利なことの一つとして、ネイティブデータ型への関数アタッチができます。
例えば、SafeMathライブラリで、using SafeMath for uint256のシンタックス(構文)を使うとします。
SafeMathライブラリはadd、sub、mulさらにdivの4つの関数を持ちます。(足す、引く、掛ける、割るの4つ。)そして以下のように、uint256からこれらの関数にアクセス可能です:

using SafeMath for uint256;

uint256 a = 5;
uint256 b = a.add(3); // 5 + 3 = 8
uint256 c = a.mul(2); // 5 * 2 = 10


テストの実行

まずSafeMathを使うことを我らがコントラクトに伝えよう。非常に基礎的なコントラクトZombieFactoryの中でこれを行う。こうすることで、それを継承するどのサブコントラクト内でもライブラリを使えるようになるからな。

①safemath.solをzombiefactory.solへインポートせよ。

import "./safemath.sol";

②using SafeMath for uint256;という宣言を加えよ。

using SafeMath for uint256;


お疲れさまでした!


<参考>
CryptoZombies - イーサリアム上でゲームを開発する方法を学習。Powered by Loom Network

【CryptZombies】レッスン5チャプター8:ERC721 takeOwnership

takeOwnership関数は、msg.senderがそのトークン/ゾンビを受け取ることを承認されているか確認し、承認されていれば_transfer関数を呼び出すだけでOKです。

テストの実行

①初めに、requireステートメントを使って、zombieApprovalsでキーが_tokenIdの場合にmsg.senderと同等となるようにせよ。こうしてもしmsg.senderがトークンを受け取ることが承認されていなかった場合、エラーが投げられる。

function takeOwnership(uint256 _tokenId) public {
    require(zombieApprovals[_tokenId] == msg.sender);
  }

②_transfer関数を呼び出すには、トークン所有者のアドレスを知る必要がある。(_fromの引数として必要だ)。幸い、このアドレスはownerOf関数で参照可能だ。ではownerというaddress変数を宣言し、それがownerOf(_tokenId)と同等となるようにせよ。

ownerOf関数はerc721.solに格納されているので、そちらを参照します。

function takeOwnership(uint256 _tokenId) public {
    require(zombieApprovals[_tokenId] == msg.sender);
    address owner = ownerOf(_tokenId);
  }

③最後に、_transfer関数を呼び出して、必要な情報全てを渡せ。(ここでは、_toの引数にmsg.senderを使う。なぜなら関数を呼び出しているのは、トークンが送られるべき者だからだ)。

function takeOwnership(uint256 _tokenId) public {
    require(zombieApprovals[_tokenId] == msg.sender);
    address owner = ownerOf(_tokenId);
    _transfer(owner, msg.sender, _tokenId);
  }

注: 上の2と3のステップは1行のコードにて行うことができるが、少しばかり読みやすくするために分割している。


お疲れさまでした!


<参考>
CryptoZombies - イーサリアム上でゲームを開発する方法を学習。Powered by Loom Network