
ブロックチェーンアプリケーション向けに安全な乱数生成技術を構築する
TechFlow厳選深潮セレクト

ブロックチェーンアプリケーション向けに安全な乱数生成技術を構築する
ブロックチェーン上にはさまざまな乱数生成方法が存在する。本稿では、それらのいくつかの方法とその長所・短所について説明し分析する。

多くのブロックチェーンアプリケーションでは、信頼できるデータソースから生成された乱数を使用する必要があります。
たとえば、NFT PFPシリーズのエアドロップでは、各NFT画像にランダムな属性の組み合わせを選択する必要があります。また、オンチェーングームでは宝箱を開ける際にアイテムをランダムにドロップさせることが求められます。どちらの場合も、安全な乱数生成器を使用することで、ユーザーがアプリケーションに対して公平に扱われることを保証できます。これらは乱数の利用シーンの一例にすぎず、一般的に言えば、ランダム性はさまざまな種類のアルゴリズム的問題を解決するための有用なツールです。
残念ながら、ブロックチェーン固有の性質により、乱数の生成は簡単ではありません。ブロックチェーンの状態は一連の決定論的ルールに基づいて進行し、各トランザクションはその入力値に基づいて特定の出力状態を生成します。これらのルールは決定論的である必要があり、これはすべてのバリデータが各トランザクションの処理を検証できるようにするためです。もしルールが決定論的でなければ、バリデータ間で状態について意見の相違が生じる可能性があります。
ブロックチェーン上にはさまざまな乱数生成方法があります。本稿では、それらのいくつかの方法とその長所・短所について説明・分析します。
参加者
乱数生成プロトコルには、通常複数の異なる参加者が関与します。各参加者はプロトコルの関係者であり、生成される結果に関心を持つ存在です。方法によって関与する参加者のサブセットは異なります。考えられる参加者には以下のようなものがあります。
アプリケーション開発者――開発者は、乱数を要求して使用するソフトウェアを記述する責任を持ちます。開発者が乱数生成プロセスに直接関与することは通常ありませんが、結果が真にランダムであることを望んでいます。たとえば、あるNFTシリーズの開発者は、誰もがレア属性をすべて持つNFTを不正に取得できないよう、NFTの鋳造プロトコルがフェアであることを確保したいと考えています。
ユーザー――ユーザーはプロトコルと相互作用することで乱数生成をリクエストします。たとえば、NFTを鋳造する人がこれに該当します。
バリデータ――多くの乱数生成プロトコルではブロックチェーンからの入力データを利用します。ブロックチェーンのバリデータ(またはマイナー/シーケンサー)は、入力データに対してある程度の制御権を持っている可能性があります。
サービスプロバイダー――多くのプロトコルには、乱数生成プロセスの一部を担当する指定されたオフチェーンのサービスプロバイダーがいます。このサービスプロバイダーは中立的な第三者であるべきですが、これにより、参加者に対する信頼を最小限に抑えるためにより複雑な乱数生成手法が必要になります。
なお、1人の人物が複数の役割を担うこともあります。たとえば、ユーザー自身がブロックチェーンネットワーク上でバリデータを運営している場合です。こうしたプロトコルにおける攻撃ベクトルの多くは、複数の参加者が密かに協力することによって生じます。
属性
乱数生成方法について議論する前に、期待される乱数生成器の属性について共通理解しておく必要があります。
各乱数生成器は2段階で動作します。まず「リクエスト段階」で、ユーザーが生成器に乱数を要求します。次に「公開段階」で、生成器が乱数を生成し、ブロックチェーン上に公開します。各段階は少なくとも1回のブロックチェーントランザクションを必要とします。単一のトランザクションで安全にオンチェーンの乱数を生成することは不可能です。ただし、異なるプロトコルでは各段階で実行される正確な計算内容が異なります。
私たちが注目すべきセキュリティ属性は主に以下の3つです。
予測不可能性――乱数をリクエストする前に、どの参加者も乱数を予測できないこと。この属性が「ランダム」という概念の正式な定義です。
決定性――乱数をリクエストした後、その値はただ一つに決まること。この属性により、結果のリクエスト後に参加者が結果に影響を与えることができないことが保証されます。
活性――乱数をリクエストした後、プロトコルの実行が即座に完了すること。つまり、リクエスト段階の完了と公開段階の完了が同時に起こることです。
乱数生成プロトコルに関する鍵となる問いは、「これらの属性がどのような条件下で成立するか?」ということです。たとえば、プロトコルがサービスプロバイダーの誠実さを要求する場合があります。こうした条件こそがプロトコルの信頼前提(trust assumption)です。信頼前提が少ないほど、プロトコルはより安全です。
乱数生成方法
信頼できる第三者
最も単純な乱数プロトコルは、サービスプロバイダーに乱数を生成させることです。ユーザーが乱数をリクエストすると、サービスプロバイダーはオフチェーンで乱数を生成し、それをブロックチェーンに再送信するだけです。
この方法は非常にシンプルですが、強力な信頼前提を必要とします。すなわち、参加者はサービスプロバイダーの誠実性を信じなければなりません。サービスプロバイダーは自由に乱数を選べるだけでなく、プロトコルを停止することも可能です。このような前提は、サービスプロバイダーがIntel SGXなどの安全な分離環境(trusted execution environment)内で計算を行うことで多少改善できますが、そういった環境もすでに何度もその不完全さが示されています(SgxPectre攻撃)。
ブロックハッシュ
単純な乱数生成戦略の一つとして、将来のブロックのブロックハッシュを使う方法があります。乱数生成リクエストのトランザクションは現在または将来のブロック番号を記録します。その後、ネットワークのバリデータがそのブロックのブロックハッシュを計算します。ブロックハッシュが利用可能になった時点で、生成器は公開用トランザクションを発行します。
ブロックハッシュによる生成方法はシンプルで、あらゆるブロックチェーンで容易に利用できます。しかし、強い信頼前提を必要とします。参加者はバリデータの誠実性を信じなければなりません。バリデータはトランザクションの並べ替えや無視によってブロックハッシュを改変することが可能です。そのため、プロトコルのユーザーがバリデータを運営していたり、バリデータと結託していたりすれば、有利な乱数を得るために攻撃を行うリスクがあります。
検証可能乱数関数(VRF)
これまでに述べた方法の問題点は、単一の参加者が乱数生成結果に影響を与えられること(つまり予測可能になること)にあります。検証可能乱数関数(Verifiable Random Function: VRF)は、複数の参加者が協力して乱数生成に影響を与える必要があるようにすることで、このような攻撃経路を排除します。
VRF とは関数 f_s(x) = (y, p) のことで、出力値 y はランダムに見えるが、入力値 x と秘密鍵 s によって確定的に計算されます。さらに、この関数は証明値 p も返します。誰でもこの p を使って、y が正しい出力かどうかを検証できます(これは非常に簡潔な説明です。VRFについて詳しくはIETF RFCを参照してください)。
ブロックチェーン上でのVRFの使い方は通常次の通りです。まず、入力値 x はユーザーが提供する部分とブロックハッシュから得られる部分とに分けられます。オフチェーンのサービスプロバイダーは秘密鍵 s を使ってブロックチェーン上のリクエストを監視し、対応する値 (y, p) をオンチェーンに提出します。公開トランザクションでは証明値 p を検証して y が正しい値であることを確認した上で、公開を行います。
VRFの利点は、以前の方法よりも信頼前提が改善されることです。すなわち、ユーザー、バリデータ、サービスプロバイダーの全員が共謀しない限り、乱数を予測することはできません。VRFの主な懸念点は「活性」です。参加者はサービスプロバイダーがトランザクションを審査しないことを信用しなければなりません。サービスプロバイダーは生成された乱数を事前に知ることができ、それをブロックチェーンに提出するかどうか選択できます。たとえば、ユーザーがコイントスをリクエストし、VRFプロバイダーと結託して結果が表だったときだけリクエストを完了させるという攻撃が考えられます。しかし、アプリケーション開発者はこうした攻撃を軽減できます。特に、ユーザーが公開失敗から利益を得られないように設計すれば、このような攻撃を行う動機がなくなります。
VRFのもう一つの欠点は、暗号学的に比較的複雑で計算負荷が高いことです。ほとんどのブロックチェーンは必要なすべての暗号基盤を内蔵していないため、乱数を公開するのに大量のガスを消費したり、複数のトランザクションを必要としたりする可能性があります。
コミット&リヴェール(Commit-and-Reveal)
VRF以外にも、信頼しない相手同士で乱数を生成するコミット&リヴェール方式があります。このプロトコルの基本的な動作は以下の通りです。
-
リクエスト段階で、ユーザーとサービスプロバイダーはそれぞれ秘密の乱数を生成します。そして、それぞれの乱数のハッシュ値をブロックチェーンに提出します。このハッシュ値をコミット値と呼びます。
-
リヴェール段階で、ユーザーとサービスプロバイダーはそれぞれ自分の乱数を公開します。各参加者は、相手が公開した数値が自分のコミット値と一致するかをチェックします。このステップが完了すると、最終的な乱数はこの2つの乱数のハッシュ値となります。ブロックチェーンへの展開では、リクエストトランザクションのブロックハッシュも最終的なハッシュ計算に含めることができます。
初期の方法と比べて、コミット&リヴェールプロトコルも信頼前提を改善しています。ユーザー、バリデータ、サービスプロバイダーの全員が共謀しない限り、乱数を予測できません。さらに、このプロトコルは単純な暗号技術しか使っていません――ハッシュ関数だけで済むため、実装が容易です。しかし、VRFと同様に「活性」に関する問題があります。ユーザーまたはサービスプロバイダーのいずれかが即座に自分の乱数を公開しなければ、プロトコルは中止されます。VRFと同様に、アプリケーション開発者は前述のVRF向け対策と同じ技術を使って、この攻撃経路を軽減できます。
なお、このプロトコルの基本的な実装では、ユーザーとサービスプロバイダーがそれぞれ2段階でトランザクションを送信する必要があるため、最低でも2回以上のトランザクションが必要になります。この欠点は、サービスプロバイダーがあらかじめ複数の乱数を前もってコミットできるような高度な展開方式で解決可能です。Pyth Entropyはコミット&リヴェールプロトコルの高度な実装例です。
VRFおよびコミット&リヴェールプロトコルはいずれも、アプリケーション開発者が「活性」を犠牲にして「予測不可能性」を獲得することを可能にします。開発者が単一のサービスプロバイダーを信頼することに不安を感じる場合は、N個のサービスプロバイダーに乱数をリクエストし、結果を組み合わせればよいのです。この方法により、不可予測性が向上します(結果に影響を与えるにはN個すべてのプロバイダーが協力しなければならない)が、「活性」は低下します(N個のプロバイダーのうち1人でも公開を拒否すれば、乱数の公開が中止される)。ただし、N個のプロバイダーのうち個々の参加者は最終的な乱数を知ることができないため、その知識を使ってプロトコルの中止タイミングを決定することはできません。
その他の方法
安全な乱数を生成するためのさらに高度な方法もあります。これらは「予測不可能性/活性」のトレードオフを上層で改善しようとするもので、プロトコルの中止には>1人のプロバイダーが必要になるように設計されています。その一例が閾値VRF(Threshold VRF)で、VRFの秘密鍵sを複数の参加者間でシェアします。別の方法としてランダムビーコン(random beacon)があります。こうした方法は確かに前述の方法よりも優れたセキュリティ特性を持ちますが、大多数のアプリケーションにとっては過剰設計であり、活性の問題はアプリケーション設計によって十分緩和できるため、導入の必要性は限定的です。
結論
本稿では、ブロックチェーン上で乱数を生成するいくつかの方法を紹介し、それぞれの長所・短所を分析しました。安全な乱数生成器を必要とするアプリケーションを開発している場合は、Pyth Entropyを確認し、Discord、Telegram、その他のソーシャルチャネルを通じてPythの貢献者に連絡して、この革新的な製品の使い方について学んでください。
TechFlow公式コミュニティへようこそ
Telegram購読グループ:https://t.me/TechFlowDaily
Twitter公式アカウント:https://x.com/TechFlowPost
Twitter英語アカウント:https://x.com/BlockFlow_News














