いますぐ動かせるHyperledger Besu !

こんにちは。株式会社日立ソリューションズの田辺です。
この記事は、:christmas_tree:[日立グループ OSS #2 Advent Calendar 2020](https://qiita.com/advent-calendar/2020/hitachi-oss2):christmas_tree:の21日目の記事になります。

# はじめに
去る2019年8月29日、それまでPantheonとして知られていたJavaベースのEthereumクライアントが、**[Hyperledger Besu](https://www.hyperledger.org/use/besu)**としてHyperledgerプロジェクトに参加しました。これまでコンソーシアム型のプラットフォームに注力していたHyperledgerプロジェクトにおいて、初めてパブリックブロックチェーン上で動作できるBesuが参加したことで、今後、より多くのユースケースに適応できることが期待されています。

この記事では、Hyperledger Besuのコミュニティが提供する代表的なサンプルを順序立てて紹介すると共に、イメージや概念が伝わるように補足やリファレンスを示しながら解説をしていきます。

次のような読者に向けて書きました。

* ブロックチェーンに興味があるエンジニア
* 今すぐサンプルを動かしながらどのようなものかを把握したいエンジニア


# 前提となる環境

この記事は次の環境で確認しました。

| # | 項目 | バージョン |
|:-----------:|:------------:|:------------:|
|1| OS | Ubuntu 18.04 LTS |
|2| Node, npm | 12.16.2, 6.14.4 |
|3| Docker | 19.03.8 |
|4| docker-compose | 1.25.5 |

> npmに含まれるnpxを必要とします

上記はWindowsにインストールされたVirtualBoxのVM上にVagrantを使用して構築しています。
OSの基本情報は次の通りです。

| # | 項目 | 値 |
|:-----------:|:------------:|:------------:|
|1| user | vagrant |
|2| IPアドレス | 192.168.33.12 |
|3| 作業ディレクトリ | /home/vagrant |



# いざ、Quick Start !

#### 1. quickstartのソース一式を取得します
次のコマンドでソース一式を取得します。
以降、ガイドに沿ってオプションを選択します。

```bash
npx quorum-dev-quickstart
```

![quickstart.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/914691/1069ae6b-5930-94a1-b8cd-96a9cffd3135.png)

> Quorumという言葉は、OSSで開発されているHyperledger BesuとGoQuorumを
> OSSレイヤーと見立てた時に、それらを総称する呼び名として使用しているようです。
> ConsenSys社がJ.P. Morgan社よりQuorumを買収した際に、リブランディングを図ったものと思われます。
> [Frequently Asked Questions](https://consensys.net/quorum/faq)

上記の画面が表示されるので、**Hyperledger Besu**を選択します。
`1`を入力してEnterを押します。


```
Do you want to try out Codefi Orchestrate? Note: choosing yes will direct you to a login/registration page. [Y/n]
```
Codefi OchestrateはBesuの上位パッケージとなるので、今回はお試しを遠慮しておきます。
`n`を入力してEnterを押します。

```
Do you wish to enable support for private transactions? [Y/n]
```
標準で用意されているサンプルがプライベートトランザクションを前提としているので有効にします。
`Y`を入力してEnterを押します。

```
Do you wish to enable support for logging with ELK (Elasticsearch, Logstash & Kibana)? [y/N]
```
ELKスタックを利用したロギングは、次回以降に試すことにして無効にします。
`N`を入力してEnterを押します。

```
Where should we create the config files for this network?
Please choose either an empty directory, or a path to a new directory that
does not yet exist.
Default: ./quorum-test-network
```
ソース一式の保存先を指定します。
`./quorum-test-network`を入力してEnterを押します。
すると、インストールが始まります。

![installation_complete.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/914691/68b7df6a-9567-8414-4be8-201c5589c29f.png)

インストールが完了すると、上記のメッセージが表示されます。
続いてディレクトリに入って`run.sh`を実行します。

#### 2. 必要な全てのコンテナを取得して起動します
`run.sh`を実行します。

```bash
cd ~/quorum-test-network
./run.sh
```
以下が表示されれば、起動が完了しています。

![run_end.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/914691/a735c31a-c70e-2390-f234-91b552aa9ff7.png)

ここで、出来上がったコンテナの構成を一覧にしてみましょう。

```bash
docker ps -a
```
以下は出力結果です。

![container_list.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/914691/5d13cabd-ae46-d88f-5bfa-7d88f637e135.png)

思っていたよりも数が多いので図にしてみました。

![structure.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/914691/c834b922-919b-a0b1-57e0-8168b5fcea72.png)

大きくは**ブロックチェーンネットワーク**を構成するコンテナと、それらを監視する**モニタリング／ツール**とに分かれています。
各コンテナの種類と推測される役割は次の通りです。

| # | コンテナの種類 | 役割 |
|:-----------:|:------------:|:------------|
|1| validator | ブロックの作成と署名を行うノードです。本環境ではコンセンサスアルゴリズムとして[IBFT2.0](https://besu.hyperledger.org/en/stable/HowTo/Configure/Consensus-Protocols/IBFT/)を使用しています。ビザンチン障害耐性を持たせるには少なくとも4台のvalidatorが必要です。 |
|2| orion | [Private Transaction Manager](https://docs.orion.consensys.net/en/stable/)と呼ばれ、プライベートトランザクションを発行する構成で必要なサービスです。 |
|3| besu | 各組織のノードを表します。menber1～3まで、3つの組織（メンバー）がある想定で構成されています。 |
|4| rpcnode | RPCリクエストを受け付けることのできるノードです。 |
|5| ethsigner proxy | トランザクションに署名をするためのサービスです。 |
|6| [prometheus](https://prometheus.io/docs/introduction/overview/) | 各ノードのメトリックを収集するサービスです。 |
|7| [grafana](https://grafana.com/grafana/) | prometheusが収集したメトリックを可視化するサービスです。 |
|8| [block-explorer](https://github.com/Alethio/ethereum-lite-explorer) | ブロック、トランザクション、およびアカウントレベルでブロックチェーンデータを探索できるサービスです。 |

> 実態として同じBesuノードでありながら、validatorとbesuについてコンテナ名が分かれている点は、コミュニティ側の意図を掘り下げた方が良いですね
  
  
`list.sh`を実行するとエンドポイントとサービスを一覧表示できます。

![endpoint.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/914691/2a51db05-a825-869f-9cfd-3d7b32a7853a.png)

ブラウザで`http://192.168.33.12:25000`を開くと、block-explorerを表示できます。

![block-explorer.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/914691/41b07f8e-d4d4-fcd4-a1cc-94d2dfbe368b.png)

ブラウザで`http://192.168.33.12:3000/d/XE4V0WGZz/besu-overview?orgId=1&refresh=10s&from=now-30m&to=now&var-system=All`を開くと、GrafanaのBesu Overview（ダッシュボード）を表示できます。

![grafana2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/914691/2505798d-8678-285c-5a00-a9c8950d2257.png)

> [Use metrics to monitor node performance](https://besu.hyperledger.org/en/stable/HowTo/Monitor/Metrics/#monitor-node-performance-using-prometheus)

ここで、環境の操作に関わるコマンドを整理しましょう。

| # | コマンド名 | 内容 |
|:-----------:|:------------:|:------------|
|1| run.sh | 環境の初期構築および全てのコンテナを起動します。 |
|2| list.sh | エンドポイントとサービスを一覧表示します。 |
|3| stop.sh| 全てのコンテナを停止します。作業を終えるためのシステム停止はこのコマンドを使います。 |
|4| resume.sh| stop.shで停止した環境を再開します。作業を再開するためのシステム起動はこのコマンドを使います。 |
|5| remove.sh| 全てのコンテナを停止してコンテナイメージを削除します。環境をクリーンする場合はこのコマンドを使用します。 |

#### 3. スマートコントラクトのデプロイとプライベートトランザクションの実行

サンプルは、3つの組織（member）が繋がれたコンソーシアムにおけるブロックチェーンにおいて、2つの組織間で秘匿化されたデータを授受するユースケースとなっています。

詳細は次の通りです。

1. コントラクトをデプロイします。
2. `member1`から` member3`に任意の値（0x2f）を送信します。
3.  3つのmemberすべてにクエリを実行します。
4.  `member1`と` member3`のみがプライベートトランザクション関わっているため、任意の値（0x2f）を取得できますが、`member2`は取得できないことを確認します。

![member3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/914691/1378019c-e8cd-a9f9-8228-4585f9e891a5.png)


サンプルのファイル構成は以下の通りです。

quorum-test-network/
　└ smart_contracts/
　　　├ contracts/
　　　│ ├ EventEmitter.sol（storeで登録しvalueで取得するシンプルなスマコン）
　　　│ └ EventEmitter.json（スマコンのABI、呼び出すために必要なIF定義ファイル）
　　　└ scripts/
　　　　├ compile.js
　　　　├ deploy.js（デプロイ、トランザクションの実行、クエリまで全部乗せ実装）
　　　　└ keys.js（orionの公開鍵、各組織のbesuノードのURLを定義）

実行は次のように行います。

```bash
cd ~/quorum-test-network/smart_contracts
npm install
node scripts/deploy.js
```

実行結果は次の通りです。

![sample.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/914691/293b85fd-1c94-fa2b-a1d7-0a5e1fad917b.png)

実行結果から`member2`だけ値を取得できていないことが、確認できました。


# 振り返り
コミュニティが提供する代表的なサンプルを動かすことには成功しましたが、やってみて色々と疑問が沸いてきたので、掘り下げていきます。

#### Besuのバージョンは？
そういえば手順のどこにも登場しなかったバージョンの情報。
次のファイルで`run.sh`の延長で環境変数に`20.10.0`として指定されていました。

```quorum-test-network/.env
# This file defines environment variables defaults for Docker-compose
# but we also use it for shell scripts as a sourced file

BESU_VERSION=20.10.0
QUORUM_VERSION=20.10.0
QUORUM_TESSERA_VERSION=20.10.0
QUORUM_ETHSIGNER_VERSION=20.10.0
QUORUM_ORION_VERSION=20.10.0
QUORUM_CAKESHOP_VERSION=0.11.0

LOCK_FILE=.quorumDevQuickstart.lock
```

> [Release 20.10.0](https://github.com/hyperledger/besu-docs/releases/tag/20.10.0)


#### ethsigner proxyは使用したのだろうか？
以下のコードによると、デモの目的のために直接`keys.js`ハードコーディングしているので使用していないとのこと。

```quorum-test-network/smart_contract/scripts/deploy.js
6  // WARNING: the keys here are demo purposes ONLY. Please use a tool like Orchestrate or EthSigner for production, rather than hard coding private keys
```

ハードコーディングの元となっている鍵情報がノード側にあるのかを捜していくと、
`keys.js`内の`besu.member1.privateKey`の値は`quorum-test-network/config/besu/networkFiles/member1/keys/key`と同じであることが確認できました。

docker-compose.ymlの方をみると、`member1besu`の`node-private-key-file`オプションの指定先が、実ファイルとしては`quorum-test-network/config/besu/networkFiles/member1/keys/key`となっているので辻褄が合いそうです。
以下は、`member1besu`のコンテナの定義です。
volumesの多くが`quorum-test-network/config`以下のファイル群を指し示しているので、各オプションに引き当てているファイルの実態も確認することができます。

> [Node keys and node address](https://besu.hyperledger.org/en/stable/Concepts/Node-Keys/)

```quorum-test-network/docker-compose.yml
  member1besu:
    << : *besu-def
    entrypoint:
      - /bin/bash
      - -c
      - |
        while [ ! -f "/opt/besu/public-keys/bootnode_pubkey" ]; do sleep 5; done ;
        /opt/besu/bin/besu \
        --config-file=/config/config.toml \
        --p2p-host=$$(hostname -i) \
        --genesis-file=/config/genesis.json \
        --node-private-key-file=/opt/besu/keys/key \
        --min-gas-price=0 \
        --privacy-enabled \
        --privacy-url=http://member1orion:8888 \
        --privacy-public-key-file=/config/orion/orion.pub \
        --privacy-onchain-groups-enabled=${PRIVACY_ONCHAIN_GROUPS_ENABLED:-false} \
        --rpc-http-api=EEA,WEB3,ETH,NET,PRIV,PERM,${BESU_CONS_API:-IBFT} \
        --rpc-ws-api=EEA,WEB3,ETH,NET,PRIV,PERM,${BESU_CONS_API:-IBFT} ;
    volumes:
      - public-keys:/opt/besu/public-keys/
      - ./config/besu/config.toml:/config/config.toml
      - ./config/besu/permissions_config.toml:/config/permissions_config.toml
      - ./config/besu/log-config.xml:/config/log-config.xml
      - ./logs/besu:/var/log/
      - ./config/besu/${BESU_CONS_ALGO:-ibft2}Genesis.json:/config/genesis.json
      - ./config/besu/networkFiles/member1/keys:/opt/besu/keys
      - ./config/orion/networkFiles/orion1/nodeKey.pub:/config/orion/orion.pub
    depends_on:
      - validator1
      - member1orion
    ports:
      - 20000:8545/tcp
      - 20001:8546/tcp
    networks:
      quorum-dev-quickstart:
        ipv4_address: 172.16.239.16
```

芋づる式的に、上の定義にて、portsで8545のrpcポートを20000で外に見せているのもわかります。
また、`rpc-http-api`にて利用可能なコンポーネントを指定しており、rpcnodeの役割としても機能していますね。

#### サンプルのdeploy.jsはどこに接続しているんだろう？

```quorum-test-network/smart_contract/scripts/deploy.js
7  const { orion, besu } = require("./keys.js");
8  const chainId = 2018;
9  const web3 = new EEAClient(new Web3(besu.member1.url), chainId);
```

```quorum-test-network/smart_contract/scripts/keys.js
16  besu: {
17    member1: {
18      url: "http://127.0.0.1:20000",
```

`besu.member1.url`をWeb3に指定しているので
20000ポートを外に向けて開いている**member1のbesuノード**に向けて接続し、操作をしていることがわかります。
ということは、このサンプルでは**ethsigner proxy**コンテナに加えて、**rpcnode**コンテナもデモ目的のために使用していない整理になりますね。

また、チェインIDは`2018`で指定しています。

```quorum-test-network/config/besu/ibft2Genesis.json
1  {
2    "config": {
3      "chainId": 2018,
```

ibft2Genesis.jsonにてchainIdに`2018`を指定しているため、サンプルで呼び出す側の実装でも合わせているのですね。



#### プライベートトランザクションはどうやって呼び出すのか？

コントラクトのデプロイは次のように実装しています。

```quorum-test-network/smart_contract/scripts/deploy.js
21  const contractOptions = {
22    data: '0x'+bytecode,
23    privateFrom: orion.member1.publicKey,
24    privateFor: [orion.member3.publicKey],
25    privateKey: besu.member1.privateKey
26  };
27  console.log("Creating contract...");
28  const c = await web3.eea.sendRawTransaction(contractOptions);
```

> [web3js-eea client library](https://besu.hyperledger.org/en/stable/HowTo/Interact/Client-Libraries/web3js-eea/#deploying-a-contract-with-sendrawtransaction)


デプロイした後のプライベートトランザクションは次のように実装しています。

```quorum-test-network/smart_contract/scripts/deploy.js
41  const contract = new web3.eth.Contract(abi);
42  // eslint-disable-next-line no-underscore-dangle
43  const functionAbi = contract._jsonInterface.find(e => {
44    return e.name === "store";
45  });
46  const functionArgs = web3.eth.abi
47    .encodeParameters(functionAbi.inputs, [value])
48    .slice(2);
49  const functionParams = {
50    to: address,
51    data: functionAbi.signature + functionArgs,
52    privateFrom: orion.member1.publicKey,
53    privateFor: [orion.member3.publicKey],
54    privateKey: besu.member1.privateKey
55  };
56  const transactionHash = await web3.eea.sendRawTransaction(functionParams);
```

> [sendRawTransaction](https://consensys.github.io/web3js-eea/latest/EEAClient.html#~sendRawTransaction)

# まとめ
さて、Hyperledger Besuを**いますぐ動かす**からの、中身はどうなっているのだろう？を掘り下げて見ていきました。
ガイドに沿ってスッと環境が構築できた点は、とても作りこまれているなと感じました。
一方で、その全容を理解するためのドキュメントは物足りない印象もありましたので、本記事がその助けになればと思います。
なお、本記事の校閲は案件で最前線を走っている、株式会社日立ソリューションズの小島が担当致しました。


# Future Work
まだまだ確認したいことは山ほどあるので、引き続き次のような項目を調査して、本記事に続けて情報発信を行っていきたいと考えています。

* ethsigner proxyを使用した構成
* プライベートトランザクションを使用しない、よりシンプルな構成。
* ロギングのためのELKスタックを有効にした構成。
* スマコン開発をシームレスに、TruffleやRemix IDEとの連携。

*Let's get started with blockchain!*