Truffle、Solidity、GanacheでEthereum DAppsを作る




TRUFFLE BOXES

EthereumでDAppsを開発するときに何をどうやって作ればいいかわからないことが多いですよね。そんな時はTRUFFLE BOXESを参考にするといいです。

ここにはいろんなサンプルアプリケーションがあり、Reactを使ったサンプルもあるのでモダンなアプリケーションを作る手がかりにもなるかと思います。

環境設定

バージョン

  • Node.js v8.9.4
  • npm v5.7.1
  • solidity v0.4.17
  • truffle v4.1.3
  • Ganache v1.0.2

インストール

truffle unbox pet-shopで今回使用するサンプルコードをダウンロードすることができます。

$ npm install -g truffle
$ mkdir pet-shop & cd pet-shop
$ truffle unbox pet-shop

pet-shopアプリケーションはMetaMask経由でトランザクションを発行し、好みのペットに対して自分のアドレスを紐づけることができるようになっています。

自分が現在どのペットと紐づいているかをスマートコントラクト経由で取得することもできるようになっています。

なのでスマートコントラクトを使った更新と読み取りの基本的なアプリケーションを作ることができます。

DApps開発

スマートコントラクト

contracs/Adoption.solを実装します。

adopt()関数はこの関数を呼び出したアドレスをpetIdを紐づけることで購入者を識別します。getAdopters()は購入者全てのアドレスを返す関数です。Solidityで実装するスマートコントラクトはこれだけです。

pragma solidity ^0.4.17;


contract Adoption {
    address[16] public adopters;

    // Adopting pet
    function adopt(uint petId) public returns (uint) {
        require(petId >= 0 && petId <= 15);

        adopters[petId] = msg.sender;

        return petId;
    }

    // Retrieving the adopters
    function getAdopters() public view returns (address[16]) {
        return adopters;
    }
}

マイグレーション

migrations/2_deploy_contract.jsを作成し下記を実装します。

var Adoption = artifacts.require("Adoption");

module.exports = function(deploer) {
    deploer.deploy(Adoption)
};

コンパイル

コンパイルは最後にWriting artifacts to ./build/contractsが表示されれば成功です。

$ truffle compile
Compiling ./contracts/Adoption.sol...
Compiling ./contracts/Migrations.sol...

Compilation warnings encountered:

