gethでPoAでノード２つで環境構築

# 概要
ノード２つのプライベートネットワークを構築してみる。コンセンサスアルゴリズムは PoA にしてみる。一般に、ネットワークへ参加するノード数が可変な場合はbootnodeをセットアップするようだが、今回はbootnodeを使わず、ノードを２つに限定し、static-nodes.json で設定とした。
通常はサーバを２台準備してそれぞれにノードをセットアップすると思われるが、今回の目的はセットアップ手順の把握であるため、１つのサーバにノードを２つセットアップしている。
~~bootnode を使っておらず、--syncmode "full" としており、ノードが１つでも落ちると全マイニングが止まってしまうかも？~~ ノード２つのうち、片方のマイニングを止めると、もう片方のマイニングは止まってしまう。ノード３つの場合は、そのうちの１ノードのマイニングを止めても、他が止まることは無い。２ノード止めると、残りの１ノードもマイニングが止まる。当記事では２ノードで構成しているが、最低３ノードは欲しい。
また、理解不足なところもあるので、追って調べなおしたい。ご指摘等は歓迎します。


# 環境
- Ubuntu 14.04
- geth 1.8.21-stable

# 環境構築手順
↓１つ目のノードのdataディレクトリを poa_data1 、２つ目のノードのdataディレクトリを poa_data2 とし、各ノードでアカウントを１つずつ作成する。

```
$ mkdir ~/poa_data1/
$ mkdir ~/poa_data2/
$ geth account new --datadir poa_data1/
INFO [01-20|06:08:38.163] Maximum peer count                       ETH=25 LES=0 total=25
Your new account is locked with a password. Please give a password. Do not forget this password.
Passphrase:
Repeat passphrase:
Address: {25440d10657f6e321a0d9e10399b4e83235f7cb0}
$ geth account new --datadir poa_data2/
INFO [01-20|06:08:56.390] Maximum peer count                       ETH=25 LES=0 total=25
Your new account is locked with a password. Please give a password. Do not forget this password.
Passphrase:
Repeat passphrase:
Address: {bf00b7fbe82cbc89682ed9e01cd487d7dfdd21b4}
$
```
↑作成したアカウント２つのアドレスが表示される。このアドレスは後で使う。

↓puppeth を使うことで、Genesisファイルを簡単に作れるらしいので、試してみる。

```
$ puppeth
+-----------------------------------------------------------+
| Welcome to puppeth, your Ethereum private network manager |
|                                                           |
| This tool lets you create a new Ethereum network down to  |
| the genesis block, bootnodes, miners and ethstats servers |
| without the hassle that it would normally entail.         |
|                                                           |
| Puppeth uses SSH to dial in to remote servers, and builds |
| its network components out of Docker containers using the |
| docker-compose toolset.                                   |
+-----------------------------------------------------------+

Please specify a network name to administer (no spaces, hyphens or capital letters please)
> networkx
```
↑適当な名前を付ける。スペース、ハイフン、大文字はダメ。

```
Sweet, you can set this via --network=networkx next time!

INFO [01-20|06:18:01.966] Administering Ethereum network           name=networkx
WARN [01-20|06:18:01.966] No previous configurations found         path=/home/vagrant/.puppeth/networkx

What would you like to do? (default = stats)
 1. Show network stats
 2. Configure new genesis
 3. Track new remote server
 4. Deploy network components
> 2
```
↑新しいgenesis を設定したいので 2 を選ぶ。

```
What would you like to do? (default = create)
 1. Create new genesis from scratch
 2. Import already existing genesis
> 1
```
↑新しく作りたいので 1 を選ぶ。

```
Which consensus engine to use? (default = clique)
 1. Ethash - proof-of-work
 2. Clique - proof-of-authority
> 2
```
↑コンセンサスアルゴリズムは、PoAにしたいので 2 を選ぶ。

```
How many seconds should blocks take? (default = 15)
> 2
```
↑ブロックの生成間隔。ここでは 2 秒に設定してみる。2 ではなく 0 とすればトランザクションが存在する時に限り、ブロックが生成されるようになる（空のブロックが生成されなくなる）。

```
Which accounts are allowed to seal? (mandatory at least one)
> 0x25440d10657f6e321a0d9e10399b4e83235f7cb0
> 0xbf00b7fbe82cbc89682ed9e01cd487d7dfdd21b4
> 0x(Enter)
```
↑先ほど作成したアドレスを入力する。アドレスは片方ではなく両方を入力しても支障は無いようである。アドレスを入力せずにEnterのみを入力すると次のステップへ進む。

