• トップ
  • ブログ一覧
  • 【中編】Ethereum Pet Shop DAppの作成とテスト~コントラクト作成編~
  • 【中編】Ethereum Pet Shop DAppの作成とテスト~コントラクト作成編~

    広告メディア事業部広告メディア事業部
    2020.07.28

    IT技術

    ~中編~Ethereum Pet ShopでDApp作成とテスト

    今回は、前回に引き続き、「Ethereum Pet Shopを使って DAppの作成からテスト」までを行ってみたいと思います!

    前編では、環境構築を行っていきましたが、今回からは実際にコントラクトを作成していきたいと思います!

    作成工程

    1. 環境設定
    2. Truffle Box を使って Truffle プロジェクトを作成
    3. コントラクト作成
    4. コントラクトをコンパイルしてデプロイ
    5. コントラクトのテスト
    6. UI 作成
    7. ブラウザ上の DApp でログインする

    コントラクトの作成

    それでは、実際にコントラクトを作成していきましょう!

    contract フォルダー内にファイルを作成

    Solidity のソースファイルである contract フォルダー内に、「Adoption.sol」ファイルを作成します。

    そして、以下のコードを記入していきます。

    1pragma solidity ^0.5.0;
    2contract Adoption {
    3address[16] public adopters;
    4// adopter変数のIDの領域
    5function adopt(uint petId) public returns (uint) {
    6  require(petId >= 0 && petId <= 15);
    7  adopters[petId] = msg.sender;
    8  return petId;
    9}
    10// getAdopters( )関数で設定したadopters変数の戻り値
    11function getAdopters() public view returns (address[16] memory) {
    12  return adopters;
    13}
    14}

    コード解説

    PetShop のコントラクト作成は、web3 と連携するため、以下のような仕様になります。

    1. function で public の adopters 変数と petID (引数)を設定
    2. 引き渡しは address 型(戻り値を修正しない view で宣言)
    3. MetaMask と Ganache に設定したアドレスで開く

    コントラクトのコンパイルとデプロイ

    EVM の実行のため、コントラクトをコンパイルしてバイトコードに変換します。

    DApp が含まれているディレクトリーで、ターミナルに以下のコマンドを入力します。

    1truffle compile

    マイグレーションファイルの作成

    マイグレーションファイルが、コントラクトを EVM へデプロイしてくれます。

    migration フォルダーの中に、「1_initial_migration.js」ファイルがあります。

    これが「migration.sol」の操作ファイルで、コントラクトが二重にコンパイルされるのを防ぎます。

    「2_deploy_contract.js」ファイルを作成

    migration フォルダーに、「2_deploy_contract.js」というファイルを作成します。

    そして、以下のコードを書き込みます。

    1var Adoption = artifacts.require("Adoption");
    2
    3module.exports = function(deployer) {
    4  deployer.deploy(Adoption);
    5};

    コントラクトのデプロイ

    ターミナルで以下のコマンドを入力し、デプロイします。

    1truffle develop
    2truffle(develop)>migrate

    コントラクトのテスト

    それでは、コントラクトをテストしてみましょう!

    Ganache アカウントの作成

    Ganache の workspace に「truffle-config.js」をアップロードします。

    Gas の設定

    アカウント作成後は、全ての Blockchain に、テスト用として100Ether ずつ入っています。

    この Ether は、デプロイする度に Gas(送金料)が消費されます。

    Gas と RPC Server の設定は、以下のようになります。

    コントラクトのテスト

    PetShop 解凍時に、空のテスト用フォルダーが生成されています。

    このフォルダーの中に「TestAdoption.sol」を作成し、以下のコードを記入します。

    1    pragma solidity ^0.5.0;
    2
    3   //上記2つのコントラクトは外部のグローバルフォルダーからインポートします
    4
    5    import "truffle/Assert.sol";
    6    import "truffle/DeployedAddresses.sol";
    7    import "../contracts/Adoption.sol";
    8
    9    contract TestAdoption {
    10     // TestAdoption関数にadoptiin変数でデプロイされたアドレスを読み込む
    11     Adoption adoption = Adoption(DeployedAddresses.Adoption());
    12
    13     // テストに使われるペットのID
    14     uint expectedPetId = 8;
    15
    16     //アドレス型のexpectedAdopter変数の戻り値
    17     address expectedAdopter = address(this);
    18
    19    }

    最初にインポートしている3つのファイルの内、「Assert.sol」と「DeployedAddresses.sol」はグローバルのTruffleファイルなので、プロジェクトフォルダーの中には有りません

    この2つのファイルの役割は、以下のようになります。

    Assert.sol

    テスト時の確認作業を行います。

    テストから成功/失敗を返すために、「同等(EQ)」「不等(NE)」などをチェックします。

    DeployedAddresses.sol

    テストを実行すると、デプロイされたコントラクトのアドレスを取得します。

    関数設定

    このコードには、以下の3つの wide variables (変数)が含まれています。

    1. DeployedAddresses
    2. expectedPetID
    3. expectedAdopter

    uint型「expectedPetID」の引数8の戻り値が、address型「expectedAdopter」に返されます。

    adopt()関数のコード修正

    テストのため、adopt()関数のコードを修正します。

    仕様は以下のようになります。

    1. expectedPetID を事前に宣言
    2. テストが成功したら、Assert.equal()に ID を返す
    3. 失敗したら、コンソールに「Adoption of the expected pet should match what is returned.」を表示

    「TestAdoption.sol」コントラクトの「Adoption」変数の後に、以下のコードを追加してください。

    1pragma solidity ^0.5.0;
    2
    3import "truffle/Assert.sol";
    4import "truffle/DeployedAddresses.sol";
    5import "../contracts/Adoption.sol";
    6
    7contract TestAdoption {
    8 // adoption変数のアドレスの戻り値をテスト
    9 Adoption adoption = Adoption(DeployedAddresses.Adoption());
    10
    11// adopt() 関数のテスト
    12function testUserCanAdoptPet() public {
    13  uint returnedId = adoption.adopt(expectedPetId);
    14
    15  Assert.equal(returnedId, expectedPetId, "Adoption of the expected pet should match what is returned.");
    16}</span>
    17
    18 // テストに使われるペットのID
    19 uint expectedPetId = 8;
    20
    21 // アドレス型のexpectedAdopter変数の戻り値
    22 address expectedAdopter = address(this);
    23
    24}

    単一ペットオーナー受け入れ確認テスト

    まず、単一ペットオーナー受け入れ確認テストをします。

    public 変数は、自動ゲッターメソッドなので、上記の Adoption Test で格納したアドレスをそのまま取得できます。

    また、保存されたデータはテスト中は存続するため、expectedPetID の値は他のテストでも取得可能です。

    TestAdoption.sol をコード修正

    「TestAdoption.sol」コントラクトを、以下のコードに修正します。

    1ragma solidity ^0.5.0;
    2
    3import "truffle/Assert.sol";
    4import "truffle/DeployedAddresses.sol";
    5import "../contracts/Adoption.sol";
    6
    7contract TestAdoption {
    8 // TestAdoption関数内でadoption変数で値を読み込む
    9 Adoption adoption = Adoption(DeployedAddresses.Adoption());
    10
    11// testUserCanaAdoptPet()関数内でadopt()変数の値returnedID,expectedPetIdを検索して等価の場合は値を返し、不等価の場合はメッセージを返す。
    12
    13function testUserCanAdoptPet() public {
    14  uint returnedId = adoption.adopt(expectedPetId);
    15
    16  Assert.equal(returnedId, expectedPetId, "Adoption of the expected pet should match what is returned.");
    17}
    18
    19  <span style="color: #ff0000;" data-mce-style="color: #ff0000;"> 
    20 // ペットオーナーが同一のデータ検索テスト
    21    function testGetAdopterAddressByPetId() public {
    22      address adopter = adoption.adopters(expectedPetId);
    23
    24      Assert.equal(adopter, expectedAdopter, "Owner of the expected pet should be this contract");
    25    }
    26</span>
    27
    28
    29 // テストに使われるペットのID
    30 uint expectedPetId = 8;
    31
    32 //アドレス型のexpectedAdopter変数の戻り値
    33 address expectedAdopter = address(this);
    34
    35}

    全てのペットオーナーの受け入れ確認テスト

    続いて、すべてのペットオーナーの受け入れ確認テストをします。

    配列は、単一キーを指定すると単一の値しか返せません。

    そのため、配列全体に対して独自のゲッターを作成しましょう。

    TestAdoption.sol をコード修正

    以下のコードを「TestAdoption.sol」に追加し、修正します。

    1pragma solidity ^0.5.0;
    2
    3import "truffle/Assert.sol";
    4import "truffle/DeployedAddresses.sol";
    5import "../contracts/Adoption.sol";
    6
    7contract TestAdoption {
    8 // テストされる adoptionコントラクトのアドレス
    9 Adoption adoption = Adoption(DeployedAddresses.Adoption());
    10
    11// adopt() 関数をテストする
    12function testUserCanAdoptPet() public {
    13  uint returnedId = adoption.adopt(expectedPetId);
    14
    15  Assert.equal(returnedId, expectedPetId, "Adoption of the expected pet should match what is returned.");
    16}
    17
    18// ペットオーナーが同一のデータ検索のテスト 
    19function testGetAdopterAddressByPetId() public {
    20  address adopter = adoption.adopters(expectedPetId);
    21
    22  Assert.equal(adopter, expectedAdopter, "Owner of the expected pet should be this contract");
    23}
    24
    25//全てのペットオーナーの検索テスト
    26function testGetAdopterAddressByPetIdInArray() public {
    27  // メモリー内のadopter変数に値を格納する
    28  address[16] memory adopters = adoption.getAdopters();
    29
    30  Assert.equal(adopters[expectedPetId], expectedAdopter, "Owner of the expected pet should be this contract");
    31}
    32</span>
    33 // テストに使われるペットのID
    34 uint expectedPetId = 8;
    35
    36 //アドレス型のexpectedAdopter変数の戻り値
    37 address expectedAdopter = address(this);
    38
    39}

    メモリー属性に注意!

    ここで、「adopter 変数のメモリー属性」に注意してください。

    メモリー属性は、値をコントラクトのストレージに保存するのではなく、メモリーに一時保存するよう Solidity に指示します。

    adopter 変数は配列であり、最初の adoption test から、「expectedPetID」が存続しています。

    そのため、「testGetAdopterAddressByPetIdInArray()」関数で、配列内の「expectedPetID」と「expectedAdopter」の戻り値を比較して検索します。

    テストを実行

    以下のコードを、ターミナルで入力します。

    1truffle test

    テスト終了後の画面

    以上で、ローカルのブロックチェーンにコントラクトを作成・展開し、コンソールで操作できるようになりました!

    おすすめの本

    マスタリング・イーサリアム ―スマートコントラクトとDAppの構築
    マスタリング・イーサリアム ―スマートコントラクトとDAppの構築

     

    後編へ続く

    後編では、UI を作成していきたいと思います!

    お楽しみに!

    こちらの記事もオススメ!

    featureImg2020.08.14ブロックチェーン特集知識編ブロックチェーンとコンセンサスアルゴリズム実装編【Blockchain Data Managerを使ってみたEt...

    featureImg2020.07.17ライトコード的「やってみた!」シリーズ「やってみた!」を集めました!(株)ライトコードが今まで作ってきた「やってみた!」記事を集めてみました!※作成日が新し...

    後編はこちら

    featureImg2020.07.28【中編】Ethereum Pet Shop DAppの作成とテスト~コントラクト作成編~~中編~Ethereum Pet ShopでDApp作成とテスト今回は、前回に引き続き、「Ethereum Pet S...

    ライトコードでは、エンジニアを積極採用中!

    ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。

    採用情報へ

    広告メディア事業部

    広告メディア事業部

    おすすめ記事

    エンジニア大募集中!

    ライトコードでは、エンジニアを積極採用中です。

    特に、WEBエンジニアとモバイルエンジニアは是非ご応募お待ちしております!

    また、フリーランスエンジニア様も大募集中です。

    background