Advent Calendar 2018 3日目です。NEMとQtです。
NEMBERをどうやったら増やせるのかをなんとなく考えているkihakuです。
ざっくり2つの方法があるのかなぁ、と思っています。
1つはNEMを意識して使わせる、1つはNEMを意識させずに使わせる、です。
前者は、NEMだからこそ、みたいなもので、アプリやプラットフォームを作る人や提供する人が「○○したいから、NEMが最適だ」みないな判断で使うケースです。
後者は、NEMBERと自分でも認識しないケースで、前者のアプリやプラットフォームを使う人です。なんか使ってたら、実はそれってNEMだった、みたいなケースですね。
今回は前者の「NEMを意識して使わせる」をやります。
どうやってNEMを採用してもらうかですが、手っ取り早い手段の1つが、設計者が使いやすいようになっていること、です。
これはNEMのウリの1つだと思うのですが、公式/有志問わずいろいろなSDK、ライブラリが提供されています。(といいつつ他のblockchainは知らないですが。。。)
https://nem.io/developers/#panel-w599b86f61d6d1-0-0-2
というわけで、今回は、QtでNEMに触ってみようと思います。
本業でここ1ヶ月くらい触る機会があったので、Qtにしてみました。QtとNEMのクロスドメインです。
1.ノードの確認
2.アドレス情報の確認(残高確認など)
3.アドレスのトランザクションの確認(取引履歴など)
送金は、時間の都合上、諦めました。(機会があれば次回に)
#NEMとは(for Qt User)
いわゆる仮想通貨・暗号通貨の1つです。
が、個人的には”仮想通貨”って思うよりも、デジタルデータを扱うためのオープンプラットフォームというほうがしっくりきます。(ここ大事)
広域的な特徴は他の仮想通貨等が1つ、これに加えて、公証(アポスティーユ認証)や、複数人署名(Multisig)があります。
次のバージョン(Cutapalt)では、アグリゲートトランザクション(複数のトランザクションをまとめて処理)なども目玉ですね。
日本のコミュニティやNEMBER(ざっくりNEMユーザー)の活動が活発です。
#Qtとは(For NEMBER)
マルチプラットフォーム対応のフレームワークです。
日本では採用事例が少ないようですが、Skypeだったり、メルセデスのコックピットディスプレイに採用されていたりします。
ツールチェーンを使用することでWindows、iOS、Android、Linux等なんでもござれです。
有償版と無償版がありますが、無償版はオープンソース(基本的にGPL\LPGL)なので要注意です。
(企業内で使うなら、あまり気にしなくていいので使いやすいかもです)
Qtは特にUIフレームワークが強く、QtQuick(QML)と呼ばれるXML+JavaScriptが使えるのが特徴です。ただ、JavaScriptは、最近のフロントエンド界隈のNode.jsをベースとしたライブラリは基本使えないと思ったほうが良いです。言語としてのシンプルなjs(?)をイメージしておくといいでしょう。
QtWidgetというUIフレームワークもありますが、今から触るならQtQuick一択だと思います。
いわゆるコントロール層やモデル層はC++になります。
#NEMにどうやって触るか
まず触ろうとするレイヤですが、タイトルの通りQtQuickを対象とします。
ネイティブアプリとして考えるなら、本来はC++のほうが筋だと思います。が、人がやらないところをやる。
というわけで、QtQuickからはNEMのNIS APIでHTTPリクエスト投げます。
NEM NIS API公式:https://nemproject.github.io/
日本語訳:http://i-yusuke.com/entry/nem-nis-api-document-japanese/
QtQuick QMLtype一覧:https://doc.qt.io/qt-5.11/qmltypes.html
#0.アプリケーションの大枠
さっそくですが、今回のアプリケーションの大まかなつくりは、以下になります。
・メニューは左側(Window幅の左から4割)
・ボディが残り、ボディにはトランザクションの結果のJSONをそのまま出力する
Windowに対して、Rectangleで仕切ります。
お作法のようなものですが、Windows全体->各パーツとRectangleで区切ることが多いようです。
これだけだと真っ黒のWindowが表示されるだけです(背景黒は個人的な趣味です)。
尚、QtQuickやQtそのものの環境構築手順や、ビルド/実行手順はここでは記載しません。
(が、癖があるのに、ネットに情報が少ないので、何かあればお気軽にTwitterのDMででも、聞いてください。)
import QtQuick 2.11
import QtQuick.Controls 2.2
import QtQuick.Window 2.11
Window {
id:_root
visible: true
width: 1280
height: 800
title: qsTr("NEM By Qt")
Rectangle{
id:_bg
anchors.fill:parent
color:"black"
Rectangle{
id:_leftMenu
anchors.margins: 8
anchors.top:parent.top
anchors.left:parent.left
anchors.bottom:parent.bottom
width:parent.width * 0.3
color:"black"
}
Rectangle{
id: _body
anchors.top:parent.top
anchors.left:_leftMenu.right
anchors.right:parent.right
anchors.bottom:parent.bottom
width:parent.width - _leftMenu.width
color:"black"
}
}
}
id
:オブジェクトにユニークな名前です。このidを指定してアクセスすることが多いです。
anchors
:配置の相対位置を決める。例えばanchors.top:parent.top
であれば、対象のオブジェクトの上部を親オブジェクトの上部とバインドする、感じです。anchors.fill
は全ての辺を一致させるプロパティです。
次に、ボディのログ表示部分を作ります。
ただ垂れ流すだけの機能にします。
_body
オブジェクト内を以下のように記述します。
Rectangle{
id: _body
anchors.top:parent.top
anchors.left:_leftMenu.right
anchors.right:parent.right
anchors.bottom:parent.bottom
width:parent.width - _leftMenu.width
color:"black"
Text{
id: _text
anchors.fill:parent
anchors.centerIn: parent
color:"white"
font.pixelSize: 18
font.family:"Meiryo UI"
wrapMode: Text.Wrap
function add(t){
text = t + text;
}
}
}
_body
と同じ領域にText(文字列表示)のオブジェクト設定しています。
add(t)
でt
に入力された文字列を既存の文字列の先頭に追加しています。新しいものが先に表示されます。(参考:QML(Qt)のドラッグ&ドロップはQt5で簡単に!)
wrapMode
:行端での折り返しの設定です。
次に、左メニューに配置するボタンを作っておきます。
ファイル名(ここではRequestButton)がクラス名になります。
QMLにはButtonクラスも用意されていますが、個人的に使いづらいのでMouseAreaとRectangleで自作することが多いです。
import QtQuick 2.11
import QtQuick.Controls 2.2
MouseArea {
id:_root
width:200
height:40
property alias label:_label.text
Rectangle{
id:_bg
anchors.fill:parent
color:"black"
border.width:2
border.color:"white"
}
Text{
id:_label
anchors.fill:parent
anchors.centerIn: parent
color:"white"
font.pixelSize: 18
font.family:"Meiryo UI"
text: "default"
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
}
MouseArea
:ボタンにするためにはマウスイベントが必要です。マウスに反応するようにするために、このMouseArea
のクラスとして継承します。Rectangle->MouseArea
と構成してもよいですが、呼び出し元でMouseEventを定義しようとすると、MouseArea->Rectangle
としておいたほうが楽ちんです。(参考:QML(Qt)でボタンを作ろう!)
property
:新しい任意のプロパティ(メンバ)の追加。alias
指定した場合には、このプロパティに指定する値は指定先のプロパティに設定されます。ここではRequestButton
のlabel
に"ぼたんのらべる"と指定すると、_label.text
にも"ぼたんのらべる"が設定され、Textとして表示されるようになります。
ざっくり、これで準備ができたので、NEMにアクセスしていきます。
#1.ノードの確認
HTTPリクエストを投げるにもノードが動いているか確認しないことには始まりません。
NEMにはMAINNET(本チャンのPublic Chain)、TESTNET(テスト用)、MIJINNET(Private Chain)の3種類があります。
公開するときには、MAINNETにしなければならないですが、とりあえず触るだけなら、TESTNETでいいでしょう。
NEMのノードは世界中にあります。
ここ(MAINNET、TESTNET)で確認しましょう。今回はhttp://150.95.145.157:7890/
を使わせていただきます。
ノードの確認は/heartbeat
を用います。
httpでGETリクエストを投げる関数を用意しておき、そこにURLとAPIを突っ込みます。
import QtQuick 2.11
import QtQuick.Controls 2.2
import QtQuick.Window 2.11
Window {
id:_root
visible: true
width: 1280
height: 800
title: qsTr("NEM By Qt")
property string testneturl:"http://150.95.145.157:7890/"
Rectangle{
id:_bg
anchors.fill:parent
color:"black"
Rectangle{
id:_leftMenu
anchors.margins: 8
anchors.top:parent.top
anchors.left:parent.left
anchors.bottom:parent.bottom
width:parent.width * 0.3
color:"black"
RequestButton{
id:_heartbeat
label:"heartbeat"
width:parent.width-2
anchors.top:parent.top
anchors.horizontalCenter: parent.horizontalCenter
onClicked: {
_root.getData("/heartbeat");
}
}
}
Rectangle{
id: _body
anchors.top:parent.top
anchors.left:_leftMenu.right
anchors.right:parent.right
anchors.bottom:parent.bottom
width:parent.width - _leftMenu.width
color:"black"
Text{
id: _text
anchors.fill:parent
anchors.centerIn: parent
color:"white"
font.pixelSize: 18
font.family:"Meiryo UI"
wrapMode: Text.Wrap
function add(t){
text = t + text;
}
}
}
}
function getData(apipath) {
var xmlhttp = new XMLHttpRequest();
var url = _root.testneturl + apipath;
var response = "";
xmlhttp.onreadystatechange=function() {
if (xmlhttp.readyState === XMLHttpRequest.DONE && xmlhttp.status == 200) {
response = xmlhttp.responseText;
print("response in :"+response); // log
_text.add("api:" +apipath + "\n" + "response:"+response+"\n")
}
}
xmlhttp.open("GET", url, true);
xmlhttp.send();
}
}
function getData(apipath)
:本来であれば、Promiseなどを用いて実装したほうが良いと思いますが、私がまだ使ったことがありませんので、xmlhttp.onreadystatechange
で直接テキスト出力しています。
print()
:IDE側の出力ウインドウへの出力をさせています。デバッグは基本これでログ出力させます。
無事確認できました。
#2.アドレス情報の確認(残高確認など)
まず、アドレスの入力が必要になります。
アドレスもNIS APIで作れますが、手っ取り早く公式のNanoWalletで作っちゃいましょう。
こちらを参考にアドレスを作り、TESTNET用のXEMを確保します。
頭文字がT
のアドレスが生成されます。
_body
直下の_heartbeat
の記述の下に以下を追加します。
Rectangle{
id:_addressinput
width:parent.width-2
height:30
anchors.topMargin: 16
anchors.top:_heartbeat.bottom
anchors.horizontalCenter: parent.horizontalCenter
color:"lightgray"
border.width:1
border.color: "lightgray"
TextField{
id:_addresstext
anchors.fill:parent
placeholderText: qsTr("Enter address")
color:"black"
font.pixelSize: 12
font.family:"Meiryo UI"
selectByMouse:true
}
}
_addresstext
:実際の入力用フィールド。_addresstext.text
に入力した値が保持されます。
ついでに、アドレス情報取得用のボタンを追加します。
RequestButton{
id:_getad
label:"Get address data"
width:parent.width-2
anchors.margins: 8
anchors.top:_addressinput.bottom
anchors.horizontalCenter: parent.horizontalCenter
onClicked: {
_root.getData("/account/get?address="+_addresstext.text);
}
}`
残高は、この中のbalance
の値を1,000,000で割った値が、XEMになります。ここでは、balance
が8950000ですので、8.95XEMが残高になります。
#3.アドレスのトランザクションの確認(取引履歴など)
取引を含め様々なリクエストをトランザクションといいます。
転送(いわゆる送金)や設定の変更などなど、いろいろなトランザクションがあります。
送信のみ、受信のみ、未承認(ブロックチェーンに刻まれる前段階)などでも取得できます。
ここでは、指定のアドレスのすべてのトランザクションを表示します。
_body
直下の_getad
の記述の下に以下を追加します。
RequestButton{
id:_gettx
label:"Get tranxaction"
width:parent.width-2
anchors.margins: 8
anchors.top:_getad.bottom
anchors.horizontalCenter: parent.horizontalCenter
onClicked: {
_root.getData("/account/transfers/all?address="+_addresstext.text);
}
}
heartbeat、account/get、account/transfersを実行した結果:
#まとめ
今回はQtQuick(QML)上のみで、NEMに触ってみました。
NEMBERにはネイティブアプリがXML+JavaScriptでサクッと作れる感じ、QtユーザーにはNEMへのアクセスの第一歩が思ったより簡単と思えていただけたらよいです。
それぞれのドメイン(領域)内では、知識や経験等のオープン化があってもクロスする領域でオープンになっていることが少ないと感じています。
今後もこういった、あまり関係なさそうなクロスドメインで「異分野への第一歩のハードル」を下げることができたら、と思います。
この記事はnemlogにも転記しています。
転記先(QtQuickでNEMに触れてみる))。