```
Which accounts should be pre-funded? (advisable at least one)
> 0x25440d10657f6e321a0d9e10399b4e83235f7cb0
> 0xbf00b7fbe82cbc89682ed9e01cd487d7dfdd21b4
> 0x
```
↑同様に入力。

```
Should the precompile-addresses (0x1 .. 0xff) be pre-funded with 1 wei? (advisable yes)
> no
```
↑お勧めは yes らしいが、今回は no とする。yes とすると 0x1から0xffまでのff個のaddress に 1 wei 配られるようである。

```
Specify your chain/network ID if you want an explicit one (default = random)
> 15
```
↑network ID を入力する。適当に 15 にしておく。

```
INFO [01-20|06:21:57.311] Configured new genesis block

What would you like to do? (default = stats)
 1. Show network stats
 2. Manage existing genesis
 3. Track new remote server
 4. Deploy network components
> 2

 1. Modify existing fork rules
 2. Export genesis configurations
 3. Remove genesis configuration
> 2

Which folder to save the genesis specs into? (default = current)
  Will create networkx.json, networkx-aleth.json, networkx-harmony.json, networkx-parity.json
> (Enter)
INFO [01-20|06:22:31.791] Saved native genesis chain spec          path=networkx.json
ERROR[01-20|06:22:31.791] Failed to create Aleth chain spec        err="unsupported consensus engine"
ERROR[01-20|06:22:31.791] Failed to create Parity chain spec       err="unsupported consensus engine"
INFO [01-20|06:22:31.791] Saved genesis chain spec                 client=harmony path=networkx-harmony.json
```
↑これで、目的の genesisファイルの生成 ができた。Aleth chain spec と Parity chain spec がエラーとなっているが、今回は使わないので問題無い。networkx-harmony.json も今回は使わない。

```
What would you like to do? (default = stats)
 1. Show network stats
 2. Manage existing genesis
 3. Track new remote server
 4. Deploy network components
> Ctrl + C
```
↑また同じものが表示される。Ctrl + C で抜ける。
puppeth で入力した情報は 隠しディレクトリ .puppeth に保存されているようだ。

↓生成された networkx.json を確認してみる。

``` networkx.json
{
  "config": {
    "chainId": 15,
    "homesteadBlock": 1,
    "eip150Block": 2,
    "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    "eip155Block": 3,
    "eip158Block": 3,
    "byzantiumBlock": 4,
    "constantinopleBlock": 5,
    "clique": {
      "period": 2,
      "epoch": 30000
    }
  },
  "nonce": "0x0",
  "timestamp": "0x5c4412ca",
  "extraData": "0x000000000000000000000000000000000000000000000000000000000000000025440d10657f6e321a0d9e10399b4e83235f7cb0bf00b7fbe82cbc89682ed9e01cd487d7dfdd21b40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  "gasLimit": "0x47b760",
  "difficulty": "0x1",
  "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "coinbase": "0x0000000000000000000000000000000000000000",
  "alloc": {
    "25440d10657f6e321a0d9e10399b4e83235f7cb0": {
      "balance": "0x200000000000000000000000000000000000000000000000000000000000000"
    },
    "bf00b7fbe82cbc89682ed9e01cd487d7dfdd21b4": {
      "balance": "0x200000000000000000000000000000000000000000000000000000000000000"
    }
  },
  "number": "0x0",
  "gasUsed": "0x0",
  "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}
```
↑片方のノードには alloc に指定されている片方のアドレスしか存在しないが、存在しない方のアドレスは無視されるようで、この後、特にエラーが発生するようなことも無い。

↓生成した networkx.json を使って初期化する。

```
$ geth --datadir poa_data1/ init networkx.json
$ geth --datadir poa_data2/ init networkx.json
```

↓ノードを１つ起動する。

```
$ geth --datadir poa_data1 --nodiscover --port 30001 --networkid 15
```

↓もう１つのノードを起動する。サーバ１台に２ノード動かしているので、poa_data1 とは異なるポートとしている。
（Windowsから操作している場合、もう１つTeraTermを起動）

```
$ geth --datadir poa_data2 --nodiscover --port 30002 --networkid 15
```

↓gethにIPCで接続し(gethコンソール)、enodeを確認する。確認した値を後で使う。
（Windowsから操作している場合、もう１つTeraTermを起動）

```
$ geth attach ipc:poa_data1/geth.ipc
> eth.getBalance(eth.accounts[0])
9.04625697166532776746648320380374280103671755200316906558262375061821325312e+74
> admin.nodeInfo.enode
"enode://cd7b3f3525d036d8c088176a695a355bb65136e1142290255f7d8b43a0366c899dc9765c3d83bb89e911d992be0db1440f5cc32ad62d6f8c006b094aeb04c115@127.0.0.1:30001?discport=0"
> 
```

