Edited at

CryptoZombiesを用いたSolidityの学習(Lesson1)


はじめに

初めまして。某大学院修士課程のkaibaです。

今回はEthereumなどのスマートコントラクトを開発するときに使用されるSolidityという言語について学習します。

C言語やJavaといった言語と比べるとマイナーであるため、Solidityに特化した参考書はとても少ないです。

そこで、今回はCryptoZombiesを用いてSolidityを学んでいきます。


CryptoZombies

CryptoZombiesはLoom Network社が提供しているオンラインレッスンツールです。

ゾンビゲームの開発を通じてSolidtyを学ぶことができます。

無料であり、かつ、日本語に対応していることから、Solidity初心者でも学びやすいツールです。(ちなみに私もSolidity初心者です...)

CryptoZombiesの画面は中央で2分割にされています。左画面にはソースの説明・演習の指示が記載されており、右画面にはソースを入力するためのエディターが表示されます。左画面の指示に沿ってプログラミングをしましょう。

夢のDApps開発を目指して、私とSolidityをマスターしましょう(^^)


Lesson1

それでは始めましょう。

Lesson1には15個のChapterがあり、このゲームで使用するキャラクターであるゾンビを作成します。


Chapter1

このChapterではゾンビの外見を設定する仕様の説明がされています。

ここで行う作業は右画面のスライドを操作しゾンビの外見を設定します。

以下は私が作成したゾンビの外見です。

zombi.png


Chapter2(コントラクト)

このChapterではコントラクトについて学びます。

Solidityはコントラクト内にカプセル化されており、ソースコードの基本ブロックとなる。

変数や関数はこのコントラクトに属することになる。

また、Solidityの1行目にはコンパイラのバージョンを示すVersion pragmaを記述する必要がある。

CryptoZombiesではVersion0.4.19を使用する。

この章では、コンパイラのバージョンを0.4.19とし、ZombieFactoryというからのコントラクトを作成します。

それでは作成してみましょう!


Contract.sol

pragma solidity ^0.4.19; //コンパイラのバージョンを忘れずに記述

contract ZombieFactory {

}


これでChapter2は終了です。


Chapter3(状態変数と整数)

Chapter2でコントラクトの骨組みを作成しました。次にChapter3では変数の扱い方について学習します。

状態変数はコントラクト内に永久的に保管されます。

このChapterで使用する変数の型は整数型です。

符号なし整数

uintで宣言します。

符号なし整数の大きさはuint8からuint256まで8刻みで宣言することが可能です。uintはuint256のエイリアスとなっています。

符号付き整数

intで宣言します。

符号なし整数の大きさはint8からint256まで8刻みで宣言することが可能です。intはint256のエイリアスとなっています。

この章では、ゾンビのDNAを格納する整数型の変数を作成します。このとき、ゾンビのDNAは16桁の正の整数で表現されます。

それでは作成してみましょう!


Contract.sol

pragma solidity ^0.4.19;

contract ZombieFactory {

uint dnaDigits = 16;

}


これでChapter3は終了です。


Chapter4(数式演算)

Chapter3では符号なし整数型の辺巣を宣言しました。Chapter4では宣言した変数を用いて演算をしていきます。

演算は以下の通りである。


  • 加算:a + b

  • 減算:a - b

  • 乗算:a * b

  • 除算:a / b

  • 剰余:a % b

  • 指数:a ** b (a^bとなる)

他のプログラム言語と同じなので簡単ですね!

この章では、ゾンビのDNAが16桁の整数であることを確認するために、もう一つuint型の変数を宣言します。

そして、宣言したuint型に10^16を代入します。

それでは作成してみましょう!


Contract.sol

pragma solidity ^0.4.19;

contract ZombieFactory {

uint dnaDigits = 16;
uint dnaModulus = 10 ** dnaDigits;

}


これでChapter4は終了です。


Chapter5(構造体)

このChapterでは構造体について学習します。構造体は複数の変数を1つにまとめて扱うことができます。

structキーワードを使って構造体を宣言することができます。

Chapter3とChapter4では整数型の変数を宣言しましたが、今回は新しくstring型の宣言を行います。

string型はUTF-8でエンコードされた動的サイズのバイト配列です。文字列を格納するときに利用されます。

この章では、string型とuint型をフィールドに持つ構造体を作成します。

それでは作成してみましょう!


Contract.sol

pragma solidity ^0.4.19;

contract ZombieFactory {

uint dnaDigits = 16;
uint dnaModulus = 10 ** dnaDigits;

struct Zombie{
string name;
uint dna;
}

}


これでChapter5は終了です。


Chapter6(配列)

1つの変数に宣言した型の値を複数個代入するときは配列を使います。

