LINEが「スマートコントラクト」を実行するまで

高橋史季氏(以下、高橋):ここからは、実際にスマートコントラクトを実行するVMとしてWASMを採用するまでにどのようなことを考えてきたのかについて詳しく見ていきます。

私は高橋といいます。前職はSIerでブロックチェーンのPoCにかかわっていました。現在は高瀬と同じチームで開発やリサーチ業務を行っています。よろしくお願いします。

さて、ここまでの話でWASMは、そのさまざまな特徴がスマートコントラクトの要件を満たしているとして、今ブロックチェーンでもとても注目されていることがわかりました。

こちらは高瀬の話にも登場した図で、ちょうどスマートコントラクトの要求事項がきれいにWASMのストロングポイントと一致していることがわかります。まずこの図を念頭に置いて、我々がVMを導入する時に考慮する必要があった3つのポイントについて説明します。

1つ目は、Scalability、つまり拡張性です。最近のブロックチェーンは、はじめからWASMを使ったVMを導入したり、EVMもeWASMへの移行を検討したりしていて、今後の主流となることが予想されます。そのため、我々も変化に対応しやすいようにVMを用意する必要がありました。

2つ目は、Language Independentです。我々はLINE Blockchain Developersを通じてブロックチェーンに触れてきたことがないユーザーも簡単にトークンが発行できるようにしました。スマートコントラクト開発においても、今までブロックチェーンに触れてきたことがないデベロッパーも参加できるようなプラットフォームにしたいと考えています。

そのためEVMのように「Solidity」という特別な言語による開発ではなく、通常のWeb開発で使われる言語でスマートコントラクトが記述できるようにする必要があります。

この2つは根本的に、WASMをVMとして採用することで要件を満たすことができると考えられます。

そして、最も重要視したのが、Fast and Efficient、つまりパフォーマンスの部分です。我々はLINEアカウントを持っている日本のユーザーに加えてグローバル市場への展開も考えています。この場合、大量のトランザクションをさばく必要があるため、パフォーマンスは特に重要な要素となります。

LINE BlockchainのVMの構造

ここからは、このパフォーマンスの部分についてどのような部分が影響してくるのか、何を考慮すべきなのかについて詳しく見ていきます。

その前に、まずはVMの構造を簡単に説明します。WASMコードをスマートコントラクトとして実行するまでの流れをざっくりと表したのがこちらの図です。

C++やRustなどのプログラミング言語で書かれたスマートコントラクトは、WASMバイトコードにビルドされます。これをネットワークにデプロイすることでブロックチェーンの各ノードに配布されます。配布されたスマートコントラクトは、VMモジュール内部にあるランタイム内で実行されます。

WASMのバイトコードは、そのままマシンコードとして実行できる実行ファイルではありません。そのため、実行可能なマシンコードに変換する必要があります。そこで、マシンコードを作成するためにコンパイルを行います。そして、最終的にユーザーからのコールがあった場合にランタイム内でコントラクトが実行されます。

なお、コンパイルしてから実行されるのか、インタプリタによって実行されるのかは、その実装によって異なります。このランタイムがスマートコントラクトを実行する時のパフォーマンスに影響を与えるところなので、ここに注目してみます。

どのようなランタイムを採用するのかということを考える上で、特にポイントとなる3つの特徴を列挙しました。それがこちらの3つです。

1つ目が、コンパイラを使って事前にコンパイルをしてから実行するのか、インタプリタで実行するのかという点です。ここでどのような方式を採用するのかによってパフォーマンスに差が出てきます。

2つ目が、no_stdをサポートしているかという点です。ブロックチェーンでスマートコントラクトを実行する時に、とある要件を満たしたい場合、このno_stdをサポートしているランタイムなのかどうかという点がかかわってきます。

3つ目が、Deterministicです。1台のサーバーで実行される場合と違って、ブロックチェーンは多くのノードが同時に同じプログラムを実行します。そのため、各ノードが同じ実行結果を返すことを保証する必要があります。no_stdとDeterministicについては、今回のプレゼンの後半で少しだけ話題に上げます。

WASMバイトコードをマシンコードに変換する

我々が今回注目する点はパフォーマンスなので、1つ目に挙げたコンパイラとインタプリタについて詳しく見ていきます。まず、WASMバイトコードを実行可能なマシンコードに変換する方法として、2つのコンパイル形式について説明します。

1つ目がJust-in-timeコンパイル、通称JITコンパイルと呼ばれる形式です。これはコードの実行時にコンパイルを行います。はじめて実行されるコードは、あらかじめ用意された中間コードを経由してネイティブのマシンコードにコンパイルされます。そして、同じコードが実行される場合、すでにコンパイルされたネイティブコードが実行されます。

