前の記事にてTruffleの公式サンプルを使ったDapp開発の概要を投稿しました。その記事の詳細について書いていきたいと思います。
→前の記事はこちら https://alis.to/Hsugi00/articles/39r4zR4zb0Ey
まずは、バックエンド(スマートコントラクト)の作成です。
これがブロックチェーンと呼ばれる部分ですね。
※前提としてTruffleのダウンロードができる状態(npmなど)であると想定しています。
バックエンド作成
1.ターミナルを開き、下記コマンドを実行します。
npm install -g truffle
2.インストールされたか確認する為にターミナルにて下記コマンドを実行します。
truffle version
下記のようにTruffleのバージョンが表示されて入ればオッケーです。
1.ダウンロードする為のディレクトリを作成し、そこへ移動します。
ターミナルにて下記コマンドを実行します。
mkdir pet-shop
cd pet-shop
移動できているか確認するには「pwd」コマンドを実行します。
下記の様に表示されていればオッケーです。
2.サンプルをダウンロードする。
ターミナルにて下記コマンドを実行します。
truffle unbox pet-shop
実行すると「pet-shop」ディレクトリの中に下記内容が入ってると思います。
contracts、migrations、test、truffle.jsなど
1.「pet-shop」ディレクトリ配下にある、「contracts」内に、「Adoption.sol」というファイルを作成します。
2.そのファイルに下記コードを記載します。
pragma solidity >0.4.99 <0.6.0;
contract Adoption {
address[16] public adopters;
function adopt(uint petId) public returns (uint) {
require(petId >= 0 && petId <= 15);
adopters[petId] = msg.sender;
return petId;
}
function getAdopters() public view returns (address[16] memory) {
return adopters;
}
}
【解説】
pragma solidity >0.4.99 <0.6.0;
使用するVersionを指定しています。執筆時点(2019/01/14)でのSolidityは0.5以降となっていますが、いろんな方が投稿されている公式サンプルを使用した解説は0.4での仕様の為、この様な書き方をしています。(余談ですが僕がこのサンプルに挑戦した時はSolidityがupdateしたばかりでこの部分で躓き解決策がわからず途方にくれていました。今後は0.5での解説記事が出てくると思いますので、後ほどそちらに挑戦すると良いと思います。Truffleの公式も0.5仕様になっていますので。)
contract Adoption {
これでAdoptionという名前のコントラクトを作成します。
address[16] public adopters;
address型でadoptersという名前の配列をpublicで定義します。 address型はイーサリアム上のアドレスを意味し、20 byteの値を保持することができます。16は長さを意味し、16個までのアドレスを保持することができます。
function adopt(uint petId) public returns (uint) {
require(petId >= 0 && petId <= 15);
adopters[petId] = msg.sender;
return petId;
}
adoptという名前の関数を定義します。petIdを引数としpetIdが0から15までの間か確認します。IDが範囲内にある場合は、呼び出しを行ったアドレスをadopters配列に追加します。この機能を呼び出した人、または呼び出したスマートコントラクトのイーサリアムアドレスは、 msg.senderから取り出すことができます。最後にpetIdを返します。
function getAdopters() public view returns (address[16] memory) {
return adopters;
}
getAdoptersという関数を定義します。これにより、採用されたペットIDと採用者のイーサリアムアドレスが格納されている配列データを出力します。
1.ターミナルにて下記コマンドを実行します。
truffle compile
完了するとbuildディレクトリが作成されます。
1.「migrations」ディレクトリの中に「2_deploy_contracts.js」というファイルを作成します。
2.「2_deploy_contracts.js」の中身は下記のコードを記載します。
var Adoption = artifacts.require("Adoption");
module.exports = function(deploy) {
deploy.deploy(Adoption);
}
いきなりパブリックチェーンにて実行するわけには行かないので、プライベートネットにて実行します。ここでは契約の展開、アプリケーションの開発、テストの実行に使用できるEthereum開発用の個人用ブロックチェーンであるGanacheを使用します。
下記よりダウンロードしてください。
1.Ganacheを起動した状態でコンソールにて下記コマンドを実行します。
truffle migrate
するとGanacheでETHが動きます。これはマイグレーションの結果、コントラクトがデプロイされ、そのコストが引かれた為です。
TruffleではコントラクトのテストコードをSolidityかJavaScriptで書くことができます。このサンプルではSolidityにてコードを書いていきます。
1.「test」ディレクトリ内に「TestAdoption.sol」というテストコードを作成します。
2.ファイル内に下記コードを記載します。
pragma solidity >0.4.99 <0.6.0;
import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";
import "../contracts/Adoption.sol";
contract TestAdoption {
Adoption adoption = Adoption(DeployedAddresses.Adoption());
uint expectedPetId = 8;
address expectedAdopter = address(this);
function testUserCanAdoptPet() public {
uint returnedId = adoption.adopt(expectedPetId);
Assert.equal(returnedId, expectedPetId, "Adoption of the expected pet should match what is returned.");
}
function testGetAdopterAddressByPetId() public {
address adopter = adoption.adopters(expectedPetId);
Assert.equal(adopter, expectedAdopter, "Owner of the expected pet should be this contract");
}
function testGetAdopterAddressByPetIdInArray() public {
address[16] memory adopters = adoption.getAdopters();
Assert.equal(adopters[expectedPetId], expectedAdopter, "Owner of the expected pet should be this contract");
}
}
【解説】
pragma solidity >0.4.99 <0.6.0;
import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";
import "../contracts/Adoption.sol";
3つのインポートを行います。
Assert.solはテストが合格/不合格かチェックを行います。変数の値を判定する際に条件分岐などをしてくれる関数がまとまったものです。
DeployedAddresses.solはテストを実行すると、テスト対象のコントラクトの新しいインスタンスをブロックチェーンに展開します。このスマートコントラクトは、展開されたコントラクトのアドレスを取得します。
Adoption.solはテストしたいスマートコントラクトです。
contract TestAdoption {
Adoption adoption = Adoption(DeployedAddresses.Adoption());
TestAdoptionというコントラクトを作成します。
DeployedAddressというスマートコントラクトを呼び出してそのアドレスを取得します。
uint expectedPetId = 8;
address expectedAdopter = address(this);
テスト用にexpectedPetId を8と宣言します。
expectedAdopterのアドレスをthis(現在のコントラクトを呼び出しているアドレス)に変更します。
function testUserCanAdoptPet() public {
uint returnedId = adoption.adopt(expectedPetId);
Assert.equal(returnedId, expectedPetId, "Adoption of the expected pet should match what is returned.");
}
ペットを採用する処理(adopt関数)をテストするコードです。
testUserCanAdoptPetという関数を定義します。returnedIdにexpectedPetIdが入り(始めに入れた8)、Assert.equal()にて合致しているか確認します。問題があった際の失敗メッセージを設定します。
function testGetAdopterAddressByPetId() public {
address adopter = adoption.adopters(expectedPetId);
Assert.equal(adopter, expectedAdopter, "Owner of the expected pet should be this contract");
}
ペットの採用者が一人であるかテストするコードです。
adopterにexpectedPetIdのアドレスが入ります(始めに入れたthis)。Assert.equalでadopterとexpectedAdopterが合致しているか確認します。
function testGetAdopterAddressByPetIdInArray() public {
address[16] memory adopters = adoption.getAdopters();
Assert.equal(adopters[expectedPetId], expectedAdopter, "Owner of the expected pet should be this contract");
}
採用された全てのペットが出力されているか確認するテストコードです。
配列はそのままでは、単一のキーで単一の値しか返すことができないので、配列全体を取得するための独自のゲッターを作成します。
adoptersに一時的に全てのデータを記録します。Assert.equalにてexpectedPetId(ID8)のアドレスと現在呼び出しているアドレスが同じか確認します。
adoptersの前に書かれたmemoryは、メモリ属性を意味します。 メモリ属性は、コントラクトのストレージに保存するのではなく、メモリに一時的に値を格納するようSolinityに指示するものです。※solidity0.5以降から明示する様になりました。
参考 https://qiita.com/himitsu-fukuda/items/4c5788787512a7d6ee13
1.ターミナルにて下記のコマンドを実行する。
truffle test
成功すれば下記の様に表示されます。
Using network 'development'.
Compiling ./contracts/Adoption.sol...
Compiling ./test/TestAdoption.sol...
Compiling truffle/Assert.sol...
Compiling truffle/DeployedAddresses.sol...
TestAdoption
✓ testUserCanAdoptPet (91ms)
✓ testGetAdopterAddressByPetId (70ms)
✓ testGetAdopterAddressByPetIdInArray (89ms)
3 passing (670ms)
以上でバックエンドの解説は終了です。
後日フロントエンドの解説もしたいと思います。