5 月 31 日、Curio(@0xcurio)は Keystone をオープンソース化しました。Keystone は、ゲームの Tick と ECS フルチェーンゲームエンジンを組み込んだ L2 チェーンであり、OP Stack に基づいています。この設計では、ECS の操作(クエリや状態の設定など)がより高速に実行できるため、ECS の状態をスマートコントラクトで記述するよりもパフォーマンスが向上します。カスタムプリコンパイルにより、スマートコントラクトは低レベルの ECS チェーンの状態にアクセスできます。ゲームロジックは Go 言語で記述することができ、Solidity ではなく、大規模な並列処理が可能です。本記事では、Curio プロジェクト自体とその原理について詳しく説明し、上記の目的をどのように実現しているかを探ります。
Curio は、エンジニアでゲームプレイヤーの Kevin Zhang(@kzdagoof)と Yijia Chen(@0x1plus)によって 2022 年に設立され、完全にスマートコントラクトで駆動するフルチェーンゲームの開発に取り組んでいます。これにより、新しいマルチプレイヤー計算方法が可能になり、すべての参加者が「共有の宇宙」に貢献できるようになり、ゲームがほぼ完全にプレイヤーによって作成されることができると創設者は述べています。同社の最初のゲームである Treaty は、オンチェーンのストラテジーゲームであり、ユーザーはスマートコントラクトを作成してデプロイすることができます。2023 年 2 月 21 日、Curio は 290 万ドルのシードラウンド資金調達を発表しました。このラウンドは Bain Capital Crypto がリードし、TCG Crypto、Formless Capital、Smrti Lab、Robot Ventures、Zonff Partners、および複数のエンジェル投資家が参加しました。
フルチェーンゲームには現在、さまざまな物語の方法があります。一般的なものには、分散型ゲーム(DeGame)、自治世界(Autonomous Worlds)があります。Curio は独自のアイデアであるユーザー生成ロジック(User Generated Logic、UGL)を提案しています。Curio が自分たちの最初のフルチェーンゲーム Treaty を作成する過程で、多くの困難に直面したと推測され、それに応じて自分たちの最初のチェーンを作成し、ゲームエンジンを組み込むというアイデアが生まれました。これは Argus と似ていますが、Argus はシャーディングメカニズムを採用しており、Curio は OP Stack を直接採用しています。
MUD の ECS ゲームフレームワーク#
まず、MUD の ECS フレームワークがどのように機能するかを見てみましょう。私が書いた MUD のゲームエンジンについての解説記事「深度解析全チェーンゲームエンジン MUD」(https://captainz.xlog.app/shen-du-jie-xi-quan-lian-you-xi-yin-qing-MUD)を読んだことがある場合、ECS はゲーム開発の柔軟性とメンテナンス性を向上させるために、ロジック、データ、エンティティを分離することによって実現されることを理解しているはずです。プログラミング言語には Solidity が使用され、ゲームオブジェクトのプロパティ状態はスマートコントラクトに格納されます。たとえば、ERC-20 コントラクトでは、各アドレスのトークン残高がマップに格納されます(アドレスから uint256 の残高へのマッピング)。各 ERC-20 コントラクトは、「アドレス」と「残高」の 2 つの列を持つテーブルと見なすことができます。これは、単一のパターン値(「残高」)を持つコンポーネントに対応します。テーブルの各行は、エンティティ(「アドレス」)とコンポーネント値(「残高」)を関連付けます。1 つのアドレスは、多くの独立した ERC-20 コントラクトで残高を保持でき、これは 1 つのエンティティが多くの独立したコンポーネント値に関連付けられることを意味します。現在の ERC-20 の参照実装では、状態とロジックが同じコントラクトにカップリングされています。ECS では、転送システムを使用してアドレスから別のアドレスにトークンを転送するロジックを処理する共通の「転送システム」があります。この転送システムは、トークンコンポーネントに格納されている状態を変更することによって機能します。
MUD 内のゲームオブジェクトのプロパティ状態はスマートコントラクトに格納されているため、ECS の状態変更(クライアントの状態をブロックチェーンノードに同期する)は常にスマートコントラクトを介して同期されます。この同期プロセスは並列に実行することはできず、頻繁にコントラクトを呼び出す必要があります。そのため、Curio はプリコンパイルコントラクトを導入してこの問題を解決しようとしています。
プリコンパイルコントラクト#
イーサリアム仮想マシン(EVM)のプリコンパイルコントラクトは、イーサリアムノードのコードに直接ハードコードされた、Solidity や他の EVM 互換言語で書かれたスマートコントラクトではない特殊なタイプのスマートコントラクトです。これらのコントラクトは通常、複雑な計算タスクを実行するために使用されます。ハードコードされた実装は、EVM で解釈実行されるコードよりも効率的です。プリコンパイルコントラクトは、パフォーマンスを最適化し、ガスコストを削減するために使用されることが一般的です。
プリコンパイル関数の実装には以下の手順が関与します:
-
アドレスの選択:プリコンパイル関数にはアドレスが必要です。イーサリアムは、
1
からff
(ff
を含む)までのアドレスをプリコンパイルコントラクトのストレージに使用することを選択しました。 -
機能の実装:プリコンパイル関数には特定の機能が必要です。これには、楕円曲線操作や大きな整数演算などの複雑な計算が含まれることが一般的です。この関数は通常、Golang や C++ で書かれ、イーサリアムノードのコードに直接統合されます。
-
ガスコストの計算:プリコンパイル関数には、実行に必要なガスを計算する関数が必要です。この関数は、入力データのサイズと操作の複雑さに基づいてガスコストを決定する必要があります。
-
イーサリアムノードへの統合:プリコンパイル関数は、イーサリアムノードのコードに統合する必要があります。これには、イーサリアムノードのコードを変更して新しいプリコンパイルコントラクトを追加し、ノードを再コンパイルおよびデプロイする作業が一般的に含まれます。
その後、スマートコントラクトは、プリコンパイル関数のアドレスを呼び出すことでこのプリコンパイル関数を使用することができます。EVM は、そのアドレスがプリコンパイルコントラクトを持っているかどうかを確認し、持っている場合は EVM はスマートコントラクトのコードを解釈実行するのではなく、ノードのコードにハードコードされた関数を直接呼び出します。
新しいプリコンパイル関数を追加するには、イーサリアムのプロトコルを変更する必要があり、通常はコミュニティの合意が必要です。また、プリコンパイル関数はイーサリアムノードのコードにハードコードされているため、この新しいバージョンのイーサリアムノードを実行するすべてのノードには、この新しいプリコンパイル関数のコードが含まれている必要があります。したがって、新しいプリコンパイル関数を追加することは、実践的には複雑で慎重なプロセスです。
Curio の解決策#
上記のプリコンパイルコントラクトの議論からわかるように、プリコンパイルコントラクトを使用することでパフォーマンスを大幅に向上させることができますが、チェーンのノードコードを変更し、ノードを再コンパイルおよびデプロイする必要があります。イーサリアムメインチェーンではこれは不可能です。そのため、Curio は OP Stack を選択しました。このカスタム Layer2 では、ECS のプリコンパイルコントラクトを追加するためにノードコードを変更しました。このカスタムプリコンパイルコントラクトにより、スマートコントラクトは低レベルの ECS チェーンの状態に直接アクセスできます。したがって、この設計では、ECS のすべての操作(クエリや状態の設定など)がより高速に実行できます。また、OP Stack ノードは「Go-Ethereum」クライアントを使用しているため、Keystone のゲームロジックは Solidity ではなく Go 言語で記述することができ、大規模な並列処理が可能です。
Keystone の ECS は、'engine' と 'game' の 2 つのディレクトリのコードによって実装されています。
'engine' ディレクトリでは、ECS の主要なデータ構造である World や Component などが定義されていることがわかります。World は ECS の基本的な世界の構造であり、エンティティ(Entities)とコンポーネント(Components)の 2 つの主要な部分から構成されています。各 Component にはデータタイプ(DataType)とエンティティから値へのマッピング(EntitiesToValue)および値からエンティティへのマッピング(ValueToEntities)が含まれています。
'game' ディレクトリでは、位置コンポーネント(PositionComp)、ターゲット位置コンポーネント(TargetPositionComp)、タグコンポーネント(TagComp)など、いくつかの特定のゲームコンポーネントが定義されています。これらのコンポーネントには、それぞれのデータ型とエンティティへの値の格納の必要性を示すフラグ(ShouldStoreValueToEntities)があります。
ゲームエンジンが組み込まれたチェーンの利点#
上記の議論からわかるように、ECS(Entity-Component-System)の状態をカスタムチェーンに直接構築することは、スマートコントラクトで ECS の状態を記述するよりも高速なパフォーマンスを実現できます。このパフォーマンスの向上は、次のような要素によるものです:
1. データ構造の最適化:スマートコントラクトで ECS の状態を記述する場合、最適化されていないデータ構造を使用する必要がありますが、Keystone では効率的なデータ構造(スパースセットなど)を使用して ECS データを格納および操作できるため、クエリや状態の設定の速度が向上します。
2. スマートコントラクトの実行オーバーヘッドの回避:EVM(Ethereum Virtual Machine)はスマートコントラクトのコードを解釈実行する必要があり、これには一定のオーバーヘッドが発生します。しかし、ECS の状態を直接チェーンに構築することで、このオーバーヘッドを回避することができます。なぜなら、ECS の操作はチェーンのコアコードで直接実行されるからです。
3. 並列化:Keystone では、ゲームロジックを Go 言語で記述することができるため、ECS 操作の速度を向上させるために Go の並列処理と並行性の機能を利用することができます。これはスマートコントラクトでは実現できないことです。なぜなら、EVM はシングルスレッドで動作するからです。
4. プリコンパイルコントラクト:ECS の状態にアクセスするためにプリコンパイルコントラクトを使用することで、ECS 操作の速度を向上させることができます。プリコンパイルコントラクトは、チェーンのノードコードにハードコードされた関数であり、EVM で解釈実行されるコードよりも高速です。
5. 状態更新の最適化:Keystone では、サブワールドで状態の更新を行い、それらの更新を親ワールドに適用する方法を採用しています。これにより、不要な状態の更新を減らし、状態の設定の速度を向上させることができます。
ゲームのティックはどこにあるのか#
GameTick の概念は、通常、ゲーム開発においてゲーム内の時間の進行を管理するために使用されます。各ティックはゲームのメインループの 1 サイクルを表し、さまざまなゲームイベントをこれらのティックに基づいてスケジュールすることができます。これが従来のゲームが「ループベース」であると言われる理由です。
一方、ブロックチェーンの状態自体には、通常の計算環境で理解される「現在の時間」という概念は含まれていません。ブロックチェーンはブロックの概念に基づいて動作し、これらのブロックはリニアにチェーンに追加されます。これらのブロックには通常、タイムスタンプが含まれていますが、これは従来の計算環境での「現在の時間」の尺度として使用されるわけではありません。
Curio はゲームティックをチェーンに組み込んでいると主張していますが、私はコードベース全体を調べましたが、ゲームティックに関するコードの断片は見つかりませんでした。したがって、Keystone がチェーン内でゲームティックをどのように実現しているのかについては非常に興味があります。Curio にはこの機能についてさらに詳細な説明をしていただける機会があることを願っています。ただし、私の推測としては、Keystone の GameTick 環境では、next_tick フィールドがゲームループの次のサイクルがいつ発生するかを決定するために使用される可能性があります。これはブロックチェーンノードサーバーの内部クロックに基づいており、ゲーム内の内部ロジックのプロセスでゲーム時間を管理するために使用されますが、ブロックチェーン内のブロックのプロセスとは別のものです。