【始めてのRipple】【私がな】PythonからJavaScript(Node.js)を呼び出し、Rippleの処理を行う


# はじめに

最近、いわゆるFintech系の方がTwitterでフォロワーになって下さる事が多かったのです。

ツイート見たりしますと、Ripple関係の話が結構出て来ています。みずほ銀行とか大きな話が多いので、デジタル土方（にもなれてないか最近）な私には縁がないかなーとか思ってました。

が、Developperサイト見てみますと、最近は当然なのかもしれませんがオープンソースプロダクトで、かつAPI系とかもタダで落とせたりしました。どうもNode.jsで提供されているAPIを使うと、実稼働しているRippleネットワークにアクセスが出来るようです。

私は最近、どの言語もダメダメになってしまった悲しいプログラマですが、その中ではPythonの利用頻度がそこそこなのです。またPythonだと機械学習系とか計算系も充実してますので、AIからRipple使って自動コントラクトとかなかなか楽しそうな事も出来そうだ思いました。今回、PythonからJavaScriptを呼び出してRipple APIにアクセスするという、土台な部分は出来ましたので、備忘の意味も込めて記事化します。


# で、何するの

今回試作したものは、機能としてはあまり意味がありません申し訳ございません。こんな感じです。

* メインはPython
* RIppleネットワークのtotalDropsというパラメータ（恐らくXRPの動きの１つ？）を一定時間、定期的に取得します。
* そのデータをcsvデータにまとめます。
* まとめたcsvデータはRで使えるようにしてみます。
* パラメータを取る所だけ、Node.jsを利用したJavaScriptを使います。

今回の成果物をベースにすると、RippleAPI呼び出しを増産してTensorFlowと繋げたりとか、いろいろ出来るようになるかと思います。

# Rippleとは