2つ目が、Ahead-of-timeコンパイル、通称AOTコンパイルです。JITコンパイルと違って、WASMのバイトコードを事前にネイティブのマシンコードにコンパイルします。そして、その後生成されたマシンコードを使って実行する方式です。

コンパイラについて、そのバックエンドをさらに見ていくと、表にあるとおり、主に「Singlepass」「Cranelift」「LLVM」の3つが利用されます。

1つ目のSinglepassですが、これはIRを使わずに直接バイナリを生成します。そのため、コンパイルが高速ですが、最適化していないので実行時のパフォーマンスはほかの2つに劣ってしまいます。

2つ目のCraneliftについてですが、これはIRに変換後、関数単位でコンパイルを実行します。コンパイルの速度とパフォーマンスはSinglepassとLLVMの中間に位置します。

そして最後のLLVMについてですが、これもIRに変換後、モジュール単位でコンパイルを実行します。コンパイルの速度は、ほかの2つのバックエンドと比べると遅くなりますが、パフォーマンスは3つのバックエンドで最速になります。

3つのバックエンドを比較すると、コンパイルが最も早いのがSinglepassで、パフォーマンスが最も出るのがLLVMであるということがわかります。

また、コンパイラ以外にも、インタプリタを使用することがあります。この場合、プロセスが起動してからすぐに実行できるメリットがありますが、実行速度はコンパイルするよりも遅くなります。

セキュリティリスクについて

ここまでの話を聞くと、みなさんはこのように思うかもしれません。「インタプリタはとても遅い。コンパイラを使ったほうがよさそうだ。その中でもLLVMが一番早そう。よし、LLVMを使おう」

しかし、事はそう簡単には進まないのです。スマートコントラクトを実行するVM内でコンパイラを利用する場合、とあるセキュリティリスクについて考慮する必要があります。

それがJIT Bombと呼ばれるものです。これは、実行よりもコンパイルに多くの時間がかかってしまうことをいいます。JIT Bombにより不適切なコードや悪意のあるユーザーがデプロイしたスマートコントラクトによって、ブロックチェーン全体がダウンしてしまうリスクがあります。

JIT Bombが起こるのはどのような場合でしょうか。それは、コンパイラの複雑な最適化とコード量が原因となります。CraneliftやLLVMは、複雑性とそのコード量からコンパイラがアップデートされるたびに、その変更される内容がJIT Bombを引き起こす可能性があるかどうかということをセキュリティ監査することが非常に難しいです。

しかしセキュリティリスクがあるとはいっても、インタプリタ形式のランタイムだけではパフォーマンスに満足できないと思われます。したがって、パフォーマンスを考慮してランタイムを採用する場合、コンパイラのバックエンドにはSinglepassを利用することが好ましいです。

しかしこの場合も、コンパイルしてマシンコードを生成するのに十分複雑な実装をしているので、JIT Bombを引き起こす可能性があるという意見もあります。そのため、Singlepassを使う場合であっても、コンパイラのアップデートのたびにその変更がJIT Bombを引き起こすかどうか、厳しいセキュリティ監査が必要になります。

何を基準にしてランタイムを選ぶべきか

ここまで、スマートコントラクトを実行するVMとしてWASMを採用する場合に、パフォーマンスの点からランタイムで使われるコンパイラとそれを採用する時に注意すべきJIT Bombについての話をしてきました。

現時点でスマートコントラクトの実行基盤として採用できるランタイムはいくつか存在しています。では、何を基準にしてランタイムを選ぶべきなのでしょうか?

どのランタイムを使うかを検討する場合、優先するポイントによって選択するプロダクトが変わってきます。

1つ目は、今までに話の中心となってきたパフォーマンスです。これは、最初にランタイムの特徴として挙げた3つのポイントのうち、コンパイラかインタプリタなのかというところがかかわってきます。

そして、パフォーマンスだけではなく、優先するポイントはいくつかあると思われます。それが例えば、ここで挙げたプライバシーやセキュリティです。

2つ目のプライバシーは、ランタイムの要素で挙げたno_stdをサポートしているかどうかという点がかかわってきます。そして3つ目のセキュリティは、ランタイムの要素で挙げたDeterministicがかかわってきます。

これらは、どのようなケースでそれぞれのポイントを優先すべきなのか、具体的な場面をイメージしながら1つずつ詳しく見ていきましょう。

まずは、今までの話の中心でもあったパフォーマンスです。例えば、あなたの開発しているサービスはユーザーをたくさん抱えており、その数は日を追うごとに増えていっています。

ビジネスロジックもすべてスマートコントラクトで実行しており、その基盤としてインタプリタのランタイムを使っていますが、このままではトランザクション数が性能の限界を超えるかもしれません。そうなってしまえば、ユーザーの離脱は避けられません。

