Ethereum のスマートコントラクト実行環境を整える

Ethereum のスマートコントラクト開発フレームワーク Populus でコンパイル・テスト・デプロイしたときのメモです。

- geth 1.7.3
- solidity 0.4.18
- populus 1.10.1

## スマートコントラクトを書いてみる

### Ethereum クライアント Geth をインストールする

Ethereum の P2P ネットワークに参加するには、 クライアントソフトウェアが必要となります。
Ethereum クライアントには C++ 実装の cpp-ethereum 、Python 実装の pyethereum などがありますが、現在推奨されているクライアントは Go 実装の Geth になります。("G"o の "Eth"ereum Client ということで Geth だそうです)

Geth がインストールされていれば、Ethereum ネットワークにフルノードとして参加することができ、マイニングやトランザクションの生成、スマートコントラクトの生成などができるようになります。
MacOS での Geth のインストールには、[Homebrew](https://brew.sh/index_ja.html) を使う方法が一番手軽です。

```
% brew tap ethereum/ethereum
% brew install ethereum
```

`geth help` コマンドでオプション一覧が表示されれば、無事にインストールが完了しています。
MacOS 以外でのインストール方法は[こちら](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum#installation-instructions)。

### Solidity をインストールする

Ethereum では、スマートコントラクトのコードは Ethereum Virtual Machine Code (EVM Code) と呼ばれるバイトコード形式で記述・実行されます。
そのまま人間が記述するには大変なので、Ethereum ではスマートコントラクトの記述に特化したプログラミング言語が開発されています。Solidity はその一つで、JavaScript に似た構文を持つ静的型付け言語です。

Solidity のインストールには、Geth 同様 Homebrew を使用します。

```
% brew install solidity
```

`solc --version` コマンドを実行するとバージョンを確認できます。
MacOS 以外でのインストール方法は[こちら](http://solidity.readthedocs.io/en/latest/installing-solidity.html)。

#### Solidity で Hello, World

以下は Solidity でのサンプルコードです。
`contract` 句で宣言されるコントラクトが基本の構成要素です。コントラクトは Java などのオブジェクト指向言語におけるクラスに似ています。そして、`function` に続くのが関数定義です。
このコードでは `get` 関数が呼ばれると "Hello World" という文字列を返します。

```HelloWorld.sol
pragma solidity ^0.4.18;

contract HelloWorld {
    function get() internal pure returns (string retVal) {
        return "Hello World";
   }
}
```

NOTE: 関数定義にて Visibility (可視性)の設定ができます。
 `external`/`public`/`internal`/`private` から選択でき、もし指定がなければ `Warning: No visibility specified. Defaulting to "public".` という警告が出ます。
また、`pure` は純粋関数の意味で、状態の読み込みと変更がないことを示します。純粋関数にもかかわらず `pure` の記述がないと `Warning: Function state mutability can be restricted to pure` という警告が出ます。詳細については[公式ドキュメント](http://solidity.readthedocs.io/en/latest/solidity-in-depth.html)を参照。

サンプルコードを `HelloWorld.sol` として保存し、下記のコマンドを実行するとコンパイルできます。
`Binary:` に続く文字列が、コンパイルされたバイトコードである EVM Code です。

```
% solc --bin hello_world.sol

======= hello_world.sol:HelloWorld =======
Binary:
60606040523415600e57600080fd5b603580601b6000396000f3006060604052600080fd00a165627a7a72305820f80d5060f45e69eba0838f389e99600d64e3bcd7f03288c42af2669d109f628e0029
```

## スマートコントラクトをテストする

ここまででスマートコントラクトを記述し、コンパイルすることができました。
しかしコンパイルしたコントラクトを実行するには、Ethereum ネットワーク上に設置しなければいけません。

Ethereum ネットワークに設置しないとデバッグできないようでは不便なので、Populus というスマートコントラクト開発フレームワークを使用します。
Populus を使うと Python のテストフレームワーク pytest でテストを記述・実行できます。

### Populus をインストールする

事前に Python、Solidity と Geth のインストールが必要となります。
依存関係のあるパッケージがいくつかあるので、Homebrew でインストールした後、pip で Populus をインストールします。

```
% brew install pkg-config libffi autoconf automake libtool openssl
% pip install populus
```

MacOS 以外の環境でのインストール方法は[こちら](http://populus.readthedocs.io/en/latest/quickstart.html#system-dependencies)。

### Populus でコンパイル・テスト・デプロイ

適当なディレクトリで `populus init` を実行すると、Populus プロジェクトが新規作成されます。

```
% populus init
```

下記のようなサンプルのコントラクトとそのテストコードが設置されるので、これを実行します。

```contracts/Greeter.sol
pragma solidity ^0.4.0;

contract Greeter {
    string public greeting;

    function Greeter() {
        greeting = 'Hello';
    }

    function setGreeting(string _greeting) public {
        greeting = _greeting;
    }

    function greet() constant returns (string) {
        return greeting;
    }
}
```

```tests/test_greeter.py
def test_greeter(chain):
    greeter, _ = chain.provider.get_or_deploy_contract('Greeter')

    greeting = greeter.call().greet()
    assert greeting == 'Hello'


def test_custom_greeting(chain):
    greeter, _ = chain.provider.get_or_deploy_contract('Greeter')

    set_txn_hash = greeter.transact().setGreeting('Guten Tag')
    chain.wait.for_receipt(set_txn_hash)

    greeting = greeter.call().greet()
    assert greeting == 'Guten Tag'
```

Populus ではコンパイルもできます。

```
% populus compile
> Found 1 contract source files
  - contracts/Greeter.sol
> Compiled 1 contracts
  - contracts/Greeter.sol:Greeter
> Wrote compiled assets to: build/contracts.json
```

コンパイル後、テストを実行します。

```
% py.test
```

実行結果に `2 passed` が返ってくればテストは通っています。
テストに成功したコードのデプロイも Populus でテストできます。Populus がローカルのメモリ上にテストチェーンを用意し、テストチェーン上にデプロイをしてくれます。

```
% populus deploy --chain tester --no-wait-for-sync
> Found 1 contract source files
  - contracts/Greeter.sol
> Compiled 1 contracts
  - contracts/Greeter.sol:Greeter
Please select the desired contract:

   0: Greeter

:                                   // 0 を入力して Enter 

Beginning contract deployment.  Deploying 1 total contracts (1 Specified, 0 because of library dependencies).

Greeter
Deploying Greeter
Deploy Transaction Sent: 0x5e9c682715478ef7984d7621ac63ef4ec0668c6368d69fd0f60c888037cdab50
Waiting for confirmation...

Transaction Mined
=================
Tx Hash      : 0x5e9c682715478ef7984d7621ac63ef4ec0668c6368d69fd0f60c888037cdab50
Address      : 0xc305c901078781c232a2a521c2af7980f8385ee9
Gas Provided : 466599
Gas Used     : 366599


Verified contract bytecode @ 0xc305c901078781c232a2a521c2af7980f8385ee9
Deployment Successful.
```

これでローカルのテストチェーン上にデプロイできることが確認できました。
テストチェーンなのですぐに終了し、コントラクトが保存されることはありませんが、`populus deploy` コマンドの `--chain` オプションで、 Ethereum テストネットワークやメインネットワークでもデプロイできるようです。

## 参考文献
- [Ethereum入門](https://book.ethereum-jp.net/)
- [Solidity公式ドキュメント](https://solidity.readthedocs.io/en/develop/)
- [Populus公式ドキュメント](http://populus.readthedocs.io/en/latest/index.html)