配列は型の後ろに[]をつけることで宣言でき、Solidityでは固定長配列と可変長配列が用意されています。


Array.sol

//要素数2の固定長配列の宣言

uint[2] array1;
//要素数5の固定長配列の宣言
string[5] array2;
//可変長配列の宣言
int[] array3;

構造体の配列も宣言することができ、状態変数はコントラクト内に永久的に保管されることから

構造体の可変長配列は1種のデータベースのような役割として利用できます。

また、可視性を指定することができ、今回はpublicで宣言します。

publicとは他のコントラクトから配列の参照を許可するキーワードです。

この章では、publicな構造体の配列を作成します。

それでは作成してみましょう!


Contract.sol

pragma solidity ^0.4.19;

contract ZombieFactory {

uint dnaDigits = 16;
uint dnaModulus = 10 ** dnaDigits;

struct Zombie{
string name;
uint dna;
}

Zombie[] public zombies;

}


これでChapter6は終了です。


Chapter7(関数の宣言)

このChapterでは関数を宣言していきます。関数はプログラムに特定の動作をさせる部分にあたり、

プログラミングの醍醐味だと個人的に思います。

functionキーワードで宣言します。

また、グローバル変数と区別するために、関数パラメータの変数名にをつけることが暗黙の了解となっています。

ローカル変数のときは
をつける習慣をつけましょう!

関数をは 関数名(引数); で呼び出せます。

この章では、string型とuint型の2つのパラメータをもつ関数を作成します。

それでは作成してみましょう!


Contract.sol

pragma solidity ^0.4.19;

contract ZombieFactory {

uint dnaDigits = 16;
uint dnaModulus = 10 ** dnaDigits;

struct Zombie{
string name;
uint dna;
}

Zombie[] public zombies;

function createZombie(string _name, uint _dna){ //変数名の前に_をつける

}
}


これでChapter7は終了です。


Chapter8(構造体と配列の操作)

このChapterではChapter6で宣言した構造体の配列にデータを格納します。

ここで構造体の配列の宣言についておさらいです。

例としてPerson構造体の配列を作成します。


StructArray.sol

struct Person{

uint age;
string name;
}

Person[] public people;


これでPerson構造体の配列を宣言できました。

次にPerson構造体の配列にデータを格納してみましょう。

配列にデータを格納するときはpushキーワードを使用します。


StructArray.sol

//構造体にkaibaというpersonを作成します

Person kaiba = (23, "kaiba");
//配列に構造体の中身であるkaibaを格納します
people.push(kaiba);

//1行にまとめることも可能
people.push(Person(23, "kaiba"));


またpushは配列の末尾にデータを追加します。


push.sol

uint[] primeNumbers;

primeNumbers.push(2);
primeNumbers.push(3);
primeNumbers.push(5);
//配列の要素は先頭から[2,3,5]となる

この章では、Chapter7で作成した関数に動作を入力します。

この関数は構造体に新しいデータを格納し配列に追加します。

この動作を1行で実装します。

それでは作成してみましょう!


Contract.sol

pragma solidity ^0.4.19;

contract ZombieFactory {

uint dnaDigits = 16;
uint dnaModulus = 10 ** dnaDigits;

struct Zombie{
string name;
uint dna;
}

Zombie[] public zombies;

function createZombie(string _name, uint _dna){
zombies.push(Zombie(_name, _dna));

}
}


これでChapter8は終了です。


Chapter9(Private/Public関数)

このChapterではPrivateな関数を作成します。Solidityの関数はデフォルトでpublicになっているため、

自分だけが使う関数をprivateにし、公開しても構わない関数をpublicとして宣言します。


Private.sol

uint[] primeNumbers;

function _addToArray(uint _primeNumber) private{ //関数名の前に_をつける
primeNumbers.push(_primeNumber);
}


関数のパラメータと同じようにprivateな関数は関数名の前に_をつけるという暗黙の了解があります。

こちらも習慣づけましょう!

この章では、Chapter7で作成した関数をPrivateな関数に作り替えます。

それでは作成してみましょう!


Contract.sol

pragma solidity ^0.4.19;

contract ZombieFactory {

uint dnaDigits = 16;
uint dnaModulus = 10 ** dnaDigits;

struct Zombie {
string name;
uint dna;
}

Zombie[] public zombies;

function _createZombie(string _name, uint _dna) private { //関数名の前に_をつける
zombies.push(Zombie(_name, _dna));
}

}


これでChapter9は終了です。


Chapter10(関数の戻り値と修飾子)

このChapterでは関数の戻り値と修飾子について学習します。

戻り値の宣言は以下の通りです。


Return.sol

string sentence = "hogehoge";

function getSentence() public returns (string){ //戻り値の型を記述する
return sentence;
}


関数の修飾子は以下の2つです。

