Catapult-serverのブートストラップを読み解いてみた

次世代nemのインフラサーバーであるコードネームCatapultがgithubにて公開され、ビルドをしてみたり、動かすための設定をいじったりと、全国のnemberは試行錯誤を重ねて楽しんでいました。
[nemtech/catapult\-server: Catapult server](https://github.com/nemtech/catapult-server)

そんな中、mijinを開発するテックビューロ社による、`docker-compose up`で数分まてばCatapult環境が立ち上がるブートストラップが公開されました。
[tech\-bureau/catapult\-service\-bootstrap: Starter project to get developers up and running with a running Catapult Service](https://github.com/tech-bureau/catapult-service-bootstrap)

公式(?)なお手本ということで、ノード立ち上げまでどんなことが行われているのかを読み解いてみました。
なお、今後のアップデートによって内容が食い違っていく可能性があることをご了承ください。

# docker-composeの構成

[catapult\-service\-bootstrap/docker\-compose\.yml at master · tech\-bureau/catapult\-service\-bootstrap](https://github.com/tech-bureau/catapult-service-bootstrap/blob/master/docker-compose.yml)

docker-composeには複数のサービスが定義されていますが、おおまかに分けると以下のことが行われます。

1. 初期アドレス、サーバ設定ファイル群の生成
2. 初期ブロックの生成
3. サーバの立ち上げ

ほとんどが初期設定によるファイル生成ですが、どんな設定をすればいいのかの参考になります。
以下、`docker-compose`で実行されるサービスで起こることを順に整理します。
なお`docker-compose #{サービス名}`で一つづつ実行しながら様子を確認してみました。

# 初期アドレス、サーバ設定ファイル群の生成

## generate-raw-addresses

まず最初に実行されるサービスです。
これは初期配布用やノードの設定に使われるアドレスを生成し、`build/generated-address/raw-address.txt`として保存しています。

Catapultには`catapult.tools.address`というアドレスを生成するコマンドが同梱されています。
[catapult\-service\-bootstrap/generate\-raw\-addresses\-if\-needed at master · tech\-bureau/catapult\-service\-bootstrap](https://github.com/tech-bureau/catapult-service-bootstrap/blob/master/bin/bash/generate-raw-addresses-if-needed)
このシェルスクリプト経由で実行されています。

あとで設定に組み込まれるアドレスはここに保存されたものが使われるので、キーペアはここで確認できます。

## store-addresses

先に生成された`raw-addresses.txt`から、後に生成するノード設定のテンプレートへ流し込むために整形されたアドレス定義を生成します。

[catapult\-service\-bootstrap/addresses\.rb at master · tech\-bureau/catapult\-service\-bootstrap](https://github.com/tech-bureau/catapult-service-bootstrap/blob/master/ruby/lib/catapult/addresses.rb#L27)
このrubyスクリプトによって整形済みのファイルが`build/generated-addresses/addresses.yml`として保存されます。

[catapult\-service\-bootstrap/addresses\.rb at master · tech\-bureau/catapult\-service\-bootstrap](https://github.com/tech-bureau/catapult-service-bootstrap/blob/master/ruby/lib/catapult/addresses.rb#L16)
このあたりの定義によって、以下のセクションごとにそれぞれで使われるアドレスとして振り分けられます。

* peer_nodes
* api_nodes
* rest_gateways
* nemesis_addresses_harvesting
* nemesis_generation_hash
* nemesis_signer_private_key
* nemesis_addresses

## generate-configs

CatapultのノードやAPIサーバ用の設定である*.propertiesや*.jsonファイルをそれぞれ生成します。
ここで、直前に生成された`build/generated-addresses/addresses.yml`が使用されます。

`ruby/bin/generate-and-write-configurations.rb`このスクリプトが`ruby/catapult-templates`以下のファイルをテンプレートとしてアドレスを流し込み、`build/catapult-config`へ各ノードの設定ファイルを、`/build/nemesis`へ初期ブロック生成の設定ファイルが保存されます。

[catapult\-service\-bootstrap/ruby/catapult\-templates at master · tech\-bureau/catapult\-service\-bootstrap](https://github.com/tech-bureau/catapult-service-bootstrap/tree/master/ruby/catapult-templates)


# 初期ブロックの生成

ノードは`Peerサーバ`２つと`APIサーバ`１つの３つ分の初期化が行われます。

* peer-node-0-nemgen
* peer-node-1-nemgen
* api-node-0-nemgen

## peer-node-0-nemgen

`dockerfiles/nemgen/userconfig/nemgen.sh`が実行され、直前で生成された`build/nemesis/block-properties-file.properties`を使い、初期ブロックを生成して`data/peer-node-0/00000`のように保存します。

[catapult\-service\-bootstrap/nemgen\.sh at master · tech\-bureau/catapult\-service\-bootstrap](https://github.com/tech-bureau/catapult-service-bootstrap/blob/master/dockerfiles/nemgen/userconfig/nemgen.sh)

他のサーバも初期ブロックの保存ディレクトリが異なるだけで、同様の処理がおこなわれます。

## init-db

`data/mongo`にMongoDBの初期DBファイルを生成して保存します。

`static-config/mongo`以下を使ってコレクションやスキーマの設定等を行い、`data/mongo`にDBファイルが保存されます。


# サーバの立ち上げ

ここまでで、ノード、API、DBサーバの初期設定が終わったので、いよいよサーバの立ち上げをしてみます。

## db

まず先に`docker-compose up -d db`でmongoDBを立ち上げておきます。

`docker-compose logs -f db`でログを表示して動作しているかを確認できます。

## peer-node-0

`Peerノード`を立ち上げてみます。`docker-compose up -d peer-node-0`。

同様に`docker-compose logs -f peer-node-0`でログを表示してみます。

```
peer-node-0_1             | 2018-05-26 06:43:57.685252 0x00007fb024ae8700: <error> (ionet::PacketSocket.cpp@456) failed when resolving address 'peer-node-1': Host not found (authoritative) (cancelled? false)
```

このような`peer-node-1`への接続でタイムアウトしたというエラーが確認できました。

これは`catapult-config/peer-node-0/userconfig/resources/peers.p2p.json`に定義されている`knownPeers`にかかれている`peer-node-1`が立ち上がっていないからです。

このファイルにて、他の`Peerサーバ`を既知のサーバとして定義するようです。

## peer-node-1

では一度ログ表示を抜けて、`docker-compose up -d peer-node-1`でもう一つのノードを立ち上げてみます。

もう一度`docker-compose logs -f peer-node-0`でログを確認すると、先程のエラーがでなくなっています。

`docker-compose logs -f peer-node-1`でもう一台のノードのログを見てもエラーはでていません。

```
peer-node-0_1             | 2018-05-26 06:59:28.796465 0x00007fb025aea700: <debug> (src::NetworkHeightService.cpp@45) network chain height increased from 92 to 93
```

しばらく様子を見ていると、このようなブロック高が上がっていくログが確認できます。
また`data/peer-node-0/00000/*`にブロックごとのファイルが生成されているのも確認できます。


## api-node-0

`Peerノード`が立ち上がり、通信も始まってブロック生成が進んでいるので、その情報を取得するために`APIノード`を`docker-compose up -d api-node-0`で立ち上げてみます。

これでノードは立ち上がりますが、APIに問い合わせを行うにはAPI RESTサーバ経由で行うため、`Catapult RESTサーバ`も立ち上げます。

## rest-gateway

`docker-compose up -d rest-gateway`でサーバを立ち上げます。
ポートが`3000:3000`でマッピングされているので、`localhost:3000`でアクセスできます。

http://localhost:3000/block/1 で初期ブロックの情報が取得できるはずです。

```
{"meta":{"hash":"D2C9D14D39C4568D9D75E6AB7B143F8C655C145822CA49BEF45D3D630BDDC288","generationHash":"AA0F8D3D9D5E7C6610DC3EE7055E3BA4EE0D8AA9C2DB497C46601B1AC94CC54A","totalFee":[0,0],"numTransactions":25,"merkleTree":["OBuL94fOt46Udsq/PH+dmkL8QiEIi40MeZoq8PZ2dN4=","4cWF2igNFzQDiFTf3XjRFpuoWXiZTnHFVZt5VyC80h4=","tZFQpYOnXfS9vI8dv/7cZ6i4Ba5tToDlvZzSR7THWHw=","pTSRSTCRqAKzAaQ5Ckli693yKux/CjXO9H6B3FjKWPw=","rk3KArNr6HEVhH21dKKMgxcqiBHXzrMeXam2xdutwPI=","BRff7PJgm+td7ItGBHp3n8/1JDPPzCzCTdLT3Q5+oRI=","sPZNdyRW70L0yup5xXJIois+AiJKX12XDx0nubJYkFk=","997muKVVL/d7FBXw8RJb3JnlvEtfYfQupmcHEBahu9E=","4dj7rBkx4Ovf84CrlDlsS450ZiiH5lQpLff4Dbf1Ncc=","VNB2Ac6BXIboulluP59kcggNkhFoIuYuIfRwzBeHxKw=","TtYMSOkBUI8F2C4agrIm+eTCDQQxR8SFBf0VhnI3SM0=","Hv1HzVdX73+ovJJXidmcUX6fjeYd7z17OddoUZ8mzCU=","CUXz9nakZs+7G7amEZr0pBiQV/nmTgiMnnAYuWvexrI=","VGL3PKA+zjlW0IaQpKX9V58D7sIQ6kn8thLU3+coxx8=","+yITEMK/YRoFOjQO1+86jF3kDU6xkFyk3LKmIHNWgKU=","V291O3JcLFbvQm+8UyZSrSgQ8u6oiNRtJm89KdPZfIo=","SkJVhIY+2VMIGtffLWAbSoJPKYFaD6VxrRSsyHyQBT0=","NTHU0GDUgkC/egeztPLPuPcj1LhMce5VeIUoWeIJ9lk=","d1lsh9Lcp9i1IB6pbnym51SpCSrWKPFUr73aFXGFBDo=","chrpJrTa7rQSMPpy+MJ3xLC9UOkGndx926/vBXrO0ig=","3Lk9R2z9Y8tYqq9ThcHXUvHTgmj0mqfDDhQ9qDvikBE=","HEokpIyjCQ9w40CGj/71n9xFNu0g/MJ+/5uL06HV5Q8=","fkeVhjGF9xA+jYAyk8T3btklNZ4tE1mWAu1wrbwU/ow=","Z5a3zLpvqztwAut3niBg1msbTAOQJYzEB3GQR0rrVFs=","Sz/8PppR+n4wsxHEXN0A711Je3C5bTe+mZtdbCx5uDU=","Sz/8PppR+n4wsxHEXN0A711Je3C5bTe+mZtdbCx5uDU=","CGem9XU3w92hxfMpaMdQDQHnudspAf1M6EtqhgdKVtQ=","XzoNdGrGPolxg7/AoYez4HMin825OcNiI1hjtDNXLwg=","f0suZxEsZDIfPcdufMwqpebSCBIcOyzuYuBjYvomun8=","Bpq8IQcM2kzXzgbKud+SE1InfpCOyeM9wHC29TozSFY=","IFVanFxIkdp6A4OD9HEtDpBSdy/VXIWYEsM5GoPUYok=","bUQMSbSaoEPsh5vtAVTHjLwCHHqu4KD21GNSViZTDsE=","/VsaOM9Z+RmSynfPeUp5/kQFFxGi9MkxcO91aqkNTpg=","vPKamiarY55ytNaZEoUKS8PrVsUS4RhrUFp65/un9so=","b6jlNCQRQmOF2+8YwOL/OCfwnsugBqv7ovZ5e8zWpyk=","7qW6QR//B7sHBzs3vAzoeRcrWtZAQWfBVEC2jwKLzcI=","QliJlD42cy2WgSpLd4gslvwgHezhFLKtiRLfrxPc/qc=","JjzWCjAdEVpexhdGb29nK+08fk0tRUyzDpMV7Ij5j1s=","qUdfpbxjStVqpY2e/J+sJwN7kDjl6pr4e6dbMDWgBhQ=","qUdfpbxjStVqpY2e/J+sJwN7kDjl6pr4e6dbMDWgBhQ=","yOQy6nWN6NDFSiPmrPXpytzQWhbxLjODTITeNs1U3zE=","RLdXP9MefX58wUwtVJaXwddfastzNCgsfFXiV0a+pvs=","ef/4Tsnm0IIFagWGL+dw5dXQ3+EDqa0YDxKtMxtNbfU=","9S0HcHOeSbzkejEa2lUaAz1/3hshb1EKZ2wqUa66rms=","or3ekl67vX2JMHyetigbMyUn2BUKG+zD0GdhkIMGkXg=","SCpHwM/Kzzz1vhSy019lDuJRlGwWjcaWs89jVF3Mx9Y=","geyf5O/FYsnAoJwKbrFXTtJ5iIFBkOF3mhrxUwdilAU=","geyf5O/FYsnAoJwKbrFXTtJ5iIFBkOF3mhrxUwdilAU=","S+U4C/ad+betMBaPPETc1RZS4He8J61tBfXjvoP9V6o=","ITHxpUzwRqvJrZjycVrmzGgW0/L/1Mp3hK1QX3yIrpA=","Kfu2NlCFKkG1i2i1vpKuCwWFECzcVHo/1WAZa4IZUmY=","Sc3eVT+gitnT37+2uLhqOoy7wLi4yFEVXSRAdhXLlv4=","8V1d2f2t1Ijj2BYJR8SvBlz2VEYPYas7k7r2BkjnBOo=","j/xajoh8JrZgqienrEGl8dejxz5yM16MpZRdlwnoiqc=","8j2VoUs2QB9ThUm+a2T0Fcv4H/l2SQ0dtvlV59SdYaQ="]},"block":{"signature":"2D489C6D5850B81CA0CFECA379BBD9C1F17E57ECB9654154ED1DA7463E18D250A4D82E4E650A9AE06E9C272E9551D67DF2D57C861927425714B098DC8B6B1402","signer":"8B34A6575D5A5E1C8E6D33BB57B1FBA2936EB602A78E7AA1741AAA2A3D756C62","version":36867,"type":32835,"height":[1,0],"timestamp":[0,0],"difficulty":[276447232,23283],"previousBlockHash":"0000000000000000000000000000000000000000000000000000000000000000","blockTransactionsHash":"F23D95A14B36401F538549BE6B64F415CBF81FF976490D1DB6F955E7D49D61A4"}}
```
もう数十ブロック程度は生成されているでしょうから、適当に番号を進めてみればそのブロックも取得できます。

これでAPIに対してリクエストが投げられる状態になったので、nem2-cliなりnem2-sdkを使って動作確認や開発を行うことができそうです。


# 日本語README

日本語READMEも公開されたので、使い方についてはこちらのほうが読みやすいでしょう。

[catapult\-service\-bootstrap/README\.ja\.md at master · tech\-bureau/catapult\-service\-bootstrap](https://github.com/tech-bureau/catapult-service-bootstrap/blob/master/README.ja.md)


# コメント

上記の動作が`docker-compose up`をするだけでサーバの立ち上がりまで勝手に進みます。
初期ファイルの生成が終わってしまえば、必要に応じて`build/catapult-config/`以下の*.propertiesファイルを変更して設定を変えてみるなどできます。

もし完全に初期化したい場合は`./clean-all`を叩けばここまでに生成されたデータは削除されます。

日本語READMEにも記述があるので参照してみてください。


# 浅い考察

catapult-serverには`Peer`と`API`のロールがあり、`API`ロールのサーバに対してリクエストを送ります。
Catapultがパブリックnemとして可動するまではまだ相当の時間がかかると思われるので、その頃までには何かしら解決するのかもですが、わざわざAPIノードを整備するためのインセンティブが気になります。

ネットワークを維持するだけならPeerロールとして、nemネットワークに必要なポートの開放等、外部とは最小限の接続にすることでセキュアな構築ができますが、APIロールはそれに加えてRestサーバとの接続にも気を使う必要が出てくるのではないでしょうか。
このあたりもどのようになっていくのか、気になるところです。