[Ethereum] Docker Composeで動かすPoAコンソーシアム

geth(go-ethereum)を使ってPoAのコンソーシアムチェーンを作り、Docker Composeで動かします。

# 前提
gethがインストールされていること(インストール手順は[こちら](https://github.com/ethereum/go-ethereum/wiki/Installation-Instructions-for-Mac))
gethのバージョンは`1.9.7-stable`にて実施

# 手順
## 1. ワークスペースの作成
```bash
$ mkdir consortium-nodes
$ cd consortium-nodes/
$ mkdir node1 node2
```

## 2. Ethereumアカウントの作成
node1と2で使用するアカウントを作成します。

```bash
# create node1 account
$ geth --datadir node1/ account new
Your new account is locked with a password. Please give a password. Do not forget this password.
Password: hogehoge # input your password
Repeat password: hogehoge

# create node2 account
$ geth --datadir node2/ account new
Your new account is locked with a password. Please give a password. Do not forget this password.
Password: hogehoge # input your password
Repeat password: hogehoge

# save password for nodes
$ echo hogehoge > node1/password.txt
$ echo hogehoge > node2/password.txt
```

## 3. genesisファイルの作成
下記の記事を参照してgenesisファイルを作成します。
[gethでPoAでノード２つで環境構築](https://qiita.com/tokuryoo/items/2b68151b07883f9696bc)
[EthereumのPoA(Proof of Authority)ネットワークを複数のノードで構築](https://qiita.com/fukumame55/items/b6005adb7cd301fc9188)

networkIdは32414とします。
作成されるgenesisファイルは下記の名称とします。

```
consortiumnodes-harmony.json
consortiumnodes.json
```

## 4.Dockerfileの作成
node1とnode2用にそれぞれのディレクトリにDockerfileを作成します。
接続先のpeerを指定するstatic-nodes.jsonは後続の作業で作成します。

```Dockerfile:./node1/Dockerfile
FROM ethereum/client-go:v1.9.7

WORKDIR /root/ethereum

COPY password.txt static-nodes.json* /root/ethereum/
COPY keystore /root/ethereum/keystore

ENTRYPOINT []

EXPOSE 8501 30311 30311/udp
```

```Dockerfile:./node2/Dockerfile
FROM ethereum/client-go:v1.9.7

WORKDIR /root/ethereum

COPY password.txt static-nodes.json* /root/ethereum/
COPY keystore /root/ethereum/keystore

ENTRYPOINT []

EXPOSE 8502 30312 30312/udp
```

## 5. docker-compose.ymlを作成する。
node1とnode2を起動するためのdocker-compose.ymlを下記のように作成します。
gethを起動する際のオプションについては下記の記事を参照。
[geth 全オプション一覧](https://qiita.com/tokuryoo/items/ba9e312f2b584404db08)

```yml:./docker-compose.yml
version: '3.4'

services:
  node1:
    build:
      context: ./node1
      dockerfile: Dockerfile
    container_name: geth_node1
    ports:
      - "8501:8501"
      - "30311:30311"
    volumes:
      - ./node1/geth:/root/ethereum/geth
    command: >
      geth --datadir /root/ethereum --syncmode 'full' --port 30311
      --rpc --rpcaddr '0.0.0.0' --rpcport 8501 --rpccorsdomain '*' --rpcvhosts '*'
      --rpcapi 'eth,web3,personal,net'
      --ws --wsaddr "0.0.0.0" --wsapi "eth,web3,personal,net" --wsorigins "*"
      --networkid 32414
      --gasprice '0'
      --unlock 0
      --password password.txt
      --mine --allow-insecure-unlock --nodiscover
  node2:
    build:
      context: ./node2
      dockerfile: Dockerfile
    container_name: geth_node2
    ports:
      - "8502:8502"
      - "30312:30312"
    volumes:
      - ./node2/geth:/root/ethereum/geth
    command: >
      geth --datadir /root/ethereum --syncmode 'full' --port 30312
      --rpc --rpcaddr '0.0.0.0' --rpcport 8502 --rpccorsdomain '*' --rpcvhosts '*'
      --rpcapi 'eth,web3,personal,net'
      --ws --wsaddr "0.0.0.0" --wsapi "eth,web3,personal,net" --wsorigins "*"
      --networkid 32414
      --gasprice '0'
      --unlock 0
      --password password.txt
      --mine --allow-insecure-unlock --nodiscover
```

コンテナを落としてもnode内のデータが消えないように、それぞれ`volumes`に`/root/ethereum/geth`を設定しています。

## 6.nodeの初期化
下記のコマンドでノードを初期化します。

```bash
geth --datadir node1/ init consortiumnodes.json
geth --datadir node2/ init consortiumnodes.json
```

## 7. static-nodes.jsonを作成する。
enode urlを確認するために一旦node1とnode2を起動します。
確認できたらstatic-nodes.jsonを作成し、nodeを止めるのと合わせてdockerイメージ諸共消し去ります。

```bash
# run
$ docker-compose up -d

# get enode id
$ docker logs geth_node1 2>&1 | grep "enode:"
$ docker logs geth_node2 2>&1 | grep "enode:"

# create static-nodes.json
$ docker-compose down --rmi all --volumes
$ echo -e "[\n  \"enode://<ここに確認したnode2のenode urlを設定する>@node2:30312\"\n]" > node1/static-nodes.json
$ echo -e "[\n  \"enode://<ここに確認したnode1のenode urlを設定する>@node1:30311\"\n]" > node2/static-nodes.json
```

## 8.nodeを立ち上げる
これで全ての準備が整いました。
下記コマンドでnode1とnode2を立ち上げればPoAコンソーシアムの完成です。

```bash
# run again
$ docker-compose up -d
```

# 9.動作確認
実際に送金してみます。

```bash
$ docker exec -it geth_node1 sh
~/ethereum # geth attach geth.ipc
> eth.sendTransaction({'from':eth.coinbase, 'to':'0x4351cf911ec8e83b6b6a9f642a3b16dc0024fafd', 'value':web3.toWei(1, 'ether')});
"0x522229494e4bd6b78345c9b2d8a369c27f40ad46ae42bc7a1409dc1b5c4435b0"
> eth.blockNumber
1
```

node1とnode2でログを確認します。
下記のようなログが出ていれば無事起動できています。

```bash
$ docker logs geth_node1
      :
INFO [12-25|14:44:29.902] Submitted transaction                    fullhash=0x522229494e4bd6b78345c9b2d8a369c27f40ad46ae42bc7a1409dc1b5c4435b0 recipient=0x4351cF911EC8E83b6B6a9F642A3b16DC0024FAfD
INFO [12-25|14:44:30.670] Commit new mining work                   number=2 sealhash=a2fb7f…13c662 uncles=0 txs=1 gas=21000 fees=0 elapsed=6.631ms
```

# まとめ
これで無事PoAのコンソーシアムチェーンがDocker Composeで動きました。
ノード数を増やしたければ、node3、node4と同じで順で増やしてぞれぞれのenode urlをstatic-nodes.jsonに書き込めば問題なく動くと思います。