valid,invalid

関心を持てる事柄について

ブロックチェーンとDapp開発の基礎を学んだ

ブロックチェーンについては、2017年末の仮想通貨バブル時代にちょっと興味が湧いたものの投機に熱中していて技術的な理解は全然していなかった。

求職活動にて見知ったことなのだが、ここ数年でブロックチェーン技術を活用するスタートアップが台頭してきただけでなく、金融・不動産・保険業などの大手企業が実証実験に乗り出している。直近で業務として触るところまでいかなくても、将来的に利活用する製品やサービスに触れる機会があろう、ということで再訪してみた。

期間は2週間ほどで集中的に、おおよそ50時間ぐらいかけて基礎の基礎を学んだ。

学んだ成果

技術の根幹となる用語や概念や取り巻く環境を少しは理解することができた。

結果として、関連するニュースを読んで「わかる!」というポイントが見つかったり、「これはXXを理解しないとわからないのだろう」と、何がわからないかに気付ける機会ができてきた。

たとえば直近、Twitterアカウントの大規模な乗っ取りがあり、Bitcoinトランザクションを通じてメッセージを発したというニュースがあった。

Twitter不正投稿の首謀者、ビットコインブロックチェーンで「メッセージ」を発信

この記事を読み、

  • メッセージはどうやって埋め込んだのか?*1
  • この送金取引の結果はBitcoinブロックチェーン上にはどのように格納されているのか?*2
  • 不当に得たBitcoinを現金化するのは難しいと考えられるがそれはなぜか?*3

等々。こうした疑問に答えられたり、新たな疑問が湧いてくるようになったりした。


また、スマートコントラクトについて、ちょっとしたDappを書いてローカルのEthereumネットワークにデプロイしたり、Webブラウザで動作するフロントエンドのアプリケーションの実装をしてみた。

production useには程遠いまでも、「スマートコントラクトってよく聞くけど何?」というところから簡易な設計と実装を経て、スマートコントラクトとDappについて手触り感を伴うイメージを持つことができた。

やったこと

本当に何もわからなかったのでまずは体系的な理解から入ることにした。

本を読んで概観を掴む

まずは本2冊読んでみた。いずれも別記事にて感想を書いた。

おすすめはブロックチェーンのしくみと開発がこれ1冊でしっかりわかる教科書』感想

『スマートコントラクト本格入門―FinTechとブロックチェーンが作り出す近未来がわかる』感想

未読だが、評判を見たところ更に一歩先に理解を深めたければ『Mastering Bitcoin』や『Mastering Ethereum』あたりが良さそうだった。

ゲーム感覚でSolidityを学ぶ

実際にスマートコントラクトを自分で書いてみるため、専用のプログラミング言語であるSolidityを学ぶことにした。

CryptZombies*4なるチュートリアルがあるので"Beginner to Intermediate Smart Contracts"コースを全部やってみた。

cryptozombies.io

オンラインエディタでコードを編集しつつ、ステップバイステップで進められる。Solidity文法の基礎、スマートコントラクトにおける基礎概念がDappのコード上でどのように表現されるのか、WebブラウザからどのようにDappと連携するのかを学ぶことができた。

f:id:ohbarye:20200719000211p:plain
奇妙なゾンビ軍団を従えることができる

最終的にどういうものができるのかイメージがあまり湧かないまま進んでいく点と、(日本語版だと)Solidity versionがやや古いのが難点と感じたが、基礎をおさえる意味合いではさほど関係なさそうだった。

Solidityは静的型付け言語でJavaScript"風"のため書き始めるハードルはさほど高くなかった。とはいえ、作法やプラクティスみたいなところで悩むこともあったのだが公式ドキュメントEthereum Stack Exchangeである程度解消できた。

余談: 仮想マシン、EVM

Solidityで書かれたスマートコントラクトはEVMと呼ばれる仮想マシン上で実行される。EVMはEthereum上でスマートコントラクトを実行するためのEthereum独自の仮想マシンであり、Solidityのような高級言語コンパイル時にEVMバイトコードになる。

つまり、JVMや.NET等の先行するVMが知らしめたように、EVMバイトコードコンパイルさえできればどんな言語でスマートコントラクトを実装しても構わないということだ!

