はじめに
Quorum は J.P Morgan が開発を進めているエンタープライズ用に Ethereum を fork したものです。具体的には go-ethereum (geth) の fork で 2018 年 4 月時点では geth 1.7.2 をベースにした Quorum 2.0.2 が最新です。
Quorum の概要と実際に private network を触って見る所までを記事にまとめました。
Qurorum とは
簡単に言うと Enterprise 版の Ethereum で、以下の機能を Ethereum に追加しています。
- トランザクションのプライバシー管理機能
- ネットワークに参加可能なノードの管理機能
- プラガブルで高速なコンセンサスメカニズム
ドキュメントの repository にあるスライドにわかりやすく説明してあります。
トランザクションのプライバシー管理
通常 Ethereum では全てのデータが公開されていますが、Quorum では特定のコントラクトやトランザクションを特定のノード間のみに公開しその他のノードには非公開にする事が可能です。
ノードの管理
Quorum ではネットワークに参加するノードを制限する事が可能です。これにより、悪意のある第三者などが勝手にノードを立ててネットワークに参加する、などといった事が起こらないように出来ます。
コンセンサスメカニズム
ご存知の通り Ethereum は Proof of work コンセンサスメカニズムを採用していますが Quorum ではネットワークに参加するノードが管理されているのでもっと高速なコンセンサスメカニズムを採用可能です。現時点では以下の 2 つが実装されているようです。
- Raft based コンセンサス
- イスタンブール BFT
実際に動かして試してみる
この repository に quorum のテスト環境がまとまっている。内容としては、7 つの quorum node を同時に動かして、上述のプライバシー管理やノード管理を実際に触って見る事が出来るというものです。
事前準備
VirtualBox と Vagrant をインストールしておきます。因みに私は Vagrant のバージョンが古すぎた為、最初の base box のダウンロードでこけてハマりました。Vagrant 2.0.3 にバージョンアップしたら問題が解決しました。
環境構築
repository の root フォルダで下記コマンドを実行
$ vagrant up
Vagrant 環境に入る
Vagrant が準備できたら中に入り、デモがあるフォルダに移動します。
$ vagrant ssh
$ cd quorum-examples/7nodes/
node を起動する
$ ./raft-init.sh
$ ./raft-start.sh
$ ps aux | grep geth
vagrant 9137 3.9 12.6 1356092 259632 pts/0 Sl 14:07 0:19 geth --datadir qdata/dd1 --raft --rpc --rpcaddr 0.0.0.0 --rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum --emitcheckpoints --permissioned --raftport 50401 --rpcport 22000 --port 21000 --unlock 0 --password passwords.txt
vagrant 9138 3.7 8.5 1082268 174140 pts/0 Sl 14:07 0:18 geth --datadir qdata/dd2 --raft --rpc --rpcaddr 0.0.0.0 --rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum --emitcheckpoints --permissioned --raftport 50402 --rpcport 22001 --port 21001
vagrant 9139 3.8 8.5 1016668 174704 pts/0 Sl 14:07 0:19 geth --datadir qdata/dd3 --raft --rpc --rpcaddr 0.0.0.0 --rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum --emitcheckpoints --permissioned --raftport 50403 --rpcport 22002 --port 21002
vagrant 9140 4.2 8.6 994516 177340 pts/0 Sl 14:07 0:21 geth --datadir qdata/dd4 --raft --rpc --rpcaddr 0.0.0.0 --rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum --emitcheckpoints --permissioned --raftport 50404 --rpcport 22003 --port 21003
vagrant 9141 3.8 8.6 1001336 176364 pts/0 Sl 14:07 0:19 geth --datadir qdata/dd5 --raft --rpc --rpcaddr 0.0.0.0 --rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum --emitcheckpoints --raftport 50405 --rpcport 22004 --port 21004
vagrant 9142 3.8 8.5 1009528 175524 pts/0 Sl 14:07 0:19 geth --datadir qdata/dd6 --raft --rpc --rpcaddr 0.0.0.0 --rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum --emitcheckpoints --raftport 50406 --rpcport 22005 --port 21005
vagrant 9143 3.9 8.4 1310672 173256 pts/0 Sl 14:07 0:19 geth --datadir qdata/dd7 --raft --rpc --rpcaddr 0.0.0.0 --rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum --emitcheckpoints --raftport 50407 --rpcport 22006 --port 21006
vagrant 9381 0.0 0.0 12944 900 pts/0 S+ 14:16 0:00 grep --color=auto geth
-
raft-init.sh
は内部でcp raft/nodekey<x> qdata/dd<x>/geth/nodekey
を呼んでいます。この nodekey を 使って生成されるのがenode
と呼ばれる geth node の固有 ID です。このenode
はノードの管理を行う際に重要です。
トランザクションのプライバシー管理を試す
まずは geth
が立ち上がっている状態で smartcontract を作ります。
$ ./runscript.sh script1.js
この smartcontract は非常にシンプルなもので simplestorage.sol
が実装で、compile された ABI と Bytecode が script1.js
に直接記載してあります。
script1.js
ではこのコントラクトを node1 と node7 にしか公開しないようにするためにコントラクトの作成時に privateFor
パラメーターを指定しています。
simpleContract.new(42, {from:web3.eth.accounts[0], data: bytecode, gas: 0x47b760, privateFor: ["ROAZBWtSacxXQrOe3FGAqJDyJjFePR5ce4TSIzmJ0Bc="]}, () => {});
privateFor
には自分の ID は含めないので、ここではコントラクトの作成者である node1 と ROAZBWtSacxXQrOe3FGAqJDyJjFePR5ce4TSIzmJ0Bc
で指定される node7 に対して公開され、それ以外のノードには公開されません。
コマンドを実行すると quorum 上に simplestorage コントラクトが作られるので確認します。
less qdata/log/1.log
に tx が記載されているのでそれを使って contract address
が確認できます。
$ geth attach ipc:qdata/dd1/geth.ipc
> eth.getTransactionReceipt("Tx hash")
{
blockHash: "0x6a41f637680140a5eec70fdee8331221954a77c70bc426abf66f400ca428fa5f",
blockNumber: 1,
contractAddress: "0x1932c48b2bf8102ba33b4a6b545c32236e342f34",
cumulativeGasUsed: 0,
from: "0xed9d02e382b34818e88b88a309c7fe71e65f419d",
gasUsed: 0,
logs: [],
logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
root: "0x51508436508985c1606f18235b61c606f1387ab1e7090ebc997f768f2a3fee0a",
to: null,
transactionHash: "0xd1ef3d4bd0051291e21470a7868cf712b674f94709ca4280d4b79a020fc21a3c",
transactionIndex: 0
}
では、このコントラクトアドレスを使ってプライバシーを確認します。まずターミナルを 3 つ立ち上げて、それぞれ node1, node4, node7 の geth のインタラクティブモードに入ります。それぞれで下記コマンドを打ってテストの準備をします。
> var address = "0x1932c48b2bf8102ba33b4a6b545c32236e342f34";
> var abi = [{"constant":true,"inputs":[],"name":"storedData","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"retVal","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[{"name":"initVal","type":"uint256"}],"type":"constructor"}];
> var private = eth.contract(abi).at(address)
まずは初期値の確認を行います。 script1.js
によると、simplestoraget コントラクトの初期値は 42 です。各ノードでそれぞれ値を読むと、node4 だけが 0 になっている事がわかります。
- node1
> private.get()
42
- node4
> private.get()
0
- node7
> private.get()
42
また、値の更新のクエリを発行時にも権限を指定する事ができます。
> private.set(4,{from:eth.coinbase,privateFor:["ROAZBWtSacxXQrOe3FGAqJDyJjFePR5ce4TSIzmJ0Bc="]});
再度各ノードで値を読んで見ると
- node1
> private.get()
4
- node4
> private.get()
0
- node7
> private.get()
4
となります。
最後に、トランザクションで node7 を権限んから外し、node4 をいれてみます。
> private.set(10,{from:eth.coinbase,privateFor:["oNspPPgszVUFw0qmGFfWwh1uxVUXgvBxleXORHj07g8"]});
各ノードで値を読んで見ると
- node1
> private.get()
10
- node4
> private.get()
0
- node7
> private.get()
0
となります。node7 は権限から外されているから読めませんが node4 も値が読めていません。これは node4 はそもそも contract 自体へのアクセス権限を持っていないからと考えられます。
終わりに
Enterprise 版 Ethereum である Qurorm を触ってみました。まだまだ何とか動かせたという感じでしたが、コンセプトは面白いなと思いました。また、トンラザクションは非常に早く Gas 代も掛からない為、旨く使えばかなり面白いサービスが作れるのでは無いかと思います。もう少し触ってみて実際のサービスでの使い方などを模索したいと思います。