Libraを複数サーバ・マルチノードで動かす

# 初めに
Facebookが発表したLibraですが、チュートリアル用に用意されているものは1サーバ上に1ノードしか動かせません。
今回は2つのサーバ上にそれぞれノードを動かして、CLI clientを用いて一方のノードに対して実行したトランザクションが他方のノードからも参照できることを確認します。

# Libraのビルド
以下のサーバを2台用意します。両方のサーバ上で以下の手順に沿ってLibraのビルドを行ってください。

| ノード|IPアドレス            |OS|
|:-----------------|:------------------|:------------------|
| ノード1| 192.168.10.1          |Ubuntu 18.04|
| ノード2| 192.168.10.2          |Ubuntu 18.04|

ホームディレクトリにfacebookというディレクトリを作成し、リポジトリをcloneします。

```bash
$ mkdir facebook
$ cd facebook
$ git clone https://github.com/libra/libra.git
Cloning into 'libra'...
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 11907 (delta 0), reused 0 (delta 0), pack-reused 11906
Receiving objects: 100% (11907/11907), 5.07 MiB | 1.50 MiB/s, done.
Resolving deltas: 100% (7513/7513), done.
Checking out files: 100% (1502/1502), done.
```

ビルドに必要なライブラリ等をダウンロードし、ビルドを行います。ビルドは少し時間がかかります。


```bash
$ cd libra
$ ./scripts/dev_setup.sh 
Welcome to Libra!

This script will download and install the necessary dependencies needed to
build Libra Core. This includes:
        * Rust (and the necessary components, e.g. rust-fmt, clippy)
        * CMake, protobuf, go (for building protobuf)

<中略>

$ source $HOME/.cargo/env
$ cargo build
<中略>
    Finished dev [unoptimized + debuginfo] target(s) in 26m 03s
```

~/facebook/libra/target/debugにバイナリが作成されているのでパスを通しておきます。

```bash
$ cd target/debug
$ export PATH=$PATH:`pwd`
```
# ネットワーク設定作業
ここからはノード1のサーバで作業を行います。ノード1のサーバ上でノードを起動するために必要な各種ファイルの作成作業を行います。
facebookディレクトリ配下にnodeというディレクトリを作成します。今後はこのディレクトリの中で作業を行います。

```bash
$ mkdir ~/facebook/node
$ cd ~/facebook/node
```

まず、キーペアを作成します。oオプションでファイル名を指定します。

```bash
$ generate_keypair -o ./mint.key
```

設定ファイルを作成します。この際、設定ファイルのテンプレートとなるファイルをbオプションを用いて指定する必要があります。テンプレートファイルがリポジトリ内のconfig/data/configs/node.config.tomlに用意されているのでこれを利用します。また、mオプションで先ほど作成したキーペアを、nオプションでノードの数（今回は2）を指定します。

```bash
$ libra-config -b ../libra/config/data/configs/node.config.toml -m ./mint.key -n 2
$ ls
trusted_peers.config.toml
seed_peers.config.toml
validator_8deeeaed65f0cd7484a9e4e5ac51fbac548f2f71299a05e000156031ca78fb9f.node.config.toml
validator_8deeeaed65f0cd7484a9e4e5ac51fbac548f2f71299a05e000156031ca78fb9f.node.keys.toml
validator_1e5d5a74b0fd09f601ac0fca2fe7d213704e02e51943d18cf25a546b8416e9e1.node.config.toml
validator_1e5d5a74b0fd09f601ac0fca2fe7d213704e02e51943d18cf25a546b8416e9e1.node.keys.toml
```

8deeeaed65f0cd7484a9e4e5ac51fbac548f2f71299a05e000156031ca78fb9fという名前のノードと、1e5d5a74b0fd09f601ac0fca2fe7d213704e02e51943d18cf25a546b8416e9e1という名前のノードの設定ファイルとキーペア、genesis.blobが作成されました。
今回は、
8deeeaed65f0cd7484a9e4e5ac51fbac548f2f71299a05e000156031ca78fb9fをノード1
1e5d5a74b0fd09f601ac0fca2fe7d213704e02e51943d18cf25a546b8416e9e1をノード2
とします。

これらの設定ファイルはIP v6用の設定になっているので編集します。まず、seed_peers.config.tomlにIPアドレスを書くところがあるので以下のように編集します。

```toml
[seed_peers]
8deeeaed65f0cd7484a9e4e5ac51fbac548f2f71299a05e000156031ca78fb9f = ["/ip4/192.168.10.1/tcp/34573"]
1e5d5a74b0fd09f601ac0fca2fe7d213704e02e51943d18cf25a546b8416e9e1 = ["/ip4/192.168.10.2/tcp/33199"]
```

