複数台Mac実機のDocker SwarmでEthereumプライベートチェーンが動かせない方法

## (動かない)構成
* 各自のPCでSwarmクラスターを構成
* attachableなOverlayネットワークを1つ作成
* Ethereumクライアントは公式イメージ(ethereum/client-go)を使用
* 各自のPCでEthereumコンテナ起動(上記Overlayネットワーク指定)
* P2P接続する(手動)

----

## プライベートチェーン用ネットワークの作成

----

## ※以下のSwarm設定動きません

----

### Swarmの初期化
* Swarmの初期化をしたホストが最初の`manager`ノードになる
* この時点で指定した2377ポートlistenしてない

```sh
docker swarm init --advertise-addr '192.168.x.x'
```

* `--listen-addr`指定したときエラーが出てる時点でもうなんかダメっぽい

```sh
docker swarm init --listen-addr '192.168.x.x'
Error response from daemon: manager stopped: failed to listen on remote API address: listen tcp 192.168.x.x:2377: bind: cannot assign requested address
```

* ファイヤーウォールはオフ
* 8545ポートのJSON-RPC呼ぶだけなら通る

----

### Swarmのクラスターに参加
* `worker`ノードとして参加する
* トークンは`manager`にもらう
* ここまででSwarmクラスターは完成(しない)
* `connection refused`で繋がらない

```sh
docker swarm join --token SW...5j 192.168.x.x:2377
```

----

### Overlayネットワークの作成
* 全コンテナがこのネットワークに参加する
* コンテナ起動時にこの`private-chain`ネットワークを指定する

```sh
docker network create -d overlay --attachable private-chain
```

----

## Ethereumノードを建てる

----

### データディレクトリの作成
* 各人の環境に合わせて好きな場所に作成
* ブロックチェーンのDB(data/ethereum/)とマイニング用のDAG(data/ethash/)を保存する
* コンテナ起動時にフルパスでボリューム指定する

```sh
mkdir -p /Users/$USER/data
```

----

### genesis.jsonの作成
* ジェネシスブロック(最初のブロック)作成用のファイル
* Ethereumの初期化時に使用する
* 上記工程で作った`data`ディレクトリ直下に`genesis.json`というファイル名で配置

```json
{
  "config": {
    "chainId": 10101,
    "homesteadBlock": 0,
    "eip155Block": 0,
    "eip158Block": 0
  },
  "alloc"      : {},
  "coinbase"   : "0x0000000000000000000000000000000000000000",
  "difficulty" : "0x20000",
  "extraData"  : "",
  "gasLimit"   : "0x2fefd8",
  "nonce"      : "0x00000000000000de",
  "mixhash"    : "0x0000000000000000000000000000000000000000000000000000000000000000",
  "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
  "timestamp"  : "0x00"
}
```

----

### イメージ取得
* 公式イメージを取得
* 何も指定せず起動すると、Ethereumメインネット(本番環境)と同期が始まってしまうので、まだ起動しない

```sh
docker pull ethereum/client-go
```

----

### コンテナの起動
* `ENTRYPOINT`を`/bin/sh`に書き換えて起動
* `-v`には上記工程で作成した`data`ディレクトリを指定
* ローカルの`bridge`ネットワーク内で起動する場合`--network`指定なし

```sh
docker run -dit --name eth1 --entrypoint /bin/sh -v /Users/$USER/data:/root/data -p 8545:8545 -p 30303:30303 ethereum/client-go
```

* 上記工程で作成したOverlayネットワーク内で起動する場合

```sh
docker run -dit --name eth1 --entrypoint /bin/sh --network private-chain -p 8545:8545 -v /Users/$USER/data:/root/data ethereum/client-go
```

----

### ジェネシスブロックの作成
* `attach`でコンテナ名を指定してに接続
* detachするには`Ctrl+p`→`Ctrl+q`
* `Ctrl-d`や`exit`すると**コンテナが終了してしまう**ので注意

```sh
docker attach eth1
```

* `genesis.json`を指定してジェネシス・ブロックを生成

```sh
geth --datadir /root/data/ethereum init /root/data/genesis.json
```

----

### アカウントの作成
* ウォレットに最初のアカウントを追加
* このアカウントにマイニング報酬が送金される

```sh
geth --datadir /root/data/ethereum account new
Passphrase:
Repeat passphrase:
Address: {1a...d5}
```

----

### Ethereumノードの起動
* `geth`を起動してREPL環境を表示
* `--miner.etherbase`には上記工程で作成したアカウントアドレスを指定
* `--allow-insecure-unlock`を付けないと`personal.unlockAccount`出来なくなってたので付ける