↓もう１つのノードも同様に enode を確認する。
（Windowsから操作している場合、もう１つTeraTermを起動）

```
$ geth attach ipc:poa_data2/geth.ipc
> eth.getBalance(eth.accounts[0])
9.04625697166532776746648320380374280103671755200316906558262375061821325312e+74
> admin.nodeInfo.enode
"enode://1ec0c192374a22d8ebaab70b2f21e8b75c2af667c4f9d9c57e553deaf45bfa5e7387e1da4f1cbff96d955dafa2957c5f0865d4dee1cdfd291a01e0ac8ab14cf8@127.0.0.1:30002?discport=0"
> 
```

↓ノードから別ノードへの接続の設定を行う。poa_data1 には poa_data2 の enodeを入力する。
自身のenodeも入力してしまうと、WARN「Removing static dial candidate  id=0x9b2c70   addr=127.0.0.1:30001 err="is self"」が出力されてしまうので今回は含めていない（含めても支障はないのかも？）。
（Windowsから操作している場合、もう１つTeraTermを起動）

```
$ vi poa_data1/static-nodes.json
[
 "enode://1ec0c192374a22d8ebaab70b2f21e8b75c2af667c4f9d9c57e553deaf45bfa5e7387e1da4f1cbff96d955dafa2957c5f0865d4dee1cdfd291a01e0ac8ab14cf8@127.0.0.1:30002?discport=0"
]
```

↓同様に、poa_data2 には poa_data1 の enodeを入力する。
（Windowsから操作している場合、もう１つTeraTermを起動）

```
$ vi poa_data2/static-nodes.json
[
 "enode://cd7b3f3525d036d8c088176a695a355bb65136e1142290255f7d8b43a0366c899dc9765c3d83bb89e911d992be0db1440f5cc32ad62d6f8c006b094aeb04c115@127.0.0.1:30001?discport=0"
]
```

↓各gethをCtrl+C で停止して、起動しなおす。--syncmode "full" を付けること。--syncmode を付けないと、マイニングした時に１ブロック作成した後、警告が表示され、マイニングが進まなくなる様子だった。
[Clique : Discarded bad propagated block#1 when syncing #14945](https://github.com/ethereum/go-ethereum/issues/14945)

```
geth --datadir poa_data1 --nodiscover --port 30001 --syncmode "full" --networkid 15
```

↓もう１つのノードも同様に再起動。

```
geth --datadir poa_data2 --nodiscover --port 30002 --syncmode "full" --networkid 15
```

↓各gethコンソールも Ctrl + D で抜けて、入りなおす。設定が反映されているか確認する。

```
$ geth attach ipc:poa_data1/geth.ipc
> admin.peers
[{
    caps: ["eth/63"],
    enode: "enode://1ec0c192374a22d8ebaab70b2f21e8b75c2af667c4f9d9c57e553deaf45bfa5e7387e1da4f1cbff96d955dafa2957c5f0865d4dee1cdfd291a01e0ac8ab14cf8@127.0.0.1:45533",
    id: "211d55cc935b3e14c40e3073c4fcf3f12e48103b17bdc1e2533a71a049fade52",
    name: "Geth/v1.8.21-stable-9dc5d1a9/linux-amd64/go1.10.4",
    network: {
      inbound: true,
      localAddress: "127.0.0.1:30001",
      remoteAddress: "127.0.0.1:45533",
      static: false,
      trusted: false
    },
    protocols: {
      eth: {
        difficulty: 1,
        head: "0x046367f44f2636b856c2e2d1137ac471c854bef2ed89690ce31e476dc329618d",
        version: 63
      }
    }
}]
>
```

↓同様にもう１つのノードも確認。

```
$ geth attach ipc:poa_data2/geth.ipc
> admin.peers
[{
    caps: ["eth/63"],
    enode: "enode://cd7b3f3525d036d8c088176a695a355bb65136e1142290255f7d8b43a0366c899dc9765c3d83bb89e911d992be0db1440f5cc32ad62d6f8c006b094aeb04c115@127.0.0.1:30001?discport=0",
    id: "644b181ec5e93656f601d4478a5d8d1ca538832ab411d7659df4e069519b6e6a",
    name: "Geth/v1.8.21-stable-9dc5d1a9/linux-amd64/go1.10.4",
    network: {
      inbound: false,
      localAddress: "127.0.0.1:45533",
      remoteAddress: "127.0.0.1:30001",
      static: true,
      trusted: false
    },
    protocols: {
      eth: {
        difficulty: 1,
        head: "0x046367f44f2636b856c2e2d1137ac471c854bef2ed89690ce31e476dc329618d",
        version: 63
      }
    }
}]
>
```

↓両方のノードで下記を行い、マイニングを開始する。personal.unlockAccount(eth.accounts[0], "パスワード", 60) だと、60秒後にマイニングが停止してしまうので注意。

```
> personal.unlockAccount(eth.accounts[0], "上記でアカウント作成時に入力したパスワード", 0)
true
> miner.start()
null
> 
```

２つのノードでマイニングのログが出力され続ける。

# 動作確認
↓poa_data1のgethコンソールで、送金先のアカウントを作って、送金してみる。

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

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

> eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value: 10})
"0x919dc9ac565ec607bf163e5917612b82d092feda4e988818d7b4ecf1bdda839d"

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