また、以下の2つの設定ファイルもIP v6用の設定になっているので編集します。
validator_8deeeaed65f0cd7484a9e4e5ac51fbac548f2f71299a05e000156031ca78fb9f.node.config.toml
validator_1e5d5a74b0fd09f601ac0fca2fe7d213704e02e51943d18cf25a546b8416e9e1.node.config.toml
それぞれ「/ip6/::1/tcp/」、「::1」となっている部分を「/ip4/0.0.0.0/tcp/」、「0.0.0.0」に変更します。

参考として、変更後のファイルは以下の通りです。

<details><summary>validator_8deeeaed65f0cd7484a9e4e5ac51fbac548f2f71299a05e000156031ca78fb9f.node.config.toml</summary><div>

```toml
[base]
peer_id = "1e5d5a74b0fd09f601ac0fca2fe7d213704e02e51943d18cf25a546b8416e9e1"
data_dir_path = "/home/myhome/facebook/node"
node_sync_retries = 7
node_sync_channel_buffer_size = 10
node_async_log_chan_size = 256

[metrics]
dir = "metrics"
collection_interval_ms = 1000
push_server_addr = ""

[execution]
address = "localhost"
port = 37875
testnet_genesis = false
genesis_file_location = "genesis.blob"

[admission_control]
address = "0.0.0.0"
admission_control_service_port = 42891
need_to_check_mempool_before_validation = false

[debug_interface]
admission_control_node_debug_port = 43337
secret_service_node_debug_port = 37533
storage_node_debug_port = 46841
metrics_server_port = 38115
address = "0.0.0.0"

[storage]
address = "localhost"
port = 33041
dir = "libradb/validator_1e5d5a74b0fd09f601ac0fca2fe7d213704e02e51943d18cf25a546b8416e9e1/db"
grpc_max_receive_len = 100000000

[network]
listen_address = "/ip4/0.0.0.0/tcp/33199"
advertised_address = "/ip4/0.0.0.0/tcp/33199"
discovery_interval_ms = 1000
connectivity_check_interval_ms = 5000
enable_encryption_and_authentication = true
role = "validator"
peer_keypairs_file = "validator_1e5d5a74b0fd09f601ac0fca2fe7d213704e02e51943d18cf25a546b8416e9e1.node.keys.toml"
trusted_peers_file = "trusted_peers.config.toml"
seed_peers_file = "seed_peers.config.toml"

[consensus]
max_block_size = 100
proposer_type = "rotating_proposer"
contiguous_rounds = 2

[mempool]
broadcast_transactions = true
shared_mempool_tick_interval_ms = 50
shared_mempool_batch_size = 100
shared_mempool_max_concurrent_inbound_syncs = 100
capacity = 10000000
capacity_per_user = 100
sequence_cache_capacity = 1000
system_transaction_timeout_secs = 86400
system_transaction_gc_interval_ms = 180000
mempool_service_port = 37901
address = "localhost"

[state_sync]
chunk_limit = 1000
tick_interval_ms = 10
long_poll_timeout_ms = 30000
max_chunk_limit = 1000
max_timeout_ms = 120000

[log_collector]
is_async = true
use_std_output = true
[vm_config.publishing_options]
type = "Open"

[secret_service]
address = "localhost"
secret_service_port = 33739
```

</div></details>

編集したら~/facebook/nodeのディレクトリごともう一方のサーバにコピーしておいてください。

最後にそれぞれのサーバでノードを起動します。
まず、ノード1のサーバでノードを起動します。fオプションで設定ファイルを、pオプションでノードのIDを指定します。

```bash
$ libra_node -f ./validator_1e5d5a74b0fd09f601ac0fca2fe7d213704e02e51943d18cf25a546b8416e9e1.node.config.toml -p 1e5d5a74b0fd09f601ac0fca2fe7d213704e02e51943d18cf25a546b8416e9e1
```


次に、ノード2のサーバでノードを起動します。

```bash
$ libra_node -f ./validator_8deeeaed65f0cd7484a9e4e5ac51fbac548f2f71299a05e000156031ca78fb9f.node.config.toml -p 8deeeaed65f0cd7484a9e4e5ac51fbac548f2f71299a05e000156031ca78fb9f
```

たくさんログが出ますが、

```bash
Successfully connected to peer
```

というログが表示されれば成功です。

# トランザクション実行
https://developers.libra.org/docs/my-first-transaction
と同様のトランザクションを実行します。チュートリアルと同様にCLI clientを用いてトランザクションを実行します。

