はじめに
こんにちは、新規事業開発室の中村(@veryNR)です。
最近までデータ分析部におりましたが今月から異動しました。
(分析ブログの方も是非見てください!)
今回は、eWASMという、現在開発が進められている既存EVMに代わる新しいEthereumの仮想マシンの仕組みについて紹介します。
eWASMは昨年秋のDevcon3でのVitalikのプレゼンで名前が登場したのでご存知の方もいるかもしれません。
簡単に言えば、Ethereumのスマートコントラクトの実行の仕組みを、EVMコードをEVMで実行する仕組みから、eWASMというEthereum用のWebAssemblyのサブセットをeWASM VMで実行する仕組みに変えるというプロジェクトで、
WebAssembly技術のパフォーマンスや開発コミュニティに支えられながらコントラクトコード実行の速度向上や開発環境の改善が期待されます。
前提知識
Ethereum Virtual Machine(EVM)
EVMは、Ethereumのスマートコントラクトを実行する、Ethereum独自の仮想マシンです。
Solidityなどの高級言語で書かれたコードは、この仮想マシンで実行するためのEVMコードにコンパイルしてデプロイされています。
EVMについてはこちらのスライドがとてもわかりやすいです。
以下では、この既存のEVMを、eWASM界隈での呼び方に習ってEVM1と呼びます。
WebAssembly(WASM)
WASMは、Webブラウザで実行できるバイナリのことです。
2015年に発表され、現在は主要なブラウザ(Chrome, Firefox, Safari, Edge)全てでサポートされています(ブラウザじゃない実行環境もある)。
JavaScriptより高速に動き、既存の高速化手法であるasm.jsよりコンパクトです。(デモ)
ゲームや画像処理などの複雑なブラウザアプリケーションでの活用が期待されています。
C/C++/Rustなど様々な言語からコンパイルできるのも特徴です(LLVMにWASMバックエンドがあるため)。
WASMの実行環境はWASMエンジンと呼ばれ、こちらは32or64ビット方式のスタックマシン(単純なスタックマシンよりはるかに工夫された設計だが割愛)となっています。
登場した背景や特徴などはこちらの記事に詳しいです。
以下、WASMとeWASMの違いに注意してお読みください。
eWASM登場の背景
eWASMとは、"Ethereum flavored WebAssembly"の略で、Ethereumのために作られたWASMのサブセットのことです。
「WASMのサブセット」なので、前述したWASMを、Ethereumで使えるように制約を加えたものと考えるとわかりやすいです。
eWASM自体はEVMコードに対応するもので、EVM1に代わるのは後述するeWASM用の仮想マシン(以下eWASM VM)ということになります。
「EVMをもっとよくしようよ」と言う提案自体は実は新しいものではなく、eWASMのEIPのissueも2015年12月に作られています。
そこでは、下記のような点でEVM1はパフォーマンスがよくないとされていました。
1. 256ビットのスタックアイテム
EVM1ではスタックマシンという計算モデルが採用されています。参考: スタックマシン - Wikipedia
EVM1ではスタックアイテムが256ビットに設計されている(256ビットのハッシュがそのままスタックに入るように?)ため、EVM1の命令がEVM1を動かす物理CPU(大抵64ビット)の命令にシンプルに対応しません。
その結果として、EVM1の命令セットの構成が一般的なマシンの命令セットと異なり複雑になってしまう上、
クライアントがEVM1の命令をどう実装するかによって計算コストが変わってしまい、gasコスト当たりの処理の重さが変わってしまいます。
2. EVM独自の高級すぎる命令たち
EVMのオペコードには、ブロックチェーンと疎通するための独自のオペコードがたくさんあります。
例: CALL(メッセージコールする), CREATE(新しいアカウントを作る), SSTORE(ストレージに保存), etc.
これらが複雑さをさらに増す上、汎用言語からのコンパイルを難しくし、Solidityなどの独自言語を使って開発せざるを得ない一因でもあります。
3. ランタイムでのgas計算
EVM1が他の仮想マシンと特に変わっている点として、実行時にgas周りの処理(gasの消費や、gasが不足した場合のプログラムの停止)を行わなければならない点があります。
EVM1のインタプリタはfor文で命令を一つ一つ実行するごとにgasを計算しており(参考: gethのコード)、これが仮想マシンの実行速度を遅くしているとされています。
一言でまとめてしまえば、EVM1はEthereum独自の仕様を盛り込みすぎて、それがボトルネックになっていると言えるのかもしれません。
このような背景から、EVM1に変わるものとしてWASMが提案されました。
WASM以外にはLLVM IRなども代案として主張されていたものの、不安定さやポータビリティの低さなどが挙げられていました。
EVM1に対するメリット
ここでは、eWASMの採用でどのようなメリットがあるのか見ていきます。
1. パフォーマンスの向上
WASMは元々機械語に近い速度で動くよう設計されている言語で、前述した問題を抱えるEVMと比べ高いパフォーマンスが期待されています。
加えて、gas計算のロジックはデプロイ時にバイトコードにあらかじめ挿入してしまう実装に変更される(後半で説明します)ため、gasの処理が仮想マシンのボトルネックになることはありません。
コントラクトの実行性能の向上は現在研究が進むシャーディングやPoS等の技術の基盤となりますし、DoS攻撃を防ぐため処理の重さに応じて設定されているgasコストが下げられ、全体的に手数料が安くなる可能性もあります。
(実際にWASMの命令に対応するgasコストはここで提案されています。)
2. WebAssemblyのエコシステム
EVM1は良くも悪くも自分たちで開発コミュニティを築きメンテナンスしなければなりませんでしたが、WASMはMozilla, Google, Microsoft, Appleといったトップクラスの企業が開発に参加しています。
また、コントラクト開発にc/c++, go, rustといった言語が使えるようになることで、それらの言語で開発されたコード資産を活用できたり、より多くの人がコントラクト開発に参加しやすくなります。
3 ブラウザで動くEthereumフルノード(?)
主要ブラウザが元々WebAssemblyエンジンを備えているため、ブラウザで動くフルノードクライアントというのも登場するかもしれません。
Dappをフルノードから使いたいような場合に、コンソールでgethを起動してMistから使う、といった形ではなく、ブラウザで完結する未来がくるかも。。。
eWASMの具体的仕様
それでは、eWASMが具体的にどう動くのか見ていきましょう。
eWASMでのコントラクト
コントラクトはWASMで書くことになりますが、Ethereumで使うために幾つか制約を加える必要があり、下記のような仕様を満たしている必要があります。
- 浮動小数点を使わない
- Ethereum Environment Interface(後述)のモジュールのみをimportし、他のモジュールはimportしていない
- mainとmemoryという二つのシンボルをexportする
- main: VMが実行する関数
- memory: EEIのモジュールが書き込むメモリスペース
Ethereum Environment Interface (EEI)
EEIとは、eWASMコントラクトがEthereumにアクセスするためのAPIです。
WebAssemblyのModuleとして実装され、eWASMの中でimportして使います。
具体的には、Ethereumの特定のアカウントの残高を取得するgetBalanceや、アカウントにメッセージを送るcallなどがあります。(一覧)
元々こういった処理はEVM1の命令として実装されていましたが、WASMでは当然このような命令がないため、モジュールとしてimportして使う仕様になっています。
System Contract
eWASM VMが利用するコントラクトです。
厳密には、「コントラクトとして定義されたインターフェース」であり、この部分が非常にわかりにくいのですが、
仮想マシンの外で実装したいロジックをコントラクトの形で定義しておき、仮想マシンがcallすることで使う仕組みと考えてください。
実は、EVM1でもあったPrecompiled Contractがこれに含まれます。
例えば、公開鍵と署名からアドレスを算出する処理(ecrecover)など暗号学的で非常に複雑な処理は、貧弱なEVM1のオペコードに直して実行したくはありません。
そこで、処理を高速化するべくクライアントの実装の中に含めて事前に機械語にコンパイルしておき、特定のハードコーディングされたアドレス(ex: 0x0000000000000000000000000000000000000001)のコントラクトが呼ばれた場合は、ブロックチェーンからではなくクライアントに実装されたロジックを実行していました。
(参考: gethのPrecompiled Contractと、コントラクト実行時にそのアドレスがPrecompiled Contractではないかチェックする処理)
あくまでインターフェースなので、コントラクトの実装部分はオンチェーンのコントラクトとしてでもいいですし、Precompiled Contractのようにクライアントで実装しても良いです。
eWASM実行環境では、EVM1のPrecompiled Contractと同じものに加え、以下のコントラクトを追加しています。
Sentinel Contract
- コントラクトをデプロイするときにVMが呼び出し、以下の処理を行います。
- 1 コードがeWASMの仕様にあっているかをチェック
- 2 gas計算のロジックを挿入する
- 3 デプロイ準備OKの目印としてpreambleつける
- コントラクトをデプロイするときにVMが呼び出し、以下の処理を行います。
EVM Transcompiler
- EVM1のバイトコードをeWASMにトランスパイルする処理を行います。
- EVM1をサポートしていない場合にVMが呼び出します。
- このSystem Contractにより、今後のクライアントはeWASMだけサポートしておけば、既存のEVMコードのコントラクトも実行できます
eWASM VM
EVM1の仮想マシン本体に代わる部分で、eWASMのコントラクトの実行時及びコントラクトのデプロイ時に使います。
- コントラクトの実行時
- コントラクトがeWASM形式ならばWASMのインタプリタで実行
- そうでない場合は、EVMコードをサポートしているかに応じてTranscompiler Contractを呼び出すなどして実行
- eWASMコントラクトのデプロイ時
- Sentinel Contractでバイトコードをバリデーション&アノテーションする
コントラクトの実行に関しては下のような図になります。
開発状況
現在はテストネット稼働に向けて開発が進んでいる (カンバン)ところで、仕様などもまだまだ議論中の部分が多いようです。
興味のある方はeWASMのgitterを覗いてみると良いかもしれません。この記事の執筆中にもコアメンバーにより活発に議論がなされています。
以下、eWASM関連のリポジトリのリンクを貼っておきます。
-
- C++によるeWASM VMの実装で、今一番注力されています
- WASMのインタプリタは現状binaryenというものを使っています
- EVMC(後述)を介して既存のクライアントと組み合わせて使われるようです。
- 現在cpp-ethereumでプロトタイプ的に動いており、go-ethereumからも動かせるようになる予定とのことです。
-
- EVM1, eWASM VMとクライアントをつなぐAPIです
- heraなどのeWASM VMを使えるようにアップデートが進んでいます
hera + EVMCの組み合わせの場合のクライアントの概念図
- ewasm-kernel
- javascriptによるeWASM VMの実装
- 完成すればnode.jsやブラウザで動かせる(かも!?)
まとめ
既存のEVMに代わりeWASMとその実行環境が開発されており、コントラクトの実行性能の向上などが期待される。
参考
宣伝
Gunosyではブロックチェーン技術の研究・開発を進めており、メンバーを募集しています。
少し話を聞いてみたい!という方も歓迎していますので、下記のリンクからの応募お待ちしております。
また弊社Gunosyでは、blockchain.tokyo を主催しています。
イベントグループへメンバー登録をしていただくとイベント参加者募集の通知が来るので、こちらもぜひ登録してみてください。