```sh
geth --networkid 10101 --datadir /root/data/ethereum --ethash.dagdir /root/data/ethash --miner.etherbase 1a...d5 --nodiscover --allow-insecure-unlock --rpc --rpcaddr '0.0.0.0' console
```

* docker起動時にgethを起動する場合は全てのオプションを1行にまとめてコンテナ起動してもいい

```sh
docker run -dit --name eth -v /Users/$USER/data:/root/data -p 8545:8545 -p 30303:30303 ethereum/client-go --networkid 10101 --datadir /root/data/ethereum --ethash.dagdir /root/data/ethash --miner.etherbase 1a...d5 --nodiscover --allow-insecure-unlock --rpc --rpcaddr '0.0.0.0' console
```

↓上記コマンドを見やすく整形したもの

```sh
docker run  -dit \
            --name eth \
            -v /Users/$USER/data:/root/data \
            -p 8545:8545 \
            -p 30303:30303 \
ethereum/client-go  --networkid 10101 \
                    --datadir /root/data/ethereum \
                    --ethash.dagdir /root/data/ethash \
                    --miner.etherbase 1a...d5 \
                    --nodiscover \
                    --allow-insecure-unlock \
                    --rpc --rpcaddr '0.0.0.0' \
                    console
```

* Ethereumで使われているポート
    * 8545: JSON-RPC
    * 8546: WebSocket
    * 8547: GraphQL
    * 30303/tcp: P2P用
    * 30303/udp: P2Pオートディスカバリ用

----

## EthereumのJSコンソール

----

### web3.js
* web3.js v0.x系のAPIが使える

```js
> eth.blockNumber
0
```

----

## P2Pネットワークの形成

----

### 他ノードへの接続
* `--nodiscover`フラグを指定してるので手動で接続する必要がある
* 接続していないことを確認

```js
> admin.peers
[]
> net.peerCount
0
```

* consoleに入ったら`enode`情報を取得

```js
> admin.nodeInfo.enode
"enode://15...1e@127.0.0.1:30303?discport=0"
```

* `enode`内のIPアドレスをコンテナのIPアドレスに変更して接続

```js
> admin.addPeer('enode://15...1e@10.x.x.x:30303?discport=0')
true
```

* やっぱり接続していないことを確認
* ピア情報がでてくる予定だった

```js
> admin.peers
[]
> net.peerCount
0
```

----

## 操作

----

### マイニング
* 初回はDAGの生成に5分ほどかかる

```js
> eth.mining
false
> miner.start(1)
INFO [04-03|15:41:46.204] Updated mining threads                   threads=1
INFO [04-03|15:41:46.204] Transaction pool price threshold updated price=1000000000
INFO [04-03|15:41:46.205] Commit new mining work                   number=1 sealhash=caeddc…34d5e1 uncles=0 txs=0 gas=0 fees=0 elapsed=322.5µs
INFO [04-03|15:41:50.540] Generating DAG in progress               epoch=0 percentage=0 elapsed=3.556s
INFO [04-03|15:41:54.010] Generating DAG in progress               epoch=0 percentage=1 elapsed=7.025s
INFO [04-03|15:41:57.472] Generating DAG in progress               epoch=0 percentage=2 elapsed=10.488s
    :
INFO [04-03|15:47:38.808] Generating DAG in progress               epoch=0 percentage=98 elapsed=5m52.229s
INFO [04-03|15:47:42.314] Generating DAG in progress               epoch=0 percentage=99 elapsed=5m55.735s
INFO [04-03|15:47:42.326] Generated ethash verification cache      epoch=0 elapsed=5m55.747s
INFO [04-03|15:47:47.763] Successfully sealed new block            number=1 sealhash=caeddc…34d5e1 hash=49ceef…74d8f4 elapsed=6m1.963s
INFO [04-03|15:47:47.765] 🔨 mined potential block                  number=1 hash=49ceef…74d8f4
> eth.mining
true
```

* マイナーを止めてマイニング停止
* DAGの生成が止まらないが、いずれそれも止まるので動揺しない

```js
> miner.stop()
```

----

### アカウントの確認

```js
> eth.accounts
["0x12af8fce5685c5e6f25da9dd8ef4da100cc9c0b8"]
> eth.accounts[0]
"0x12af8fce5685c5e6f25da9dd8ef4da100cc9c0b8"
```

----

### 残高の確認
* 100京wei = 1ether

```js
> eth.getBalance(eth.accounts[0])
15
> web3.fromWei(eth.getBalance(eth.accounts[0]),'ether')
15
```

----

