【2019年3月版】Hyperledger Indy 事始め

# Hyperledger Indy 初めて見ました。始めてみました。
Hyperledger Indy についての詳しい紹介などは本家サイトをご覧ください。
https://www.hyperledger.org/projects/hyperledger-indy

本稿では Hyperledger-Indy の`indy-sdk`からjs版のクライアントサンプルを動かして indy がどんなもんか感触を確かめる、というところまでの手順をメモするものです。indyの使い方、扱い方には至っておりません。。。

## indy プロジェクトとリポジトリの関係

まず、indyプロジェクトの実装があるgithubのリポジトリについてなんですが、けっこう入り組んでいるのでプリミティブな箇所だけご紹介します。

- 1. [indy-node](https://github.com/hyperledger/indy-node) : Hyperledger-Indy メインプロジェクト。ネットワークの本体
    - 1-1. [indy-plenum](https://github.com/hyperledger/indy-plenum) : Indy ネットワークのプロトコル。ビザンチン問題解決、ブロックチェーンなどなど
- 2. [indy-sdk](https://github.com/hyperledger/indy-sdk) : Indyのクライアントライブラリ、匿名資格証明書(anoncreds)、CLI、各言語向けのクライアントライブラリラッパー
- 3. [indy-crypto](https://github.com/hyperledger/indy-crypto) : Indy の共通
暗号処理ライブラリ。Rustで書かれてます。indy-node、indy-sdkの双方からリンクされてます。
 
Indyのクライアントを開発する上では主に indy-sdkを使用した上での開発作業になります。
また、共通ライブラリのindy-cryptoがRustで書かれているお陰でsdkのコアライブラリはRustです。
`Rust→シェアライブラリ(libindy.so)→各言語ラッパー→クライアント` という依存関係になっており、なかなかハードです。

## indy プロジェクトのドキュメント

indyプロジェクトはドキュメントがかなり広範囲に散逸しているため一元的な情報収拾が難しい状況にあります。
以下、今回の環境構築で参照(発掘)した有益であろうと思われるドキュメントのリンクを残しておきます。

- indy-node README : https://github.com/hyperledger/indy-node/blob/master/README.md
    - Hyperledger-indy の入り口。
- indy-sdk README : https://github.com/hyperledger/indy-sdk/blob/master/README.md
    - indy-sdkの入り口。割とあっさり重要なリンクが**ふんだんに**含まれている。
- indy-sdk ドキュメント : https://hyperledger-indy.readthedocs.io/projects/sdk/en/latest/
    - indy-sdk/docs 以下のリポジトリ内容から生成されるindyのドキュメント。これ重要。
- https://chat.hyperledger.org/channel/indy : indy本家のチャットチャンネル。ググっても出てこない情報はだいたいここにあります。半年分くらい遡ってからページ内検索をした方がググるより有益です。

また上記以外にもリポジトリの各階層には割と細かくREADMEがあるのでこまめにチェックが必要です。(だからまとまってないんだけどね・・・)


# 構築する環境
今回、構築する環境はちょっとアレなんですが以下のようになってます。

- Ubuntu 18.04 の実機
    - lxc: Ubuntu 16.04 -> indy-sdkビルド環境
        - docker: Ubuntu 16.04 -> indy-nodeのテストネットワーク(自動生成)
        - docker: Ubuntu 16.04 -> indy-sdkのサンプル実行環境(手動生成)

Xenialの実機が用意できなかったのでlxcでXenialコンテナ作りました。サンプル実行環境の構築に手間取ったのも第1弾ロケットがコンテナである影響、あると思います。
用意できるならXenial実機が素直に良いと思います。(いや、このためにわざわざ買ってこないけどね。)

indy-nodeのテストネットワークはindyプロジェクト内のテストで使用するためあらかじめDockerfile、docker-comopse.ymlが用意されています。そのためコマンドで立ち上げるだけでOKです。
サンプル実行環境のdockerについては以下で作成手順をご紹介いたします。

また[dockerが動くlxcコンテナの作り方についてはこちら](https://qiita.com/koinori/items/3063bc5091661e45508f)をご参照ください。

# 環境の構築手順
docker,docker-composeなどのツールは上記のページでインストール済みであるとします。

## 0. `indy`ユーザーの作成
以下の手順ではrootではないsdkビルド専用のユーザー`indy`での作業をメインに行なっていきます。
またindyユーザーがdockerを実行するために、dockerグループにindyを追加します。

```console
# useradd -m indy
# echo 'indy:indypass' |chpasswd
# gpasswd -a indy docker
Adding user indy to group docker
```
~~(こういうところがね〜、dockerは嬉しくないんだけどね〜)~~

## 1. indy-sdk のビルド環境を構築する
それではさっそく以下の手順に習って、indy-sdkのビルド環境を構築していきましょう。
indy-sdk の環境構築、ビルド手順は一通り以下のURLに記載されています。

- https://github.com/hyperledger/indy-sdk/blob/master/docs/build-guides/ubuntu-build.md

### 1-1. Rust のインストール

indy ユーザーで Rust をインストールします。

```
$ curl https://sh.rustup.rs -sSf | sh
```

表示される選択肢で、`1`を選択。

### 1-2. 依存パッケージのインストール

続いてビルドに必要なツール類を apt でインストール。ここでは indy ユーザーから抜けて root で実行します

```console
$ exit
# apt-get install -y \
   build-essential \
   pkg-config \
   cmake \
   libssl-dev \
   libsqlite3-dev \
   libzmq3-dev \
   libncursesw5-dev
```
### 1-3. libsodiumをビルド
以下の手順でlibsodium をソースからビルド

```console
# cd /tmp && \
  curl https://download.libsodium.org/libsodium/releases/old/libsodium-1.0.14.tar.gz | tar -xz && \
   cd /tmp/libsodium-1.0.14 && \
   ./configure --disable-shared && \
   make && \
   make install && \
   cd .. && \
   rm -rf /tmp/libsodium-1.0.14
```

### 1-4. deb生成ツールのインストール

`indy`ユーザに戻り、`cargo`コマンドから`deb`を生成するためのプラグインをインストールします。

- https://github.com/mmstick/cargo-deb

```console
# su - indy
$ cargo install cargo-deb
```

これで本体をビルドする環境は整いました。

## 2. ソースのクローン
Github からindy-node、indy-sdkをクローンします。

```
~$ git clone https://github.com/hyperledger/indy-node.git
~$ git clone https://github.com/hyperledger/indy-sdk.git
```

## 3. libindyのビルド

この手順は今回のサンプルを実行する手順では必須ではありません。とりあえずサンプルだけ動かしたい場合はこの手順を飛ばして下の"JS版サンプルの実行"の手順に進んで頂いてOKです。

### 3-1. ビルド 
`libindy` のビルドを行います。

```console
$ cd ~/indy-sdk/libindy
$ cargo build
```
`cargo build`のオプションなしでは`libindy/target/debug`以下にアセンブリが生成されます。
`--release`オプションで`libindy/target/release`以下にアセンブリが生成されます。

### 3-2. テストの実行

`libindy`のテスト実施にはテスト用の`indy-node`のネットワークが必要です。
**別ターミナル**を開いてindyユーザーでテスト用のネットワークを立ち上げておきます。

```console
$ cd indy-node/environment/docker/pool/
~/indy-node/environment/docker/pool$ ./pool_start.sh
```
で indy 動作用の Docker コンテナがモリモリ作成されます。初回は長大な時間がかかりますがノード4つが立ち上がります。

```
Creating pool network pool-network
...
Starting node node1 10.0.0.2
...
Starting node node2 10.0.0.3
...
Starting node node3 10.0.0.4
...
Starting node node4 10.0.0.5
...
```
テスト用のネットーワクが立ち上がったら**元のターミナル側**でlibindyのテストを実行します。

```
$ RUST_TEST_THREADS=1 TEST_POOL_IP=10.0.0.2 cargo test
```
### 3-3. 番外編 debパッケージ

#### 3-3-1. debパッケージの生成
debパッケージは以下のコマンドで生成できます。アセンブリを手作業でインストールするのはアレなので`dpkg`コマンドで直接インストールできるようにdebファイルを作っておきます。

```console
$ cargo build --release
$ cargo deb
```
`libindy/target/debian/`以下にdebファイルが生成されます。

#### 3-3-2. debのインストール
debからインストールするにはindyを抜けてrootユーザーでdpkgコマンドを使用します。

```console
$ exit
# dpkg -i /home/indy/indy-sdk/libindy/target/debian/libindy_1.8.1_amd64.deb
```

### 4. CLI
本家ドキュメントではオプショナルですが、libindyのビルドやインストールがうまくいっているかどうかを確かめるにはcliのビルドが手っ取り早いです。

#### 4-1. CLIのビルド
以下のコマンドでcliフォルダに移動してビルドを行います。

```console
$ cd ../cli
$ RUSTFLAGS=" -L ../libindy/target/debug" cargo build
```
また、`deb`ファイルや`apt`で`libindy`、`libindy-dev`を直接インストールした場合は、RUSTLAGSなしでビルド可能です。

```console
$ cargo build
```
ここでエラーが出る場合は、アセンブリの生成やインストールがミスっていることがわかります。
ただし、`CLI`は直接 libindyを参照しているわけではなく、`indy/wrappers/rust`のRustラッパーを経由しての`libindy`の使用です。どちらのビルドがうまくいっているか or いってないかをログをみて判断する必要があります。
つまり、
> `libindy`はOKか? → `Rust Wrapper`はOKか? → `CLI`はOKか?

の順にチェックしていく必要があります。
`CLI`のビルドに限らずHyperledger Indyのクライアント開発においては上記のように**言語ラッパーがちゃんとはまっているか？**を頭に入れておく必要があります。

#### 4-2. CLIのインストール
以下の手順でCLIコマンドが使用可能になります。

```console
$ cargo install
warning: Using: Using `cargo install` to install the binaries for the package in current working directory is deprecated, use `cargo install --path .` instead. Use `cargo build` if you want to simply build the package.
  Installing indy-cli v1.8.1 (/home/indy/indy-sdk/cli)
    Finished release [optimized] target(s) in 0.22s
  Installing /home/indy/.cargo/bin/indy-cli
$ indy-cli
indy>
indy>
indy> exit
Goodbye...
```

#### TODO: 4-3. CLIの使用

CLIの使用方法についてはちょっと膨大なのでTODOとさせてください。すいません。。

# JS版サンプルの実行
JS版サンプルプログラムの概要は以下のページにあります。
https://github.com/hyperledger/indy-sdk/tree/master/samples/nodejs
ただし今回はそもそも`npm install`がうまくいかなかったので、このままの手順ではないです。

## 0. `libindy`用テストネットワークの停止
上記のlibindyのテストを実施した場合はテストネットが`10.0.0.2`で動いていると思いますが、サンプルプロジェクトでは新規にネットワークを立ち上げるため、libindyテスト用のネットワークは停止させます。
**別のターミナル**を開き、以下のコマンドでテストネットワークを停止します。

```console
~$ cd indy-node/environment/docker/pool/
~/indy-node/environment/docker/pool$ ./pool_stop.sh
``` 
## 1. サンプル実行用テストネットワークの準備
サンプル実行用のネットワークを構築するのは以下のdocker-compose.ymlです。
`indy-sdk/docs/getting-started/docker-compose.yml`
また、ここから参照されている実体のDockerfileは以下です。
`indy-sdk/ci/indy-pool.dockerfile`

このdockerfile, docker-compose.ymlのネットワークは先のpool_start.shで作成されるdockerコンテナが4つ立ち上がる構成ではなく、1つのdockerコンテナ内に、ノードが4つ立ち上がり別ポートでアクセスするネットワークとなっています。

### 1-1. `indy-pool.dockerfile`の修正
indy-pool.dockerfileで指定されているライブラリのバージョンが若干古いのと、indy-nodeがListenするIPアドレスがdocker-composeとあっていないので以下の変数に修正を加えます。

```dockerfile:indy-pool.dockerfile
...
ARG indy_plenum_ver=1.6.721
ARG indy_node_ver=1.6.860
...
ARG pool_ip=10.0.0.2
...
```

### 1-2. `docker-compose.yml` の修正
`docker-compose.yml`ではindyのネットワークの他に、`jupyter`コンテナが立ち上がってjupyterから直接ノードを叩ける環境を提供してくれます。
ありがたいですが現状ではちょっといらないので、コメントアウトしてしまいます。

```docker-compose:docker-compose.yml
version: '2'
services:
  indy_pool:
    build:
      context: ../../ci/
      dockerfile: indy-pool.dockerfile
      args:
        pool_ip: '10.0.0.2'
    image: indy_pool
    container_name: indy_pool
    working_dir: /home/indy
    ports:
      - "9701:9701"
      - "9702:9702"
      - "9703:9703"
      - "9704:9704"
      - "9705:9705"
      - "9706:9706"
      - "9707:9707"
      - "9708:9708"
    networks:
      pool_network:
        ipv4_address: 10.0.0.2
    volumes:
       - sandbox:/var/lib/indy/sandbox/
#  jupyter:
#    build:
#      context: .
#      dockerfile: getting-started.dockerfile
#    command: jupyter notebook --ip=0.0.0.0
#    image: getting-started
#    container_name: getting_started
#    working_dir: /home/indy
#    volumes:
#       - ./getting-started.ipynb:/home/indy/getting-started.ipynb
#      - sandbox:/home/indy/sandbox
#    ports:
#      - "8888:8888"
#    networks:
#      - pool_network
#    links:
#      - indy_pool
networks:
  pool_network:
    driver: bridge
    ipam:
      driver: default
      config:
        -
          subnet: 10.0.0.0/24
volumes:
     sandbox:
```
また、ネットワークに`pool_network`、そしてボリュームに`sandbox`が生成されるのが注目点です。

### 1-3. テストネットワーク起動
docker-composeのお陰でネットワークの起動は簡単です。

```console
$ cd ~/indy-sdk/docs/getting-started/
$ docker-compose up &
```
`indy-pool`という名前のコンテナが起動します。

### 1-4. 接続設定ファイルの取得
ネットワークに接続するには各ノードへの接続トークなどが記載されている設定ファイルの`pool_transactions_genesis`が必須です。
今回のサンプルではプログラム内に直打ち(!!)されているので必要ないですが、通常はマストかと思います。
以下の手順で取得します。

```console
$ docker exec -it indy_pool bash

$ cat /var/lib/indy/sandbox/pool_transactions_genesis
```
表示される内容をコピペなどしておき、接続情報を退避しておきましょう。
また indy のログなどは上記の `/var/lib/{ネットワーク名}/{ノード名}.xxx`などのようにファイルが生成されます。

### 1-5. ネットワークの停止
停止の手順は非常に簡単ですが、indyプログラムの動作確認で非常に重要なのが**ボリュームの削除**です。

```console
$ docker-compose down
$ docker volume rm getting-started_sandbox
```
ブロックチェーンであるindyは一度、テストプログラムを流してしまったら元の状態には戻せないので、再度サンプルを動かすためにはボリュームの削除しかないです。
接続のエラーでうまくいかない場合にもなんらかの情報が残ってしまっている可能性がありますので、サンプルプログラムからの接続がうまくいかずにネットワークを再起動する場合にもボリュームの削除がマストです。
(というか、調子悪かったらだいたいボリューム削除すればなんとかなる。)

## 2. サンプルプログラム実行環境の構築
サンプルプログラムの実行環境だけをdockerで切り出すのにはいくつか理由があります。(というか切り出さないと全然ダメでした。)

1. indy-nodeやindy-plenumの開発環境はUbuntu16.04+python3がマスト
2. indylibのjs wrapperは`libindy.so`をリンクするために`npm install`時に `node-gyp rebuild`します。お察しかと思いますが`node-gyp`はpython 3未対応です。
3. JSのサンプル動かすのにpyenvするのはアレなのでさっさとコンテナ化してしまおう。
4. サンプルプログラムからjs wrapperへのリンクが "../../wrappers/nodejs"でファイルパス指定されてて(しかも上位階層)、`npm install`がうまく動かなかった。これはコンテナから実行しているのが原因だと思います。仕方ないので、ファイルパス指定やめてリポジトリからインストールするようにしました。

はい、力不足で申し訳ない。。。`node-gyp`とpython3の共存については詳しい方にお任せしてさっさと動かしてみたいと思います。

### 2-1. `Dockerfile`の作成
js版のサンプルプログラムは以下の箇所にあります。
`indy-sdk/tree/master/samples/nodejs/`
ここに以下の`Dockerfile`を作成します。

```docker:Dockerfile
FROM ubuntu:xenial

RUN sed -i.bak -e "s%archive.ubuntu.com%jp.archive.ubuntu.com%g" /etc/apt/sources.list

RUN apt update && \
  apt upgrade -y && \
  apt-get install -y build-essential pkg-config make cmake curl git \
  libssl-dev libsqlite3-dev libzmq3-dev libncursesw5-dev python \
  apt-transport-https

RUN cd /tmp && \
  curl https://download.libsodium.org/libsodium/releases/old/libsodium-1.0.16.tar.gz | tar -xz && \
  cd /tmp/libsodium-1.0.16 && \
  ./configure --disable-shared && \
  make && \
  make install && \
  cd .. && \
  rm -rf /tmp/libsodium-1.0.16

RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 68DB5E88
RUN echo "deb https://repo.sovrin.org/sdk/deb xenial stable" >> /etc/apt/sources.list
RUN apt-get update -y && apt-get install -y \
  libindy libindy-dev

RUN curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash

SHELL ["/bin/bash", "-c"]
RUN source ~/.nvm/nvm.sh && nvm install --lts && nvm use --lts

WORKDIR /opt
RUN git clone https://github.com/hyperledger/indy-sdk.git

WORKDIR /opt/indy-sdk/samples/nodejs
RUN sed -i -e "s%file:\.\./\.\./wrappers/nodejs%^1.8.1%" package.json
RUN source ~/.nvm/nvm.sh && nvm use --lts && npm install
ENV TEST_POOL_IP=10.0.0.2
```
要はこれまでのindy-sdk開発環境丸ごととpython2.7、nodejsを入れた環境を構築しています。
ちょっと`npm install`でうまくいかず試行錯誤したので、いくつかのバージョンのnodeを試すために`nvm`を使いました。が、`sh`では`source`できないので`bash`を使うなど小さな苦労が積み重なっているのでここにそのまま掲載します。

以下のコマンドでイメージをビルドします。

```console
$ docker build -t indy-sample-nodejs .
```

### 2-2. サンプルプログラムの実行
では、コンテナを動かしてサンプルプログラムを実行します。肝はテストネットと同じdockerのネットワークに参加する部分です。

``` 
$ docker run -it --rm --net=getting-started_pool_network --ip=10.0.0.15 indy-sample-nodejs bash
```
サンプルプログラム実行環境のコンテナが立ち上がり、中に入った状態となります。位置はDockerfileのWORKDIRで指定した`/opt/indy-sdk/samples/nodejs`となります。
接続情報となるトークンの最終確認です。以下のコマンドで、util.jsの中身がざーっと表示されます。

```console
$ cat src/util.js
```
ここで上記で揚げた**接続情報の直打ち(!!)**の内容が表示されています。先の手順で取得した`pool_transactions_genesis`の中身とトークンなどが違っていたら修正します。(多分、そのまま行けると思います。)
そして以下のコマンドですべてのサンプルプログラムが実行されます。

```
$ node src/main.js
gettingStarted.js -> started
Open Pool Ledger: pool1
==============================
=== Getting Trust Anchor credentials for Faber, Acme, Thrift and Government  ==
------------------------------
"Sovrin Steward" -> Create wallet
"Sovrin Steward" -> Create and store in Wallet DID from seed
==============================
== Getting Trust Anchor credentials - Government Onboarding  ==
------------------------------
"Sovrin Steward" > Create and store in Wallet "Sovrin Steward Government" DID
"Sovrin Steward" > Send Nym to Ledger for "Sovrin Steward Government" DID
...
```
この`gettingStarted.js -> started`以下で流れているログが、https://github.com/hyperledger/indy-sdk/blob/master/docs/getting-started/indy-walkthrough.md#about-alice 以下で解説されているself-sovereignのシナリオをプログラムとして実行した結果となります。
これでやっとindyのシナリオがちゃんと解読できますね〜！

いや〜、長かった。。。

# ですけどこれ、スタートラインっすね
ということでサンプルプログラムはなんとか動きましたがまだまだスタートラインに立ったばかりです。引き続きindyクライアントの開発にチャレンジしてみたいと思います。
本日は以上です。