Quorumのサンプル動作

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

という人向けです。

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

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

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

## リンクまとめ

- ホームページ
 - https://www.goquorum.com/
- Git
 - https://github.com/jpmorganchase/quorum
- ドキュメント
  - http://docs.goquorum.com/en/latest/
- サンプル
  - https://github.com/jpmorganchase/quorum-examples

## 主要な機能

- __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