Python使いがLibraを爆速で試してみる（行けるところまで）

# はじめに

LibraとはFacebookが開発している仮想通貨。Facebook、VISA、Uber、Spotifyなどが参加するリブラ財団を後ろ盾として持ち、法定通貨と連動するステーブルコインということで、他の仮想通貨とは一線を画している感があります。最近は、仮想通貨系の勉強会でもLibraの話題になると参加者が何割増しかになるみたいですし(笑)

2020年のリリースを予定しているとのことですが、米政府からも懸念を示されていたりして実際にどうなるのかはもう少し経たないとわかりません。一方で、純粋に技術的な観点でLibraを見た場合、幾つか興味深い点が有るようです。まずはその手始めとして「触ってみる」というのをやってみたいと思います。

# Libraを試す

Libraを試したい場合、[本家サイト](https://developers.libra.org/)の通りにやればほぼ問題なくできるでしょう。ただ、Rustで書かれたソースgit cloneしてRustのツールチェーンもインストールしてビルドして、となるとそこそこ時間がかかります。そしてまだ開発中なのでcloneしたタイミングに寄っては動かない場合もある。実際、私も不可解なエラーがでて動作せず、git pullして更新したら動くようになりました。

その他のやり方としてはDockerのimageをpullしてやるというのもある([DockerでFacebookのリブラ(Libra)のテストネットをとりあえず触る](https://qiita.com/sot528/items/4af8cf9daa8322d40443))みたいですが、サイズが3GB超なのでそれはそれで気合が要ります。

で、見つけたのが[pylibra](https://pypi.org/project/pylibra/)というPythonのモジュール。以前はlibra-pythonと呼称していたようですが名前が変わったみたいです。Libraのソースを使わずにクライアント機能を実装しているのですが、これを試してみます。

### pylibraのインストール

pyenvとpyenv-virtualenvが入っている前提で進めます。ない場合はMacの場合だとhomebrewで `brew install pyenv pyenv-virtualenv`とかでサクッと入れられます。で、専用の環境を作ってそこにpylibraを入れます。pythonを起動して、pylibraがimportできればOK。

なおここは私がハマったところですが、Pypiに上がっている最新版にはfaucetからの入金に不具合があり、github上の最新を取ってくる必要があります。

```shell
$ pyenv virtualenv 3.7.4 libra
$ pyenv local libra
$ pip install git+https://github.com/bandprotocol/pylibra.git@master
$ python
Python 3.7.4 (default, Jul 14 2019, 17:36:13)
[Clang 10.0.1 (clang-1001.0.46.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pylibra
>>>
```

### ウォレットの生成

まずはじめにウォレットの機能から。引数なしに`LibraWallet()`を呼ぶと新しいウォレットが生成されます。

```python
from pylibra import LibraWallet
wallet = LibraWallet()
```

Walletオブジェクトには`to_mnemonic()`というメソッドがあり、そのウォレットのニーモニックを取り出せます。そして、ウォレット生成時にそれを与えると前に生成したウォレットを再生成できます。

```python
mnemonic = wallet.to_mnemonic()
print(mnemonic) # e.g. "student deliver dentist cat gorilla sleep proud naive gown fiber awkward weasel"

wallet2 = LibraWallet(mnemonic) # walletとwallet2は同一
```

ただし、このmnemonicは本家のLibra CLIの生成するものとは互換性がないらしく、その点は注意が必要です。

### アカウントの生成

次にアカウントの生成です。Walletオブジェクトの`get_account()`を呼ぶとアカウントが取得できます。引数はcounter(nonce)で同じ値を与えれば同じアカウントを取得できます。Accountオブジェクトには`address`、`private_key`、`public_key`が属性として設定されます。

```python
account_0 = wallet.get_account(0)
print(account_0.address)     # c1b66e314403c2de08c07c3311acbf259140c563fed5dbf180dd436ff98a7de7
print(account_0.private_key) # 642b4a979778b6347266b59203dfd07549f8112364da95d146d707adbd105e20
print(account_0.public_key)  # c59ba4172b0f0bc0382a7cbfd125696ecfe7812779242e6c37962f41dad997c2
```

### クライアントの生成

ここまでがローカルで処理されるウォレットとアカウントの生成でしたが、いよいよブロックチェーンのネットワークに接続した処理になります。そのためにはまずクライアントを生成します。Libraのノードとはprotocol buffer のメッセージをやり取りすることでお話できますが、そのためのクライアントです。

```python
from pylibra import LibraClient
client = LibraClient()
```

`LibraClient`の引数に`host:port`を与えると特定のノードとお話できますが、ここではテストネットのノードに繋ぎに行っています。

### 残高追加

出来たばかりのアカウントの残高は当然ながら0です。そこにテストネットの公式faucet から送金してもらって残高を増やします。そのために`mint_with_faucet`というメソッドを使います。2つ目の引数は送金してもらう金額ですが、単位は mini-libra ($=10^{-6}$ LBR)なので、10000を指定すると0.01LBRとなります。

```python
client.mint_with_faucet(account_0, 10000)
```

そしてClientオブジェクトの`get_account_state()` メソッドをアカウントを引数に呼び出すと、そのアカウントの状態を取得できます。

```python
account_0_state = client.get_account_state(account_0)
print(account_0_state.balance)　# 10000
print(account_0_state.sequence_number)
print(account_0_state.received_events_count)
print(account_0_state.sent_events_count)
```

### 送金

せっかくLibraを入手できたのでこれを他のアカウントに送金してみましょう。新たにもう一つアカウントを作ってそこに向けた送金依頼を出します。まずは`account_1`を作りそこに今度は3000入れておきましょう。

```Python
account_1 = wallet.get_account(1)
client.mint_with_faucet(account_1, 3000)
account_1_state = client.get_account_state(account_1)
print(account_1_state.balance)　# 3000
```

で、1000を`account_1`移動させるトランザクションを作り、`send_transaction`する。

```python
from pylibra.transaction import TransferTransaction

tx = TransferTransaction(account_1, 1000)
client.send_transaction(account_0, tx)
```

よしよし。で、結果を見てみる。

```python
account_0_state = client.get_account_state(account_0)
print(account_0_state.balance)　# 10000
account_1_state = client.get_account_state(account_1)
print(account_1_state.balance)　# 3000
```

あれ、変わってない？

そうなんです。どうやら現時点で送金の機能は壊れているみたいです。Githubのissuesを見るとそんなレポートが複数あり、Libraテストネットの進化に追いついていっていない模様。まあでも、ここまで試せたのでとりあえず良しとしましょう。

# まとめ

LibraをインストールせずにPythonのクライアントライブラリを使って試すというのをやってみました。まだまだ機能が足りていませんが、Libraが落ち着いてくればお手軽にLibraをいじれる方法の一つになりそうなので引き続きWatchしていきたいと思います。