ノード1サーバのfacebookディレクトリ配下にclient1とclien2というディレクトリを作成します。今後はこのディレクトリの中で作業を行います。

```bash
$ mkdir ~/facebook/client1
$ mkdir ~/facebook/client2
$ cd ~/facebook/client1
```

まずノード1に接続するclientを起動します。この際、先ほど作成したmint.keyとtrusted_peers.config.tomlが必要になるので、nodeディレクトリからコピーしておきます。

```bash
$ cp ../node/mint.key .
$ cp ../node/trusted_peers.config.toml .
```
CLI clientを起動します。mオプションでキーペアを、aオプションでIPアドレスを、pオプションでノード1の設定ファイル（validator_1e5d5a74b0fd09f601ac0fca2fe7d213704e02e51943d18cf25a546b8416e9e1.node.config.toml）の中にあるadmission_control_service_portの番号を、sオプションでtrusted_peers.config.tomlを指定します。

```bash
$ client -m ./mint.key -a localhost -p 42891 -s ./trusted_peers.config.toml
Connected to validator at: localhost:42891
usage: <command> <args>

Use the following commands:

account | a 
        Account operations
query | q 
        Query operations
transfer | transferb | t | tb 
        <sender_account_address>|<sender_account_ref_id> <receiver_account_address>|<receiver_account_ref_id> <number_of_coins> [gas_unit_price_in_micro_libras (default=0)] [max_gas_amount_in_micro_libras (default 100000)] Suffix 'b' is for blocking. 
        Transfer coins (in libra) from account to another.
dev 
        Local move development
help | h 
        Prints this help
quit | q! 
        Exit this client


Please, input commands: 

libra% 
```

https://developers.libra.org/docs/my-first-transaction
に書かれているコマンドを実行していきます。

```bash
libra% account create
>> Creating/retrieving next account from wallet
Created/retrieved account #0 address bc9e6e3b8ddf96fbd57b6541d6286df319ecb4a23aad2039c7c75ae6a60471f1
libra% account create
>> Creating/retrieving next account from wallet
Created/retrieved account #1 address aac6755196d5108f4119ed5bca7faef7d90af00815ed156fc5844cb3b9cb23eb
libra% account mint 0 110
>> Minting coins
Mint request submitted
libra% account mint 1 52
>> Minting coins
Mint request submitted
libra% query balance 0
Balance is: 110.000000
libra% query balance 1
Balance is: 52.000000
libra% query sequence 0
>> Getting current sequence number
Sequence number is: 0
libra% query sequence 1
>> Getting current sequence number
Sequence number is: 0
libra% transfer 0 1 10
>> Transferring
Transaction submitted to validator
To query for transaction status, run: query txn_acc_seq 0 0 <fetch_events=true|false>
libra% query sequence 0
>> Getting current sequence number
Sequence number is: 1
libra% query sequence 1
>> Getting current sequence number
Sequence number is: 0
libra% query balance 0
Balance is: 100.000000
libra% query balance 1
Balance is: 62.000000
```



ノード2に接続するclientを起動します。ノード1と同じですが、mintトランザクションは実行しないため、mint.keyは必要ありません。

```bash
$ cd ~/facebook/client2
$ cp ../node/trusted_peers.config.toml .
```
CLI clientを起動します。aオプションでIPアドレスを、pオプションでノード2の設定ファイル（validator_8deeeaed65f0cd7484a9e4e5ac51fbac548f2f71299a05e000156031ca78fb9f.node.config.toml）の中にあるadmission_control_service_portの番号を、sオプションでtrusted_peers.config.tomlを指定します。

```bash
$ client -a 192.168.10.2 -p 40409 -s ./trusted_peers.config.toml
Connected to validator at: 192.168.10.2:40409
usage: <command> <args>

Use the following commands:

account | a 
        Account operations
query | q 
        Query operations
transfer | transferb | t | tb 
        <sender_account_address>|<sender_account_ref_id> <receiver_account_address>|<receiver_account_ref_id> <number_of_coins> [gas_unit_price_in_micro_libras (default=0)] [max_gas_amount_in_micro_libras (default 100000)] Suffix 'b' is for blocking. 
        Transfer coins (in libra) from account to another.
help | h 
        Prints this help
quit | q! 
        Exit this client


Please, input commands: 

libra% 
```

ノード1で実行したトランザクションとその結果がノード2からクエリできることを確認します。
その際、`account create`を実行した際に画面に表示されたaccountのaddressが必要になります。今回は以下の2つです。
bc9e6e3b8ddf96fbd57b6541d6286df319ecb4a23aad2039c7c75ae6a60471f1
aac6755196d5108f4119ed5bca7faef7d90af00815ed156fc5844cb3b9cb23eb

