はじめに
こんにちは、新規事業開発室のOsuke(@zoom_zoomzo)です。 Twitterなどでご存知の方も多いと思いますが、今月6月からGunosyに所属しブロックチェーン事業を行っていくことになりました。
簡単に自己紹介をすると、これまでは個人でブロックチェーン分野、主にイーサリアムに関するリサーチを行い、ZooMというブログなどで情報発信を中心に行ってきました。
これまで個人で活動していたので慣れないことも多いですが、引き続き情報発信もしっかり行っていきたいと思いますのでこれからもよろしくお願いします!
そこで、今回は入社報告も兼ねて、イーサリアムのスマートコントラクトにおける擬似乱数生成についての解説記事を書いていきたいと思います。
スマートコントラクトで安全な擬似乱数生成が困難な理由
イーサリアムをベースとしたゲームDappsや代表者選出を分散的に行いたい場面などで、擬似乱数を活用する必要性が出てきます。
しかし、イーサリアムのような分散ネットワークでの擬似乱数生成は脆弱性を生じやすく注意して開発しなければなりません。
まず、安全に使用できる擬似乱数を生成する上で重要なことは主に以下の2点があげられます。
- その値を誰も予測することができないこと(特にマイナーが操作できないように)
- 複数のコントラクトで同じ値にならないこと
イーサリアムのような分散ネットワークで安全に使用できる擬似乱数の条件として、その出力ランダム値が誰にも予測できない必要があります。
基本的に、擬似乱数はある入力値に対してアルゴリズムを通すことで、その出力値をランダム値として利用することになりますが、入力値を入れる人自身もそのランダム値を予測することができないことが必要になるのです。
なぜなら、例えばイーサリアムをベースとして、ランダムに報酬が得られるようなDappsを想定すると、特定の人がランダム値を予測できてしまうと不正に報酬を得ることができてしまうことになります。
さらに、後述するようにブロック由来のデータを入力値として利用してしまうと、ランダム値に見えても同じブロック高では複数のコントラクトで同じランダム値が生成できてしまうという問題も存在します。
要するに、イーサリアムのような分散ネットワークでの安全な乱数生成が難しい理由というのは、トランザクションにより生成した擬似乱数が安全である、とトラストレスに合意形成を得る必要があるからです。 例えば、ネットワークのひとつのノードが安全な擬似乱数を生成できたとしても、その安全性はそのノードをトラストする必要があります。この擬似乱数を上手くネットワーク全体が「安全である」と合意形成を得なければならないのです。(さらに、マイナーが不正操作できないようにすることを考慮すると、これはさらに困難になります。)
そこで、ここからはブロックチェーン由来のデータをインプットとして活用した「安全ではない擬似乱数生成」について解説した後に、ブロックチェーン外部のデータを活用した「比較的安全な擬似乱数生成」についてお伝えしていきます。
安全ではない擬似乱数生成
ブロックチェーンに記録された値の利用
基本的に、乱数生成のインプットとしてブロックチェーンに記録されるデータを利用すると潜在的にマイナーがその乱数を悪意を持って操作できてしまう危険性があります。
例えば、乱数生成に利用されうるブロックチェーンのデータとしては以下のようなデータがあげられます。
block.coinbase
:ブロックを生成したマイナーのアドレスblock.difficulty
:PoW由来のブロックの生成難易度block.gaslimit
:ブロックに含めることができる最大のgas量block.number
:ブロック高block.timestamp
:ブロックが生成された時間
これらのうちdifficultyやtimestampは一見、乱数として利用することができるように感じますが、これらの値もマイナーがブロックチェーンに記録することになるのでマイナーに不正操作されてしまう脆弱性があります。例えば、block.timestampを乱数として利用したゲームで高額報酬が得られる場合、マイナーが不正操作するインセンティブになってしまうのです。
なのでこのような値をベースに擬似乱数を生成することは避けなければなりません。
ブロックハッシュを利用した擬似乱数生成
EVMにおいて、block.blockhash(uint blockNumber)
を利用するとblockNumberとして与えられたブロック高におけるブロックハッシュ値を得ることができます。
例えば、以下のようなブロックハッシュが考えられます。
block.blockhash(block.number)
:トランザクションを処理しているブロックのハッシュblock.blockhash(block.number -1 )
:直近のブロックのハッシュblock.blockhash()
:256ブロック前のブロックハッシュ(スケーラビリティのため得られるブロックハッシュは256ブロック前までに制限されています)
これらのうち、block.blockhash(block.number)
については0x00が返ってきてしまうので注意しなければなりません。なぜなら、この関数を処理している時点ではそのブロック高のハッシュ値は決まっていないからです。
また、block.blockhash(block.number-1)
やblock.blockhash()
については他のコントラクトでinternal messageにより同じ関数を呼び出すことができてしまうので、複数のコントラクトで同じランダム値が生成されてしまう脆弱性があります。
さらに、ブロックハッシュの他の活用方法として任意の値saltを用いたblock.blockhash(block.number - salt)
などを擬似乱数として活用することも考えられます。
例えば、コントラクトに任意の変数saltをprivate修飾子をつけて保持していれば他のコントラクトがsaltを参照することを防ぐとともに上記のblock.blockhash(block.number - salt)
を利用することができます。
しかし、このsaltについてはオンチェーンでの参照を防ぐことはできますが、web3.eth.getStorageAt()
などでオフチェーンから参照することができてしまいランダム値の予測をすることができてしまうのです。
以上のようにブロックハッシュを用いた擬似乱数の安全な生成も難しいことが分かります。
より安全な擬似乱数生成
以上のように基本的にブロックチェーン由来のデータを活用した擬似乱数生成はマイナーが不正操作するインセンティブを与えてしまったり、同じブロックに含まれると複数のコントラクトで同じランダム値が得られてしまうという脆弱性がありました。
Oraclizeの利用
Oraclize - blockchain oracle service, enabling data-rich smart contracts
Oraclizeとは、ブロックチェーン外部の情報とブロックチェーン内部を橋渡しするサービスです。つまり、Oraclizeを活用することでWEB APIからブロックチェーン外部のランダムソースを利用することが可能になるのです。
この場合、ブロックチェーン自体のデータは活用しないので上述したような脆弱性は起こりえないので安全性は高まることになります。しかし、一方でOraclizeのサービス自体とランダムソースとなるrandom.orgなどを信用する必要があります。
つまり、もしランダムソースとなるサービス自体やそれをブロックチェーン内部へ持ってくるOraclizeのサービス自体で不正が起きたら、その擬似乱数は安全ではなくなってしまいます。
RANDAO
あるアルゴリズムに対して一人のユーザーが入力する方法だと、そのユーザーが乱数を簡単に予測できてしまいます。
それに対し、RANDAOでは複数のユーザーがそれぞれ任意のインプットを入力することで、より安全な乱数を生成しようとする方法になります。つまり、参加者自身も他の参加者がどのようなインプットを入力したか分からないので、乱数を予測することが困難になるのです。
さらに、インプットを入力してくれたRANDAO参加者に乱数を利用した時に得られた手数料を報酬として与えることでRANDAOに参加するインセンティブを与えています。
具体的にこのRANDAOは以下の3つのフェーズに分かれたプロセスによって行われていきます。
フェーズ1:commitフェーズ
このフェーズではそれぞれの参加者が任意のインプットをコミットするフェーズです。RANDAO用のコントラクトに参加者が任意の値sのハッシュ関数sha3(s)とともに特定のデポジットを含めるトランザクションを送信します。(ここでデポジットを含める理由は、参加者がプロセスの途中で勝手に離脱しないようにするためです。)
値sは秘密値に相当するのでそれぞれの参加者は自身で管理しておく必要があります。
また、コミットフェーズを安全に行うために以下のようなルールも設定されています。
- このとき同じ値のsha3(s)が提出された場合は、最初に提出されたsha3(s)のみが有効
- 十分な数の参加者がいない場合は、そのブロック高におけるプロセスは停止する(参加者が少ないほど乱数予測可能性が高まってしまうので)
フェーズ2:revealフェーズ
このフェーズでは、最初のcommitフェーズに参加した人がそれぞれのハッシュ値の元となる値sを提示し、コントラクト側でコミットされたsha3(s)と正しく一致するか検証が行われます。
commitフェーズで参加した人は秘密値sをrevealしないとデポジットした資金を返金してもらえないので、正しく値sを提示するインセンティブとなっています。
コミットされた秘密値sが全て集まると次のフェーズに移ることができます。
フェーズ3:擬似乱数生成フェーズ
集まったすべての値sを利用することで次のフェーズで乱数が生成されていきます。つまり、コントラクトで関数の引数にf(s1,s2,...,sn)として、返り値が擬似乱数として活用されることになるのです。
ほかのコントラクトがこの擬似乱数を利用したい場合は一定の手数料が必要になります。
そして、RANDAO参加者にcommitフェーズでのデポジットが返金されるとともに、乱数利用の手数料が参加報酬として与えられます。
以上のようなプロセスでRANDAOによる乱数生成は行われ、参加者への報酬と乱数使用による手数料が課せられることになるのです。
まとめ
スマートコントラクトにおける擬似乱数生成の安全性について解説しました。
擬似乱数を悪用しても得られる報酬額がそこまで多くない場合は、マイナーが不正を働くコストの方が高くなることも多いため、ブロックチェーンのデータを一時的に擬似乱数生成のインプットとして使われるケースも多くなっています。
また、現時点ではインセンティブ設計に基づいたRANDAOの仕組みが比較的安全な擬似乱数生成の代表となっています。しかし、スマートコントラクトでの擬似乱数生成はさまざまな脆弱性の原因となりうる部分であり、BLS署名を応用するなどより安全な方法の開発も進められているのが現状です。
宣伝
Gunosyではブロックチェーンを始めとしてスマートスピーカー、VR/ARと言った新規領域での研究・開発を進めており、メンバーを募集しています。
少し話を聞いてみたい!という方も歓迎していますので、下記のリンクからの応募お待ちしております。
また弊社Gunosyでは、blockchain.tokyo を主催するなど、ブロックチェーンや仮想通貨に関する研究開発を進めています。
イベントグループへメンバー登録をしていただくとイベント参加者募集の通知が来るので、こちらもぜひ登録してみてください。