ブロックチェーンを応用した決済プロトコルです。まぁ流行り系の技術になるかなーとか思います。[本家はここです。](https://ripple.com/)

Rippleの説明に関しては、GiantGox様の以下サイトがとてもよくまとまっています。
http://gtgox.com/

↑の「Rippleとは」ぐらいを見ていただければ概要は理解できるかなと思います。

# Ripple APIとは

[デベロッパーサイトがここに](https://ripple.com/build/)あります。以下、そこからの引用。

_Rippleの分散型決済ネットワークは、誰もが利用できるオープンソースの技術のもとに構築されています。デベロッパーがオープンソース・プラットフォームでソリューションを作るために利用可能なツールはこちらにあります。_

Ripple APIとは上記でいう「利用可能なツール」の１つなようで、JavaScriptでRippleサーバにアクセスし、お金の移動とかが出来るみたいです。

RippleAPIはnode_js上で動作し、かつECMAScript 6（ES2015） を利用します。つまりはbabel-nodeを使って実行することでシステムにアクセスがで出来るって事みたいです。

APIの仕様などは以下のサイトで。
https://ripple.com/build/rippleapi/

スクリプトはこんな感じで書くんだよーというのはここ。
https://ripple.com/build/rippleapi/#boilerplate

今回は↑のご沙汰に従い、getLedgerというAPIを使って、totalDropsというデータを取得します。APIの説明はここ。
https://ripple.com/build/rippleapi/#getledger

# 実行環境の構築

[Rippleにはビギナーズガイド](https://ripple.com/build/rippleapi-beginners-guide/)がありますので、そこを見ながらやると良いです。ここではいくつか補足を。

APIの説明でも書きましたが、node_jsが実行できる環境が必要です。node_jsのセットアップに関しては情報が豊富にあるかと思いますので、省略します。RippleAPIを使う際、執筆時（2016年8月）では 0.12、4.x以上が必要ですので、そういう環境を構築してください。

私はMacOSXのnode_js　0.12.4 上に構築しました。

node_jsを動くようにすれば、以下の手順でbabelを入れれば準備完了となるみたいです（これは[ビギナーズガイド](https://ripple.com/build/rippleapi-beginners-guide/)の受け売り）

1. 適当なフォルダを作成して移動。
2. 必要ならgit init（私はやりませんでした）。
2. そこでpackage.jsonを書きます。内容は[ビギナーズガイド](https://ripple.com/build/rippleapi-beginners-guide/)にありますので、コピペすれば良いです。
3. ```npm install``` とすると、babelなどが入ってきます。
4. ガイドにもありますように、npm install時にWarningが出ますがまぁそこは「はいはい」って感じで良いみたいです。

これで後はJavaScriptのプログラム書いて、以下のように実行すれば動かすことが出来るようになります。

```bash
./node_modules/.bin/babel-node スクリプト名
```

# JavaScriptでRippleAPIを呼び出す

今回はAPIを１個呼び出すだけですので、RIppleのサンプルほぼそのままです。なので説明するよりもコードを見た方が理解が早いかと思います。

```javascript
"use strict";
const RippleAPI = require("ripple-lib").RippleAPI;

const api = new RippleAPI({
  server: "ここにRippleAPIのURLを入れて下さい" // Public rippled server
});

api.connect().then(() => {
  // Ripple Networkに接続した後の処理をここに書きます。

  // 具体的にはAPIを呼び出します。
  return api.getLedger()

}).then(info => {

  // APIの処理が完了すると、ここに来ます。
  // info(名前はご自由らしい）に戻り値があります。

  // 今回は、totalDropsを取り出して出力します。
  // →これはAPIドキュメントの「Return Value」で定義されてます。
  // consoleに出した文字列をPythonに渡すという感じです。
  console.log(info.totalDrops);

}).then(() => {

  // で、やること終わったので、Rippleから切断です。
  return api.disconnect();

}).then(() => {

}).catch(console.error);
```

要はAPI呼び出して、結果をconsosole.logで出してPythonに通知するだけです。

__serverの所にはRippleAPIのURLを入れてください。__

以下のサイトでURLがあります。
https://ripple.com/build/rippled-apis/#websocket-api

もしくは、以下にあるサンプルにURLが書かれていますので、そこからのコピペでも良いかと思います。
https://ripple.com/build/rippleapi/#boilerplate

本記事では、上記のJavaScriptを「get_totalDrops.js」ファイルで扱う前提で話を進めさせて戴きます。それを単独で実行するとこんな感じで、totalDropsの数字が出てきます（出るまで数秒～１０秒ぐらいかかります）

```bash
$ ./node_modules/.bin//babel-node get_totalDrops.js 
99997222654452897
```

# PythonからJavaScriptを呼び出す。

と書きましたが、今回は極めて手抜きな方法です。

* pythonファイルと、JavaScriptファイルを同じフォルダに置く
* node_js＆rippleAPI　と　python　がどちらも実行できる環境にする。
* pythonのcommands.getoutputを使って実行するだけ
* コンソールの文字列が戻り値で戻される。

今回取得するデータは数字なので、こんな感じで関数化します。

```py
import commands

RIPPLE_API_COMMAND = "./node_modules/.bin//babel-node get_totalDrops.js"
def get_totalDrops():
    return long(commands.getoutput(RIPPLE_API_COMMAND))
```

RIppleAPIを呼び出すget_totalDrops.jsをcommands.getoutputで実行します。その結果は文字列で来ます。今回は数値のみを出力させていますので、それをlongに変換して戻り値としています。

今回は戻り値１つでしたが、複数の結果を戻すときはcsvとかにすればこれの応用でやれます。バルクデータの場合はまた別のロジックが必要になると思います。

# Python側の仕様

今回は

* RippleAPIでデータを収集
* データを加工
* 加工したデータの可視化

を一通りやってみようと思います。そのため、以下のような要件でやってみます。

1. 指定された時間毎に、指定された回数、RippleAPI・getLedgerを呼び出します。
2. 戻り値（totalDrops）から前回との差分を算出します。
3. totalDropsと差分を蓄積します。
4. 指定された回数呼び出したら、蓄積した結果などを基に２つのCSVファイルを生成します。

今回はRで結果を出してみますので、以下２つのCSVファイルを出してみます。

__解析パラメータ用のCSV__

ここには開始時刻と、各データの間隔（単位：秒）とを入れます。以下は例。

```
time, by
2016/8/20 7:59, 300
```

1行目はタイトルで、2行目に 開始時刻 , 間隔 と設定します。


__データのCSV__

ここには実際の結果（totalDropsと差分）を入れます。以下は例。

```
total, diff
99997224489730292,0
99997224488731486,998806
99997224487789148,942338
99997224487350204,438944
99997224486780979,569225
```

同じく1行目はタイトルで、2行目から totalDrops , 差分　という感じで次々入れておきます。上記の例は5回行った例です。

# Python側のコード

これもそう処理が複雑な訳ではないので、いきなりコードを掲示させて戴きます。

```py
#!/usr/bin/python
# coding: UTF-8

import commands
import time
import datetime

# --------- 以下は適当に変えてみてもらってよいです。

#何秒ごとにRippleAPIを呼び出すか
RIPPLE_WAIT_SECS   = 300

#何回RIppleAPIを呼び出すか。
RIPPLE_NUM_OF_GET_TOTALDROPS = 10

# ---------

# パラメータのCSVファイル名
RIPPLE_PARA_CSV_FILENAME = "ripple_para.csv"
# totalDropsデータ、差分の入ったcsvファイル名
RIPPLE_DROP_CSV_FILENAME = "ripple_drops.csv"
# 今回作ったget_totalDrops.jsの実行コマンド操作
RIPPLE_API_COMMAND = "./node_modules/.bin//babel-node get_totalDrops.js"
# 改行コード定義
RIPPLE_CR = "\r\n"

# RippleAPIのgetLedgerを呼び出します。数値で戻ります。
def get_totalDrops():
    return long(commands.getoutput(RIPPLE_API_COMMAND))

# 面倒なのでｗ、クラス化しました。
class RippleTotalDros:

    def __init__(self):
        self.total = long(0)
        self.diff = long(-1)
        self.start_time = ""
        self.drop_csv = ""
        self.para_csv = ""

    # 現在時刻ベースに文字列生成
    # need_seconed = True  例: 2016/8/15 12:27:5
    # need_seconed = False 例: 2016/8/15 12:27
    def get_date_time(self, need_second):
        d = datetime.datetime.today()
        text = str(d.year) + "/" + str(d.month) + "/" + str(d.day)
        text += " " + str(d.hour) + ":" + str(d.minute)
        if (need_second == True):
            text += ":" + str(d.second)
        return text

    # getLedgerの戻り値（totalDrops）から差分を算出
    # メンバ変数にそれぞれ保存
    def calc_drop_data(self, result):
        if self.diff >= 0:
            self.diff = self.total - result
        else:
            self.diff = 0
        self.total = result

    # パラメータ設定用CSVをメンバ変数 para_csvに作成
    def make_para_csv(self):
        self.start_time = self.get_date_time(False)
        self.para_csv = "time, by" + RIPPLE_CR
        self.para_csv += self.start_time + "," + str(RIPPLE_WAIT_SECS) + RIPPLE_CR

    # RippleAPIを呼び出して、データ収集する
    # APIを呼び出して、その結果を基にデータのCSVデータを作成します。
    # この処理は指定された時間×指定した回数かかるので、時間かかります。
    def collect_totalDrops(self):
        self.drop_csv = "total, diff" + RIPPLE_CR
        print "start to correct " + self.start_time
        for i in xrange(RIPPLE_NUM_OF_GET_TOTALDROPS):
            result = get_totalDrops()
            self.calc_drop_data(result)
            date_time = self.get_date_time(True)
            print i , " :" + date_time + " total=" , self.total , " diff=" , self.diff
            self.drop_csv += str(self.total) + "," + str(self.diff) + RIPPLE_CR
            if i < RIPPLE_NUM_OF_GET_TOTALDROPS - 1:
                time.sleep(RIPPLE_WAIT_SECS) # 最後だったら待たずに抜けます。

    # 作成されたCSVファイルをファイルにします。
    def write_csv(self):
        f = open(RIPPLE_PARA_CSV_FILENAME, "w")
        f.write(self.para_csv)
        f.close()
        f = open(RIPPLE_DROP_CSV_FILENAME, "w")
        f.write(self.drop_csv)
        f.close()


# --- メイン
print "Ripple Collet totalDrops."
ripple = RippleTotalDros()
ripple.make_para_csv()          # パラメータCSVデータ作成
ripple.collect_totalDrops()     # データ収集＆CSVデータ作成
ripple.write_csv()              # CSVをファイルに書き込み
print "Complete. csv -> " + RIPPLE_DROP_CSV_FILENAME
```

スキル低い人が書いたので、是非はともかくｗ、コメントなども参考にしつつ見てもらえれば、内容は分かるかと思います。この例では5分おきに10回ほど実行します。

ちょっと前にも書きましたが、get_totalDrops.jsを同じフォルダにおき、node_js＆rippleAPI　と　python　のどちらも実行できる環境がある事が前提になりますので、注意して下さい。このPythonを実行すると、以下のようになります。

```bash
$ python ./get_totalDrops.py 
Ripple Collet totalDrops.
start to correct 2016/8/21 13:7
0  :2016/8/21 13:8:1 total= 99997222541580190  diff= 0
1  :2016/8/21 13:13:33 total= 99997222534612330  diff= 6967860
2  :2016/8/21 13:19:6 total= 99997222528462961  diff= 6149369
3  :2016/8/21 13:24:36 total= 99997222523885100  diff= 4577861
4  :2016/8/21 13:30:4 total= 99997222514133479  diff= 9751621
5  :2016/8/21 13:35:36 total= 99997222505328424  diff= 8805055
6  :2016/8/21 13:41:7 total= 99997222500019477  diff= 5308947
7  :2016/8/21 13:46:37 total= 99997222496816135  diff= 3203342
8  :2016/8/21 13:52:5 total= 99997222493532687  diff= 3283448
9  :2016/8/21 13:57:35 total= 99997222488190979  diff= 5341708
Complete. csv -> ripple_drops.csv
```

更に、ripple_para.csvとripple_drops.csvという２つのCSVファイルが出来ます。

# Rで結果を確認

私はPythonもほぼ素人ですが、Rはもっと素人です（何この日本語ｗ）。今回は有意な解析というより、可視化できるかの確認という意味合いが強いです。コードを作成するにあたり、以下、奥村様のサイトを参考にさせていただきました。

https://oku.edu.mie-u.ac.jp/~okumura/stat/timeseries.html

__plotするためのデータ作成__ 

plotの元データはこうやって作ってみました。

```r
ripple.para = read.csv("ripple_para.csv")
ripple.by_csv = as.numeric(ripple.para[1,2])
ripple.start_time = as.character(ripple.para[1,1])

ripple.drops = read.csv("ripple_drops.csv")
ripple.matrix <- as.matrix(ripple.drops)

base_time = as.POSIXct(ripple.start_time)
time_scale = seq(base_time, by=ripple.by_csv,  along.with=ripple.matrix[,2])
```

要はcsvを読み込んで、そのデータをマトリックス化。時間を横軸にするので、その調整をした感じです。

__totalDropsの可視化__

totalDropsのplotはこんな感じ。totalDrpos, diff なのでy軸は[,1]となります。

```r
plot(time_scale, ripple.matrix[,1], type="o", pch=16, xlab="time(sec)", ylab="total drops")
```

結果はこうなります。
![totalDrops.PNG](https://qiita-image-store.s3.amazonaws.com/0/129334/a9bf2ff5-c8e6-b4f1-9f89-cbd05f791ed0.png)

だんだん数値が落ちています。

__差分の可視化__

差分のplotはこんな感じ。totalDrpos, diff なのでy軸は[,2]となります。

```r
plot(time_scale, ripple.matrix[,2], type="o", pch=16, xlab="time(sec)", ylab="drops")
```

結果はこう。
![diff.PNG](https://qiita-image-store.s3.amazonaws.com/0/129334/f74ab8e3-ef1e-f7c4-cdc2-70bc3ffc7b25.png)
差分なので動いているという感じになりました。


# ライセンス

以下使わせて戴きました。素晴らしいソフトウェアを提供して下さり、ありがとうございます。

* （一応書きます…）上記のコードはパブリックドメインとします。当然ですが使用した際の損害は誰も請け負ってくれません。そこだけ注意で。
* Python自体は[PSF (Python Software Foundation)ライセンス](https://www.python.org/download/releases/2.5/license/)です。
* ↑の情報は[WikipediaのPython](https://ja.wikipedia.org/wiki/Python#.E3.83.A9.E3.82.A4.E3.82.BB.E3.83.B3.E3.82.B9)がソースです。
* Node.jsのライセンスは[ここ](https://raw.githubusercontent.com/nodejs/node/master/LICENSE)。[WikiPedia](https://ja.wikipedia.org/wiki/Node.js)によるとMITライセンスな模様。
* RippleAPIのライセンスですが、おそらく[これ](https://github.com/ripple/ripple-lib/blob/develop/LICENSE)ではないかと思います。 えっASIS？
* Rのライセンスは[ここ](https://www.r-project.org/Licenses/)。原則はGPL系と理解しています。

以上です。