```bash
libra% query balance bc9e6e3b8ddf96fbd57b6541d6286df319ecb4a23aad2039c7c75ae6a60471f1
Balance is: 100.000000
libra% query balance aac6755196d5108f4119ed5bca7faef7d90af00815ed156fc5844cb3b9cb23eb
Balance is: 62.000000
libra% query sequence bc9e6e3b8ddf96fbd57b6541d6286df319ecb4a23aad2039c7c75ae6a60471f1
>> Getting current sequence number
Sequence number is: 1
libra% query sequence aac6755196d5108f4119ed5bca7faef7d90af00815ed156fc5844cb3b9cb23eb
>> Getting current sequence number
Sequence number is: 0
```

残高やシーケンスがノード1と一致していことがわかります。
また、`query txn_range <start_version> <limit> <fetch_events=true|false>`でトランザクションの内容をクエリできます。

```bash
libra% query txn_range 0 10 false
>> Getting committed transaction by range
Transaction at version 0: SignedTransaction { 
 raw_txn: RawTransaction { 
        sender: 000000000000000000000000000000000000000000000000000000000a550c18, 
        sequence_number: 0, 
        payload: {, 
                transaction: genesis, 
                args: [  
                ]
        }, 
        max_gas_amount: 0, 
        gas_unit_price: 0, 
        expiration_time: 18446744073709551615s, 
}, 
 public_key: Ed25519PublicKey(
    PublicKey(CompressedEdwardsY: [102, 79, 110, 143, 54, 234, 203, 23, 112, 250, 135, 157, 134, 194, 193, 208, 250, 254, 161, 69, 232, 79, 167, 214, 113, 171, 122, 1, 26, 84, 213, 9]), EdwardsPoint{
        X: FieldElement51([1852396171027154, 860490020578256, 813265495276734, 709853280255065, 1167621827020382]),
        Y: FieldElement51([1101944985636710, 232757151007481, 1838365913843466, 1610695743894562, 172989498791850]),
        Z: FieldElement51([1, 0, 0, 0, 0]),
        T: FieldElement51([416741632430606, 2066765412282255, 1539741622329517, 1822242378190878, 341152508952685])
    }),
), 
 signature: Ed25519Signature(
    Signature( R: CompressedEdwardsY: [237, 53, 137, 203, 231, 16, 247, 105, 95, 90, 93, 148, 44, 153, 95, 115, 197, 80, 157, 146, 26, 163, 230, 224, 250, 157, 132, 135, 3, 254, 126, 13], s: Scalar{
        bytes: [78, 48, 115, 67, 168, 40, 194, 198, 240, 218, 60, 222, 119, 88, 146, 116, 78, 52, 115, 47, 85, 205, 72, 198, 115, 132, 17, 166, 234, 152, 86, 5],
    } ),
), 
 }
Transaction at version 1: SignedTransaction { 
 raw_txn: RawTransaction { 
        sender: 000000000000000000000000000000000000000000000000000000000a550c18, 
        sequence_number: 0, 
        payload: {, 
                transaction: mint_transaction, 
                args: [ 
                        {ADDRESS: bc9e6e3b8ddf96fbd57b6541d6286df319ecb4a23aad2039c7c75ae6a60471f1},
                        {U64: 110000000}, 
                ]
        }, 
        max_gas_amount: 140000, 
        gas_unit_price: 0, 
        expiration_time: 1566832205s, 
}, 
 public_key: Ed25519PublicKey(
    PublicKey(CompressedEdwardsY: [102, 79, 110, 143, 54, 234, 203, 23, 112, 250, 135, 157, 134, 194, 193, 208, 250, 254, 161, 69, 232, 79, 167, 214, 113, 171, 122, 1, 26, 84, 213, 9]), EdwardsPoint{
        X: FieldElement51([1852396171027154, 860490020578256, 813265495276734, 709853280255065, 1167621827020382]),
        Y: FieldElement51([1101944985636710, 232757151007481, 1838365913843466, 1610695743894562, 172989498791850]),
        Z: FieldElement51([1, 0, 0, 0, 0]),
        T: FieldElement51([416741632430606, 2066765412282255, 1539741622329517, 1822242378190878, 341152508952685])
    }),
), 
 signature: Ed25519Signature(
    Signature( R: CompressedEdwardsY: [105, 84, 144, 28, 62, 83, 198, 96, 163, 63, 75, 144, 103, 157, 195, 238, 64, 73, 71, 81, 141, 202, 32, 109, 31, 227, 212, 99, 165, 202, 152, 67], s: Scalar{
        bytes: [98, 251, 140, 141, 128, 184, 104, 160, 34, 211, 71, 123, 125, 182, 170, 171, 252, 178, 108, 52, 140, 188, 52, 168, 111, 173, 125, 64, 100, 0, 151, 11],
    } ),
), 
 }
Transaction at version 2: SignedTransaction { 
 raw_txn: RawTransaction { 
        sender: 000000000000000000000000000000000000000000000000000000000a550c18, 
        sequence_number: 1, 
        payload: {, 
                transaction: mint_transaction, 
                args: [ 
                        {ADDRESS: aac6755196d5108f4119ed5bca7faef7d90af00815ed156fc5844cb3b9cb23eb},
                        {U64: 52000000}, 
                ]
        }, 
        max_gas_amount: 140000, 
        gas_unit_price: 0, 
        expiration_time: 1566832211s, 
}, 
 public_key: Ed25519PublicKey(
    PublicKey(CompressedEdwardsY: [102, 79, 110, 143, 54, 234, 203, 23, 112, 250, 135, 157, 134, 194, 193, 208, 250, 254, 161, 69, 232, 79, 167, 214, 113, 171, 122, 1, 26, 84, 213, 9]), EdwardsPoint{
        X: FieldElement51([1852396171027154, 860490020578256, 813265495276734, 709853280255065, 1167621827020382]),
        Y: FieldElement51([1101944985636710, 232757151007481, 1838365913843466, 1610695743894562, 172989498791850]),
        Z: FieldElement51([1, 0, 0, 0, 0]),
        T: FieldElement51([416741632430606, 2066765412282255, 1539741622329517, 1822242378190878, 341152508952685])
    }),
), 
 signature: Ed25519Signature(
    Signature( R: CompressedEdwardsY: [190, 204, 179, 21, 144, 117, 11, 137, 212, 180, 53, 10, 29, 199, 177, 28, 228, 33, 175, 62, 144, 210, 105, 1, 55, 49, 118, 199, 171, 130, 220, 173], s: Scalar{
        bytes: [9, 215, 147, 62, 131, 31, 62, 231, 111, 36, 27, 211, 136, 98, 162, 130, 133, 191, 164, 17, 46, 99, 134, 198, 79, 73, 141, 235, 74, 51, 199, 15],
    } ),
), 
 }
Transaction at version 3: SignedTransaction { 
 raw_txn: RawTransaction { 
        sender: bc9e6e3b8ddf96fbd57b6541d6286df319ecb4a23aad2039c7c75ae6a60471f1, 
        sequence_number: 0, 
        payload: {, 
                transaction: peer_to_peer_transaction, 
                args: [ 
                        {ADDRESS: aac6755196d5108f4119ed5bca7faef7d90af00815ed156fc5844cb3b9cb23eb},
                        {U64: 10000000}, 
                ]
        }, 
        max_gas_amount: 140000, 
        gas_unit_price: 0, 
        expiration_time: 1566832243s, 
}, 
 public_key: Ed25519PublicKey(
    PublicKey(CompressedEdwardsY: [58, 63, 85, 167, 117, 103, 100, 191, 235, 179, 53, 106, 137, 189, 118, 49, 49, 188, 198, 31, 116, 80, 162, 32, 7, 142, 235, 8, 189, 121, 112, 219]), EdwardsPoint{
        X: FieldElement51([1772812698879353, 304530301646940, 2169944738223635, 697707544457128, 1010507603644876]),
        Y: FieldElement51([1239654923059002, 331256709347308, 1999946232748790, 1974243208739343, 1608618190343864]),
        Z: FieldElement51([1, 0, 0, 0, 0]),
        T: FieldElement51([1387442142995314, 665264140131818, 629072752354239, 1547256085650866, 1618042475867041])
    }),
), 
 signature: Ed25519Signature(
    Signature( R: CompressedEdwardsY: [39, 182, 130, 122, 90, 0, 201, 1, 194, 27, 81, 52, 242, 45, 110, 98, 60, 12, 17, 236, 189, 81, 36, 30, 154, 102, 116, 6, 219, 164, 207, 94], s: Scalar{
        bytes: [246, 245, 235, 81, 114, 195, 190, 184, 156, 253, 124, 61, 188, 51, 9, 136, 42, 47, 172, 127, 191, 83, 158, 6, 244, 38, 174, 58, 140, 5, 77, 12],
    } ),
), 
 }
```
genesisトランザクションが1つとmintトランザクションが2つ、アカウント間で送金した際のpeer_to_peer_transactionがノード2からもクエリできることが確認できました。