solidityのコード無料ソフトcryptozombiesの詳解をしていきます。
少し古いですが多分今でも通用すると思います。
ぼくはプログラミング言語が初めてなので、不正確な部分もあると思いますので、
この記事は参考程度にご覧ください。
Making the Zombie Factory
全体コードの確認
全体のコードを見てみます。
1 pragma solidity >=0.5.0 <0.6.0;
2
3 contract ZombieFactory {
4
5 event NewZombie(uint zombieId, string name, uint dna);
6
7 uint dnaDigits = 16;
8 uint dnaModulus = 10 ** dnaDigits;
9
10 struct Zombie {
11 string name;
12 uint dna;
13 }
14
15 Zombie[] public zombies;
16
17 function _createZombie(string memory _name, uint _dna) private {
18 uint id = zombies.push(Zombie(_name, _dna)) - 1;
19 emit NewZombie(id, _name, _dna);
20 }
21
22 function _generateRandomDna(string memory _str) private view returns (uint) {
23 uint rand = uint(keccak256(abi.encodePacked(_str)));
24 return rand % dnaModulus;
25 }
26
27 function createRandomZombie(string memory _name) public {
28 uint randDna = _generateRandomDna(_name);
29 _createZombie(_name, randDna);
30 }
31
32 }
コードの流れを追った説明
外部からテキスト(具体的にはゾンビの名前)を入力することで、ゾンビが自動的に生成される仕組みになっています。
27 function createRandomZombie(string memory _name) public {
28 uint randDna = _generateRandomDna(_name);
29 _createZombie(_name, randDna);
30 }
このプロセスは、ユーザーが createRandomZombie
関数を呼び出すことから始まります。
ユーザーはこの関数にゾンビに付けたい名前(_name
)を文字列として渡します。
22 function _generateRandomDna(string memory _str) private view returns (uint) {
23 uint rand = uint(keccak256(abi.encodePacked(_str)));
24 return rand % dnaModulus;
25 }
_generateRandomDna
関数が内部的に呼び出されます。
この関数は、入力された名前からランダムなDNA(数値)を生成します。
これは、入力文字列に基づいて計算されたハッシュから数値を生成し、dnaModulus
(ここでは 10 の 16 乗)で割った剰余を取ることで行われます。
17 function _createZombie(string memory _name, uint _dna) private {
18 uint id = zombies.push(Zombie(_name, _dna)) - 1;
19 emit NewZombie(id, _name, _dna);
20 }
生成されたDNAとユーザーが入力した名前を使用して、_createZombie
関数が呼び出されます。
この関数は、新しい Zombie
構造体を作成し、zombies
配列に追加します。
そして、配列内の新しいゾンビのインデックス(ID)を計算します。
※pragma solidity ^0.8.0;の場合
18 uint id = zombies.push(Zombie(_name, _dna)) - 1;
というところが
zombies.push(Zombie(_name, _dna));
uint id = zombies.length - 1;
が正しい記載になります。
19 emit NewZombie(id, _name, _dna);
NewZombie
という名前のイベントが発火されます。
そのイベントに関連するデータがブロックチェーンのトランザクションログに記録されます。
コードの詳細
1 pragma solidity >=0.5.0 <0.6.0;
Solidityコンパイラのバージョンを指定します。
ここでは、バージョン0.5.0以上かつ0.6.0未満であることを要求しています。
コンパイラ:プログラミング言語で書かれたコードをコンピュータが直接実行可能な形式に変換するプログラムやソフトウェアのこと
ーーー
3 contract ZombieFactory {
ZombieFactory
という名前の新しいスマートコントラクトを宣言しています。
スマートコントラクト:特定の条件が満たされた時に、自動的に特定のアクションを行うコードのセットのこと
ーーー
5 event NewZombie(uint zombieId, string name, uint dna);
新しいゾンビが作成されたときに発動するイベントNewZombie
を定義しています。
このイベントは、ゾンビのID、名前、DNAをパラメータとして持っています。
event:特定のアクションが発生したことを外部に通知するために使われる。
外部:例えばフロントエンドアプリケーションやサーバー側のアプリケーション
uint:符号なし整数のこと
string:主にASCII文字やUTF-8エンコードされた文字列を表すために使用される。
ーーー
7 uint dnaDigits = 16;
dnaDigits
という名前の状態変数を宣言し、16という値を代入しています。
これは、後で使用されるゾンビのDNAが16桁であることを示しています。
状態変数:プログラムがブロックチェーン上に存在する限り、その値が維持されます。
ーーー
8 uint dnaModulus = 10 ** dnaDigits;
dnaModulus
という状態変数を宣言し、10の dnaDigits
乗(この場合は 10の16乗)の値を代入しています。
これは、後でDNAの値を計算する際の上限として使用されます。
ーーー
10 struct Zombie {
11 string name;
12 uint dna;
13 }
Zombie
という構造体を宣言し、name
(文字列型)と dna
(符号なし整数型)という2つのプロパティを持たせています。
構造体:異なる型のデータを一つの単位でまとめるために使用されるプログラミングの構造
具体例:Zombie
という構造体に name
(文字列型)と dna
(符号なし整数型)の2つのフィールドがあります。
これにより、各
インスタンスに名前と年齢のデータを関連付けて保存できます。Zombie
ーーー
15 Zombie[] public zombies;
Zombie
構造体のインスタンスを格納する公開(public)動的配列 zombies
を宣言しています。
インスタンス:ここではゾンビの個体情報のこと。
動的配列:サイズが実行時に変更可能な配列のこと。
“[]”を使用すると動的配列を宣言することになる。
ーーー
17 function _createZombie(string memory _name, uint _dna) private { ... }
_createZombie
という名前のプライベート関数を宣言しています。
この関数は string
型の _name
と uint
型の _dna
の2つのパラメータを取り、新しいゾンビを zombies
配列に追加します。
function:関数を宣言
プライベート関数:その定義されたコンテキスト内でのみアクセス可能な関数のこと
配列:同じデータ型の複数の要素を連続的に格納するためのデータ構造のこと。
Solidityで関数を宣言する際には、関数の可視性(public
、private
、internal
、external
)、状態変更の可否(pure
、view
)、およびその他の修飾子(payable
など)を指定することが重要です。
ーーー
18 uint id = zombies.push(Zombie(_name, _dna)) - 1;
新しい Zombie
インスタンスを zombies
配列に追加し、追加されたゾンビのIDを計算しています。
zombies.push(Zombie(_name, _dna))
は配列にゾンビを追加し、配列の新しい長さを返します。
新しいゾンビのインデックス(ID)は、この長さから1を引くことで得られます。
push:動的配列の末尾に新しい要素を追加し、配列の長さを増加させる。
zombies
という配列に新しいZombie
個体を追加(push)する操作を行なっている。
例えば配列の長さを5とした場合、配列のインデックス(ID)は0から始まるので、最後の要素のインデックスは4になる。このため”-1”をしている。
ーーー
19 emit NewZombie(id, _name, _dna);
NewZombie
のイベントを発火し、この情報をスマートコントラクトの外部に通知します。
emit:イベントを発行または外部に通知すること
ーーー
22 function _generateRandomDna(string memory _str) private view returns (uint) { ... }
_generateRandomDna
という名前のプライベート関数を宣言しています。
この関数は文字列 _str
を入力として受け取ります。
memory:一時的なデータの保管場所
変数が関数の実行中のみ存在し、関数の実行が終了すると消去されることを意味する。
view:プログラムの状態に影響を与えず、データを読み取ることができる。
ーーー
23 uint rand = uint(keccak256(abi.encodePacked(_str)));
_str
をエンコードし、keccak256
ハッシュ関数を適用します。
その結果を uint
型にキャストし、rand
という変数に代入しています。
keccak256:イーサリアムのスマートコントラクト開発において使用されるハッシュ関数の一つ。
任意の長さのデータを取り、一定の長さ(256ビット)のハッシュ値を生成する。
abi.encodePacked:異なるデータ型の変数を一つのバイト列にまとめる(パックする)こと
エンコード:データを特定の形式やコードに変換するプロセスのこと
ハッシュ関数:特定の入力から固定長のハッシュ値を生成する。
ハッシュ値:ハッシュ関数によって生成される固定長の値
キャスト:あるデータ型の変数や値を別のデータ型に変換するプロセスのこと
ーーー
24 return rand % dnaModulus;
rand
を dnaModulus
で割った剰余を返しています。
これにより、DNAの値は指定された桁数内に収まります。
return:何らかのデータを処理し、その結果を戻り値として返す。
ーーー
27 function createRandomZombie(string memory _name) public { ... }
createRandomZombie
という名前の公開関数を宣言しています。
この関数は _name
を入力として受け取り、ランダムなDNAを持つ新しいゾンビを作成します。
ーーー
28 uint randDna = _generateRandomDna(_name);
_generateRandomDna
関数を呼び出し、得られたランダムなDNAを randDna
に代入します。
ーーー
29 _createZombie(_name, randDna);
_createZombie
関数を呼び出し、_name
と randDna
を使って新しいゾンビを作成します。