今回はNEMのトランザクションを発行してみます。
(最後にXEMBook-sdkを利用した送金サンプルプログラムを載せています。HTML1ファイルをブラウザで開くだけで実行できるのでぜひお試しください。)
トランザクション発行で一番難しいのは何でしょうか?
署名やブラウザ依存を吸収するAPIコールなどは、既存のライブラリが充実しているので実は簡単です。もっとも難しいのはNEMのノードに伝えたいデータをNEMのわかる言葉に詰め込みなおすシリアライズという作業です。こればかりは外部ライブラリを利用することはできず、NEMのコミュニティが公開するライブラリを使用するしかないのです。
そのライブラリがまたその他のライブラリに依存して。。。とかなるとそのプラットフォームへの敷居はぐんと上がります。私は基本的にシリアライズする部分だけを提供するライブラリがあってもいいのではないかと思います。将来的に公開予定のxembook-sdkもそういったコンセプトで開発を進めています。
では、NEMに伝えたいデータセットをどうやって作り上げていくのかを見てみましょう。
let data = {
'type': 0x101,
'version': 0x68000000 | 1,
'signer': SENDER_PUBLIC_KEY,
'timeStamp': timeStamp,
'deadline': timeStamp + due * 60
};
let custom = {
'recipient': RECIPIENT_ADDRESS,
'amount': amount,
'fee': totalFee,
'message': message,
'mosaics': mosaics
};
let entity = $.extend(data, custom);
let result = serializeTransaction(entity);
let kp = KeyPair.create(SENDER_PRIVATE_KEY);
let signature = kp.sign(result);
let obj = {'data':ua2hex(result), 'signature':signature.toString()};
return $.ajax({
url: NEM_ENDPOINT ,
type: 'POST',
contentType:'application/json',
data: JSON.stringify(obj)
});
data と custom というデータオブジェクトを結合して entity オブジェクトを作成しています。dataはどんなトランザクションにも共通して必要な基本情報、customが発生させるトランザクションによって変化するデータです。今回は単純な送金トランザクション(0x101)を作成します。
次に、entityオブジェクトをシリアライズ化しresultという配列に詰め込みます。
ここがNEM独自の仕様になります。NEMではどんな言語でも開発できる、と言いますが様子するにこのシリアライズという作業によって言語に依存したデータフォーマットを共通フォーマットに書き直しているのです。
一方で送信者の秘密鍵からキーペアを作成し先ほどのresultを署名します。署名についてはed25519に対応しているライブラリを探す必要があります。今回はnacl-fast.jsというライブラリを少し修正して使用しました。
最後にjsonオブジェクトとしてresultを16進数に変換したものと署名を詰め込んでNEMのノードにPOSTします。今回はjQueryを利用しました。
これでNEMのノードが理解できるデータとして取り扱ってもらうことができます。
結果としてSUCCESSが帰って来ればトランザクションのプッシュに成功です。どんなな額の送金でも行えることになります。
プログラミングに慣れ親しんだ人であればそれほど難しくないということが分かっていただけたでしょうか?ブロックチェーンはすごくハイレベルな人じゃないと使いこなせないような印象がありますが、とくにNEMは誰にでも使ってもらえるように非常に洗練された設計により作り上げられています。
ぜひ、みなさんの使っているシステムにも「+ブロックチェーン」してみてください。
追記:
最後にjsで完全に動作するスクリプト例を挙げておきます。
XEMBook-sdk は20190420のスナップショットを利用しています。
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>送金テスト</title>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.min.js"></script>
<script src="https://s3-ap-northeast-1.amazonaws.com/xembook.net/xembook-sdk/snapshot/20190420/xembook-sdk.js"></script>
<script src="https://s3-ap-northeast-1.amazonaws.com/xembook.net/xembook-sdk/snapshot/20190420/nacl-fast.js"></script>
<script src="https://s3-ap-northeast-1.amazonaws.com/xembook.net/xembook-sdk/snapshot/20190420/xembook-keypair.js"></script>
<script>
$(function(){
var PRIVATE_KEY = "ここに送金元の秘密鍵を入力";
var sendToAddress = "NBZNQL2JDWTGUAW237PXV4SSXSPORY43GUSWGSB7";//送信先のアドレス(現在はXEMBookアカウント)
var transfer = function(){
return getNemInfo("/time-sync/network-time").then(
function(res){
let kp = KeyPair.create(hex2ua(PRIVATE_KEY));
let signer = kp.publicKey.toString();
let timeStamp = Math.floor(res.receiveTimeStamp/1000);
let amount = parseInt(1 * 1000000, 10);
let message = {payload:utf8ToHex("Hello! NEM transfer!"),type:1};
let mosaics = null;
let entity = {
'type': 0x101,
'version': getVersion(104,1),
'signer': signer,
'timeStamp': timeStamp,
'deadline' : timeStamp + 3600,
'recipient': "NBZNQL2JDWTGUAW237PXV4SSXSPORY43GUSWGSB7",
'amount': amount,
'fee': getFee(amount,message,mosaics),
'message': message,
'mosaics': mosaics
};
let tx = serializeTransaction(entity);
return postNemSignedInfo("/transaction/announce",tx,kp.sign(tx));
}
);
}
function doTransfer(){
var result = confirm("1XEM送金します。よろしいですか?");
if(result){
transfer().then(
function(data){
alert("送金しました");
console.log(data);
});
}
}
$("#btnTest").click(function(){doTransfer();return false;});
//Keypair外で秘密鍵を扱うための臨時ロジック
var hex2ua = function hex2ua(hexx) {
var hex = hexx.toString(); //force conversion
var ua = new Uint8Array(hex.length / 2);
for (var i = 0; i < hex.length; i += 2) {
ua[i / 2] = parseInt(hex.substr(i, 2), 16);
}
return ua;
};
});
</script>
</head>
<body>
<h1>送金テスト</h1>
<button id="btnTest">送金</button>
</body>
</html>