実際にSolidity以外にもEVMで動作するプログラミング言語はEthereum Wiki (https://eth.wiki/en/concepts/evm/ethereum-virtual-machine-(evm)-awesome-list) をチラッと見ただけでも7つあった。Solidityの次に有力なのはPython"風"の文法を持つVyperだろうか。

また、言語だけでなくEVM自体の実装も複数存在する。いくつかはすでに開発が停止しているようだが…。

この辺の動向を見守るのも面白そうだ。

Dappを書いてみる

ローカルでEthereumネットワークを立ち上げ、このネットワーク上にDaapをデプロイしてみた。

『スマートコントラクト本格入門―FinTechとブロックチェーンが作り出す近未来がわかる』にてgethを使ったネットワークの操作を学んだが、Ganacheを使うことでとても快適になった。

Web開発的にいえばテストデータ(seed data)が予め突っ込まれているデータベースがシュッと立ち上がり、実行されたUPDATE/INSERT(トランザクション)が全部記録として残り、さらにそれらをGUIで確認できる、みたいな感じ。

f:id:ohbarye:20200718235021p:plain
Ganache公式サイトより

CLIで操作できるものもあり、Docker imageも用意されている。慣れてきてGUI不要だったりDocker Composeでまとめて立ち上げたいならCLIを使えば良い。

また、Dapp開発のフレームワークとしてはTruffleを使ってみた。CLIでプロジェクトを立ち上げる(≒rails new)ことができ、デプロイやマイグレーション(≒rails migrate)、テストの実行を行うことができる。テストはSolidityでも書けるし、JavaScriptchaiやmocha風に書くこともできる!

Web開発との差分

静的型付け言語でプログラミングできてテストも書けてデプロイパイプラインを作れるCLIツールもあって…なんだかWeb開発とさほど変わらないのか?と思ったがそんなことはなかった。

Web開発と似てる部分もあるがだいぶ違うので「あ〜わかるわかる」と理解ったふりをせずに謙虚な姿勢で学ぶ必要がある

差分はたくさんあるので、その中でも特に印象深かった点を2つだけ書いてみる。

コスト意識

Dappではストレージに書き込んだり、コントラクトをインスタンス化したりするのにコストがかかる。ここでいうコストとはCPU・ストレージといった計算資源を使うことだけでなく、その操作の多寡に応じて実際にEthereumを消費するということ(Gasと呼ばれる)。Web appでいえばPOSTやPATCHリクエストを送るのにお金がかかる、というイメージ。

コントラクトを設計・実装する際には冪等性やメソッドの可視性といったWeb API的な観点のみならず、このメソッドはどれだけ高価なのかを意識することになる。そのうえ、ストレージに保存されるデータ量でコストが変わるため変数の型もしっかり考える必要がある。(1 byteですむようなデータにuint256など使ったらレビューで指摘されるのだろう)

デプロイしたらロールバックできない

スマートコントラクトは一度デプロイするとEthereum上で自律的に動き続ける。この実行環境は開発者が管理するサーバではなくP2Pネットワークのマシンたちなので誤ってデプロイしてしまったものを停止したりロールバックしたりはできない

修正後のコントラクトをデプロイする場合も別インスタンスで別アドレスを持つコントラクトアカウントが生まれることになるので旧バージョンは残り続ける。Web API的に言うと/v1/my_contractendpointにまずいところがあり直しても/v2/my_contractがデプロイされ、v1は生き続ける、みたいな。こわい。

さらに、コントラクトのコードはネットワークの参加者には公開されるため、機密情報・秘匿情報を含んではいけない。一度含んでしまったらこれも取り消せない。

モバイルアプリ開発でも同様の不可逆性はあるが、そちらには強制アップデートの仕組みもあるので、それ以上に厳しい。

脆弱性について学ぶ

上述の通りロールバックがきかないため、スマートコントラクトプログラミングでは慎重に脆弱性を潰していく必要がある。

セキュリティの指針やベストプラクティスをまとめたEthereum Smart Contract Best Practicesを読むのもためになった。

とりわけ面白かったのはReentrancyとして知られる脆弱性

Reentrancy

コントラクトAのコードを実行する際に外部のコントラクトBのメソッドを呼び出せる。その外部のコントラクトBが呼び出し元のコントラクトAを呼び出すこともできる。*5

たとえばBankAccountコントラクトが持つwithdrawメソッド内で残高の更新(ストレージへの書き込み)を完了する前に外部への送金を行うと、その送金を受け取った側へ処理フローが移る。そこからさらにwithdrawを呼ばれると、ストレージに記録されている残高が減ることなく資金が送金され続ける*6

実際に過去にこの脆弱性を突いた事件があり、"Ethereum Smart Contract Security Best Practices"にも記されているので、興味があれば掘り下げてみると面白いと思う。(少し探せば脆弱性があるコードを修正した実際のcommitまでたどりつける)

フロントエンドを書いてみる

Ethereumと連携するフロントエンドアプリケーションも書いてみた。Webブラウザで動作するやつ。

ローカルまたはリモートのEthereumネットワークと通信するためのweb3.jsというライブラリがあり、これを用いる。*7*8

web3.jsはあくまで外部とのインタフェース、言うなればaxiosなんかのようなAPI clientとしてのモジュールに過ぎないので、それ以外の部分の実装はReactでもVue.jsでもjQueryでもなんでも良い(CryptZombiesはjQueryを用いて説明している)。

Promiseベースのインタフェースなので馴染み深い。

かなりweb frontendぽい感じに見えてきたので、ここからちょっと違う話をする。

MetaMask

web3.jsから直接任意のEthereumに繋ぐこともできるが、Chrome ExtensionであるMetaMaskを介してネットワークに接続したり、アカウント情報を取得するのが乙なようだ。

MetaMaskはweb3.jsとEthereumの間にいるプロキシのようなやつで、接続先のネットワークやアカウントを管理できる便利Extension。web3.jsで送金処理を実行する場合にも一度MetaMaskが立ち上がって取引の内容を確認される。

www.youtube.com

同期/非同期

ブロックチェーン上のデータを参照するだけのcall(GETリクエストに相当)によるコントラクトのメソッド呼び出しでは同期的に値を返すことに何の問題もないが、トランザクションを生成することになるリクエストでは注意が必要。

ブロックチェーンで保存されるデータの変更や書き込みリクエストは即時に処理されるわけではない。ネットワークの承認によって初めてブロックチェーンにデータが格納されるためだ。なのでコントラクト側で発行するイベントをクライアント側でsubscribeして結果を受け取ったあとのコールバックを書いたりできる。

Web APIでも要求を受け付けたことだけを一旦レスポンスとして返し、裏側で非同期に処理が行われ、結果を取得するにはポーリングなりWebsocketなりで対処したりすることはある。その対応がわりと頻繁に必要になりそうだが、もっとうまいやり方はあるかもしれない。

その他

上記の他にも色んなリソースも見ていたが、とりわけ読み応えがあるのはこの辺。

あとはVitalik ButerinによるEthereumのWhite Paper!読んでも正直全てを理解できていないのだけど、人間って19歳でこんなこと考えられるんだ…と感嘆せずにはいられない。*9

おわりに

ブロックチェーン技術とその周辺は日々新しい情報が生まれる領域であり、これらの学習だけでは実務レベルにはまったく不十分ということがわかってきた。Web開発でいえば「Rails tutorialやりました」以前のところにいる。

しかしながらこういった基礎的な部分を押さえておくことで関連ニュースの情報処理効率は少なからず上昇するし、また、将来なんらかの形でブロックチェーンを活用した製品に利害関係者・消費者として接する機会が来るのはだいぶ確定的に思えてきた。その時に必要なリテラシーを備える意味でも、こういった学習を時たま行っていくのは上々の投資効果があると考える。

*1:メッセージはBitcoinアドレスの一部として埋め込まれている。通常、ビットコインアドレスは公開鍵から生成されるが、秘密鍵なしでもvalidな値("1"と混同しないように"l"を除外するとか)であればアドレスとして利用することはできる(ただしビットコインアドレスから公開鍵と秘密鍵を知ることはできないので誰もそのアドレスから送金できない)。

*2:データベースのようにレコードのmutableな値をupdateするのではなく、https://en.bitcoin.it/wiki/Transaction に従って取引ごとのOutputが記録されるのみ。残高を見る時はUTXOという仕組みで、Outputから未使用のものを集計している

*3:Bitcoin Tumblerと呼ばれる、不当に得たBitcoinとcleanなBitcoinを混ぜ合わせて洗浄するマネーロンダリングの手法があるが、全ての取引記録が改ざん不能な形で記録されている以上は追跡から逃れるのは難しい。過去に同様の手口で資金洗浄を試みたが失敗した例がある

*4:Ethereum上で稼働するゲームに、仮想子猫を売買できるCryptKittiesというものがあり、それをパロったやつ。

*5:あるAPIから外部のAPIを呼び出すというのはWeb appでもよくあることだが、Dappでは送金処理の際に外部のコントラクトのメソッドがコールバックとして呼び出されるのでけっこうカジュアルかつ頻繁に発生するようだ。

*6:コールスタックに上限があるので循環して無限ループが起きることはない

*7:ether.jsというのもあるが触っていない

*8:iOSAndroid用のintegrationもあるようだ

*9:年齢で人を評価するとかそういう次元でなく19歳の自分と比べて絶望する感覚