### アカウントのアンロック
* 第3引数を`0`にするとプロセス起動中ずっとアンロック
* `--allow-insecure-unlock`を付けないとエラーになるので注意

```js
> personal.unlockAccount(eth.accounts[0],null,0)
Unlock account 0x12af8fce5685c5e6f25da9dd8ef4da100cc9c0b8
Passphrase:
true
```

----

### トランザクションの送信
* いったんマイナーを止めとく
* トランザクションの送信には手数料(ETH)が必要

```js
> bn = eth.blockNumber
13
> eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[0],value:web3.toWei('1','ether')})
"0x0a...87"
```

----

### トランザクションプール
```js
> txpool.status
{
  pending: 1,
  queued: 0
}
> txpool.content
{
  pending: {
    0x12AF8FCE5685C5e6F25DA9dd8eF4Da100CC9c0B8: {
      2: {
        blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
        blockNumber: null,
        from: "0x12af8fce5685c5e6f25da9dd8ef4da100cc9c0b8",
        gas: "0x15f90",
        gasPrice: "0x3b9aca00",
        hash: "0x52bea06df31909b95fd201c098d4d4cc727de5bda9595370edfa9966a76d3d70",
        input: "0x",
        nonce: "0x2",
        r: "0xc0dc7663149fba52e9a4ab357c8b37cfb051794187dfc9cd1f021b318077fa5a",
        s: "0x441004d231de0e425bdfdbe961a07fdcedce38b347f3b79809394299f2988b6",
        to: "0x12af8fce5685c5e6f25da9dd8ef4da100cc9c0b8",
        transactionIndex: "0x0",
        v: "0x4f0e",
        value: "0xde0b6b3a7640000"
      }
    }
  },
  queued: {}
}
> txpool.inspect
{
  pending: {
    0x12AF8FCE5685C5e6F25DA9dd8eF4Da100CC9c0B8: {
      2: "0x12AF8FCE5685C5e6F25DA9dd8eF4Da100CC9c0B8: 1000000000000000000 wei + 90000 gas × 1000000000 wei"
    }
  },
  queued: {}
}
```

---

### ブロックの確認
* マイニングを開始すると上記保留されてたトランザクションがブロックに取り込まれる
* `txpool`を確認すると空になってるはず

```js
> eth.getBlock(bn+1)
{
  difficulty: 131072,
  extraData: "0xd8...78",
  gasLimit: 3181688,
  gasUsed: 21000,
  hash: "0x5e...c3",
  logsBloom: "0x00...00",
  miner: "0x12...b8",
  mixHash: "0x8a7c653952bf27288666a31263923d037dda944f4ce41260a4474e0df0d54e6b",
  nonce: "0x3bdf8017b06618ae",
  number: 13,
  parentHash: "0x0e31202579fa2c97bb9546e261bf037821bd9c75a935e1a64c0b648b959e08ab",
  receiptsRoot: "0x4287c2ed8bce2ced130f5c319d2cde9bc0fceefc273de7406b7112c68175d049",
  sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
  size: 651,
  stateRoot: "0x2eb370e6cc431ede290354bcb0b0abec74f16bacb2903703459c0f3e02d9a675",
  timestamp: 1554883148,
  totalDifficulty: 1838016,
  transactions: ["0x170250400e5f087b90a235023f2460e9031bac12ad2683663fea2111711fb5a2"],
  transactionsRoot: "0x2df21628faf3e362cd5bec2c90ad6a5d0bca59c3b75bc2e2f51a832806b4d09b",
  uncles: []
}
```

----

### トランザクションの確認

```js
> eth.getTransaction(eth.getBlock(bn+1).transactions[0])
{
  blockHash: "0x5ec8f2c51e7e2460a7def8cca57dbbf833d39f651f4b40168c9a4b92a149c5c3",
  blockNumber: 13,
  from: "0x12af8fce5685c5e6f25da9dd8ef4da100cc9c0b8",
  gas: 90000,
  gasPrice: 1000000000,
  hash: "0x170250400e5f087b90a235023f2460e9031bac12ad2683663fea2111711fb5a2",
  input: "0x",
  nonce: 1,
  r: "0xffd2eb20323e2fa0576fcad9c9fd3a5650f8e388e40d9b56a2e0765c1fc31f41",
  s: "0x792e01ad9da9663f020c679db8c8e8afbd04d6e4260687b64d1f9e7519cd3d4c",
  to: "0x12af8fce5685c5e6f25da9dd8ef4da100cc9c0b8",
  transactionIndex: 0,
  v: "0x4f0d",
  value: 1000000000000000000
}
```