簡単なEtherのwalletを作る(1)
Last updated
Last updated
MeteorによるDapp開発の第一歩として、Ethereumの組み込み通貨であるEtherをアカウント間で送金することができる簡単なwalletアプリ「simple-ether-wallet」を作成してみます。
本章で説明するサンプルアプリのソースコードはGitHub上に公開しています。
今回作成するアプリは、アカウント情報とEthereumネットワーク情報を表示する「ダッシュボード」ビューと、Etherの送金と過去の送金履歴の表示を行う「送金」ビューの2つで構成されます。各情報項目は、ユーザーがブラウザ上で再読み込み等の特別な操作をすることなくEthereumネットワーク上での最新の情報が自動的に更新されていくリアクティブな動きをします。
本節では「ダッシュボード」ビューの部分を実装することで、Meteorの使い方とEthereum関連のパッケージの使い方に慣れることを目標にします。「送金」ビューについては次節以降で実装していきます。
【simple-ether-walletのダッシュボードビュー】
【simple-ether-walletの送金ビュー】
まず下準備として、今回作成するwalletからの接続を受けるように下記のコマンドでgethを起動しておきます。ここではネットワークIDが10のテストネットに接続しています。本格的にDappを公開するまではテストネットにて動作を確認するほうが良いでしょう。
上記コマンドは幾つか新しいコマンドオプションを追加しています。今回作成するDappとノードの連携はGethのRPC(Remote Procedure Call)のAPI機能を利用するのでその設定をコマンドオプションで行っています。
--rpc
:gethのRPCサーバとしてのAPIを有効化します。
--rpcaddr "192.168.5.6"
:読者の環境に合わせてgethノードのIPアドレスを指定します。ローカル環境で試験を行うなら、"127.0.0.1"か"localhost"を指定することも可能です。
--rpcport "8545"
: RCP APIのポート番号を指定します。(特に問題なければデフォルトの8545を指定すればよいです。)
--rpccorsdomain "*"
: クロスドメインアクセスを許可するドメイン。ここでは任意のドメインを許可しています。
また、以下のオプションも加えています。
--mine
:gethの起動と同時に採掘を開始するオプション
--unlock 0xa7653f153f9ead98dc3be08abfc5314f596f97c6"
: 指定されたアドレスのアカウントのロックを解除します。読者の環境に合わせて、coinbaseのアドレスを指定してください。(起動時にパスワードが求められます。)
■■ TIP ■■
gethのconsoleを利用せずバックグラウンドでgethを起動しておくと毎度gethを起動する手間が省けて便利です。その場合は 1. 上記コマンドのconsole
のオプションを外したコマンドを実行 2. プロンプト上でパスワードを聞かれたら、入力。 3. [Ctrl]+[Z]キーを押下しプロセスを一時停止 4. bg
コマンドを実行し、プロセスをバックグラウンド実行に移行
の手順で行えばよいでしょう。
Meteorをインストールした環境で適当なディレクトリに、今回作成するsimple-ether-walletのMeteorプロジェクトを作成します。
「Meteorを使ってみる」節と同様に、この初期状態のWebアプリで念のためアクセス可能かを確認してみます。上記コマンドを実行して新しく作成されたsimple-ether-wallet
ディレクトリ(以下、プロジェクトRoot)に移動して
コマンドを実行します。実行するとしばらくしてコンソールに=> App running at: http://localhost:3000/
と表示されるのでWebブラウザでアドレスに「http://localhost:3000/ 」と入力しアクセスします。すると下図のような(単純な)Webアプリケーションが表示されます。
起動して画面が表示されることが確認できたら、デモ用の余分なコードを削除して行きます。まず、プロジェクトRoot直下のserver
ディレクトリを内部のファイルごと削除します。また、client
ディレクトリ内のmain.js
内の記述を全て削除し空ファイルの状態にします。そしてmain.html
ファイルは下記のコードに書き換えます。
main.html
View this Commit On GitHub (Tag:"Step001")
■■ Meteor TIP ■■
Meteorにはプロジェクト内のディレクトリ名には下記のルールがあります。
server
ディレクトリ以下のファイルは、サーバサイドのみで実行されます。(今回作成するwalletでは全てクライアント(ブラウザ)側で処理を行い、サーバ側の処理を行わないためserver
ディレクトリごと削除しました。)
client
ディレクトリ以下のファイルはクライアントサイド(ex.ブラウザ上)のみで実行されます。
プロジェクト直下のファイル、および、上記以外のディレクトリ以下のファイルはサーバサイドとクライアントサイドの両方で実行されます。
また、静的なコンテンツ、例えば画像データやフォントデータ等はpublic
ディレクトリに配置されるのが慣習です。
Meteorには標準の機能以外の拡張機能をパッケージとしてインストールすることで様々な機能が追加可能です。Ethereumへの接続も、拡張機能としてパッケージを導入することで容易に可能になります。ここでは今後使用する以下の4つのパッケージを追加します。
twbs:bootstrap: CSSフレームワーク「bootstrap」のパッケージ。
ethereum:web3:EthreumノードとRPC接続するためのライブラリが含まれるパッケージ。
ethereum:accounts:ethereum:web3パッケージのラッパーパッケージで、Ethereumのアカウント関連の情報をmeteor上でリアクティブに取得可能にするパッケージ。
ethereum:blocks:ethereum:web3パッケージのラッパーパッケージで、Ethereumのブロックチェーン関連の情報をmeteor上でリアクティブに取得可能にするパッケージ。
プロジェクトRootに移動し下記のコマンドを実行します。
■■ Meteor TIP ■■
プロジェクトに追加されたパッケージは隠しディレクトリ.meteor
以下のpackages
ファイルに自動的に記載されます。実際に今回追加した4つのパッケージがpackages
ファイルの末尾に追記されているのを確認してみてください。
今回追加したパッケージを利用しEthereumノードに接続します。 client
ディレクトリ以下にlib
ディレクトリを作成しその下に以下のコードを記述したinit.js
ファイルを配置します。
client/lib/init.js
この状態でWebアプリケーションを起動してアクセスしてみます。表示される画面は変わらず「Hello, world!!」が表示されますが、アクセス時にブラウザには今回追加したパッケージ及びinit.js
もロードされているためブラウザからEthereumノードにRPCでアクセスが可能になっています。Chromeの開発者ツールのConsoleを起動 し、Ethereumノードに対してアカウントリストを問い合わせる
のコマンドを実行してみます。 実行結果として
のようなアカウントの配列が返されれば、ブラウザからEthereumノードへのアクセスが成功しています。もしこのような結果が返らない場合はgethの起動とそのオプション、アドレスなどを再度確認してください。
View this Commit On GitHub (Tag:"Step002")
■■ Meteor TIP ■■
init.js
ファイルをclient/lib
以下に配置したのは、初期化の処理を今後追加されていくその他の処理よりも先に処理したい理由からです。MeteorではプロジェクトRoot以下のファイルをロードする順序として、lib
という名称のディレクトリ以下のファイルを最初に読み込むというルールがあるため、今回のinit.js
は例えばmain.html
やmain.js
よりも先にMeteorによりロードされる事になります。Meteorがファイルをロードする順序は公式ドキュメント(英語)の「File Load Order」節に詳細が記載されているので参考にしてください。
これまでの作業でブラウザからEthereumノードへの接続が可能になりました。これを利用して画面に「Node Status」項目を表示するようにしていきます。
まずは、client/main.html
とclient/main.js
ファイルを下記のコードに書き換えます。
client/main.html
client/main.js
これらのコードを追加することで下図のような、Ethereumノードの状態のテーブルが表示されるはずです。
ここでは大きく2つ、「テンプレート」と「テンプレートヘルパー」を用いてEthereumノードの状態の取得から表示までを行いました。少しこれらのコードを詳しく見ていきます。
client/main.html
はおおよそ通常のHTMLファイルの構造と同様ですが、幾つかの部分でMeteor独特の記述が現れています。これはMeteorがテンプレートエンジンとして独自の「Spacebar」を採用しており、その構文が含まれていることによります。
Spacebarには主に3種類のタグが規定されています。
1つめはInclusionsタグと呼ばれるもので、{{> xxx }}
の構文で用います。このタグが配置された場所に、xxx
の部分で指定された名前と同じname属性を持つ<template>
タグの内容を挿入する働きをします。今回のclient/main.html
の例では{{> nodeStatusComponent}}
が、<template name="nodeStatusComponent">
と</template>
に囲まれた部分の内容に置き換わることになります。
2つめは「Expressionsタグ」と呼ばれるもので{{xxx}}
の構文で用います。現在のオブジェクトの属性値か、またはすぐ後に後述するテンプレートヘルパーの関数の戻り値に置き換わる働きをします。今回の client/main.html
の例では{{isMining}}
は client/main.js
のisMining
関数の返り値に置き換わることになります。
最後は「block helpersタグ」と呼ばれるもので{{#each}}…{{/each}}
や {{#if}}…{{/if}}
のような構文で用いられるタグで、テンプレート内での処理フローを制御する働きをします。
client/main.js
にはテンプレートヘルパーが定義されています。Meteorでは表示とロジックを分離する設計がされており、表示はテンプレートが、そして表示のためのデータの取得や加工などのロジックはテンプレートヘルパーがその役割を担います。
テンプレートヘルパーはTemplate.<myTemplate>.helpers(helperObject)
の形式で定義し、ここで<myTemplate>
の部分にヘルパーが対象とするテンプレート名に置きかえます。またhelerObject
はヘルパー関数が連想配列形式で列挙されたオブジェクトになります。
今回のclient/main.js
の例では対象とするテンプレート名からTemplate.nodeStatusComponent.helpers(...)
として定義され引数として、web3
オブジェクトからノード状態のプロパティを返するため幾つかの関数が定義されています。
この後、アカウント情報とブロック情報を表示する2つのコンポーネント(accountStatusComponentとblockStatusComponent)を追加していきます。これらも先のnodeStatusComponentと同様にテンプレートとそのヘルパーをそれぞれmain.html
、main.js
に追記しても問題ありませが、ここではコンポーネント毎にソースファイルを分けて管理することで見通しを良くします。
たとえソースファイルを分けても、Meteorは自動的にclientディレクトリ以下のファイルをロードの順序の規則に則って読み込み、それらを連結して1つのソースファイルと同様に扱うため、動作には影響ありません。
ここでは各種表示コンポーネントのファイルはclient/templates/components
ディレクトリ以下に配置し、テンプレート名がtemplateName
の場合、template_name.html
とtemplate_name.js
というファイル名がつけていくこととします。この慣習に倣ってそれぞれmain.html
とmain.js
に記述したコードを一部取り出して、以下のファイルを作成します。
【※】main.html
とmain.js
内の当該コード箇所は削除します。
client/templates/components/node_status_component.html
client/templates/components/node_status_component.js
View this Commit On GitHub (Tag:"Step003")
次に、ノードに登録されているアカウント情報を表示する「Account Balance」と、Ethereumネットワーク内のブロックチェーンの情報を表示する「Block Status」の2つのコンポーネントを追加します。
まずclient/main.html
にこれらのコンポーネントのテンプレートを呼び出し表示するためのInclusionsタグ{{> accountBalanceComponent}}
、{{> blockStatusComponent}}
を追加します。
client/main.html (一部抜粋)
さらにテンプレートとテンプレートヘルパーも追加します。ここでテンプレートのidは今回追加したInclusionタグと同じものにします。
client/templates/components/account_balance_component.html
client/templates/components/account_status_component.js
client/templates/components/block_status_component.html
client/templates/components/block_status_component.js
ここで、それぞれのヘルパーは、nodeStatusComponent
のヘルパとは異なり、情報をweb3オブジェクトから取得するのではなく、そのラッパーであるEthAccounts
やEthBlocks
から取り出しています。これらのラッパー・オブジェクトを利用することで、状態が変わると自動的に表示が更新されるリアクティブな表示が可能になります。
また、client/templates/components/account_status_component.html
内で{{#each accounts}}...{{/each}}
のblock helpersタグが追加されています。これはヘルパーでのaccountsメソッドで取得されるのはアカウントオブジェクトの配列であり、その配列の要素づつ取り出し、それをaccountBalanceItem
テンプレート側でname
やbalance
属性を取得するようにしています。
最後に、blockStatusComponent
のヘルパ内で、UNIX時間表記で得られる採掘日時を通常の日時表記で表示されるようunix2datetime
関数を呼び出しているので、この関数のコードを追加します。
client/lib/modules/time_utils.js
これらが正しく記述されたら、下図のような画面が表示されます。ここで、今回追加した「Account Balance」「Block Status」の項目はリアクティブな表示になっていることを実際に確かめてみてください。採掘が成功するたびにEtherebaseのbalance値やブロック情報の項目が、特に手動でリロードをすることなく自動的に更新されるのが見て取れるはずです。また、ノードに新しいアカウントを作成した際も自動的にアカウント情報が追加更新されることになります。
View this Commit On GitHub (Tag:"Step004")
最後に「Node Status」の項目をリアクティブな動作をするようにしましょう。Account StatusとBlock Statusは、それぞれethereum:accounts
、ethereum:blocks
のパッケージを利用したために、特別なことをしなくてもパッケージ側でリアクティブな動作をしてくれました。残念ながら「Node Status」項目で表示するHashrate等はこのようなリアクティブな動作サポートするようなパッケージが用意されていません。そのため自分自身でそのような動作をするよう実装していきます。
基本方針として、「Is Mining?」「Hashrate」「Peer Count」の項目の値を Web3 APIから定期的(1秒間隔)に取得し、取得した値をMeteorのSessionオブジェクトに格納し、画面にはそのSessionオブジェクトの値を表示するということを行います。
MeteorにおいてSessionオブジェクトは、同一セッション内(同じユーザーかつ同じブラウザ・タブ内)でグローバル、かつ、単一(シングルトン)のオブジェクトです。このオブジェクトにはKey-value形式でデータを格納することが可能で、リアクティブなデータストアとして利用可能です。
Sessionオブジェクトを利用するには、コンソール上でプロジェクトRootに移動し、下記のコマンドを実行してsession
パッケージを追加します。
これでSessionオブジェクトを利用できるようになりましたので、まずはSessionオブジェクトを初期化する関数(initSessionVars
)を定義します。
client/lib/modules/init_session_vars.js
次に、定期的にWeb3 APIから値を取得する関数(observeNode
)を定義します。Meteor.setInterval
関数を利用して1秒に1回、Web3 APIの非同期関数で求める値を問い合わせて、APIから返った値をSession
オブジェクトに格納する処理を行っています。
client/lib/modules/observe_node.js
次に、上記で定義したinitSessionVars
関数とobserveNode
関数がWallet起動時に呼び出すコードをclient/lib/init.js
の末尾に追記します。
client/lib/init.js
以上で、Walletの起動時から定期的にノードの最新の値を問い合わせて、結果をSession
オブジェクトに格納する処理が実装されました。次にこれをリアクティブに画面に表示します。これはnodeStatusComponent
のテンプレートヘルパ内でそれぞれの値の取得先をSession
オブジェクトからKeyを指定して取得するように変更するだけです。先述の通りSession
はリアクティブなデータソースであるため、Sessionオブジェクトの値が更新されれば自動的にブラウザ上の表示も更新されるようMeteor側で制御してくれます。
client/templates/components/node_status_component.js
を下記のコードに書き換えます。(先のコードからの変更点はそれぞれのreturn
で返す値だけです。)
client/templates/components/node_status_component.js
以上の変更を行った上で再度Webアプリの動作を確認すると、「Node Status」の部分もリアクティブな表示が実現していることが確認できるはずです。特に「Hashrate」項目は1秒ごとにめまぐるしく変わっていくのが見て取れるでしょう。
View this Commit On GitHub (Tag:"Step005")
この状態でWebアプリケーションにアクセスすると、下図のような画面が表示されるはずです。