↓同期されているかを確認したい。poa_data2のgethコンソールで確認。

```
> eth.getBalance("0xb4d5e9684ba006b84e8459d07b9c76a4c3bcf5fc")
10
> eth.getTransaction("0x919dc9ac565ec607bf163e5917612b82d092feda4e988818d7b4ecf1bdda839d")
{
  blockHash: "0x9a1acc0d3b53d6105adbd2333a9b44a73e276facfd53070e7e291e0c95e7b8f3",
  blockNumber: 1,
  from: "0xea2d56c51d100a87d7942516a80bf0cadb6b9fc8",
  gas: 90000,
  gasPrice: 1000000000,
  hash: "0x919dc9ac565ec607bf163e5917612b82d092feda4e988818d7b4ecf1bdda839d",
  input: "0x",
  nonce: 0,
  r: "0x9c577bceb82a5455accddc5b84c1bccaf00e40d1a699ce92b9da9a49e82c6cd6",
  s: "0x3e6d80a778bfa6c47af878f707d889c41f493fced2fe9b54ddc9418a72efe26b",
  to: "0xb4d5e9684ba006b84e8459d07b9c76a4c3bcf5fc",
  transactionIndex: 0,
  v: "0x1c",
  value: 10
}
> 
```
問題無く、同期されている様子ですね。

# 今回試していないこと
@fukumame55 氏の 
[Ethereumのプライベートチェーンでトランザクションが発生した時のみ、マイニングを実施する方法](https://qiita.com/fukumame55/items/bd57a5462e80bcc9125f)
も試したい。

@yuksekig 氏の
[PoAのプライベートチェーンネットワークに後からsignerノードを追加する方法(geth)](https://qiita.com/yuksekig/items/c78a462ab42b031b2e7a) と
[gethでPoAでプライベートチェーンを構築している場合、ブロック生成間隔を0にすれば自ずとAUTOMINING状態になる](https://qiita.com/yuksekig/items/eb50ac5d7673c2749aa5)
も試したい。

# 参考
感謝。

- [Clique : Discarded bad propagated block#1 when syncing #14945](https://github.com/ethereum/go-ethereum/issues/14945)
- [@shiki_tak - EthereumのプライベートネットをPoAで構築する](https://qiita.com/shiki_tak/items/e0635a0ca4e322716573)
- [@fukumame55 - EthereumのPoA(Proof of Authority)ネットワークを複数のノードで構築](https://qiita.com/fukumame55/items/b6005adb7cd301fc9188)
- [ブロックチェーンEthereum入門 2](http://www.intellilink.co.jp/article/column/ethereum02.html)
- [Geth(Go Ethereum)でプライベートネットワーク用のbootnodeをセットアップする](https://kkmax-develop.com/ethereum-geth-setup-bootnode/)
- [Using puppeth To Manually Create An Ethereum Proof Of Authority (Clique) Network On AWS](https://medium.com/@collin.cusce/using-puppeth-to-manually-create-an-ethereum-proof-of-authority-clique-network-on-aws-ae0d7c906cce)
- [How to use static-nodes.json / trusted-nodes.json to prevent connection loss on private network?](https://ethereum.stackexchange.com/questions/7261/how-to-use-static-nodes-json-trusted-nodes-json-to-prevent-connection-loss-on)
- [Not able to start miner on Ethereum POA - Block sealing failed](https://ethereum.stackexchange.com/questions/37237/not-able-to-start-miner-on-ethereum-poa-block-sealing-failed)
- [Block sealing failed, when consensus engine is configed to clique. #15282](https://github.com/ethereum/go-ethereum/issues/15282)