背景
- 以前、PoA(Proof of Authority)のコンセンサスアルゴリズムを用いたプライベートチェーンの構築方法をこちらの記事に記載させていただきました。
- 無事、複数ノードでのマイニングが実現でき、difficultyも定数のまま、1sに1回、低負荷でブロックを生成できるようになりましたが、今度は空のブロックが常に生成され続けるという悩みに直面しました。
- そこで、トランザクションが発生した時のみ、マイニングを開始するというロジックを実装し、空ブロックの生成を抑えることに成功したので、その手順について記事に残すことにしました。
- このテクニックを使うことにより、無駄なブロックの生成を抑えることができるため、マイニングによるCPU負荷を下げられるPoAと組み合わせると、更に安定性の高いプライベートチェーンの構築が可能になると思われます。
前提条件
- この記事の前提として、以下の記事に従い、複数ノードでのPoAネットワークが構築されていることを前提とします。
手順
トランザクションの発生時のみ、マイニングを実行するJavaScriptの作成
- 今回、トランザクションの発生時のみ、マイニングを実行させたいため、Pendingとなっているトランザクションがあったときのみ、実行するJavaScriptを用意します。
- 具体的には以下の通りとなります。
function checkPending() {
if (eth.getBlock("pending", true).transactions.length > 0) {
if (eth.mining) return;
miner.start(1);
} else {
miner.stop();
}
}
eth.filter("latest", function(err, block) { checkPending(); });
eth.filter("pending", function(err, block) { checkPending(); });
checkPending();
- こちらのファイルを、
mine_pending.js
など適当なファイル名で保存します。
node1での上記JavaScriptの実行
- node1で上記のJavaScriptを実行するため、コンソールモードで立ち上げを行います。
- 詳しくは前提条件のセクションに記載した記事の内容を参考にしてください。(このコマンドは、あくまで例ですので、addressやbootnodesのURLなどは適宜置き換えてください。)
geth --datadir node1/ --syncmode 'full' --port 30311 --rpc --rpcaddr 'localhost' --rpcport 8501 --rpcapi 'personal,db,eth,net,web3,txpool,miner' --bootnodes 'enode://abaeeba464158234f51971698a7f956b8ef66f42aa7c6f61daaea68ee6d60e5dc6d3a0fbdef2eb17c1a508ebf5314bc29b0363064e1ce130d65e427c8d89ce5b@127.0.0.1:30310' --networkid 1515 --gasprice '0' --unlock '0x92d366c53b5789c2208db13a86acb5e8400ef8f0' --password node1/password.txt console
- するとコンソールが立ち上がるので、以下のコマンドを実行します。
- このコマンドを実行すると、まさにトランザクションが発行されるのを待つ状態になります。
> loadScript('./mine_pending.js')
true
node2でのminingの開始 (node3以上の場合も同様)
- PoAでは信頼ノードとして設定されたノード全員がMiningを実施している事が前提となり、例えminingを開始していたとしても、他のnodeがminingを実施していないときは、自身のノードもwait状態になります。
- よって、node2については、通常通り、miningを開始する形でOKです。
- もし、3つ以上のノードを立てる場合においても、node1以外はnode2と同様に、mining開始状態にしてOKです。
- 具体的な起動コマンドはこんな感じになります。 (こちらも、あくまで例ですので、addressやbootnodesのURLなどは適宜置き換えてください。)
geth --datadir node2/ --syncmode 'full' --port 30312 --rpc --rpcaddr 'localhost' --rpcport 8502 --rpcapi 'personal,db,eth,net,web3,txpool,miner' --bootnodes 'enode://abaeeba464158234f51971698a7f956b8ef66f42aa7c6f61daaea68ee6d60e5dc6d3a0fbdef2eb17c1a508ebf5314bc29b0363064e1ce130d65e427c8d89ce5b@127.0.0.1:30310' --networkid 1515 --gasprice '0' --unlock '0x06cb591585e3d12c881da2b7d2edb0bb9268c706' --password node2/password.txt --mine
node1へのIPC接続と送金処理の実施
- JavaScriptにより、transactionの発生を待っているnode1へIPC接続し、コンソールを立ち上げます。
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: 3762 (Mon, 16 Apr 2018 22:10:28 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
>
- この状態で、送金処理を行ってみます。
eth.sendTransaction({'from':eth.coinbase, 'to':'0x06cb591585e3d12c881da2b7d2edb0bb9268c706', 'value':web3.toWei(3, 'ether')})
"0xa9992a211e31d29da137d836aa0d545d2e12bfa5d2ec0ba1b823401d88fb2592"
- 以下のコマンドを打って、トランザクションが処理されたことを確認します。
> eth.getTransactionReceipt("0xa9992a211e31d29da137d836aa0d545d2e12bfa5d2ec0ba1b823401d88fb2592")
{
blockHash: "0xcac9a9fda5c0ec7f51d5a94f5c7faa9c5d8c9998569fa71815f6a30e5c2d9136",
blockNumber: 3763,
contractAddress: null,
cumulativeGasUsed: 21000,
from: "0x92d366c53b5789c2208db13a86acb5e8400ef8f0",
gasUsed: 21000,
logs: [],
logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
status: "0x1",
to: "0x06cb591585e3d12c881da2b7d2edb0bb9268c706",
transactionHash: "0xa9992a211e31d29da137d836aa0d545d2e12bfa5d2ec0ba1b823401d88fb2592",
transactionIndex: 0
}
処理終了後に、node1, node2のマイニングが一時停止していることの確認
- 無事トランザクションが処理されましたが、これでマイニングが動き続けていたのでは意味がありません。
- トランザクションの処理が終わったら、マイニングが正しく終了していることを確認します。
node1の状態
- 正しくマイニングが実行され、一時停止状態になっています。
node2の状態
- node2についても同様に、一時停止状態になっています。
- 無事、node1もnode2もトランザクション終了後には一時停止状態になっていることを確認できました。
まとめ
- この通り、IPC接続などにより、トランザクション実行を受け付けるnodeにおいて(この記事ではnode1のことを指します)、トランザクションの発生までマイニングを待つJavaScriptを実行させることにより、全ノードでのマイニングを停止させ、無駄なマイニングによる空ブロックの生成を抑えることができます。
- PoAによって、CPUの負荷も下げられ、さらにこのテクニックにより、無駄な空ブロックの生成も抑えられるので、更に安定性の高いプライベートチェーンの構築が可能になると思われます。
謝辞
-
今回の記事を作成するにあたり、下記の記事を参考にさせていただきました。
-
How to make miner to mine only when there are Pending Transactions?
-
有用な情報を提供いただいたことを心より感謝いたします。