view

値の変更や書き込みをせず、参照のみを行います。


View.sol

function getSentence() public view returns (string){ 


pure

アプリ内のデータを参照できなくします。


Pure.sol

function _sum(uint a, uint b) private pure returns (uint){

return a * b;
}

この章では、string型のパラメータを1持つPrivateな関数の宣言をします。

戻り値はuint型で、変数の参照だけが可能です。

それでは作成してみましょう!


Contract.sol

pragma solidity ^0.4.19;

contract ZombieFactory {

uint dnaDigits = 16;
uint dnaModulus = 10 ** dnaDigits;

struct Zombie {
string name;
uint dna;
}

Zombie[] public zombies;

function _createZombie(string _name, uint _dna) private {
zombies.push(Zombie(_name, _dna));
}

function _generateRandomDna(string _str) private view returns (uint){

}
}


これでChapter10は終了です。


Chapter11(Keccak256と型キャスト)

このChapterではKeccak256で疑似乱数を作成し、型キャスト変換を行って変数に代入します。

Keccak256はハッシュ関数の1つです。

ハッシュ関数は一方向性で、入力値に対してランダムな値を返します。

型キャストは変数の型を変更する作業のことを指します。

この章では、string型の引数をKeccak256に代入し、その結果をuint型に型キャストをします。

戻り値はuint型で、変数の参照だけが可能です。

また、ゾンビのDNAは16桁であるため、疑似乱数を剰余を使って丸め込みます。

それでは作成してみましょう!


Contract.sol

pragma solidity ^0.4.19;

contract ZombieFactory {

uint dnaDigits = 16;
uint dnaModulus = 10 ** dnaDigits;

struct Zombie {
string name;
uint dna;
}

Zombie[] public zombies;

function _createZombie(string _name, uint _dna) private {
zombies.push(Zombie(_name, _dna));
}

function _generateRandomDna(string _str) private view returns (uint) {
uint rand = uint(keccak256(_str)); //型キャスト
return rand % dnaModulus;
}

}


これでChapter11は終了です。


Chapter12(統合)

この章では、今まで作成した関数を統合して疑似乱数からゾンビを作るPublic関数を作成します。

それでは作成してみましょう!


Contract.sol

pragma solidity ^0.4.19;

contract ZombieFactory {

uint dnaDigits = 16;
uint dnaModulus = 10 ** dnaDigits;

struct Zombie {
string name;
uint dna;
}

Zombie[] public zombies;

function _createZombie(string _name, uint _dna) private {
zombies.push(Zombie(_name, _dna));
}

function _generateRandomDna(string _str) private view returns (uint) {
uint rand = uint(keccak256(_str));
return rand % dnaModulus;
}

function createRandomZombie(string _name) public {
uint randDna = _generateRandomDna(_name);
_createZombie(_name, randDna);
}
}


これでChapter12は終了です。


Chapter13(イベント)

このChapterではEventを追加します。

Eventはブロックチェーン上で何か動作があったときに、コントラクトからアプリのフロントエンドに通知を送る機能です。

特定のEventをlistening状態にすることで、任意のアクションを起こすことができます。


Event.sol

event IntegersSumed(uint a, uint b, uint result);

function sum(uint _a, uint _b) public{
uint result = _a + _b;
//関数が呼び出されたことをアプリに伝える
IntegersSumed(_a, _b, result);
return result;
}


この章では、ゾンビを作るたびにフロントエンドに伝え、アプリに表示するEventを追加します。

それでは作成してみましょう!


Contract.sol

pragma solidity ^0.4.19;

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;

function _createZombie(string _name, uint _dna) private {
uint id = zombies.push(Zombie(_name, _dna)) - 1;
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 {
uint randDna = _generateRandomDna(_name);
_createZombie(_name, randDna);
}
}


これでChapter13は終了です。


Chapter14(Web3.js)

このChapterではWeb3.jsについての説明があります。

EthereumにはWeb3.jsというJavascriptのライブラリが存在します。

Web3.jsの詳しい説明は今後のLessonで学習します。

この章では、ゾンビに名前を与えるとランダムに外見が変化することを確認します。

それでは実際に確認しましょう!

zombie.png

外見が変わることを確認できました。

これでChapter14は終了です。


Chapter15

このChapterは作ったZombieの共有を行えるページになります。

特にまとめる要素はないです。


おわりに

本記事ではCryptoZombiesのLesson1をまとめした。

Lesson1はプログラムの初歩的な部分であるため、それほど難しくないと思いました。

今後もSolidityの勉強を続けていきたいです。

また、Qiita初投稿なので御見苦しい記事になっていると思いますが大目に見て欲しいです...

それでは、次回の記事でお会いしましょう。


参照

CryptoZombies