Ethereumのローカルネットワーク上で独自コインを送金する

# はじめに
こんにちは、Misocaの洋食（@yoshoku）です。

この記事は、[Misoca Advent Calendar 2017](https://qiita.com/advent-calendar/2017/misoca)の20日目の記事です。

ビットコインをはじめ暗号通貨（仮想通貨）がスゴイ人気ですね。メジャーな暗号通貨は、値段が高騰しすぎて手が出せそうにありませんが、暗号通貨を支えるブロックチェーンには興味があります。今回は、オープンソースのブロックチェーンプラットフォームである[Ethereum](https://en.wikipedia.org/wiki/Ethereum)を使って、ローカルネットワーク上で独自コインを実装してみたいと思います。

# 準備
## ネットワーク　
Ethereumのネットワークには、いわゆるパブリックブロックチェーンと、テストネットワークがあります。さらにテストネットワークには、世界中のノードが参加できるModern testnetと、限られたノードのみが参加できるlocal private testnetがあります。本稿では、local private testnetを使用します。

## クライアント
[Ethereumのクライアント](http://ethdocs.org/en/latest/ethereum-clients/choosing-a-client.html)には様々なものがあります。本稿では、Ethereum Foundationが開発している、Go言語で実装されたクライアントgo-ethereum（Geth）を使用します。Macであれば、HomebrewでEthereumをインストールすることで利用できます。

```bash
$ brew tap ethereum/ethereum
$ brew install ethereum
$ geth version
Geth
Version: 1.7.3-stable
...
```

## テストネットワークの構築
ローカル環境にテストネットワークを構築するためには、データ保存用のディレクトリと、ブロックチェーンの最初のブロックの情報を記したGenesisファイルが必要になります。GenesisファイルはJSON形式で記述します。テストネットワークなのでdifficultyは小さめにしてあります。

```json:genesis.json
{
  "config": {
    "chainId": 5,
    "homesteadBlock": 0,
    "eip155Block": 0,
    "eip158Block": 0
  },
  "difficulty": "0x4000",
  "gasLimit": "0x8000000",
  "alloc": {}
}
```

Gethを初期化します。データ保存用のディレクトリはtestnetとしました。

```bash
$ mkdir testnet
$ geth --datadir testnet init genesis.json 
```

## アカウントの作成
本稿では、独自のコインを発行することが目的です。コインをやりとりするために、アカウントを二人分作成します。アカウントは、gethコマンドで作成できます。

```bash
$ geth --datadir testnet account new
Your new account is locked with a password. Please give a password. 
Do not forget this password.
Passphrase: foobar01 # パスフレーズは画面に表示されません。
Repeat passphrase: foobar01 # パスフレーズは画面に表示されません。
Address: {12507a4a19b059d19a45156669c5adf3a2beacdb}
```

同じようにして「foobar02」というパスフレーズで、二人目のアカウントを作成しました。

## Etherの採掘
Ethereum上で実行するアプリケーションを（スマート）コントラクトと呼びます。コントラクトを実行するためには、Ethereum上の暗号通貨であるEtherで、手数料（Gas Fee）を支払う必要があります。そこで、ローカルプライベートネットワークで、Etherを採掘します。採掘したEtherは、ローカルプライベートネットワークでのみ使用可能です。

Etherの採掘は、Gethのコンソール上で行います。「netowrkid」は予約済みの0〜3以外の数値を指定して下さい。「nodiscover」は他ノードから見つからないようにします。「maxpeers 0」は他ノードと接続しないようにします。

```bash
$ geth --networkid 123 --nodiscover --maxpeers 0 --datadir testnet console
```

コマンドを実行すると、Javascriptベースのコンソールが起動します。採掘をはじめましょう。採掘中は沢山メッセージが出力されます。

```bash
> miner.start(1)
...
```

適当なところで、止めましょう。メッセージが出力されていても気にせず、コンソールに停止コマンドを入力します。

```bash
> miner.stop()
...
# しばらくメッセージが出ることがありますが、良きところで止まってくれます。
```

先ほど作成したアカウントの残高を確認します。単位がetherではなくweiなのでとても大きな額が表示されます。採掘はこれで完了です。

```bash
> eth.getBalance(eth.accounts[0])
# とても大きな額
```

# コントラクト指向プログラミング言語Solidity
本稿では、コントラクトの実装に、[Solidity](https://solidity.readthedocs.io/en/develop/)を使用します。コントラクトを実装するためのプログラミング言語には、他にSerpentなどがありますが、おそらくSolidityが最も人気があり、ネットから情報を得やすいと思います。Solidityは、Javascriptに似た文法を持っています。Macであれば、Homebrewでインストールできます。

```bash
$ brew install solidity
$ solc --version
solc, the solidity compiler commandline interface
Version: 0.4.19+commit.c4cbbb05.Darwin.appleclang
```

## Hello, world
サンプルプログラムとして「Hello, world」を表示してみましょう。greetメソッドを呼び出すと「Hello, world」という文字列を返すだけのシンプルなものです。

```javascript:hello.sol
pragma solidity ^0.4.16;

contract HelloWorld {
  function greet() public pure returns (string) {
    return 'Hello, world';
  }
}
```

## コンパイルと実行
コンパイルはsolcコマンドで行います。

```bash
$ solc --bin --abi hello.sol

======= hello.sol:HelloWorld =======
Binary: # 長いので省略します。実行時に必要です。
Contract JSON ABI
# 長いので省略します。JSONが出力されます。実行時に必要です。
```
実行はGethのコンソールで行います。

```bash
> personal.unlockAccount(eth.accounts[0])
Unlock account 0x12507a4a19b059d19a45156669c5adf3a2beacdb
Passphrase: # アカウント作成時のパスフレーズを入力します。
true
> data = '0x' + '上記のコンパイル時に出力されたBinaryの部分をコピペします。'
> abi = 上記のコンパイル時に出力されたJSONをコピペします。
> contract = eth.contract(abi)
> HelloWorld = contract.new({from: eth.accounts[0], data: data, gas: 1000000})
...
# 「address: undefined」となっている場合は、miner.start()で採掘します。
> miner.start(1)
> HelloWorld.address
"0xed736a6d40c96ddf35088cb695c5bdbb192d8990"
> miner.stop()
> HelloWorld.greet.call()
"Hello, world"
```

無事にHello, worldが表示されました。

# 独自コインの実装
ブロックチェーン上に作られた通貨を、一般にトークン（Token）と呼びます。Ethereum上に実装されるべきトークンの仕様については、[EIP-20 Token Standard](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md)という文書にまとめられています（以前はERC20と呼ばれていました）。本稿では、これらの仕様を満たすのは大変なので、シンプルに送金だけを実装します。

```javascript:mcoin.sol
pragma solidity ^0.4.16;

contract MCoin {
  /**
   * mappingは、ハッシュの様なもので、
   * balanceOfは、address型をkeyとして、
   * uint256型の残高を表すvalueを持つ。
   * address型はアカウントのアドレスを扱う。
   */
  mapping (address => uint256) public balanceOf;

  /**
   * いわゆるコンストラクタ
   * 送金元の最初の残高を設定する。
   */
  function MCoin(uint256 _initialSupply) public {
    balanceOf[msg.sender] = _initialSupply;
  }

  /**
   * コインを_toに_valueだけ送金する。
   */
  function transfer(address _to, uint256 _value) public {
    /* 送金元の残高を減らして（残高チェックなどは省略）、 */
    balanceOf[msg.sender] -= _value;
    /* 送金先に加える。 */
    balanceOf[_to] += _value;
  }
}
```
Hello, worldの時と同様にコンパイルして、バイナリデータとabiを得ます。

```bash
$ solc --bin --abi mcoin.sol

======= mcoin.sol:MCoin =======
Binary: # 長いので省略します。実行時に使用します。
Contract JSON ABI
# 長いので省略します。実行時に使用します。
```
Gethのコンソール上で実行します。

```bash
> personal.unlockAccount(eth.accounts[0])
Unlock account 0x12507a4a19b059d19a45156669c5adf3a2beacdb
Passphrase: # アカウント作成時のパスフレーズを入力します。
true
> data = '0x' + '上記のコンパイル時に出力されたBinaryの部分をコピペします。'
> abi = 上記のコンパイル時に出力されたJSONをコピペします。
> contract = eth.contract(abi)
# 初期値は100コインにしてみました。
> MCoin = contract.new(100, {from: eth.accounts[0], data: data, gas: 1000000})
...
# 「address: undefined」となっている場合は、miner.start()で採掘します。
> miner.start(1)
...
> MCoin.address
"0xb843c36d0928fe0f035d48c0b28510de098d5286"
> miner.stop()
# 初期値がセットされてるか確認します。
> MCoin.balanceOf.call(eth.accounts[0])
100
# 10コイン送金します。
> MCoin.transfer.sendTransaction(eth.accounts[1], 10, {from: eth.accounts[0]})
> miner.start(1)
...
> miner.stop()
# 送金結果を確認します。
> MCoin.balanceOf.call(eth.accounts[0])
90
> MCoin.balanceOf.call(eth.accounts[1])
10
```

無事に送金できました!!

# おわりに
記事を書き終えた後で、類似の入門記事が幾つかあることに気づきました...環境やバージョンによって手順などが微妙に異なるということで、ネットの海を汚すことをお許し下さい。参考までにリンクを掲載しておきます。1. は、Ethereum公式のもので、本稿を書く上で参考にしました。本稿では、ターミナル上で進めてきましたが、IDEを使ってコントラクトを実行するのが良いみたいですね^^;

1. [Create a cryptocurrency contract in Ethereum](https://www.ethereum.org/token) 
2. [How to create a token/coin on Ethereum](https://medium.com/bitvest/how-to-create-a-token-coin-on-ethereum-e01c97b1dac0)
3. [最小構成のEthereum Tokenのprivatenetへの登録](https://qiita.com/KumanoT/items/b7fe65b8512f0d8dd88b)
4. [EthereumにGethを使ってオレオレトークンを発行するまで](https://qiita.com/sbjib/items/6b74e30f9c3ebe64b821)

Ethereumは、Solidityでコントラクトを作成することにより、ブロックチェーン上で色々なことができるので、アイディア次第で新しいことができそうです。