ブロックチェーン間でじゃんけんゲームをしてみませんか? ルールは簡単で、N個のチェーンにじゃんけんのコントラクトを実装し、お互いに通信しながらじゃんけんを実施し、最後に結果を取得します。なんだか、とっても簡単な話に聞こえますよね?
それではやってみましょう!
本記事では、ブロックチェーン間でじゃんけんをすることを通して、クロスチェーンメッセージを使ったアプリケーションの開発体験を垣間見ることを目的としています。といっても、プログラミングや開発を主軸とした話ではないので、身構える必要はありませんよー
舞台は次のように設定します。
開発者はN個のチェーンにコントラクトを実装し、「ある時間になったら」じゃんけんゲームを始めるように定義します。一人の開発者が勝手にコントラクトを整備して、勝手にじゃんけんさせるという異様な光景を想定してください。
簡単のためにセッションIDは考えません。要は、じゃんけんは一回限りで、あいこになっても再戦しません。一回じゃんけんしたら、二度とじゃんけんをしません。
ブロックチェーン間のコミュニケーション手段として、汎用クロスチェーンメッセージングプロトコル(以下CCMPとします)を使用します。具体例をあげるなら、LayerZeroなんかが有名でしょうか。
そして、コントラクトは次のような動きをします。
時間が来たら、まず自分の手を考えます。簡単のために、コントラクトは手を出すためにランダムな値(注)を生成し、3で割った余りで自分の手を考えます。例えば、1余ったらグー、2余ったらチョキ、割り切れたらパーといった感じです。
そしたら、コントラクトはCCMPを使って対戦相手のチェーンに自分の手を知らせます。
それと同時に、コントラクトは対戦相手のチェーンから手の情報を受け取ります。「出た手の組み合わせによって勝敗状況はどうなるのか」ということはじゃんけんのルールから自明なので、全員の手がわかったら勝敗がわかります。繰り返しますが、あいこになっても再戦しません。
一人の開発者がコントラクトを実装するので、自分の手を秘匿化する必要はありません。つまり、後出しじゃんけんされるリスクは考えません。
条件が出そろいました! メッセージのやり取りについて、もうちょっと深く考えてみましょう。
注: ブロックチェーンは基本的にランダムな値の生成に対応してないのですが、ここは簡単のために…
ここで使用するCCMPは、次のような機能を有するとします。
あらゆるチェーンに対応している。
メッセージングは二者間のチェーンで行われる。それは双方向ではなく、手紙のように一方的なやり取りである。
宛先がメッセージを受け取れなかった場合、送信者に未到達エラーを通知する機能がある。
メッセージを送信するごとにガス代がかかる。
すべてのチェーンが互いに対等な立場だとします。
つまり、ゲームを取り仕切るゲームマスターのような存在は考えないものとします。ある特定のチェーンに注目したとき、そのチェーンは何回メッセージを送ることになるのでしょうか。
このように、自分以外のチェーンにメッセージを送るので、そのチェーンはN-1回メッセージを送ることになります。このようなチェーンは合計N個存在するので、このゲームにおけるメッセージの伝達回数はN(N-1)回です。Nの2乗の項が出てきてしまったので、チェーンが増えれば増えるほど、ガス代が跳ね上がることが予想されます。
これを解決するには、ゲームマスターを設定します。つまり、対戦者はゲームマスターのチェーンに自分の手を提出し、ゲームマスターからはじき出される結果を受け取るという算段です。これにより、メッセージの伝達回数はNの2乗から線形に削減されます。
ここで、じゃんけんの途中で離席してしまう困ったチェーンがいることを考えましょう。具体的には、じゃんけんをしている途中で、ネットワークがダウンしてしまうような状況です。そのような場合でも、じゃんけんは成立するのでしょうか。
すべてのチェーンが互いに対等な立場だとします。(ゲームマスターの存在はいったん忘れてください!)
この場合、どういった状況になると困るのかを考えたいのですが、その前にこの一文を思い出しましょう。
よって、メッセージが到達しなかった場合は、宛先のチェーンが離席したと考えることができるので、送信元のチェーンは離席したチェーン抜きでゲームを続行できます。離席したチェーンの存在がわかったらみんなに通知できればなおよいですね。(その分ガス代がかかりますが)
しかしながら、そもそもメッセージを送信しなかったチェーンを、ほかのチェーンはどのように離席状態であると判断するのでしょうか。これには、「ある時間経過してもメッセージを送ってこないチェーンは、離席したものと考える」という処理を実装する必要があります。そうしないと、一向に離席判定を下すことができません。
と、ここである懸念が浮かんでくる方がいるのではないでしょうか。例えば、自分の手を対戦相手に続けて送信している途中で離席(障害)が発生してしまった場合です。チェーンA~Eの5人がじゃんけんをする途中で、Aが離席してしまう場合を考えてみましょう。そうすると、次のような困ったことが起こる可能性があります。
この図は、「チェーンAが対戦相手の手の情報を知ってから、自分の手の情報を送り始める」という状況を考えています。このとき、導びかれるゲームの結果がチェーンによって異なってしまいます。とてもじゃんけんゲームが成立しているとはいいがたい状況です。こうならないようにするためには、ゲームの結果が一致しているかどうかを、すべてのチェーン間で合意を取ることで解消できますが、そうしたらまた余計なガス代がかかってしまいます。また、もし結果が一致してなかったとしたら、ゲームを無効にして切り戻すのか、多数決をとって強引に結果を決めるのか、開発者はあらかじめ決めておかなければなりません。
以上のように、障害のようなイレギュラーな状態を考慮すると、クロスチェーンのやり取りに原子性(やり取りが完結するか、問題等が起きたら元の状態に戻るかの二択しかない性質)をもたせつつ、アプリケーションとして表現するのは大変そうであることが伺えます。
あなたはさらに開発を進め、セッションIDを導入し、じゃんけんの再戦機能を作ることができました。もし今後魅力的なチェーンが出てきて、そのチェーンにもじゃんけんに参加できるようにしたくなった場合、何をする必要があるでしょうか。
答えは、「既にある全てのチェーンのコントラクトに手を加える必要がある」です。既存のコントラクトには、新しく追加されたチェーンとやり取りする機能を追加しなければなりません。大変ですね。
以上のように、クロスチェーンメッセージングを用いたアプリケーションは、機能を豪華にすればするほど、より多くのチェーンに対応すればするほど、必要なメッセージの数が爆発的に増えてしまうという欠点があります。なぜなら、チェーン間で状態の同期をとらなければならないからです。また、将来の拡張性に目を向けた際、必要な工数も膨れ上がる傾向にあります。
例えば、チェーンAにあるトークンと、チェーンBにあるトークンと、チェーンCにあるトークンを担保に、チェーンDでお金を借りるにはどうしたらよいのでしょうか? オラクルに基づき、各チェーンに担保として預けた資産の価値を確認し、お金を借りる(ことを許可する)機能を作るのは大変であることが予想されます。(それ以外にも、レンディングプロトコルには様々な機能がありますよね)
そのため、クロスチェーンメッセージングを用いたアプリケーションは、以下のようなものに限られるのが現状です。
2つ(や小数)のチェーンが相互作用するようなアプリケーション(ブリッジとか)
1つのチェーンがほぼすべての状態を管理するようなアプリケーション(この記事だと、ゲームマスターがいるじゃんけんゲームのような)
このように、クロスチェーンメッセージングは魅力あるソリューションなのですが、開発者の負担が大きいってことを下記ツイートよりも上手に伝えたかったのであります!
ちなみに、ぼくがアンバサダーをやってるZetaChainのオムニチェーンスマートコントラクトという機能では、このような問題を解決できるとされているみたいですよ?
なし