Quorumでprivate transactionをスクラッチで生成する

以下、理由でまとめました。

* [公式ドキュメント](http://docs.goquorum.com/en/latest/)では、環境構築～ノード構成～トランザクション生成まで、フローとしてまとまってなかった
* 実行スクリプトなど冗長だった
* ネット上漁っても、一気通貫でまとまっているものがなかった

## 作るもののイメージ
![quorum_test.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59445/4d592d1d-abe6-336c-63d6-d96219497d5d.png)

1. transaction(Tx)をnode1に送信
2. node1は、TxManager1にTxを送信
3. TxManager1からTxManager2にpayload/hash/keyを送信、TxManager2はTxManager1にACK or Nackを送信
4. ③が成功したら、TxManager1からnode1にhashを送信
5. node1からnode2にTxを送信


## 大まかな手順下

1. [tesseraのインストール](#tesseraのインストール)
2. [tesseraノードの構成作成](#tesseraノードの構成作成)
3. [quorumのインストール](#quorumのインストール)
4. [quorumノードの構成作成](#quorumノードの構成作成)
5. [プライベートトランザクション生成、確認](#プライベートトランザクション生成、確認)

## 前提条件
* 手順としてまとめたので、少し冗長かもしれません・・・
* 実行環境
  * OS:ubuntsu 18.04
  * tessera:v0.10.4
  * git: v2.17.1

## tesseraのインストール
* ダウンロード

```console:console
$ wget https://oss.sonatype.org/service/local/repositories/releases/content/com/jpmorgan/quorum/tessera-app/0.10.4/tessera-app-0.10.4-app.jar
```

* リネーム

```console:console
$ mv tessera-app-0.10.4-app.jar tessera.jar
```

## tessera node1の構成作成
* tessera node1のディレクトリを作成、及び鍵の生成

```console:console
$ mkdir node1-t
$ cd node1-t
$ java -jar ../tessera.jar -keygen -filename node1(=鍵名)
Enter a password if you want to lock the private key or leave blank
⇒任意のパスワードを入力
Please re-enter the password (or lack of) to confirm
⇒パスワード再入力
```

* コンフィグファイルの作成

```json:node1-t/config.json
{
    "useWhiteList": false,
    "jdbc": {
        "username": "sa",
        "password": "",
        "url": "jdbc:h2:/<yourpath>/node1-t/db1;MODE=Oracle;TRACE_LEVEL_SYSTEM_OUT=0",
        "autoCreateTables": true
    },
    "serverConfigs": [
        {
            "app": "ThirdParty",
            "enabled": true,
            "serverAddress": "http://localhost:9081",
            "communicationType": "REST"
        },
        {
            "app": "Q2T",
            "enabled": true,
            "serverAddress": "unix:/<yourpath>/node1-t/tm.ipc",
            "communicationType": "REST"
        },
        {
            "app": "P2P",
            "enabled": true,
            "serverAddress": "http://localhost:9001",
            "sslConfig": {
                "tls": "OFF"
            },
            "communicationType": "REST"
        }
    ],
    "peer": [
        {
            "url": "http://localhost:9001"
        },
        {
            "url": "http://localhost:9003"
        }
    ],
    "keys": {
        "passwordFile": "/<yourpath>/node1-t/pass",
        "keyData": [
            {
                "privateKeyPath": "/<yourpath>/node1-t/node1.key",
                "publicKeyPath": "/<yourpath>/node1-t/node1.pub"
            }
        ]
    },
    "alwaysSendTo": []
}
```
ここで、公式では"passwordFile": "pass"の代わりに、"passwords": []で記載があります。
しかし、これをすると、「標準入力からパスワードを入れるな」と怒られるので、対応をとりました。
passファイルを別途用意し、"passwordFile": "<passパス>"で対応しています。

* パスワードファイルの作成

```json:node1-t/pass
<node1作成時に入力したパスワード>
```

* tessera node2のディレクトリを作成、及び鍵の生成

```console:console
$ mkdir node2-t
$ cd node2-t
$ java -jar ../tessera.jar -keygen -filename node2(=鍵名)
Enter a password if you want to lock the private key or leave blank
⇒任意のパスワードを入力(※node1のパスワードとは別)
Please re-enter the password (or lack of) to confirm
⇒パスワード再入力
```

* コンフィグファイルの作成

```json:node2-t/config.json
{
    "useWhiteList": false,
    "jdbc": {
        "username": "sa",
        "password": "",
        "url": "jdbc:h2:/<yourpath>/node2-t/db1;MODE=Oracle;TRACE_LEVEL_SYSTEM_OUT=0",
        "autoCreateTables": true
    },
    "serverConfigs": [
        {
            "app": "ThirdParty",
            "enabled": true,
            "serverAddress": "http://localhost:9083",
            "communicationType": "REST"
        },
        {
            "app": "Q2T",
            "enabled": true,
            "serverAddress": "unix:/<yourpath>/node2-t/tm.ipc",
            "communicationType": "REST"
        },
        {
            "app": "P2P",
            "enabled": true,
            "serverAddress": "http://localhost:9003",
            "sslConfig": {
                "tls": "OFF"
            },
            "communicationType": "REST"
        }
    ],
    "peer": [
        {
            "url": "http://localhost:9001"
        },
        {
            "url": "http://localhost:9003"
        }
    ],
    "keys": {
        "passwordFile": "/<yourpath>/node2-t/pass",
        "keyData": [
            {
                "privateKeyPath": "/<yourpath>/node2-t/node2.key",
                "publicKeyPath": "/<yourpath>/node2-t/node2.pub"
            }
        ]
    },
    "alwaysSendTo": []
}
```
node1同様、passファイルを別途用意し、"passwordFile": "<passパス>"で対応しています。

* パスワードファイルの作成

```json:node1-t/pass
<node2作成時に入力したパスワード>
```

* tessera nodeの起動スクリプト作成

```sh:start_tessera.sh
#!/bin/bash
java -jar tessera.jar --configfile node1-t/config.json >> tessera1.log 2>&1 &
java -jar tessera.jar --configfile node2-t/config.json >> tessera2.log 2>&1 &
```
スクリプトを作ったほうが楽なので作りました。

8. tessera nodeの起動

```console:console
$ sudo chmod +x start_tessera.sh
$ ./start_tessera.sh
```

* 起動確認

```console:console
$ ls node1-t/ & ls node2-t/

[実行結果]
config.json  db1.mv.db  db1.trace.db  node1.key  node1.pub  pass  tm.ipc
config.json  db1.mv.db  db1.trace.db  node2.key  node2.pub  pass  tm.ipc
```

## quorumのインストール
* quorumをgit clone

```console:console
$ git clone https://github.com/jpmorganchase/quorum.git
```

* quorumのビルド

```console:console
$ cd quorum
$ make all
```

* buildバイナリのパス設定

```console:console
$ export PATH=$(pwd)/build/bin:$PATH
```
毎回実行を前提とする場合は、~/.bashrcにパスを通すほうが楽です。

## quorum nodeの構成作成
* node1のディレクトリ作成、及び管理アカウントの作成

```console:console
$ mkdir node1
$ geth --datadir node1 account new

[実行結果]
Password: 
⇒任意のパスワードを入力
Repeat password:
⇒パスワード再入力
```

* genesis.jsonファイルの作成

```json:genesis.json
{
    "alloc": {
      "0xアドレス": {
        "balance": "1000000000000000000000000000"
      },
     "0xc5c7b431e1629fb992eb18a79559f667228cd055": {
        "balance": "2000000000000000000000000000"
      }
  },
   "coinbase": "0x0000000000000000000000000000000000000000",
  "config": {
     "homesteadBlock": 0,
     "byzantiumBlock": 0,
     "chainId": 10,
     "eip150Block": 0,
     "eip155Block": 0,
     "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
     "eip158Block": 0,
     "isQuorum": true
    },
   "difficulty": "0x0",
   "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000",
   "gasLimit": "0xE0000000",
   "mixhash": "0x00000000000000000000000000000000000000647572616c65787365646c6578",
   "nonce": "0x0",
   "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
   "timestamp": "0x00"
  }

```

* nodekey、enodeの作成

```console:console
$ bootnode --genkey=nodekey
$ cp nodekey node1
$ bootnode --nodekey=node1/nodekey --writeaddress > node1/enode
$ cat node1/enode
```

* node1のstatic-nodes.jsonの作成

```json:static-nodes.json
[
    "enode://<手順3で確認したenodeのハッシュ値@127.0.0.1:21000?discport=0&raftport=50000"
]
```

* node1のstatic-nodes.jsonファイルの配置

```console:console
$ cp static-nodes.json node1
```

* node1の初期化

```console:console
$ geth --datadir node1 init genesis.json

[実行結果]
INFO [07-02|18:27:29.226] Maximum peer count                       ETH=50 LES=0 total=50
INFO [07-02|18:27:29.226] Smartcard socket not found, disabling    err="stat /run/pcscd/pcscd.comm: no such file
・
・
・
```

* node1の起動スクリプトの作成

```sh:startnodes.json
#!/bin/bash
PRIVATE_CONFIG=ignore nohup geth --datadir node1\
 --nodiscover --verbosity 5 --networkid 31337 --raft --raftport 50000\
 --rpc --rpcaddr 0.0.0.0 --rpcport 22000\
 --rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum,raft\
 --emitcheckpoints --port 21000 >> node.log 2>&1 &
```

* node1の起動

```console:console
$ sudo chmod +x startnode.sh
$ ./startnode.sh
```

* 起動確認

```console:console
$ geth attach node1/geth.ipc
(以下はgethインタープリタ内の操作)
>raft.cluster

[実行結果]
[{
    hostname: "127.0.0.1",
    nodeActive: true,
    nodeId: "6b7294b1ad3a6f086f730b4f408ca0791c61c10447d3969734df1b4ac4ad6add86a09052974ce1685e8f7a0f320019a078d19fe96abba50dd0511a5c58bd4779",
    p2pPort: 21000,
    raftId: 1,
    raftPort: 50000,
    role: "minter"
}]
```
raftクラスタに参加しているnodeは、自分自身のnodeであることnodeIDから確認できます。

* node2のディレクトリ作成、及びnodekey、enodeの作成

node2に関しても、同じような手順で作成します

```console:console
$ mkdir nocde2
$ bootnode --genkey=nodekey2
$ cp nodekey2 node2/nodekey
$ bootnode --nodekey=node2/nodekey --writeaddress > node2/enode
$ cat node2/enode
```

* node2のstatic-nodes.jsonの配置

```console:console
$ cp static-nodes.json node2
```

* node2の初期化

```console:console
$ geth --datadir node2 init genesis.json

[実行結果]
INFO [07-02|18:56:56.532] Maximum peer count                       ETH=50 LES=0 total=50
INFO [07-02|18:56:56.532] Smartcard socket not found, disabling    err="stat /run/pcscd/pcscd.comm: no such file or directory"
INFO [07-02|18:56:56.535] Allocated cache and file handles 
・
・
・        
```

* node2の起動スクリプトの作成

```sh:startnodes2.sh
#!/bin/bash
PRIVATE_CONFIG=ignore nohup geth --datadir node2 --nodiscover --verbosity 5 --networkid 31337 --raft --raftport 50001 --raftjoinexisting 2 --rpc --rpcaddr 0.0.0.0 --rpcport 22001 --rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum,raft --emitcheckpoints --port 21001 >> node2.log 2>&1 &
```

* node2の起動

```console:console
$ sudo chmod +x startnode2.sh
$ ./startnode2.sh
```

* node2の追加

```console:console
>raft.addPeer("enode://[node2のenodeハッシュ値]@127.0.0.1:21001?discport=0&raftport=50001")
2
>raft.cluster
[{
    hostname: "127.0.0.1",
    nodeActive: true,
    nodeId: "c7234394cc370f9444d1b0bc60d553f9a5cf18da677aafdc0cedf99993bf07ac247d1d827c87e666c01ade7996ce26a4a101dc17782b3a493db71cbce1bfb2a5",
    p2pPort: 21001,
    raftId: 2,
    raftPort: 50001,
    role: "verifier"
}, {
    hostname: "127.0.0.1",
    nodeActive: true,
    nodeId: "6b7294b1ad3a6f086f730b4f408ca0791c61c10447d3969734df1b4ac4ad6add86a09052974ce1685e8f7a0f320019a078d19fe96abba50dd0511a5c58bd4779",
    p2pPort: 21000,
    raftId: 1,
    raftPort: 50000,
    role: "minter"
}]
```

もちろん、追加されたnode2のnodeIDも確認できます。

* node1, node2のgethプロセス停止

```console:console
$ ps(以下のような状態になっている)
15392 pts/0    00:00:00 geth
15430 pts/0    00:00:00 geth
・
・
・

(上の例だと)
$ kill -9 15392 & kill -9 15430
```
gethプロセスをkillしないと、次に行うgeth.ipcの起動ができません。
理由は、種々のportを使いまわしているため、portが既にbindされているよ、と怒られます。

## プライベートトランザクション生成、確認
* コントラクトデプロイ用のスクリプト作成

```js:private-contract.js
a = eth.accounts[0]
web3.eth.defaultAccount = a;

// abi and bytecode generated from simplestorage.sol:
// > solcjs --bin --abi simplestorage.sol
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" }], "payable": false, "type": "constructor" }];

var bytecode = "0x6060604052341561000f57600080fd5b604051602080610149833981016040528080519060200190919050505b806000819055505b505b610104806100456000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632a1afcd914605157806360fe47b11460775780636d4ce63c146097575b600080fd5b3415605b57600080fd5b606160bd565b6040518082815260200191505060405180910390f35b3415608157600080fd5b6095600480803590602001909190505060c3565b005b341560a157600080fd5b60a760ce565b6040518082815260200191505060405180910390f35b60005481565b806000819055505b50565b6000805490505b905600a165627a7a72305820d5851baab720bba574474de3d09dbeaabc674a15f4dd93b974908476542c23f00029";

var simpleContract = web3.eth.contract(abi);
var simple = simpleContract.new(10, { from: web3.eth.accounts[0], data: bytecode, gas: 0x47b760, privateFor: ["3eAOnJSpWwRE2zC+GsPFHGJhKkg6mIEtePeoabhtVjo="] }, function (e, contract) {
    if (e) {
        console.log("err creating contract", e);
    } else {
        if (!contract.address) {
            console.log("Contract transaction send: TransactionHash: " + contract.transactionHash + " waiting to be mined...");
        } else {
            console.log("Contract mined! Address: " + contract.address);
            console.log(contract);
        }
    }
});
```
simpleContract.new()引数の、privateForフィールドにtessera node2の公開鍵を設定する。

* node1、node2の起動スクリプトを作成

```sh:start.sh
#!/bin/bash
PRIVATE_CONFIG=/<yourpath>/node1-t/tm.ipc 
nohup geth --datadir node/node1 --nodiscover --verbosity 5 --networkid 31337 --raft --raftport 50000 
--raftjoinexisting 1 --rpc --rpcaddr 0.0.0.0 --rpcport 22000 --rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum,raft 
--emitcheckpoints --port 21000 >> node1.log 2>&1 &
```
これも、スクリプトを作ったほうが楽なので作ります。

* node1、node2の起動

```
$ sudo chmod +x start.sh
$ ./start.sh
```

* 起動確認

```console:console
$ ps

[実行結果]
5737 pts/0    00:00:46 java　⇒　tessera node1
5738 pts/0    00:00:45 java　⇒　tessera node2
6363 pts/0    00:00:03 geth　⇒　quorum node1
6364 pts/0    00:00:03 geth　⇒　quorum node2
```

* トランザクション生成、確認

```console:console
$ geth attach --datadir node/node1

<node1のアカウントロックを解除>
> personal.unlockAccount(eth.accounts[0])
true

>loadScript(“private-contract.js”)
[出力結果]
Contract transaction send: TransactionHash: 0x0456febcd5f6aca1eb2789164cf41bde08d7204aa30ca37a61c6aa02cd3a86ba waiting to be mined...　　　　　　　　　　①トランザクションハッシュ値
true
> Contract mined! Address: 0xe347834bc2abead836693f8972912db7a14e7dd2
[object Object]

>eth.getTransaction("①トランザクションハッシュ値")
[出力結果]
{
  blockHash: "0x064f126b250ef49a2bf2b65a4053422fc602265cf5290eb4de5ad64a5ea48c21",
  blockNumber: 1,
  from: "0x95d18ba75f2bbd35420522f80c1abc265aeae3c3",
  gas: 4700000,
  gasPrice: 0,
  hash: "0x0456febcd5f6aca1eb2789164cf41bde08d7204aa30ca37a61c6aa02cd3a86ba",
  input: "0x79691bdd774a6c9f4ef99a73271a0df92fd6acc5d6a5ad04e598b07ad5bceddd5614b466f0811697e66f0a4ecb2da8eb249f194ebece2fc1424b9dbdddfe002e",
  nonce: 0,
  r: "0x3a2887245cbd4051ca4d3b0b876988414ce525f40d69af540df0720ec7ddee3f",
  s: "0x657dd7a6d3ff74dab7f93636c87bc686f57d9c5c2a9d574e30f4530788388ed0",
  to: null,
  transactionIndex: 0,
  v: "0x25",
  value: 0
}

>eth.getTransactionReceipt("①トランザクションハッシュ値")
[実行結果]
{
  blockHash: "0x064f126b250ef49a2bf2b65a4053422fc602265cf5290eb4de5ad64a5ea48c21",
  blockNumber: 1,
  contractAddress: "0xe347834bc2abead836693f8972912db7a14e7dd2",
  cumulativeGasUsed: 0,　　　　②コントラクトアドレス
  from: "0x95d18ba75f2bbd35420522f80c1abc265aeae3c3",
  gasUsed: 0,
  logs: [],
  logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  status: "0x1",
  to: null,
  transactionHash: "0x0456febcd5f6aca1eb2789164cf41bde08d7204aa30ca37a61c6aa02cd3a86ba",
  transactionIndex: 0
}

>var addr = “②コントラクトアドレス”
>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" }], "payable": false, "type": "constructor" }];

>var private = eth.contract(abi).at(addr)
>private.get()
[出力結果]
10
⇒private-contract.jsで定義した値

```
node1, node2で"10"という値が共有できていることが確認できます。

## まとめ
* プライベートトランザクションを、フルスクラッチで実行する手順をまとめました。
* ブロックチェーンは、ここらへんのインフラ設定周りで、どうも導入のハードルが高い気がしてました。
  * ここらへがカチッと決まれば、コンテナでネットワークを組めばよいですが、その前段階では特に手数が多いのがなんとかなればいいです。

## 参考
* [quorum公式ドキュメント](https://docs.goquorum.com/en/latest/)
* [quorum github](https://github.com/jpmorganchase/quorum)
* [Ethereum入門](https://book.ethereum-jp.net/first_use/contract)
* [go-ethereum github](https://github.com/ethereum/go-ethereum#defining-the-private-genesis-state)