/Users/private_work/pet-shop/contracts/Migrations.sol:11:3: Warning: No visibility specified. Defaulting to "public".
  function Migrations() {
  ^
Spanning multiple lines.
,/Users/private_work/pet-shop/contracts/Migrations.sol:15:3: Warning: No visibility specified. Defaulting to "public".
  function setCompleted(uint completed) restricted {
  ^
Spanning multiple lines.
,/Users/private_work/pet-shop/contracts/Migrations.sol:19:3: Warning: No visibility specified. Defaulting to "public".
  function upgrade(address new_address) restricted {
  ^
Spanning multiple lines.

Writing artifacts to ./build/contracts

デプロイ

デプロイする前にGanacheを起動しておきます。Ganacheは起動するだけでローカル環境にブロックチェーンを起動してくれて、さらにアドレスを10個作ってそれぞれに100ETHずつ最初から持たせてくれます。

Ganacheを使うと開発がものすごくしやすくなるので絶対に使いましょう!

Adoption.solのデプロイはコントラクトアドレスが表示されれば成功です。

$ truffle migrate
Using network 'development'.

Running migration: 1_initial_migration.js
  Replacing Migrations...
  ... 0x6aad3f602ed67552ce6ecd2e4691e7e597cf53fe402ebf8b30ee2b0c2175b3bb
  Migrations: 0x8cdaf0cd259887258bc13a92c0a6da92698644c0
Saving successful migration to network...
  ... 0xd7bc86d31bee32fa3988f1c1eabce403a1b5d570340a3a9cdba53a472ee8c956
Saving artifacts...
Running migration: 2_deploy_contracts.js
  Replacing Adoption...
  ... 0xf669341114c23fd66bd5d6d5597e208ff0327145172e0fd3356cd2ae25925540
  Adoption: 0x345ca3e014aaf5dca488057592ee47305d9b3e10
Saving successful migration to network...
  ... 0xf36163615f41ef7ed8f4a8f192149a0bf633fe1a2398ce001bf44c43dc7bdda0
Saving artifacts...

Ganache上でトランザクションも確認できます。

テストコード

テストコードもSolidityで書きます。Adoption.solに実装した変数と関数に対してテストを行なっています。

pragma solidity ^0.4.17;

import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";
import "../contracts/Adoption.sol";

contract TestAdoption {
    Adoption adoption = Adoption(DeployedAddresses.Adoption());

    // Testing the adopt() function
    function testUserCanAdoptPet() public {
        uint returned = adoption.adopt(8);

        uint expected = 8;

        Assert.equal(returned, expected, "Adoption of pet ID 8 should be recorded.");
    }

    // Testing retrieval of a single pet's owner
    function testGetAdopterAddressByPetId() public {
        // Expected owner is this contract
        address expected = this;

        address adopter = adoption.adopters(8);

        Assert.equal(adopter, expected, "Owner of pet ID 8 should be recorded.");
    }

    // Testing retrieval of all pet owners
    function testGetAdopterAddressByPetIdInArray() public {
        // Expected owner is this contract
        address expected = this;

        // Store adopters in memory rather than contract's storage
        address[16] memory adopters = adoption.getAdopters();

        Assert.equal(adopters[8], expected, "Owner of pet ID 8 should be recorded.");
    }
}

テストを実行すると3つともちゃんとテストが通りました。

$ truffle test
Using network 'development'.

Compiling ./contracts/Adoption.sol...
Compiling ./test/TestAdoption.sol...
Compiling truffle/Assert.sol...
Compiling truffle/DeployedAddresses.sol...


  TestAdoption
    ✓ testUserCanAdoptPet (120ms)
    ✓ testGetAdopterAddressByPetId (54ms)
    ✓ testGetAdopterAddressByPetIdInArray (122ms)


  3 passing (963ms)

フロントエンド開発

stc/app.jsをドキュメントに沿って実装します。ここはただの作業になるのでweb3jsの初期化部分だけ説明します。

web3がundefinedじゃないという状況はMetaMask内臓のweb3がちゃんとブラウザでグローバル変数として使えるときです。グルーバルなweb3が取得できればcurrentProviderを使って新たにweb3インスタンスを作成し、MetaMask経由でトランザクションを発行できるようになります。

ここら辺は最初理解しづらいですがかなり重要なところなので頑張って理解できるようになってください。

initWeb3: function() {
  // Is there an injected web3 instance?
  if (typeof web3 !== 'undefined') {
    App.web3Provider = web3.currentProvider;
  } else {
    // If no injected web3 instance is detected, fall back to Ganache
    App.web3Provider = new Web3.providers.HttpProvider('http://localhost:7545');
  }
  web3 = new Web3(App.web3Provider);

  return App.initContract();
}

DAppsの起動

Truffleにはlite-serverという機能でサーバのホスティングもできるようになっています。この機能を使って実装したアプリをブラウザから確認できるようになります。

package.jsonにnpm run devでlite-serverを実行できるようにscriptsが記述されているので下記で実行します。

$ npm run dev

> pet-shop@1.0.0 dev /Users/private_work/pet-shop
> lite-server

** browser-sync config **
{ injectChanges: false,
  files: [ './**/*.{html,htm,css,js}' ],
  watchOptions: { ignored: 'node_modules' },
  server:
   { baseDir: [ './src', './build/contracts' ],
     middleware: [ [Function], [Function] ] } }
[Browsersync] Access URLs:
 -------------------------------------
       Local: http://localhost:3000
    External: http://172.16.42.30:3000
 -------------------------------------
          UI: http://localhost:3001
 UI External: http://172.16.42.30:3001
 -------------------------------------
[Browsersync] Serving files from: ./src
[Browsersync] Serving files from: ./build/contracts
[Browsersync] Watching files...

MetaMask連携

アプリが起動したら次はMetaMask連携を行います。Custom RPCを選択し、http://127.0.0.1:7545を入力してSaveするだけです。これでMetaMaskがローカルで起動しているブロックチェーンにアクセスできるようになります。

Ganacheアカウントのインポート

DAppsで使うアカウントをGanacheからMetaMaskにインポートします。GanacheのACCOUNTから鍵マークをクリックします。

アドレスのprivate key(秘密鍵)が表示されるのでこれをコピーします。

MetaMaskのImport Accountにprivate keyを貼り付けるとGanacheで作成されたアカウントをMetaMaskから使えるようになります。

これでGanacheで起動させたブロックチェーンに対してGanacheで作られたアドレスからトランザクションを発行できます。

まとめ

想像していたよりも簡単にDAppsを作ることができました。なんでもいいからとりあえず作ってみるとかなり理解が進むのでオススメのサンプルコードだと思います。

今回はメインがSolidityのスマートコントラクトの解説だったのでフロントエンド開発も今後説明を追記していきます。

最後に

最後にブロックチェーンエンジニアって何?どうやったらなれるの?という人向けに書いた下の記事も読んでみてください。

>>ブロックチェーンエンジニアになるには何をすべきか

おすすめ書籍

Ethereumを使ったDApps開発を学びたいなら今だとこの1冊が1番良いです!開発環境の構築から使うべきツール、フレームワーク、実装方法・注意点まで網羅的に解説されている書籍なのでおすすめです。出版も2018年1月ということでかなり新しい本で、DMM Bitcoinを作っているネクストカレンシー所属の方が書いているので信頼できます。

ビットコインとブロックチェーンの詳細をしっかりと学びたい方にはこちらの書籍が非常におすすめです。ウォレットの仕組み、楕円曲線暗号、P2Pプロトコル、公開鍵暗号などビットコインを支える技術について詳細に解説されています。また、サンプルコードを通して実際に手を動かして学べるので非常に濃い内容となっています。

The following two tabs change content below.

髙妻智一

2013年CyberAgent新卒入社 スマホゲームを作る子会社に所属し、サーバーサイドのエンジニアを担当。2年目の終わりから新規子会社の立ち上げに参加し、サーバーサイドのエンジニアリーダーとしてサービースのリリースから運用までを担当。 2018年仮想通貨のスマホウォレットを提供するGinco Incにブロックチェーンエンジニアとして入社。






よく読まれている関連記事はこちら



2 件のコメント

  • ですね。。。笑 修正しました。ご指摘ありがとうございます!

  • コメントを残す

    メールアドレスが公開されることはありません。 が付いている欄は必須項目です