Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

 背景

  • 最近、Ethereumのプライベートネットワークを構築し、スマートコントラクトを用いたアプリケーションの開発を行っています。
  • このプライベートネットワークを一般的なProof of Workのコンセンサスアルゴリズムにて構築すると、マイニングの速度が12~13秒程度になるように、difficultyが調整されており、参加ノードが限定されているプライベートネットにおいて、マイニングに対する報酬すら必要ないにもかかわらず、1トランザクション実行する度に10秒以上待たされることや、複雑な問題を解くためにCPUリソースが奪われていき、辛いなと思っていました。
  • そこで、なんとかして、difficultyを低くしたいと思っていましたが、Proof of Workでは difficultyを定数にする設定はなく、gethのソースコードであるgo-ethereumCalcDifficulty関数の戻り値を1に修正するなどして強引にしのごうとしていました。
  • しかし、go-ethereumのソースコードをあさっている内に、もう一つのCalcDifficulty関数が存在することに気が付き、こちらは私の望む定数を返していることがわかりました。
  • 後者のCalcDifficultyはPoA(Proof of Authority)と呼ばれる、Ethereumネットワーク上で、マイニングに参加できるノードを限定できるコンセンサスアルゴリズムのソースコードであることがわかり、PoAでプライベートネットワークを作成すれば、私の望むdifficultyが固定されたEthereumネットワークが構築できると考えました。
    • PoAについてはこちらの記事などを参照ください。
  • そこで、以下2つの記事を参考にさせていただきながら、複数ノードでのPoAネットワークの構築を試み、無事に構築に成功したため、構築手順を備忘録として残すことにしました。

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

前提条件

  • go-ethereumがインストールされていること
    • Macでのインストール手順はこちら
  • gethのバージョンは 1.8.3-stable にて実施
  • 最新版のgethにアップデートしておくことを推奨します。

ワークスペースの作成

  • 今回のプライベートネットワークを作る上での、rootとなるワークスペースを作成します。
$ mkdir devnet
$ cd devnet
devnet$ mkdir node1 node2

Ethereumアカウントの作成

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

node1のアカウント

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のアカウント

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}

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

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

genesisファイルの作成

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

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

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

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

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

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

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

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

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が出力されていることを確認します。

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の初期化を行います。

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へ置き換えてください。
sudo ln -s /Users/your-home-directory/Library/Caches/Homebrew/ethereum--git/build/bin/bootnode /usr/local/bin/bootnode

bootnodeの初期設定

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

bootnodeの起動

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

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の起動

  • コマンドの実行例は以下のとおりです。
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とは異なるものを設定します。
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

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

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

>

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

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

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

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

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

> 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で後から参加ノードを追加する方法についても、また別の記事にて記載していきたいと思います。

謝辞

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away