EthereumのPoA(Proof of Authority)ネットワークを複数のノードで構築

#　背景
- 最近、Ethereumのプライベートネットワークを構築し、スマートコントラクトを用いたアプリケーションの開発を行っています。
- このプライベートネットワークを一般的なProof of Workのコンセンサスアルゴリズムにて構築すると、マイニングの速度が12~13秒程度になるように、difficultyが調整されており、参加ノードが限定されているプライベートネットにおいて、マイニングに対する報酬すら必要ないにもかかわらず、1トランザクション実行する度に10秒以上待たされることや、複雑な問題を解くためにCPUリソースが奪われていき、辛いなと思っていました。
- そこで、なんとかして、difficultyを低くしたいと思っていましたが、Proof of Workでは difficultyを定数にする設定はなく、gethのソースコードである[go-ethereum](https://github.com/ethereum/go-ethereum)の[CalcDifficulty関数](https://github.com/ethereum/go-ethereum/blob/d429a92f09e476c431830cef48690dc3784153c7/consensus/ethash/consensus.go#L297)の戻り値を1に修正するなどして強引にしのごうとしていました。
- しかし、go-ethereumのソースコードをあさっている内に、[もう一つのCalcDifficulty関数](https://github.com/ethereum/go-ethereum/blob/5f8888e11606296c9582496974c0f6b96a882146/consensus/clique/clique.go#L669)が存在することに気が付き、こちらは私の望む定数を返していることがわかりました。
- 後者のCalcDifficultyはPoA(Proof of Authority)と呼ばれる、Ethereumネットワーク上で、マイニングに参加できるノードを限定できるコンセンサスアルゴリズムのソースコードであることがわかり、PoAでプライベートネットワークを作成すれば、私の望むdifficultyが固定されたEthereumネットワークが構築できると考えました。
    - PoAについては[こちら](https://blog.kaspersky.co.jp/good-good-blockchain/18000/)の記事などを参照ください。
- そこで、以下2つの記事を参考にさせていただきながら、複数ノードでのPoAネットワークの構築を試み、無事に構築に成功したため、構築手順を備忘録として残すことにしました。
    - [Setup your own private Proof-of-Authority Ethereum network with Geth
](https://hackernoon.com/setup-your-own-private-proof-of-authority-ethereum-network-with-geth-9a0a3750cda8)
    - [EthereumのプライベートネットをPoAで構築する](https://qiita.com/shiki_tak/items/e0635a0ca4e322716573)

# Ethereum PoA(Proof of Authority)ネットワークの構築手順

## 前提条件
- go-ethereumがインストールされていること
    - Macでのインストール手順は[こちら](https://github.com/ethereum/go-ethereum/wiki/Installation-Instructions-for-Mac)
- gethのバージョンは `1.8.3-stable` にて実施
- 最新版のgethにアップデートしておくことを推奨します。

## ワークスペースの作成
- 今回のプライベートネットワークを作る上での、rootとなるワークスペースを作成します。

```bash
$ mkdir devnet
$ cd devnet
devnet$ mkdir node1 node2
```

## Ethereumアカウントの作成
- 作成したnode1とnode2のそれぞれで、アカウントの作成を行います。

**node1のアカウント**

```bash
devnet$ geth --datadir node1/ account new
Your new account is locked with a password. Please give a password. Do not forget this password.
Passphrase: hogehoge (これはあくまで例です)
Repeat passphrase: hogehoge
Address: {92d366c53b5789c2208db13a86acb5e8400ef8f0}
```

**node2のアカウント**

```bash
devnet$ geth --datadir node2/ account new
Your new account is locked with a password. Please give a password. Do not forget this password.
Passphrase: fugafuga (これはあくまで例です)
Repeat passphrase: fugafuga
Address: {06cb591585e3d12c881da2b7d2edb0bb9268c706}
```

作成したアカウントのパスワードを以下のコマンドで保存しておきます。

```bash
devnet$ echo 'hogehoge' > node1/password.txt
devnet$ echo 'fugafuga' > node2/password.txt
```

## genesisファイルの作成
- go-ethereumに付属しているpuppethツールを使うと、簡単にgenesis.jsonを作ることができます。
- puppethを起動するためには以下のコマンドを実行します。

```bash
devnet$ puppeth
```

対話式の質問が表示されるので、まず、適当なネットワーク名を指定します。

```bash
Please specify a network name to administer (no spaces, please)
> devnet
```

新しいgenesisファイルを作成するため、２を選択します

```bash
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
```

今回はPoAでのネットワーク構築のため、2を選択します。

```
Which consensus engine to use? (default = clique)
 1. Ethash - proof-of-work
 2. Clique - proof-of-authority
> 2
```

ブロックが生成される時間間隔を指定します。　1以上の値を指定できます。

```
How many seconds should blocks take? (default = 15)
> 5 // for example
```

マイニングできるaccountを指定します。
ここでは、先ほど作成したアドレスを指定します。

```
Which accounts are allowed to seal? (mandatory at least one)
> 0x92d366c53b5789c2208db13a86acb5e8400ef8f0
> 0x06cb591585e3d12c881da2b7d2edb0bb9268c706
```

こちらも同様です。

```
Which accounts should be pre-funded? (advisable at least one)
> 0x92d366c53b5789c2208db13a86acb5e8400ef8f0
> 0x06cb591585e3d12c881da2b7d2edb0bb9268c706
```

今回のネットワークのネットワークIDを指定します。
1~10の値は指定しないでください。

```
Specify your chain/network ID if you want an explicit one (default = random)
> 1515 // for example. Do not use anything from 1 to 10
```

ここは特に何も入力しなくてOKです。

```
Anything fun to embed into the genesis block? (max 32 bytes)
>
```

genesis fileを出力するため、ここでは2を選択します。

```
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
```

以下では２を選択します。

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

genesis fileのファイル名を指定し、ファイルを出力します。

```bash
Which file to save the genesis into? (default = devnet.json)
> genesis.json
INFO [01-23|15:16:17] Exported existing genesis block
```

Control + Cで終了し、genesis.jsonが出力されていることを確認します。

```bash
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
> ^C
```

## nodeの初期化
以下のコマンドを打って、node1, node2の初期化を行います。

```bash
devnet$ geth --datadir node1/ init genesis.json
devnet$ geth --datadir node2/ init genesis.json
```

## bootnodeの構築

### bootnodeコマンドのPATH設定 (Mac環境のみ？)
- プライベートネットワークでお互いのnodeが検出できるように、bootnodeサーバを立てる必要があります。
- なお、Mac環境で、Homebrewでインストールを行った場合など、bootnodeコマンドが標準設定では叩けない場合があるようなので、bootnodeのバイナリへのシンボリックリンクを `/usr/local/bin/` 以下などに作成する必要があります。
- 以下では、MacでHomebrewでインストールを行った際のbootnodeの、シンボリックリンクへの作成例です。
- `your-home-directory`の部分はご自身の、ホームディレクトリのpathへ置き換えてください。

```bash
sudo ln -s /Users/your-home-directory/Library/Caches/Homebrew/ethereum--git/build/bin/bootnode /usr/local/bin/bootnode
```

### bootnodeの初期設定
- 以下のコマンドを打って、bootnodeを初期設定し、bootのためのkeyを出力します。

```bash
bootnode -genkey boot.key
```

### bootnodeの起動

以下のコマンドを叩くと port: 30310でbootnodeが起動し、bootnodeのURLが表示されます。

```bash
bootnode -nodekey boot.key -addr localhost:30310
INFO [04-15|15:02:27] UDP listener up self=enode://cb5cbc52b004449197b77cf227047ec8ef15ccbb3398ed60872bc02e3c6b2cf9ee3f8f47047f84a872125ca7c4fea83dbcb4d9eef72957ad54df1f3a0ea6b3b5@127.0.0.1:30310
```

この例では、以下がbootnodeのアドレスになり、node1とnode2の起動時に指定します。

`enode://cb5cbc52b004449197b77cf227047ec8ef15ccbb3398ed60872bc02e3c6b2cf9ee3f8f47047f84a872125ca7c4fea83dbcb4d9eef72957ad54df1f3a0ea6b3b5@127.0.0.1:30310`


## ノードの起動
- さて、ようやくnode1 ~ node2を準備が整ったので、以下のコマンドで、node1, node2のそれぞれのノードを起動させます。

### node1の起動

- コマンドの実行例は以下のとおりです。

```bash
geth --datadir node1/ --syncmode 'full' --port 30311 --rpc --rpcaddr 'localhost' --rpcport 8501 --rpcapi 'personal,db,eth,net,web3,txpool,miner' --bootnodes 'enode://cb5cbc52b004449197b77cf227047ec8ef15ccbb3398ed60872bc02e3c6b2cf9ee3f8f47047f84a872125ca7c4fea83dbcb4d9eef72957ad54df1f3a0ea6b3b5@127.0.0.1:30310' --networkid 1515 --gasprice '0' --unlock '0x92d366c53b5789c2208db13a86acb5e8400ef8f0' --password node1/password.txt --mine
```

**幾つかのオプションについての説明**

- port
    - enodeとしてnode間で検出するためのportを設定します。 今回の例では node1, node2, bootnode　が同じインスタンス上で起動されているため、bootnodeやnode2と異なるポートをアサインする必要がありますが、ことなるIPアドレスを持っている場合は、同じでも構いません。
- bootnodes
    - bootnodeの起動時に表示されるbootnodeのアドレスを指定します。
- networkid
    - genesisファイルに設定したNetwork IDを指定します。
- gasprice
    - Miningを実施する、Transactionの最低gas priceを設定します。
    - ここではprivate networkなので、無償でも受け付けOKとして、0を設定しています。
- unlock
    - 必須ではないですが、起動時にアンロックするアカウントのアドレスを指定します。
- password
    - アカウントのパスワードが記載されたファイル名を指定します。

### node2の起動
- node2も同様に以下のコマンドで立ち上げます。
- port, rpcportはbootnodeやnode1とは異なるものを設定します。

```bash
geth --datadir node2/ --syncmode 'full' --port 30312 --rpc --rpcaddr 'localhost' --rpcport 8502 --rpcapi 'personal,db,eth,net,web3,txpool,miner' --bootnodes 'enode://cb5cbc52b004449197b77cf227047ec8ef15ccbb3398ed60872bc02e3c6b2cf9ee3f8f47047f84a872125ca7c4fea83dbcb4d9eef72957ad54df1f3a0ea6b3b5@127.0.0.1:30310' --networkid 1515 --gasprice '0' --unlock '0x06cb591585e3d12c881da2b7d2edb0bb9268c706' --password node2/password.txt --mine
```

起動に成功すると以下のように、それぞれのnodeにおいて、miningが開始します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/64437/6e0d3d8b-adbd-b317-ab4a-275df1773bf6.png)

## 起動したプライベートネットワークでのトランザクションの実行

- 無事、node1, node2が起動し、プライベートネットワークが起動したため、Geth Javascript Consoleからの送金処理などを行ってみます。
- 別のコンソールを立ち上げ、以下のコマンドを実行し、IPC接続によって起動させます。

```bash
$ cd devnet
devnet$ geth attach node1/geth.ipc
Welcome to the Geth JavaScript console!

instance: Geth/v1.8.3-stable/darwin-amd64/go1.10.1
coinbase: 0x92d366c53b5789c2208db13a86acb5e8400ef8f0
at block: 36 (Sun, 15 Apr 2018 15:07:16 JST)
 datadir: /Users/atsushi-fukuda/devnet/node1
 modules: admin:1.0 clique:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0

>
```

起動したコマンドで、試しにコマンドを実行してみます。

```bash
> net.version
"1515"
> eth.blockNumber
435
> eth.coinbase
"0x92d366c53b5789c2208db13a86acb5e8400ef8f0"
```

無事動いていることを確認し、node2への送金処理を行ってみます。

```bash
> eth.sendTransaction({'from':eth.coinbase, 'to':'0x06cb591585e3d12c881da2b7d2edb0bb9268c706', 'value':web3.toWei(3, 'ether')})
"0x92044963438540aa14941ae252e041c32c3ed8923227272ee5928ed4e9900b01"
```

Transactionが処理されたかどうかを以下のコマンドで確認します。

```bash
> eth.getTransactionReceipt("0x92044963438540aa14941ae252e041c32c3ed8923227272ee5928ed4e9900b01")
{
  blockHash: "0xe6a2b8e3ba698c940c5a54053effd3129ebc05b034ab49088ac557c442d801eb",
  blockNumber: 509,
  contractAddress: null,
  cumulativeGasUsed: 21000,
  from: "0x92d366c53b5789c2208db13a86acb5e8400ef8f0",
  gasUsed: 21000,
  logs: [],
  logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  status: "0x1",
  to: "0x06cb591585e3d12c881da2b7d2edb0bb9268c706",
  transactionHash: "0x92044963438540aa14941ae252e041c32c3ed8923227272ee5928ed4e9900b01",
  transactionIndex: 0
}
```

# まとめ
- こちらの手順で、無事、PoAのプライベートネットワークにて、送金処理が実行できました。
- PoAはdifficultyが定数に固定されているため、プライベートネットワーク前提でのEthereum構築においては、高速なマイニング速度や高い安定性、そして低いマシンスペックでも動く省エネルギー性を獲得することができます。
- PoWのネットワーク構築で、どうも動きが不安定だという方は、是非こちらの手順を試してみていただければと思います。
- PoAで後から参加ノードを追加する方法についても、また別の記事にて記載していきたいと思います。


# 謝辞
- 今回の記事を作成するにあたり、下記２つの記事を参考にさせていただきました。 
- 非常にわかりやすい内容で、有用な情報を提供いただいたことを心より感謝いたします。
    - [Setup your own private Proof-of-Authority Ethereum network with Geth
](https://hackernoon.com/setup-your-own-private-proof-of-authority-ethereum-network-with-geth-9a0a3750cda8)
    - [EthereumのプライベートネットをPoAで構築する](https://qiita.com/shiki_tak/items/e0635a0ca4e322716573)