catapultのDappsウォレットを作ってみる


はじめに

ブロックチェーンを利用したWebアプリを作る際に、どうしても問題となるのが秘密鍵の管理です。

アプリで文章や図形をかいて、それをすぐにトランザクションにのせて送信したりするような場合、アプリは秘密鍵にアクセスしなければなりません。

それができない場合、他のウォレットを使ってトランザクションを送信するなりの手順を書かなければなりません。

しかし、どちらかというと作りたいのはアプリ的な方であって、秘密鍵の管理についてはあまり関わりたくない、という場合があります。

そこで、未署名トランザクションを作成して渡したら、署名と送信はやってくれるようなものがあれば、とてもうれしい。

Ethereumにはそういうものがあり、MetaMaskなどがそうです。これは、ChromeやFirefoxの拡張機能としてインストールします。WebページからMetaMaskにトランザクションを渡すと、MetaMaskが署名と送信をやってくれます。

こういうのを世間ではDappsウォレットとか呼ぶようです。

今回は、そんな感じなものを作ってみようと思いました(完成していません)。

ほかには、Twitterのウィジェットや、Googleアナリティクスみたいに、scriptタグを埋め込むだけでいいよ、的なアプローチもあるかと思います(Portisなど)。

見た目

Chrome拡張としてインストールした際には、右上にアイコンが表示されます。

これをクリックすると、ダッシュボードが表示されます。

Webアプリから操作する

Webアプリのグローバルスコープに、window.nem2がインジェクトされます。

これを使って、Webアプリからウォレットにアクセスすることになります。ここには、秘密鍵にアクセスするためのメソッドが用意されていません。なので、秘密鍵がWebアプリに知られることはありません。

しかし、トランザクションを送信するためのメソッドがあるため、これを使うことによって、ユーザーのアカウントで指定したトランザクションを送信することができるようになります。

ちなみに、署名の処理はChrome拡張機能側のグローバルスコープで処理されるため、Webアプリのスコープからこれ以外の情報を覗くことはできません。

使用例1、アドレスを表示するには(jQueryを使って)

window.nem2.getAddress().then((address) => {
    $(selector).text(address)
})

こんな感じになります。

また、トランザクションを署名・送信するには(nem2-sdk-browserifyを使って)、window.nem2.sendTransactionTransactionインスタンスを渡します。

const tx = nem.TransferTransaction.create(
    nem.Deadline.create(),
    recipientAddress,
    [nem.NetworkCurrencyMosaic.createRelative(0)],
    nem.PlainMessage.create('Hello Catapult!'),
    nem.NetworkType.MIJIN_TEST
);

window.nem2.sendTransaction(tx).then((data) => {
    console.log(data)
}).catch((e) => {
    console.error(e)
});

すると、確認画面が新しいタブで開きます。

window.nem2.sendTransactionPromiseを返します。Confirmボタンを押すと、トランザクションが送信されresolveされます。Rejectボタンを押すと何もせずrejectされます。

確認画面に表示する項目は、トランザクションタイプ毎に異なるため、いまのところTransaction.toJSON()をそのまま表示しています。

※拡張機能がnem2-sdk-browserifyをインジェクトするべきかどうか。どうするべきでしょうか。インジェクトする場合はWebアプリがいくぶんスリムになるメリットがあるかもしれません。しない場合、インジェクトするsdkのバージョンのアップデートが難しくなるというデメリットがありそうです。

構造とか

※間違ってたらごめんなさい。

まず、角丸の枠でスコープを示しています。

Scope Web 1/2は、Webページに関するものです。

Scope Backgroundは、Chrome拡張機能が起動すると動き続けるものです。

Scope Popupは、拡張機能アイコンをクリックして表示される画面のものです。

Webアプリ読み込みの流れ

任意のWebページが読み込みがトリガーされると、contentscript.jsが実行されます。ここで、Scope Web 1にwindow.nem2をインジェクトします。

contentescript.jsはWebページのスコープ(Scope Web 1)に直接アクセスすることはできません。しかし、WebページのDOMにアクセスすることができます。そこで、scriptタグを挿入することでWebページのスコープで任意のコードを実行することができます。

その後、Webページの読み込み処理が開始します。ここで、Webページはアドレスなど情報を取得する処理を書いておけば、画面にそれらを表示することができます。

さて、Webアプリがアドレスの取得やトランザクションの送信を行う際、それらの保存場所であるScope Backgroundまでアクセスしなければなりません。それらのルートは以下のようになっています。

window.nem2contentscript.jswindow.postMessageを使ってデータのやり取りを行います。

contentscript.jsbackground.jschrome.runtime.sendMessageを使ってデータのやりと取りを行います。

こうして、background.jsが行った処理の結果を返すことができます。

Popupの流れ

Popupとは、拡張機能アイコンをクリックして表示される画面のことです。

この画面では主にアカウントの情報や設定を行うことになります。

拡張機能アイコンがクリックされると、Scope Popupは初期化されpopup.htmlが読み込まれます。その際、この中のscriptタグで指定してあるjsファイルが実行されます。画面が閉じるとスコープは破棄されます。

なので、変数に情報を保持し続けることは行わずに、表示の度に情報を取得することになると思います。

図にあるように、chrome.extension.getBackgroundPageを使ってbackground.jsとデータのやりと取りを行います。

Scope Backgroundで残高や履歴の情報を保持するか、もしくはアドレスとエンドポイントの情報を使ってブロックチェーンにアクセスするか、といった形になると思います。

秘密鍵などの保存

秘密鍵の保存はlocalStorageに行います(たぶん)。セキュアな方法があれば教えてくださるととても喜びます。background.jsのスコープでwindow.localStorageを利用することで、他の拡張機能やWebアプリからのアクセスを防ぐことができます。

ここに暗号化された秘密鍵を何かしらの文字列の形で保存しておきます。拡張機能の起動時にパスワードを要求し、それを使って必要な時にだけ復号化することになります。

まだできていないもの

  • エンドポイントの切り替え
  • マルチアカウント
  • パスワード変更
  • アカウント作成

おわりに

Chrome拡張を使って、Webアプリ開発者が秘密鍵の管理をしなくてよくなる方法を検討してきました。

こういったアプリが増えていくことによって、そのブロックチェーンはどんどん使いやすくなっていくと思います。

チラシの裏ですが、フレームワークにVue.jsは使えませんでしたので、reactを使っています。Chrome拡張を作るうえで苦労しているのが、いろいろなスコープがあったり、アクセスのお作法があったりして大変です。セキュリティのためには当然なのですが。

あといちおうリポジトリです。
https://github.com/planethouki/nem2-test-dapps-wallet