はじめに
「Ethereumは使ったことがあるけど、Ethereum Enterprise版を使ったことがない。」
「Quorumを聞いたことはあるが、使ったことがない。」
という人向けです。
Ethereum Enterprise版の Quorum の動かし方を学びます。
Quorumとは
Quorum は、トランザクション/コントラクトのプライバシーと新しいコンセンサスメカニズムを備えたイーサリアムベースの分散台帳プロトコルです。
クォーラムはgo-ethereum
のフォークであり、go-ethereum
のリリースに合わせて更新されます。
リンクまとめ
- ホームページ
- Git
- ドキュメント
- サンプル
主要な機能
- Privacy: Quorumは、パブリック/プライベート状態の分離を通じてプライベートトランザクションとプライベートコントラクトをサポートし、P2P暗号化メッセージ交換(ConstellationおよびTesseraを参照)を使用して、プライベートデータをネットワーク参加者に直接転送します
-
Alternative Consensus Mechanisms: 許可されたネットワークでPOW / POSを使用する必要がないため、Quorumは代わりに、コンソーシアムチェーンにより適した複数のコンセンサスメカニズムを提供します。
- Raft-based Consensus: ブロック時間、トランザクションの最終性、およびオンデマンドブロック作成を高速化するためのコンセンサスモデル
- Istanbul BFT: AMISによるトランザクションの最終性を備えたPBFTにヒントを得たコンセンサスアルゴリズム。
- Peer Permissioning: スマートコントラクトを使用したノード/ピアの許可。既知の関係者のみがネットワークに参加できるようにします。
- Higher Performance: Quorumは、公開gethよりも大幅に高いパフォーマンスを提供します
ダウンロード
Quorum のサンプルを Git から取得します。
$ git clone https://github.com/jpmorganchase/quorum-examples
$ cd quorum-examples
動作確認
Dockerで立ち上げます。
$ docker-compose up -d
Dockerが立ち上がったか確認してみましょう。
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
86c1fe5b22fc quorumengineering/quorum:2.4.0 "/bin/sh -c 'UDS_WAI…" 9 seconds ago Up 5 seconds (health: starting) 8546/tcp, 21000/tcp, 30303/tcp, 50400/tcp, 30303/udp, 0.0.0.0:22002->8545/tcp quorum-examples_node3_1
7a495a567f8d quorumengineering/quorum:2.4.0 "/bin/sh -c 'UDS_WAI…" 9 seconds ago Up 5 seconds (health: starting) 8546/tcp, 21000/tcp, 30303/tcp, 50400/tcp, 30303/udp, 0.0.0.0:22000->8545/tcp quorum-examples_node1_1
1d7079247b29 quorumengineering/quorum:2.4.0 "/bin/sh -c 'UDS_WAI…" 9 seconds ago Up 5 seconds (health: starting) 8546/tcp, 21000/tcp, 30303/tcp, 50400/tcp, 30303/udp, 0.0.0.0:22005->8545/tcp quorum-examples_node6_1
be5a3f8e5a0e quorumengineering/quorum:2.4.0 "/bin/sh -c 'UDS_WAI…" 9 seconds ago Up 5 seconds (health: starting) 8546/tcp, 21000/tcp, 30303/tcp, 50400/tcp, 30303/udp, 0.0.0.0:22003->8545/tcp quorum-examples_node4_1
7deb7a02d97a quorumengineering/quorum:2.4.0 "/bin/sh -c 'UDS_WAI…" 10 seconds ago Up 6 seconds (health: starting) 8546/tcp, 21000/tcp, 30303/tcp, 50400/tcp, 30303/udp, 0.0.0.0:22001->8545/tcp quorum-examples_node2_1
d483ea254c41 quorumengineering/quorum:2.4.0 "/bin/sh -c 'UDS_WAI…" 10 seconds ago Up 5 seconds (health: starting) 8546/tcp, 21000/tcp, 30303/tcp, 50400/tcp, 30303/udp, 0.0.0.0:22006->8545/tcp quorum-examples_node7_1
cf8c85e59cd6 quorumengineering/quorum:2.4.0 "/bin/sh -c 'UDS_WAI…" 10 seconds ago Up 6 seconds (health: starting) 8546/tcp, 21000/tcp, 30303/tcp, 50400/tcp, 30303/udp, 0.0.0.0:22004->8545/tcp quorum-examples_node5_1
4c22e504b943 quorumengineering/cakeshop:0.11.0-RC2 "/bin/sh -c 'DDIR=/q…" 12 seconds ago Up 7 seconds (health: starting) 8080/tcp, 8102/tcp, 0.0.0.0:8999->8999/tcp quorum-examples_cakeshop_1
de2c803c03c1 quorumengineering/tessera:0.10.3 "/bin/sh -c 'if [ \"$…" 12 seconds ago Up 9 seconds (health: starting) 9000/tcp, 0.0.0.0:9085->9080/tcp quorum-examples_txmanager5_1
f39fb0103c4f quorumengineering/tessera:0.10.3 "/bin/sh -c 'if [ \"$…" 12 seconds ago Up 8 seconds (health: starting) 9000/tcp, 0.0.0.0:9081->9080/tcp quorum-examples_txmanager1_1
aa4f16bf26bc quorumengineering/tessera:0.10.3 "/bin/sh -c 'if [ \"$…" 12 seconds ago Up 8 seconds (health: starting) 9000/tcp, 0.0.0.0:9083->9080/tcp quorum-examples_txmanager3_1
2b964d12ba0c quorumengineering/tessera:0.10.3 "/bin/sh -c 'if [ \"$…" 12 seconds ago Up 8 seconds (health: starting) 9000/tcp, 0.0.0.0:9084->9080/tcp quorum-examples_txmanager4_1
29276e52b8c4 quorumengineering/tessera:0.10.3 "/bin/sh -c 'if [ \"$…" 12 seconds ago Up 8 seconds (health: starting) 9000/tcp, 0.0.0.0:9086->9080/tcp quorum-examples_txmanager6_1
04c87773a143 quorumengineering/tessera:0.10.3 "/bin/sh -c 'if [ \"$…" 12 seconds ago Up 9 seconds (health: starting) 9000/tcp, 0.0.0.0:9082->9080/tcp quorum-examples_txmanager2_1
a94c254ca903 quorumengineering/tessera:0.10.3 "/bin/sh -c 'if [ \"$…" 12 seconds ago Up 9 seconds (health: starting) 9000/tcp, 0.0.0.0:9087->9080/tcp quorum-examples_txmanager7_1
Nodeへのアクセス
次のコマンドは、docker-compose up -d
で立ち上げてから、数分経過しないとエラーになります。。。
また、エラーにならなくても、consoleが出るまでに数分かかります。
$ docker exec -it quorum-examples_node1_1 geth attach /qdata/dd/geth.ipc
Welcome to the Geth JavaScript console!
instance: Geth/node1-istanbul/v1.8.18-stable-20c95e5d(quorum-v2.4.0)/linux-amd64/go1.11.13
coinbase: 0xd8dba507e85f116b1f7e231ca8525fc9008a6966
at block: 6 (Thu, 27 Feb 2020 10:06:16 UTC)
datadir: /qdata/dd
modules: admin:1.0 debug:1.0 eth:1.0 istanbul:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0
エラー時は以下、エラーの時はすぐに出力されます。
$ docker exec -it quorum-examples_node1_1 geth attach /qdata/dd/geth.ipc
panic: MustNew: Failed to connect to Constellation (/qdata/tm/tm.ipc): lstat /qdata/tm/tm.ipc: no such file or directory
goroutine 1 [running]:
github.com/ethereum/go-ethereum/private/constellation.MustNew(0xc00003800f, 0x10, 0xc00003800f)
/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/private/constellation/constellation.go:108 +0x1de
github.com/ethereum/go-ethereum/private.FromEnvironmentOrNil(0x11bbb1d, 0xe, 0xc0001ef5a0, 0xc0001ef580)
/go-ethereum/build/_workspace/src/github.com/ethereum/go-ethereum/private/private.go:20 +0x64
コントラクトのデプロイ
コンソールに入って、下記のコマンドを実行し、コントラクトをチェーン上へデプロイします。(これもコンソールが立ち上がってから通るまでに、数分かかりました。。。)
> loadScript('/examples/private-contract.js')
Contract transaction send: TransactionHash: 0xcca72b49194d7d1a90746ac0cc9f50c1ac870b8c914d2079c044e7b7065abd74 waiting to be mined...
true
> Contract mined! Address: 0x1932c48b2bf8102ba33b4a6b545c32236e342f34
[object Object]
生成したトランザクションの中身を確認してみましょう。
> eth.getTransaction("0xcca72b49194d7d1a90746ac0cc9f50c1ac870b8c914d2079c044e7b7065abd74")
{
blockHash: "0x1d678eac0ebf0a7c3c2b2f222f93190d8f27a3951129c397857ddf2a190f51df",
blockNumber: 19,
from: "0xed9d02e382b34818e88b88a309c7fe71e65f419d",
gas: 4700000,
gasPrice: 0,
hash: "0xcca72b49194d7d1a90746ac0cc9f50c1ac870b8c914d2079c044e7b7065abd74",
input: "0x2fb0476e23e90ac27f7a202550f6497e967b80a5776cbf9475a14f562a61b213ec7d914302c3a948d8f459c160adbfaf2a2c9a2e4f6eb5b750746de4f3df7c3e",
nonce: 0,
r: "0xbe181b4eaad9559338f49cae1a8add46938518f7f582bd26e19ed06dab73b8e1",
s: "0x5fcd1eb6afa30c1277b2cf7fb8a0b11fd0a85796637f2992955ec88da8eb0046",
to: null,
transactionIndex: 0,
v: "0x25",
value: 0
}
コントラクトを生成できたので、Node1からコントラクトへアクセスしてみましょう。
> var address = "0x1932c48b2bf8102ba33b4a6b545c32236e342f34"
undefined
> 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"}];
undefined
> var private = eth.contract(abi).at(address)
undefined
> private.get()
42
42が取得できていることがわかります。
他の Node からのコントラクトへアクセス
同様に、node4 と node7 から取得できるかを確認してみます。
Node4
$ docker exec -it quorum-examples_node4_1 geth attach /qdata/dd/geth.ipc
Welcome to the Geth JavaScript console!
instance: Geth/node4-istanbul/v1.8.18-stable-20c95e5d(quorum-v2.4.0)/linux-amd64/go1.11.13
coinbase: 0xf512a992f3fb749857d758ffda1330e590fa915e
at block: 320 (Thu, 27 Feb 2020 10:27:12 UTC)
datadir: /qdata/dd
modules: admin:1.0 debug:1.0 eth:1.0 istanbul:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0
> private.get()
ReferenceError: 'private' is not defined
at <anonymous>:1:1
> var address = "0x1932c48b2bf8102ba33b4a6b545c32236e342f34"
undefined
> 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"}];
undefined
> var private = eth.contract(abi).at(address)
undefined
> private.get()
0
Node7
$ docker exec -it quorum-examples_node7_1 geth attach /qdata/dd/geth.ipc
Welcome to the Geth JavaScript console!
instance: Geth/node7-istanbul/v1.8.18-stable-20c95e5d(quorum-v2.4.0)/linux-amd64/go1.11.13
coinbase: 0xb131288f355bc27090e542ae0be213c20350b767
at block: 407 (Thu, 27 Feb 2020 10:29:38 UTC)
datadir: /qdata/dd
modules: admin:1.0 debug:1.0 eth:1.0 istanbul:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0
> var address = "0x1932c48b2bf8102ba33b4a6b545c32236e342f34"
undefined
> 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"}];
undefined
> var private = eth.contract(abi).at(address)
undefined
> private.get()
42
node4からは0、node7からは42が取得されています。
動作のまとめ
したがって、ノード1と7がプライベートコントラクトの状態を読み取ることができ、その初期値は42です。
private-contract.js
を見ると、42
の値がコントラクトの作成時に設定された値であることがわかります。
ノード4は状態を読み取ることができません。
コントラクトの値の書き換え
node1 のコンソールで下記のように設定すると、値が書き換わります。
> private.set(4,{from:eth.accounts[0],privateFor:["ROAZBWtSacxXQrOe3FGAqJDyJjFePR5ce4TSIzmJ0Bc="]});
"0x189a5755c0a1104445cb83a4689221494794832ab6e6cefa5ee6a02696fc38ac"
> private.get()
4
これを、Node4 と Node7 から確認したのが下記です。
Node4
> private.get()
0
Node7
> private.get()
4
Node1, Node7 からアクセスできて、Node4からはアクセスできていないことがわかります。
Permissions
Node Permissioningは、事前に定義された一連のノード(remotekey / enodesで識別される)のみが許可されたネットワークに接続できるようにするQuorumの機能です。
geth attach path/to/geth.ipc
で個々のノードに接続し、接続されたノードを確認するためにadmin.peers
を使用します。
> admin.peers
[{
caps: ["istanbul/64"],
enode: "enode://239c1f044a2b03b6c4713109af036b775c5418fe4ca63b04b1ce00124af00ddab7cc088fc46020cdc783b6207efe624551be4c06a994993d8d70f684688fb7cf@172.16.239.17:40062",
id: "3cb4aaea0f49f73c9de4a34db131288f355bc27090e542ae0be213c20350b767",
name: "Geth/node7-istanbul/v1.8.18-stable-20c95e5d(quorum-v2.4.0)/linux-amd64/go1.11.13",
network: {
inbound: true,
localAddress: "172.16.239.11:21000",
remoteAddress: "172.16.239.17:40062",
static: false,
trusted: false
},
protocols: {
istanbul: {
difficulty: 1169,
head: "0x765088e2cea778a865703c5a057398ce6d4d5d62de8785e8ae898547da0ac4c8",
version: 64
}
}
}, {
caps: ["istanbul/64"],
enode: "enode://0ba6b9f606a43a95edc6247cdb1c1e105145817be7bcafd6b2c0ba15d58145f0dc1a194f70ba73cd6f4cdd6864edc7687f311254c7555cc32e4d45aeb1b80416@172.16.239.12:54236",
id: "995dbe18829f1affb75402e66571d97f340c8495b661a823f2c2145ca47d63c2",
name: "Geth/node2-istanbul/v1.8.18-stable-20c95e5d(quorum-v2.4.0)/linux-amd64/go1.11.13",
network: {
inbound: true,
localAddress: "172.16.239.11:21000",
remoteAddress: "172.16.239.12:54236",
static: false,
trusted: false
},
protocols: {
istanbul: {
difficulty: 1167,
head: "0xd8ff13d791864d599dbed913f416af92413cc2422ebe9f3e14bb575c795c5728",
version: 64
}
}
}, {
caps: ["istanbul/64"],
enode: "enode://eacaa74c4b0e7a9e12d2fe5fee6595eda841d6d992c35dbbcc50fcee4aa86dfbbdeff7dc7e72c2305d5a62257f82737a8cffc80474c15c611c037f52db1a3a7b@172.16.239.16:21000?discport=0&raftport=50400",
id: "ab62dd7df5863a5f3bb61f458157d4437104e3b8df4451a85f7b2438ef6699ff",
name: "Geth/node6-istanbul/v1.8.18-stable-20c95e5d(quorum-v2.4.0)/linux-amd64/go1.11.13",
network: {
inbound: false,
localAddress: "172.16.239.11:52212",
remoteAddress: "172.16.239.16:21000",
static: true,
trusted: false
},
protocols: {
istanbul: {
difficulty: 1169,
head: "0x765088e2cea778a865703c5a057398ce6d4d5d62de8785e8ae898547da0ac4c8",
version: 64
}
}
}, {
caps: ["istanbul/64"],
enode: "enode://579f786d4e2830bbcc02815a27e8a9bacccc9605df4dc6f20bcc1a6eb391e7225fff7cb83e5b4ecd1f3a94d8b733803f2f66b7e871961e7b029e22c155c3a778@172.16.239.13:21000?discport=0&raftport=50400",
id: "c39143f98d04e97bd9e31ac1e36cbeb565b061217930767886474e3cde903ac5",
name: "Geth/node3-istanbul/v1.8.18-stable-20c95e5d(quorum-v2.4.0)/linux-amd64/go1.11.13",
network: {
inbound: false,
localAddress: "172.16.239.11:43438",
remoteAddress: "172.16.239.13:21000",
static: true,
trusted: false
},
protocols: {
istanbul: {
difficulty: 1167,
head: "0xd8ff13d791864d599dbed913f416af92413cc2422ebe9f3e14bb575c795c5728",
version: 64
}
}
}, {
caps: ["istanbul/64"],
enode: "enode://3d9ca5956b38557aba991e31cf510d4df641dce9cc26bfeb7de082f0c07abb6ede3a58410c8f249dabeecee4ad3979929ac4c7c496ad20b8cfdd061b7401b4f5@172.16.239.14:21000?discport=0&raftport=50400",
id: "c75f7dcb9fd6063f0ada0998f512a992f3fb749857d758ffda1330e590fa915e",
name: "Geth/node4-istanbul/v1.8.18-stable-20c95e5d(quorum-v2.4.0)/linux-amd64/go1.11.13",
network: {
inbound: false,
localAddress: "172.16.239.11:47280",
remoteAddress: "172.16.239.14:21000",
static: true,
trusted: false
},
protocols: {
istanbul: {
difficulty: 1166,
head: "0xdb21189404e3e5d8c111ca1ced00f149033bedcd9c23e844c12ac149c47b9b49",
version: 64
}
}
}, {
caps: ["istanbul/64"],
enode: "enode://3701f007bfa4cb26512d7df18e6bbd202e8484a6e11d387af6e482b525fa25542d46ff9c99db87bd419b980c24a086117a397f6d8f88e74351b41693880ea0cb@172.16.239.15:59474",
id: "f06c06f1e958cb2edf90d8bfb912de287f9b047b4228436e94b5b78e3ee16171",
name: "Geth/node5-istanbul/v1.8.18-stable-20c95e5d(quorum-v2.4.0)/linux-amd64/go1.11.13",
network: {
inbound: true,
localAddress: "172.16.239.11:21000",
remoteAddress: "172.16.239.15:59474",
static: false,
trusted: false
},
protocols: {
istanbul: {
difficulty: 1169,
head: "0x765088e2cea778a865703c5a057398ce6d4d5d62de8785e8ae898547da0ac4c8",
version: 64
}
}
}]
Permissions の構成
gethノードのリモートキーに基づいて許可が付与されます。リモートキーpermissioned-nodes.json
はで指定され、個々のノードの<datadir>
の下に配置されます。
[
"enode://ac6b1096ca56b9f6d004b779ae3728bf83f8e22453404cc3cef16a3d9b96608bc67c4b30db88e0a5a6c6390213f7acbe1153ff6d23ce57380104288ae19373ef@127.0.0.1:21000?discport=0&raftport=50401",
"enode://0ba6b9f606a43a95edc6247cdb1c1e105145817be7bcafd6b2c0ba15d58145f0dc1a194f70ba73cd6f4cdd6864edc7687f311254c7555cc32e4d45aeb1b80416@127.0.0.1:21001?discport=0&raftport=50402",
"enode://579f786d4e2830bbcc02815a27e8a9bacccc9605df4dc6f20bcc1a6eb391e7225fff7cb83e5b4ecd1f3a94d8b733803f2f66b7e871961e7b029e22c155c3a778@127.0.0.1:21002?discport=0&raftport=50403",
"enode://3d9ca5956b38557aba991e31cf510d4df641dce9cc26bfeb7de082f0c07abb6ede3a58410c8f249dabeecee4ad3979929ac4c7c496ad20b8cfdd061b7401b4f5@127.0.0.1:21003?discport=0&raftport=50404",
"enode://3701f007bfa4cb26512d7df18e6bbd202e8484a6e11d387af6e482b525fa25542d46ff9c99db87bd419b980c24a086117a397f6d8f88e74351b41693880ea0cb@127.0.0.1:21004?discport=0&raftport=50405",
"enode://eacaa74c4b0e7a9e12d2fe5fee6595eda841d6d992c35dbbcc50fcee4aa86dfbbdeff7dc7e72c2305d5a62257f82737a8cffc80474c15c611c037f52db1a3a7b@127.0.0.1:21005?discport=0&raftport=50406",
"enode://239c1f044a2b03b6c4713109af036b775c5418fe4ca63b04b1ce00124af00ddab7cc088fc46020cdc783b6207efe624551be4c06a994993d8d70f684688fb7cf@127.0.0.1:21006?discport=0&raftport=50407"
]
Permissions の有効化/無効化
個々のノードは、gethを立てる際に-permissioned
コマンドラインフラグを渡すことで許可を有効または無効にできます。
有効になっている場合、<datadir>/permissioned-nodes.json
のノードのみが接続できます。
さらに、これらは、このノードがアウトバウンド接続を行うことができる唯一のノードです
終わりに
サンプルだけでは少々物足りないですね。
もっと調査します。
Nodeについて調査しました。
https://qiita.com/ryu3/items/2552a0a48e3e5164aa26