Ethereum スマートコントラクト入門：geth のインストールから Hello World まで

# 前提知識

- Blockchain、Ethereum、Smart Contract 等の概念的理解（[参考図書](http://qiita.com/amachino/items/b59ec8e46863ce2ebd4a#参考図書)）
- 基本的なプログラミングスキル
 
# プライベートチェーンの立ち上げ

今回は、geth を使ってローカルマシン上に Ethereum のプライベートチェーン（自分だけがアクセス可能なブロックチェーン）を立ち上げます。

[geth](https://github.com/ethereum/go-ethereum/wiki/geth) は Go言語で実装された Ethereumクライアントです。アカウントの作成からマイニングまで、Ethereum に関わる多くの機能を担います。


## geth のインストール

### macOS の場合

```shell-session
$ brew tap ethereum/ethereum
$ brew install ethereum
```

### Ubuntu の場合

```shell-session
$ sudo add-apt-repository -y ppa:ethereum/ethereum
$ sudo apt update
$ sudo apt install ethereum
```

## geth の起動

データを入れるためのディレクトリを任意の場所名前で作成し、ターミナル上でそのディレクトリ内に移動しておいてください。ここでは、ホームディレクトリ直下の `my_eth_chain` としました。

```shell-session
$ mkdir ~/my_eth_chain
$ cd ~/my_eth_chain
```

以下のオプションを付けて `geth` を起動します。

```
$ geth --dev --datadir .
```

```
--dev        : 開発用途のプライベートチェーン設定で起動する
--datadir .  : カレントディレクトリをデータディレクトリに指定する
```

そうしたら別のターミナルを開き、以下のコマンドで geth のコンソールを開きます。`geth.ipc` があるディレクトリ (datadir) の中で実行してください。

```shell-session
$ geth attach geth.ipc
Welcome to the Geth JavaScript console!

instance: Geth/v1.6.7-stable-ab5646c5/darwin-amd64/go1.8.3
 modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 shh:1.0 txpool:1.0 web3:1.0

> 
```

このコンソールでは、[Web3 API](https://github.com/ethereum/wiki/wiki/JavaScript-API) を通じて geth ノードとインタラクティブにやり取りすることができます。



## アカウントの作成

まだアカウントが１つもない状態なので、コンソールからアカウントを１つ作成します。新規アカウント用のパスワードを尋ねられますが、今回は自分だけが使う開発用のプライベートチェーンなので、空でも構いません。

```
> personal.newAccount()
Passphrase: 
Repeat passphrase: 
...
"0x13118d9a345f83433506e20f8e9ae8cb0dddc5e9"
```

`0x` から始まる文字列が今作成したアカウントのアドレスです。このアカウントの秘密鍵は `~/my_eth_chain/keystore` の中に作成されています。

もう１つアカウントを作っておきましょう。

```
> personal.newAccount()
...
"0x11cc1aeb1e7cbba8b64fa8dfd638f09f9188ea5f"
```

次のコマンドは、作成済みのアカウントのアドレスを配列で表示します。

```
> eth.accounts
["0x13118d9a345f83433506e20f8e9ae8cb0dddc5e9", "0x11cc1aeb1e7cbba8b64fa8dfd638f09f9188ea5f"]
```

このとき、1番目のアドレス (`eth.accounts[0]`) は、Coinbase (Etherbase) と呼ばれる特別なアカウントです。`eth.coinbase` でも Coinbase アドレスは確認できます。

```
> eth.coinbase
"0x13118d9a345f83433506e20f8e9ae8cb0dddc5e9"
```

ブロックをマイニングしたときの報酬 (Ether) は、その geth ノードの Coinbase アカウントが受け取ることになります。


## Ether の取得（マイニング）

`eth.getBalance()` で Coinbase アカウントの現在の残高 (Balance) を確認してみると、まだ 0 であることが確認できると思います。

```
> eth.getBalance(eth.accounts[0])
0
```

では Ether を受け取るために、マイニングを開始してみましょう。

```
> miner.start()
```

geth が動いている方のターミナル画面を見てみると、次々とブロックが採掘されているのが確認できると思います。`--dev` オプション付きで起動したチェーンでは採掘難易度が低く設定されているので、短時間でマイニングを進めることができます。

そうしたら一旦マイニングを止めて、Coinbase アカウントの Ether 残高を再度確認してみると、残高が増えているはずです。

```
> miner.stop()
true
```
```
> eth.getBalance(eth.accounts[0])
55000000000000000000
```

大きな桁の数字が出ましたが、この数字の単位は ether ではなく wei になります。wei は Etherum における最小の単位で、1 ether = 10<sup>18</sup> wei なので、上の場合は 55ether の報酬を受けたことになります（11ブロックを採掘した結果です）。

## Ether の送金

次に送金のテストとして、先程作った２つ目のアカウントに対して、１つ目のアカウント (Coinbase) から 1ether (=10<sup>18</sup>wei) を送ってみましょう。

現在の残高は 0 です。

```
> eth.getBalance(eth.accounts[1])
0
```

`eth.sendTransaction({ from: 送金元アドレス, to: 送金先アドレス, value: 送金額 (wei) })` が送金メソッドですが、これを実行するためには、先立って送金元アカウントのアンロックが必要です。

```
> personal.unlockAccount(eth.accounts[0])
Passphrase: 
true
```

では送金を実行します。

```
> eth.sendTransaction({ from: eth.accounts[0], to: eth.accounts[1], value: 1000000000000000000 })
...
"0x75cb60459676362efe9fb2973de9333ad5e2e64d41f68e0c3861ddf552ccdffb"
```

トランザクションの ID が表示されましたが、この段階ではまだ送金は完了していません。採掘して、このトランザクションがブロックチェーンに書き込まれる必要があります。

今回立ち上げたプライベートチェーンではノードは自分しかいないので、マイニングも自分でする必要があります。マイニングを開始して、１ブロック以上採掘してください。

```
> miner.start()
```

すると、`eth.accounts[1]` の残高が 1ether = 10<sup>18</sup>wei になっていることが確認できると思います。

```
> eth.getBalance(eth.accounts[1])
1000000000000000000
```

# スマートコントラクトで Hello World

それでは、Ethereum の核心であるスマートコントラクトに移ります。スマートコントラクトの専用記述言語である [Solidity](https://solidity.readthedocs.io/) を使ってプログラムを書き、先程立ち上げたプライベートチェーンにコードをデプロイするという流れになります。

## solc のインストール

まず、Solidity のコンパイラである solc をインストールします。solc でコンパイルされたバイトコードは、Ethereum の EVM (Etherum Virtual Machine) という仮想マシンで実行されます。Java のバイトコードが JVM で実行されるのと一緒です。

### macOS の場合

```shell-session
$ brew install solidity
```
### Ubuntu の場合

```shell-session
$ sudo apt install solc
```

## Solidity コードの記述

以下の Solidity コードを書いて、`HellowWorld.sol` という名前で任意の場所に保存してください。

```HelloWorld.sol
pragma solidity ^0.4.13;

contract HelloWorld {
    string message;

    function setMessage(string _message) {
        message = _message;
    }

    function sayHello() returns (string) {
        return message;
    }
}
```

`sayHello()` メソッドを呼んだら、`setMessage()` で設定された文字列 (`message`) を返すだけの単純なコントラクトです。

`contract HelloWorld` は通常のプログラミング言語におけるクラスのようなものですが、contract の状態変数として宣言されたデータ（ここでは `string message`）は、ブロックチェーン上に永続的に記録されることになります。

## Solidity コードのコンパイル

新しくターミナルを立ち上げて、`HelloWorld.sol` を保存したディレクトリ内で以下のコマンドを実行することで Solidity コードをコンパイルします。

```shell-session
$ solc --bin --abi HelloWorld.sol
```

すると、`Binary` と `Contract JSON ABI` という２つの項目の結果が表示されます。

```
======= HelloWorld.sol:HelloWorld =======
Binary: 
6060604052341561000f57600080fd5b5b6102de8061001f6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063368b877214610049578063ef5fb05b146100a6575b600080fd5b341561005457600080fd5b6100a4600480803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610135565b005b34156100b157600080fd5b6100b9610150565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100fa5780820151818401525b6020810190506100de565b50505050905090810190601f1680156101275780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b806000908051906020019061014b9291906101f9565b505b50565b610158610279565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156101ee5780601f106101c3576101008083540402835291602001916101ee565b820191906000526020600020905b8154815290600101906020018083116101d157829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061023a57805160ff1916838001178555610268565b82800160010185558215610268579182015b8281111561026757825182559160200191906001019061024c565b5b509050610275919061028d565b5090565b602060405190810160405280600081525090565b6102af91905b808211156102ab576000816000905550600101610293565b5090565b905600a165627a7a7230582068aa6f9fa0b0611b69c6cdd9f024612fc651736ea66b3319e407b34d22b2ea5d0029
Contract JSON ABI 
[{"constant":false,"inputs":[{"name":"_message","type":"string"}],"name":"setMessage","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"sayHello","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"}]
```
Binary が EVM のバイトコードで、ABI (Application Binary Interface) は、コントラクトのインタフェース情報になります。

## プライベートチェーンへのデプロイ

上で得られた Binary と ABI を使って、ブロックチェーン上に HelloWorld コントラクトのデプロイ（コントラクトのアカウント作成）を行います。

起動中の geth のコンソールに戻って、以下のコマンドを実行してください。`bin` の方には、コンパイル結果の `Binary` の先頭に `0x` を付けた上で文字列にして代入します。

```
> bin = "0x6060604052341561000f57600080fd5b5b6102de8061001f6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063368b877214610049578063ef5fb05b146100a6575b600080fd5b341561005457600080fd5b6100a4600480803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610135565b005b34156100b157600080fd5b6100b9610150565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100fa5780820151818401525b6020810190506100de565b50505050905090810190601f1680156101275780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b806000908051906020019061014b9291906101f9565b505b50565b610158610279565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156101ee5780601f106101c3576101008083540402835291602001916101ee565b820191906000526020600020905b8154815290600101906020018083116101d157829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061023a57805160ff1916838001178555610268565b82800160010185558215610268579182015b8281111561026757825182559160200191906001019061024c565b5b509050610275919061028d565b5090565b602060405190810160405280600081525090565b6102af91905b808211156102ab576000816000905550600101610293565b5090565b905600a165627a7a7230582068aa6f9fa0b0611b69c6cdd9f024612fc651736ea66b3319e407b34d22b2ea5d0029"

> abi = [{"constant":false,"inputs":[{"name":"_message","type":"string"}],"name":"setMessage","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"sayHello","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"}]
```
ABI インタフェースから `HelloWorld` のコントラクトオブジェクトを作成します。

```
> contract = eth.contract(abi)
```

そして、コントラクトをチェーン上に登録するトランザクションを送信します。

```
> HelloWorld = contract.new({ from: eth.accounts[0], data: bin, gas: 1000000 })
```
既にマイニングしていれば、このトランザクションもすぐにブロックチェーンに取り込まれます。マイニングが止まっているのであれば、`miner.start()` をしてください。

```
> HelloWorld
{
  abi: [{
      constant: false,
      inputs: [{...}],
      name: "setMessage",
      outputs: [],
      payable: false,
      type: "function"
  }, {
      constant: false,
      inputs: [],
      name: "sayHello",
      outputs: [{...}],
      payable: false,
      type: "function"
  }],
  address: "0x784c38f5fc8e2dcb9b3258272eb7a35a55e0ed52",
  transactionHash: "0x3a998bcefbe8097c363b2981390eca4e3e1c5f81b07281f8fabaaab60541ed9e",
  allEvents: function(),
  sayHello: function(),
  setMessage: function()
}
```
HelloWorld オブジェクトの `address` 部分にアドレスが入っていれば、このスマートコントラクトはブロックチェーン上に正しく書き込まれています。

## スマートコントラクトの呼び出し

コンソール上の HelloWorld オブジェクトを使って、コントラクトのメソッドを呼び出してみましょう。

スマートコントラクトのメソッドの実行方法には、`sendTransaction()` と `call()` との2種類があります。

### sendTransaction()

`sendTransaction()` は、ブロックチェーン上に値を書き込むメソッドを呼び出すときに使用します。今回の例で言えば、`setMessage()` メソッドは、状態変数 `message` の値を更新するので `sendTransaction` を使う必要があります。

`sendTransaction()` は、マイナーに採掘される必要があり、そのための費用（手数料）がかかります。手数料は Gas という単位で支払われます。

### call()

`call()` は、ブロックチェーンの状態を変化させない “Read Only” のメソッドの場合に使用します。今回の例で言えば、`sayHello()` メソッドは状態変数の更新を伴わないので、`call()` で呼び出せます。

マイナーによる新たなブロック生成は必要ないので、`call()` には手数料 (Gas) は発生しません。

## Hello, world!

では実際にデプロイしたコントラクトを呼び出してみましょう。呼び出し方は、

```
コントラクトオブジェクト.メソッド名.call(引数)

コントラクトオブジェクト.メソッド名.sendTransaction(引数, { from: 送信元アドレス })
```

です。

最初に `sayHello()` を実行すると、まだ `message` の値はセットされていないので空文字が返ってきます。

```
> HelloWorld.sayHello.call()
""
```

では、"Hello, world!" という文字列を `message` に代入するために、以下の sendTransaction を実行します。

```
> HelloWorld.setMessage.sendTransaction("Hello, world!", { from: eth.accounts[0] })
"0xa1d4221d6859245ca79c9945b97bdfa941d6e5442472be38b88159a2124d7098"
```

トランザクションIDが表示され、マイニングしていればすぐにブロックチェーンに反映されます。

（このとき `Error: authentication needed: password or unlock` と怒られたら、`personal.unlockAccount(eth.accounts[0])` で送金元アカウントをアンロックしてください）

そして、再度 `HelloWorld.sayHello.call()` を実行することで、めでたく "Hello, world!" と表示されました。

```
> HelloWorld.sayHello.call()
"Hello, world!"
```

# おわりに

geth のインストールから、簡単なスマートコントラクトのデプロイ、実行までを行いました。Solidity による Ethereum スマートコントラクト開発の情報はまだまだ少ないですが、この記事がきっかけになってブロックチェーン/イーサリアム開発者が日本でも増えればいいなと思います。

# 参考図書

- [『いまさら聞けない　ビットコインとブロックチェーン』](https://www.amazon.co.jp/dp/B06XT9NS73/)
    - ゼロからブロックチェーンのことを学びたい人向け
- [『スマートコントラクト本格入門―FinTechとブロックチェーンが作り出す近未来がわかる』](https://www.amazon.co.jp/dp/B06X188NQG/)
    - スマートコントラクトの概念を学びたい人向け
- [『ブロックチェーン　イーサリアムへの入り口』](https://www.amazon.co.jp/dp/B073TRC78W/)／[『はじめてのブロックチェーン・アプリケーション Ethereumによるスマートコントラクト開発入門』]
(https://www.amazon.co.jp/dp/B07416W2PY/)
    - geth や Solidity の具体的な技術入門書

# 参考リンク

- [Ethereum.org](https://ethereum.org/)
    - Ethereum Project のページ
- [geth](https://github.com/ethereum/go-ethereum/wiki/geth)
    - Go言語製の Ethereum クライアント
- [Solidity](https://solidity.readthedocs.io/)
    - 開発言語 Solidity のドキュメント
- [Web3 JavaScript API](https://github.com/ethereum/wiki/wiki/JavaScript-API)
    - Ethereum の JavaScript API
- [JSON RPC API](https://github.com/ethereum/wiki/wiki/JavaScript-API)
    - Ethereum の JSON RPC API
- [Truffle](http://truffleframework.com/)
    - Ethereum 開発フレームワーク
- [The DAO](https://github.com/slockit/DAO)
    - The DAO のスマートコントラクト（[参考1](https://medium.com/@mach/the-dao-%E4%BA%8B%E4%BB%B6%E3%81%8B%E3%82%89%EF%BC%91%E5%B9%B4-%E7%86%B1%E7%8B%82%E3%81%99%E3%82%8Bico-%E3%83%90%E3%83%96%E3%83%AB%E3%81%A8-%E3%81%93%E3%82%8C%E3%81%8B%E3%82%89%E3%81%AE%E8%B3%87%E9%87%91%E8%AA%BF%E9%81%94%E6%89%8B%E6%B3%95-48a90c7c20c5), [参考2](http://hackingdistributed.com/2016/06/18/analysis-of-the-dao-exploit/)）