Ethereumのフレームワーク「Truffle」とローカル開発環境「Ganache」入門ログ

# 概要

Ethereumを使ったスマートコントラクトの開発をしていて、ローカルの開発環境としてGanacheが便利だよ、と聴いたのでその入門ログを残します。

リアルタイムで導入しながら記事を書いています。たまにはこういう記事の書き方もいいですね。

# 前提

当方、メインネットへのスマートコントラクトのデプロイ（という言い方が正しいかどうかすらわからない）とかやったことないですし、[入門書](https://www.amazon.co.jp/%E3%83%96%E3%83%AD%E3%83%83%E3%82%AF%E3%83%81%E3%82%A7%E3%83%BC%E3%83%B3%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E9%96%8B%E7%99%BA%E3%81%AE%E6%95%99%E7%A7%91%E6%9B%B8-%E5%8A%A0%E5%B5%9C-%E9%95%B7%E9%96%80/dp/4839965137)を読んで一通りやったくらいです。
これくらいのレベルの人間でも環境構築して動作させるくらいは訳ない、という意味でのGanache解説記事だと思っていてくださいm(_ _)m

# 導入手順

## Google検索

「Ethereum Ganache」でGoogle検索すると

[Ethereum のローカル開発環境 Ganache を使ってみる](https://qiita.com/kyrieleison/items/8ef926faa4defa8fe930)
[Truffle Suite | Ganache - Truffle Framework](https://truffleframework.com/ganache)
[Ganache で始める Ðapp 開発](https://m0t0k1ch1st0ry.com/blog/2017/12/05/ganache/)
あたりが出てきました。

まあ公式を見るのが最善でしょう。ブロックチェーン領域はメジャーバージョンアップで過去のドキュメントが参考にならなくなることもしばしば。

[公式のクイックスタート](https://truffleframework.com/docs/ganache/quickstart)を見ながらやってみます。

## Ganacheのダウンロード

[GitHub Releases](https://github.com/trufflesuite/ganache/releases)からDMGファイルを落とします。

次に、CLIをインストールします。と思ったのですがこれはデスクトップ版とCLI版がある、ということなんですね。せっかくなのでこちらも入れてみます。

```shell
npm install -g ganache-cli
+ ganache-cli@6.1.8
added 4 packages from 36 contributors in 32.249s
```

[README](https://github.com/trufflesuite/ganache-cli/blob/master/README.md)によると、Dockerバージョンもあるみたいですね。

```shell
docker run -d -p 8545:8545 trufflesuite/ganache-cli:latest
```

### CLIバージョンでスマートコントラクトの実行を試す

デスクトップ版を使っている解説記事が多いですが、ここではあえてCLIで。
ganache-cliコマンドを叩くと、それだけでGanacheが起動するようです。

```
$ ganache-cli
Ganache CLI v6.1.8 (ganache-core: 2.2.1)

Available Accounts
==================
(0) 0x6c96ae5a0710680aaa4c57b5683b2bf89343HOGE (~100 ETH)
(1) 0x85aa9d90973598ef2cf8f08a37a16421034aHOGE (~100 ETH)
・・・
Gas Price
==================
20000000000

Gas Limit
==================
6721975

Listening on 127.0.0.1:8545
```

本当かな？ということで試してみましょう。

まずは適当なスマートコントラクトを記述します。

```javascript
pragma solidity ^0.4.18;
import "../node_modules/openzeppelin-solidity/contracts/token/ERC20/StandardToken.sol";

contract DappsToken is StandardToken {
  string public name = "DappsToken";
  string public symbol = "DTKN";
  uint public decimals = 18;

  constructor(uint initialSupply) public {
    totalSupply_ = initialSupply;
    balances[msg.sender] = initialSupply;
  }
}
```

次に、マイグレーションファイルを書きます。

```javascript
var DappsToken = artifacts.require("./DappsToken.sol");

module.exports = function (deployer) {
  var initialSupply = 1000;
  deployer.deploy(DappsToken, initialSupply);
}
```

これらをそれぞれcontracts、migrationsディレクトリに入れて、

```shell
npm install -g truffle
truffle compile
truffle migrate
```

を叩きます。このあたりは[Truffleの公式ドキュメント](https://truffleframework.com/docs/truffle/getting-started/installation)にも書かれているので、そちらを参照しながら進めてください。

Ganacheによってすでにローカル開発環境としてブロックチェーンが起動しているので、スマートコントラクトがデプロイされます。（デプロイって単語は合っているのかな）

```shell
Running migration: 2_deploy_dapps_token.js
  Deploying DappsToken...
  ... 0xcab58c136b6bafe5c7eb7fa4aaHOGE42cb6589d93cd9fc62cfa99ae3598aHOGE
  DappsToken: 0xd4f045b7615084aHOGE9441357203dd4e079HOGE
Saving successful migration to network...
  ... 0xd58206d125c10cb5f457a2de854bc2HOGE1d5819177f902c38c8b7b1396eHOGE
Saving artifacts...
```

次に、スマートコントラクトを実行できるプログラムを書きます。Node.jsを採用しました。余談ですがasync awaitはやっぱり便利ですね・・・。やっていることは、2者で送受金やっているだけです。

```javascript:execContract.js
var Web3 = require('web3');
const web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'));

class web3Client {
  constructor() {
    this.ERC20_TOKEN = '0xd4f045b7615084aHOGE9441357203dd4e079HOGE';
  }
  get contract() {
    return new web3.eth.Contract(this.minAbi, this.ERC20_TOKEN);
  }
  get minAbi() {
    return [
      // balanceOf
      {
        "constant": true,
        "inputs": [{
          "name": "_owner",
          "type": "address"
        }],
        "name": "balanceOf",
        "outputs": [{
          "name": "balance",
          "type": "uint256"
        }],
        "type": "function"
      },
      {
        "constant": false,
        "inputs": [{
            "name": "_to",
            "type": "address"
          },
          {
            "name": "_value",
            "type": "uint256"
          }
        ],
        "name": "transfer",
        "outputs": [{
          "name": "",
          "type": "bool"
        }],
        "payable": false,
        "stateMutability": "nonpayable",
        "type": "function"
      },
      // decimals
      {
        "constant": true,
        "inputs": [],
        "name": "decimals",
        "outputs": [{
          "name": "",
          "type": "uint8"
        }],
        "type": "function"
      }
    ];
  }
  async getBalance(account) {
    return new Promise((resolve, reject) => {
      this.contract.methods.balanceOf(account).call({
        from: account
      }, (error, balance) => {
        resolve(balance);
      });
    });
  }
  async transfer(from, to, value = 1) {
    return new Promise((resolve, reject) => {
      this.contract.methods.transfer(to, value).send({
        from
      }, (error, transactionHash) => {
        resolve(transactionHash);
      });
    })
  }
}

const _main_ = async () => {
  let balance = await new web3Client().getBalance('0x6c96ae5a0710680aaa4c57b5683b2bf89343HOGE');
  console.log('送信者の残高', balance);
  balance = await new web3Client().getBalance('0x85aa9d90973598ef2cf8f08a37a16421034aHOGE');
  console.log('受信者の残高', balance);
  const transferResult = await new web3Client().transfer('0x6c96ae5a0710680aaa4c57b5683b2bf89343HOGE', '0x85aa9d90973598ef2cf8f08a37a16421034aHOGE', 1);
  console.log('送信に成功しました。トランザクション：', transferResult);
  balance = await new web3Client().getBalance('0x6c96ae5a0710680aaa4c57b5683b2bf89343HOGE');
  console.log('送信者の残高', balance);
  balance = await new web3Client().getBalance('0x85aa9d90973598ef2cf8f08a37a16421034aHOGE');
  console.log('受信者の残高', balance);
}

_main_();
```
ソースコード中のポイントを書いておきます。

Ganacheを起動したときに、8545ポートで起動したというコンソールへのログが出ていたので、それをそのまま書きます。

```shell
const web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'));
```

スマートコントラクトをデプロイしたときに、DappsToken: hogehogehoge...というログが出ていたと思うのですが、それをコピーして定数に入れます。
この辺はどう考えても環境変数に持たせたほうがいいので、実際に開発するときはそのあたりも考えてください。


```javascript
    this.ERC20_TOKEN = '0xd4f045b7615084aHOGE9441357203dd4e079HOGE';
```

あとはGanacheを起動したときに「Available Accounts」として出てきたアカウントから任意の2アカウントを選んで、getBalanceとtransfer関数の引数に渡すようにしています。

以上のソースコードを実行すると

```shell
$ node execContract.js
送信者の残高 1
受信者の残高 999
送信に成功しました。トランザクション： 0xcdcded20fa32c4e6754db630c05b25697a9b1309f58a012b6f7228fe546e7f0f
送信者の残高 0
受信者の残高 1000
```

このように、送金に成功していることがわかります。1000イーサ持っている方から送らせたほうが見栄えが良かったですがもう気にしない・・・

CLIに戻ってみると、ログが出ています。

```shell
eth_gasPrice
eth_sendTransaction

  Transaction: 0xcdcded20fa32c4e6754db630c05b25697a9b1309f58a012b6f7228fe546e7f0f
  Gas usage: 21547
  Block Number: 9
  Block Time: Wed Oct 24 2018 14:53:29 GMT+0900 (JST)

eth_getTransactionReceipt
```

# まとめ

特にまとまりのない記事でしたが、見よう見まねで自分のPCでブロックチェーンを動かすことに成功しました。
以前Gethを使ってEthereumを動かしたことがあったのですが、コマンドは長いしマイニングも自分で実行しないといけないしで、かなり面倒だったところが、今回はコマンド一発でそちら側の構築が終わるというのはなかなかお手軽です。

Truffle側のドキュメントも目を通してみてますが、サンプルも豊富なので勉強が捗りそうです。