【2019年7月版】Hyperledger Indy のサンプル(ちょい古め)を動かしてみた

前回ビルドに挑戦したHyperledger Indyですが引き続き動作確認を行ってまいります。というか、ユースケースがいまいちピンと来ないので、懲りずに古いサンプルを動かしてみました。

- 前回の記事：[【2019年3月版】Hyperledger Indy 事始め](https://qiita.com/koinori/items/4c97098754c7d78a3ea6)

紆余曲折ありましたがなんとか動いたので手順などの解説をいたします。~~(めちゃくちゃ大変だった。)~~

まず、[修正済みのソースはこちら](https://github.com/Yoshinori-Koide-PRO/indy-ssivc-tutorial)です。
手っ取り早く動かしたい方、DockerインストールしたUbuntuで上記リポジトリをcloneしてReadmeの手順に従っていただければOKです！

以下では通常の動作手順について、順を追って解説します。

# 1. コンテナのビルド

まず、Rustでのindyソースをコンテナ内でビルドして、デモに必要な各種のコンテナを作成します。

[こちらの修正済みのプロジェクト](https://github.com/Yoshinori-Koide-PRO/indy-ssivc-tutorial) からソースをクローンしておきます。以下では、`~/indy-ssivc-tutorial`にクローン済みとして説明いたします。

また、docker コマンドは `sudo` 付きで動かす環境であるとして説明します。

## 1-1. indy コンテナのビルド

以下のコマンドで、indy 本体のネットワークを構成する docker コンテナをビルドします。

```console
$ cd ~/indy-ssivc-tutorial/von-network
$ sudo ./manage build
```

## 1-2. API コンテナのビルド

以下のコマンドで indy とアクセスする API や管理ツールなどのコンテナ群をビルドします。

```console
$ cd ~/indy-ssivc-tutorial/TheOrgBook/docker
$ sudo ./manage.sh build
```

## 1-3. デモアプリコンテナのビルド

以下のコマンドで indy のデモを行う Web アプリ用のコンテナ群をビルドします。

```console
$ cd ~/indy-ssivc-tutorial/permitify/docker
$ sudo ./manage build
```

このあたりになるとディスクスペースやメモリなど逼迫してくるかもしれません。不要な docker イメージは削除して軽くしておいたほうがいいかもしれません。

# 2. 起動

無事に docker コンテナがビルドできたら同様の順番でコンテナを起動していきます。

## 2-1. indy ネットワークの起動

以下のコマンドで indy のテストネットワークを起動します。

```console
$ cd ~/indy-ssivc-tutorial/von-network
$ sudo ./manage start
```

これでindy用のコンテナがいくつか起動します。

## 2-2. API コンテナ群の起動

続いて indy に直接接続する API 用のコンテナ群を起動します。
**新しいコンソールを開き**、

```console
$ cd ~/indy-ssivc-tutorial/TheOrgBook/docker
$ sudo ./manage.sh start
```
で、API用のコンテナがいくつか起動します。

### 2-2-1. 起動するサイト・サービス

この時点でいくつかのサイトが立ち上がります。

- ダッシュボード Web UI ・・・ http://localhost:9000
- チュートリアルサイト ・・・ http://localhost:8080
- Swagger ・・・ http://localhost:8081
- Solr ・・・ http://localhost:8983
- PostgreSQL・・・tcp://localhost:5432

Solrはまぁ、おまけみたいなものです。

## 2-3. デモアプリコンテナ群の起動

以下のコマンドでデモアプリ用のコンテナ群を起動します。
**新しいコンソールを開き**、

```console
$ cd ~/indy-ssivc-tutorial/permitify/docker
$ sudo ./manage start all APPLICATION_URL=http://192.168.1.10 THE_ORG_BOOK_APP_URL=http://192.168.1.10:8080
```
で、OKです。

ここで重要なのが、`APPLICATION_URL`で渡しているのが、デモアプリ用のコンテナ群が立ち上がるホストのIPアドレス、`THE_ORG_BOOK_APP_URL`で渡しているのが、上記のAPI用コンテナ群が立ち上がるホストのアドレス(ポート番号込み)です。
今回は1台のDockerホスト上で全てのコンテナを動かすので同一のIPアドレスとなっています。
また、この変数を設定せずに単に `./manage start all` と実行した場合は、*ローカルホストから***のみ***のアクセス* が有効になります。

### 2-3-1. 起動するサイト・サービス

ここでデモ用のサイト群がわーっと立ち上がります。

- Gov ID ・・・ http://localhost:5000
- Fabor 大学 ・・・ http://localhost:5006
- Acme 就職 ・・・ http://localhost:5007
- Acme 給与受け取り ・・・ http://localhost:5008
- Thrift 銀行 ・・・ http://localhost:5009

上記の手順で `APPLICATION_URL` を使用している場合は、localhostの代わりにそのIPアドレスでアクセス可能です。

# 3. コンテナの停止

各コンソールで `Ctrl+C` でプロセスを落とせば `Graceful Shutdown` となります。

# 4. 使用済みの container と volume の削除

[前回の記事でも注意として記載しました](https://qiita.com/koinori/items/4c97098754c7d78a3ea6#1-5-%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF%E3%81%AE%E5%81%9C%E6%AD%A2)が、indyはブロックチェーンのプロダクトでありますので一度、動かして**ブロックが生成されてしまうと後戻りができません**。
テストネットを止めて再起動、ということが簡単にできませんので、デモサイトを落とした後はこの**削除の手順が必須**です。

## 4-1. indy 用コンテナの削除

以下のコマンドで削除を行います。

```console
$ cd ~/indy-ssivc-tutorial/von-network
$ sudo ./manage rm
```

## 4-2. API用コンテナの削除

同様に以下のコマンドで使用済みコンテナの削除を行います。

```console
$ cd ~/indy-ssivc-tutorial/TheOrgBook/docker
$ sudo ./manage.sh rm
```

## 4-3. デモサイト用コンテナの削除

同様に以下の手順で削除します。

```console
$ cd ~/indy-ssivc-tutorial/permitify/docker
$ sudo ./manage rm
```
基本的な動作方法は以上です。

## 4-4. 動かし方

実際のデモサイトのアクセス方法などは[こちらをご覧ください](https://github.com/Yoshinori-Koide-PRO/indy-ssivc-tutorial#demo)。

また、ここでどんなことを実現したいのか？については以下の記事でわかりやすく解説していただいてます。

- [Hyperledger Indyで綴るAliceの物語~準備編](https://qiita.com/pro_matuzaki/items/615a0a42b71ce1cc2731)

これらのデモサイトでどんなことができるのか、できるようにしたいのか、はお分かりいただけたかと思います。

# 5. どうやって動かしたのか

ここからが一応の本題です。この`ちょい古めのサンプル`は、最新の Rust ではコンパイルが通りません。
また、API用サーバーやデモサイトなどのコードが大量にあるため、libindy を最新のものにしてコンパイル通ればオK☆、なんてことにはなりません。
当時の AS IS で動作確認が必要なのであります。
そんなわけで今回の古めのサンプル(といっても一昨年だけど。。。)をちゃんと動作させるに当たって、どうやって手当てをしたか、について述べていきたいと思います。

## 1. Rustのビルド

まず、もっとも困難だったのが `どのバージョンのRustならビルドが通るのか`、そして同時に `全体がよろしく収まってくれるか`です。上記でも述べましたが各種パッケージを最新にすれば現状のRustでビルドが通るのは前回のトライで確認済みです。しかし、参照するコードが大量にあるためコア部分のみバージョンアップする、なんてことはとてもできないのです。

というわけで、**当時の libindy はどうやってビルドしていたの？**をどうやって調べたか、ですが、まず cargo のパッケージを調べました。

以下が、例えばですが、indy 1.6.7 の登録情報です。
https://crates.io/crates/indy/1.6.7
当たり前ですがちゃんとビルドされてcargoに登録されています。・・・どうやって？
でも、単にビルドして、アップロードしてるわけじゃないよね・・・？
たぶん jenkins か circle-ci とかでUTは通してるはずでしょう・・・というわけで・・・

https://github.com/hyperledger/indy-sdk/tree/master/libindy/ci

はい、libindy が ci でテストを実施している流れは `ubuntu.dockerfile` に書いてあります。そしてlibindyをビルドするrustをインストールする箇所で・・・

```dockerfile:ubuntu.dockerfile
...
USER indy

RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain 1.36.0
ENV PATH /home/indy/.cargo/bin:$PATH
...
```

はい、Rustのバージョン指定してますね！つまり、**その当時の ubuntu.dockerfile を見ればいい** というわけですね！

そして `当時` とはいつなのか？というと・・・`indy-ssivc-tutorial/von-network/Dockerfile` に以下の記述があります。

```dockerfile:von-network/Dockerfile
...
RUN git clone https://github.com/hyperledger/indy-sdk.git
WORKDIR /home/indy/indy-sdk/libindy
RUN git fetch
RUN git checkout 778a38d92234080bb77c6dd469a8ff298d9b7154
...
```

この `checkout 778a38d92234080bb77c6dd469a8ff298d9b7154` のハッシュ値を github のURLに貼り付けるとコミットログを見ることができます。具体的には以下のURLで確認できます。

https://github.com/hyperledger/indy-sdk/commit/778a38d92234080bb77c6dd469a8ff298d9b7154

`6 Mar 2018` のコミットであることが確認できます。**この当時の ubuntu.dockerfile を見ればいい**というわけですね！~~いや、大変だった！ｗ~~

というわけで`ubuntu.dockerfile`のコミットログを確認してみましょう。

https://github.com/hyperledger/indy-sdk/commits/master/libindy/ci/ubuntu.dockerfile

このリストから2018.03.06以前の最新版を探してくればOK、ということになります。
というわけで・・・

https://github.com/hyperledger/indy-sdk/commit/43b5e88e7b8c0ba9f0db600a0366319783760406#diff-6b80b089e2db8e11e1d59adbe01eecf2

この `26 Feb 2018` のコミット近辺の rust のバージョンが分かればいいんですね～というわけで、`ubuntu.dockerfile` を見てみますと・・・

```
...
ENV RUST_ARCHIVE=rust-1.21.0-x86_64-unknown-linux-gnu.tar.gz
ENV RUST_DOWNLOAD_URL=https://static.rust-lang.org/dist/$RUST_ARCHIVE
...
```

上記の定義で `1.21.0` をダウンロードしてますね。。。う～ん、tar.gz をそのままダウンロードしてるのはちょっとなぁ。。。と、次のコミットを確認します。

https://github.com/hyperledger/indy-sdk/commit/b86cd0f1ea4fb0520edb5552c7212998bdc1435d#diff-6b80b089e2db8e11e1d59adbe01eecf2

ここでの `ubuntu.dockerfile` を見てみますと・・・

```
...
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain 1.21.0
...
```

`rustup` でバージョン指定する方法が書かれてますね！これっぽい！！

いや、実際には `1.21.0` ではダメで、次の ["Apr 23, 2018" の "Update rust to 1.25.0."](https://github.com/hyperledger/indy-sdk/commit/1758e47e4c098ab9fb0fb7938fa1394151563f27#diff-6b80b089e2db8e11e1d59adbe01eecf2) が微妙にダメ(ビルドは通ったけど、サイト動かすとダメ)で、次の ["May 30, 2018" の "Increment Rust version to 1.26.0"](https://github.com/hyperledger/indy-sdk/commit/3cf9f614200b28047c9d53f130b02bffc2797bd6#diff-6b80b089e2db8e11e1d59adbe01eecf2) でうまくいくようになりました。

これで Rust のコンパイルエラーはなくなりました。~~すでに3日ほど経過してます。~~

## 2. 参照パッケージのバージョン

Dockerfile 内で `apt install` で `indy_plenum` などをインストールしてますが、リポジトリサイトの `"deb https://repo.sovrin.org/deb xenial $indy_stream"` からご指定のバージョンは既に削除されているのですね・・・

というわけで現在登録されているバージョンの最古のものから順にちゃんとバージョン間の依存性の整合性がとれているものを割と**総当たり**で試してみました。とは言え、古いもの順ではうまくいく組み合わせはすぐ見つかりました。

結果、`indy_plenum_ver=1.2.242`、`indy_node_ver=1.2.301` が適しているようです。

## 3. tob-web のビルドが通らない

`tob-web/package.json` に以下のような参照があります。

```json:tob-web/package.json
...
    "localize-router": "git+https://github.com/cywolf/localize-router.git",
...
```

これは最新版ではコケます。当時の依存関係とは変わっているのですから当然です。~~(っていうかリポジトリ直接はやめてｗ)~~
そして以下の issue を発見しました。

https://github.com/IBM-Blockchain-Identity/indy-ssivc-tutorial/issues/26

やっぱりビルド通らないよね？と思っていたら・・・["バージョン `1.0.1`に直したよ"](https://github.com/IBM-Blockchain-Identity/indy-ssivc-tutorial/issues/26#issuecomment-450420007) というコメントがあったので、その通りにいたしました。

あら不思議、ビルドが通っちゃう・・・

## 4. permitify のビルドが通らない。

このIndyのサンプルプログラムは permitify など Django ベースのWebサイトを多数、起動するのですが、Django の起動がうまくいったか失敗したかがぱっと見ではわからないので、ちょっとハマりました。

コンテナではうまくいっているように見えて、コンテナの中に入って psするとpyhton のプロセスもあるけど、ssコマンドで確認するとポートをlistenしていない。。。というわけで `manage.py start` とかやってみると初めてエラーメッセージがダラーっと出てて来る、そんな状態でした。Pythonプロセスが無駄に生きてるからやっかいなんですよね～。

とりあえず、エラーメッセージが出てくれればもう後はググって対応だけなのでなんとかなりました。

依存関係のバージョンを以下のように修正しただけです。

- attr : 17.3.0 -> 17.4.0
- von-agent: 0.5.0 -> 0.5.2

~~これだけで半日かかってますけど。~~

## 5. localhost からしか動作しない

これはだいぶ困りました。実際の手順では http://localhost:8080 のチュートリアルサイトから http://localhsot:500x の各サイトへリンクが表示されています。
そしてそれが `localhost` 決め打ちなんですね。。。

いや、それはどうなのよ～？とソース(Angular) を見てみましたが、外のAPI(http://localhost:8081)から取得するだけで Angular側では全然、決めていない・・・オーマイ。。。となってしまったわけです。

いろいろ見てみると、docker-compose.ymlで以下のような記述があります。

```yml:permitify/docker/docker-compose.yml
...
      TEMPLATE_NAME: faber_college
      THE_ORG_BOOK_API_URL: ${THE_ORG_BOOK_API_URL}
      THE_ORG_BOOK_APP_URL: ${THE_ORG_BOOK_APP_URL}
      APPLICATION_IP: 0.0.0.0
      APPLICATION_PORT: 8080
      APPLICATION_URL: ${APPLICATION_URL}:5006
...
```

これはなんか、`${APPLICATION_URL}`は自分のサイトのアドレスで `${THE_ORG_BOOK_APP_URL}` は http://localhost:8080 に戻る時のURLなんじゃないの？っぽいよね？？という勘と、`manage`スクリプトで

```shell:permitify/docker/manage
...
configureEnvironment () {
  for arg in $@; do
    case "$arg" in
      *=*)
        export ${arg}
        ;;
    esac
  done

  export APPLICATION_URL=${APPLICATION_URL-http://localhost}
  # export THE_ORG_BOOK_API_URL=${THE_ORG_BOOK_API_URL-https://django-devex-von-dev.pathfinder.gov.bc.ca/api/v1}
  # export THE_ORG_BOOK_APP_URL=${THE_ORG_BOOK_APP_URL-https://devex-von-dev.pathfinder.gov.bc.ca}
  export THE_ORG_BOOK_API_URL="http://$DOCKERHOST:8081/api/v1"
  export THE_ORG_BOOK_APP_URL="http://$DOCKERHOST:8080"
  export TOB_INDY_SEED=the_org_book_issuer_000000000000
  export LEDGER_URL=http://von-web:8000
}
...
```
ここで、'=' が含まれていたら `export`しているので変数渡せるようにしてくれているんだということと、`APPLICATION_URL`、`THE_ORG_BOOK_APP_URL` のアドレスがlocalhostとdockerのhostでlocalhostじゃないとダメな理由がはっきりしましたね。`localhost`は100歩譲って外から見たIPでも動くと思いますが、`Docker host`は完全に docker が動いているホストのdockerネットワーク上のIPアドレスなのでそのマシンから見ないとこのアドレスは解決できないでしょう、ということですね。たぶん。

そんなわけで `THE_ORG_BOOK_APP_URL`はいきなり`$DOCKERHOST`を使わずに、外からちゃんと指定できる余地を残しておけばよいのです。以下のように。

```shell:permitify/docker/manage
...
export THE_ORG_BOOK_APP_URL=${THE_ORG_BOOK_APP_URL-http://$DOCKERHOST:8080}
...
```

で、起動時に外から見えるIPアドレスを教えてあげればOKなわけです。きっと。

```
$ ./manage start all APPLICATION_URL=http://192.168.1.10 THE_ORG_BOOK_APP_URL=http://192.168.1.10:8080
```

この指定がうまく動いたので、Indyデモサイトは外部から http://192.168.1.10:8080 でめでたくアクセスできるようになりました。

いったん、以上といたします。いや～長かった。。。