そのような問題を回避するためにも、パフォーマンスを優先しましょう。この場合、インタプリタ形式ではなく「Wasmer」のようにコンパイルをして最適化を行ってから実行するランタイムが好ましいです。この場合は、コンパイラのバックエンドとしてはSinglepassを使用し、JIT Bombを引き起こすかどうかを確認する厳しいセキュリティ監査が必要になります。

プライバシーの問題にも留意する

2つ目のプライバシーについて見てみましょう。例えば、あなたの朝の1杯のコーヒーを売っているサービスがあるかもしれませんし、リフレッシュ休暇のために旅行を計画する時、宿泊場所や交通機関の予約を行うサービスがあるかもしれません。そのようなサービス上で購入履歴や旅の予約状況がスマートコントラクトで管理されていたとします。

今のシステムでは、取引履歴はすべてブロックチェーン上で公開されており、あなたがどのタイミングでコーヒーを飲んでいるのか、あなたがいつどこに行ったのか、誰もが知る状況にあります。

もちろん、コーヒーの購入履歴が公開されていることなどささいなことだと思うかもしれませんが、ブロックチェーンを使っていないサービスで履歴がすべて公開されていることはあり得ないので、ユーザーはそこにギャップを感じるかもしれません。

そのため、ブロックチェーンを使ったサービスでも取引内容を秘匿化したいというニーズは、当然のものです。そこでスマートコントラクトによるトランザクションを秘匿化するためのソリューションとして「TEE」を使いたいと思います。このTEEとは、Trusted Execution Environmentの略で、CPUでクリティカルな処理を既存のOSから物理的に隔離して実行するための機能のことを意味します。

近年では、ブロックチェーンのトランザクションを秘匿化するためのソリューションの1つとして、このTEEにも注目が集まっています。TEEの内部でスマートコントラクトを実行する場合、no_stdをサポートしているランタイムを利用する必要があります。

最後は、セキュリティです。セキュリティの高さが非常に重要で、絶対にダウンさせてはならないサービスがあるとします。例えば、金融商品を扱うようなサービスです。この場合、セキュリティリスクを少しでも下げるためには、Deterministicに対して厳密になる必要があります。

例えば、「wasmi」というランタイムは、ブロックチェーンで利用されることを目的として開発されたランタイムなので、厳密なDeterministicが要求される場合は、こちらを採用するほうが好ましいでしょう。しかし、インタプリタで実行されるため、パフォーマンスはWasmerのようにコンパイルを行う場合よりも遅くなるというデメリットもあります。

以上、ランタイムを採用する上で考える3つのポイントについて話をしてきました。実際、優先することによって当初利用していたランタイムを変更したプロジェクトもいくつかあります。

それをまとめた表がこちらです。「Holochain」は当初、インタプリタで実行されるwasmiを利用していましたが、パフォーマンスを考慮してコンパイルを行っているWASMへ移行しました。

「Secret Network」は、「Cosmos SDK」をベースに開発されているプロジェクトで、TEEを用いて状態遷移の秘匿化を目指しています。Cosmos SDKをベースにしているブロックチェーンのために、スマートコントラクトの実行モジュールとして「CosmWasm」というプロジェクトがありますが、それはWasmerを使っています。

しかし、Wasmerはno_stdをサポートしていないため、スマートコントラクトの実行結果を秘匿化することはできません。そのため、彼らは内部で使われているランタイムをWasmerからno_stdをサポートしているwasmiへと移行しました。

また、これはまだ未定ですが、「Polkadot」では、Deterministicの問題が解決されればwasmiから「V8」もしくは「SpiderMonkey」へと移行する計画があるということを表明しています。

パフォーマンス向上のために

ブロックチェーンのプラットフォームによって優先するポイントが異なるため、ランタイムの変更が行われることがわかりました。

では、我々のプラットフォームでは何が重要になるのでしょうか? 最初にも述べたように、我々はパフォーマンスを優先するため、インタプリタではなく、コンパイルを行うランタイムを採用します。しかし、グローバル市場での展開を目指すことを考慮すると、さらに高いパフォーマンスが必要となることが予想されます。

そこで、Singlepass自体も継続的に最適化して、よりパフォーマンスを向上させることを目指しています。

まとめです。ブロックチェーンのスマートコントラクトの実行基盤としてWASMを使うには、VMを構築する必要があります。その中で使うランタイムがパフォーマンスに影響しますが、ほかにも考慮する点があるので、実際にVMの開発を行う場合は何を優先するのかによって採用するランタイムを選択する必要があります。

以上で、我々のセッションは終了となります。ご清聴ありがとうございました。