【本文】
Ethereumのブロックチェーン基盤をベースにしてDApps(Decentralized Apps:分散型アプリケーション)開発の初歩について実例を挙げて紹介します。
【今回利用した開発環境等】
(※バージョンは割愛)
- OS: Lubuntu(Ubuntuの軽量版)
- ブロックチェーン基盤: geth
- スマートコントラクト: Solidity
- フロント画面: node.js(+ web3のライブラリ)
【事前準備】
gethやSolidity及びnode.jsはインストール済みであること。
スマートコントラクトのプログラムコードは、前回紹介したサンプル「Etherum Browser-Solidityを利用したコントラクト開発・デプロイ・実行」(Qiita投稿)をそのまま流用。(※但し、イベント関数を追加。)
デプロイ(ブロックチェーンに登録)後、コントラクト・アドレスが発行されるので、abi情報と共に控えておく。
<test.sol>
(説明)
- set_numメソッドで適当な値(int)をブロックチェーン上にセットし、 get_numメソッドでセットした値を取り出す。
- event関数を利用し、set_numメソッド実行状態をイベント監視。(今回追加)
test.solpragma solidity ^0.4.0; contract test { event ev_set_num(int num); int a; function set_num(int num){ a = num; ev_set_num(num); } function get_num() returns(int){ return a; } }
- node.jsの開発環境を作成し、web3のライブラリをインストール。(node.jsのインストール方法については割愛。)
> npm install web3
【DApps開発】
- DApps画面アプリの本体となるモジュールを作成。
exec_set_or_get_num.js//box-logs var box_logs_pending = document.querySelector("#box_logs_pending"); var box_logs_complete = document.querySelector("#box_logs_complete"); var addr_contract = "0x4b77c9f54eaa1f444460e6d92018c6e87aee2c10"; var abi = [{"constant":false,"inputs":[],"name":"get_num","outputs":[{"name":"","type":"int256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"num","type":"int256"}],"name":"set_num","outputs":[],"payable":false,"type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"num","type":"int256"}],"name":"ev_set_num","type":"event"}]; var obj_contract = web3.eth.contract(abi).at(addr_contract); //example:address var addr = document.querySelector("#addr"); var addr_all = String(web3.eth.accounts); var addr_array = addr_all.split(","); var addr_len = addr_array.length; for (i = 0; i < addr_len; i++) { document.getElementById("addr").innerHTML += "address[" + i + "]: " + addr_array[i] + "<br>"; console.log(addr_array[i]); } //set_num var btn_set_num = document.querySelector("#btn_set_num"); let input_from; let input_pw; let input_set_num; btn_set_num.onclick = function() { input_from = document.querySelector("#input_from").value; input_pw = document.querySelector("#input_pw").value; input_set_num = document.querySelector("#input_set_num").value; //Unlock address web3.personal.unlockAccount(input_from,input_pw,600); //Send Transaction let response_setnum = obj_contract.set_num.sendTransaction(input_set_num, {from:input_from, gas:1000000} ); console.log("response_setnum: ", response_setnum); } //get_num var output_get_num = document.querySelector("#output_get_num"); var btn_get_num = document.querySelector("#btn_get_num"); btn_get_num.onclick = function() { let response_getnum = obj_contract.get_num.call(); output_get_num.innerHTML = response_getnum; } /* イベント監視 */ //送信 var txhash; var tx_info; var filter = web3.eth.filter("pending"); //resultにはblock hashが返される。 filter.watch(function(error, result) { txhash = result; tx_info = web3.eth.getTransaction(txhash); box_logs_pending.innerHTML += "tx: " + txhash + "<br>" + "(送信元アドレス): " + tx_info.from + "<br>"; }); //完了 var send_addr; var event = obj_contract.ev_set_num(); event.watch(function(error,result) { var send_addr = web3.eth.getTransaction(result.transactionHash).from; box_logs_complete.innerHTML += "tx: " + result.transactionHash + " :[状態] complete" + "<br>" + "[完了情報] " + "(ブロックNo.): " + result.blockNumber + "(送信元アドレス): " + send_addr + " (入力値); " + result.args.num + "<br>"; });
- web3インスタンスの生成とセットアップファイルを作成。(本体に直接定義してもよい。)
connect_web3.js//web3インスタンスの生成とセットアップ var web3 = require("web3"); var web3 = new Web3(); web3.setProvider(new web3.providers.HttpProvider("http://localhost:8545"));
- DAppsアプリのフロント画面となるindex.html及びスタイルシートの作成。(スタイルシートは割愛。)
index.html<!doctype html> <html lang="ja"> <head> <meta charset="utf-8"> <title>testdapps</title> </head> <body> <h2>testdapps</h2> <!--アドレスを取得--> <div class="form-group"> <label for="get_Address">[アドレス(テスト用)]:</label> <br> <div id="addr"></div> </div> <!--値をセット--> <div class="form-group"> <!--コントラクト実行アカウントを入力--> <br> <label for="input_from">[送信用アドレス]:</label> <br> <input type="text" name="input_from" id="input_from" class="form-control"> <br><br> <!--パスワードを入力--> <label for="input_pw">[パスワード(送信用アドレス)]:</label> <br> <input type="password" name="input_pw" id="input_pw" class="form-control"> <br><br> <!--値を入力--> <label for="input_set_num">[入力値]:</label> <br> <input type="text" name="input_set_num" id="input_set_num" class="form-control"> <br> </div> <br> <div class="form-group"> <button id="btn_set_num" onclick="input_set_num();">送信ボタン</button> </div> <br> <!--値を出力--> <div class="form-group"> <label for="output_get_num">[出力値]:</label> <pre id="output_get_num"></pre> <button id="btn_get_num">取得ボタン</button> </div> <br> <!-- ログ出力 --> <div class="form-group-box_logs"> <label for="box_logs">ログ情報(送信):</lavel> <pre id="box_logs_pending"></pre> <label for="box_logs">ログ情報(完了):</lavel> <pre id="box_logs_complete"></pre> </div> <!--参照先scriptを記載--> <script type="text/javascript" src="../node_modules/web3/dist/web3.min.js"></script> <script type="text/javascript" src="../lib/connect_web3.js"></script> <script type="text/javascript" src="./javascripts/exec_set_or_get_num.js"></script> <!--スタイルシート--> <link rel="stylesheet" type="text/css" href="./stylesheets/index_style.css"> </body> </html>
- 出来上がりは以下のような画面となる。(デザインセンスはないので。。。)
【DApps画面の実行確認】
- [送信用アドレス]欄に、任意のアドレスを入力、[パスワード]欄に[送信用アドレス]欄に入力したアドレスのロック解除パスワードを入力。[入力値]欄に任意の整数値を入力。
- 送信ボタンを押下後、ログ情報(送信)欄にpending状態のコントラクト実行トランザクション情報が出力される。
- マイニングが実行され、トランザクションがブロックチェーンに取り込まれると、ログ情報(完了)欄に、コントラクト実行時のトランザクションハッシュ、格納先のブロック番号、送信者アドレス、入力値の情報が出力される。
- 実際にブロックチェーンに取り込まれているか、ブロック番号(1307)を基にgethコンソール上で確認。
> eth.getBlock(1307)
- transactionsのトランザクションハッシュ値とDAppsアプリ画面上の出力結果(tx)が一致しているのでブロックチェーンに取り込まれていることが分かる。
transactions: ["0x8a487e4f2521446f016650577d794026bb63438560d51cef14139885748b7c44"]
- 更に詳細情報を知りたい場合、トランザクションハッシュ値を基にgethコンソール上で以下のコマンドを発行する。
> eth.getTransaction("0x8a487e4f2521446f016650577d794026bb63438560d51cef14139885748b7c44")
- トランザクションの中身を覗くと、input欄が以下のようになっている。入力値は「500」なので、input欄に示された「1f4」(16進数)を10進数に変換すると「500」となり一致する。
input: "0x545a48d000000000000000000000000000000000000000000000000000000000000001f4"
- DAppsアプリ画面の取得ボタンを押下すると、[出力値]欄に「500」が出力される。
以上。
【後記】
今回は、DAppsを初めて作成したので、コントラクト等処理は極単純なものにしておきました。
ブロックチェーン基盤(geth)とフロント画面をweb3ライブラリを使用して連携させる際、pending状態のトランザクションやブロックに格納されたことを検知するイベント監視の仕方をある程度習得できたのは良かったです。
(※未だ分からない部分も多いですが。。。)
DApps開発をする中で色々調べている間に、他にも興味深いライブラリや処理があったので、何れ紹介できればと思います。