Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

Quorumのサンプル動作

More than 1 year has passed since last update.

はじめに

「Ethereumは使ったことがあるけど、Ethereum Enterprise版を使ったことがない。」
「Quorumを聞いたことはあるが、使ったことがない。」

という人向けです。

Ethereum Enterprise版の Quorum の動かし方を学びます。

Quorumとは

Quorum は、トランザクション/コントラクトのプライバシーと新しいコンセンサスメカニズムを備えたイーサリアムベースの分散台帳プロトコルです。

クォーラムはgo-ethereumのフォークであり、go-ethereumのリリースに合わせて更新されます。

リンクまとめ

主要な機能

  • 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>の下に配置されます。

permissioned-nodes.json
[
  "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

ryu3
お仕事の依頼はメールへお願いします! ロボット/AIの研究開発、キャッシュレス化、ブロックチェーンの開発、AWS、kubernetesなど、トレンドの技術を追いかけているフルスタックエンジニアです。 大学ではロボット/人工知能の研究に携わり、ロボカップという競技で日本/世界大会に参加し、大学院を首席で卒業しています。
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