スマートコントラクト開発の詳細な分析: Move と Rust の比較研究
原題:「Smart Contract Development—Move vs.Rust》
原文編纂:Guo Qianwen、Chain Catcher
原文編纂:Guo Qianwen、Chain Catcher
最初のレベルのタイトル
1 はじめに
最近、高性能の L1 パブリック チェーンとして登場している Aptos と Sui に関する議論が本格化していますが、Move スマート コントラクト プログラミング言語は、これらの新しいチェーンに不可欠な要素です。一部の開発者は積極的に Move に注目し、これがスマート コントラクト開発の未来であると宣言しています。他の人はより慎重で、Move は既存のプログラミング言語と比べて新しすぎるものを提供できないと主張しました。
暗号化投資家はまた、これらの L1 パブリック チェーンの独自性が、現在高性能 L1 の主要プレーヤーであり、スマート コントラクト プログラミングとして Rust を使用していることで知られる Solana とどのように競合できるのか疑問に思っています。
しかし、私たちがこれまで見てきた議論は一定の深みには達しておらず、これらの新しいテクノロジーが私たちに与える影響を真に理解することはできません。これは議論の両極に当てはまります。Move懐疑論者はMoveを何の役にも立たないものとして却下し、その微妙な(しかし重要な)側面を理解していませんが、Move支持者はMoveを過剰に宣伝し、それがどのような素晴らしいものなのかを理解していません。これは大きな中間点と曖昧さをもたらし、外部の観客、暗号化開発者、投資家がこのトピックに注目するようになりますが、彼らは自分たちの見解に自信がありません。
この投稿では、Move、その新しいプログラミング モデル、Sui ブロックチェーン、Move のパワーをどのように活用するか、および Solana とそのプログラミング モデルとの比較について、技術的に深く掘り下げていきます。 Moveの機能を強調するために、Solana/RustとSui/Moveを比較してみます。なぜなら、ある物事をすでによく知っている別の物事と比較すると、理解が容易になるからです。Move には他にも Aptos Move などの亜種があり、いくつかの点で若干異なります。この記事の目的は、Move のさまざまなバリエーション間の微妙な違いについて説明することではなく、Move の一般的な利点と、Solana プログラミング モデルとの比較を示すことです。したがって、わかりやすくするために、この記事では 1 つのバリアント (Sui Move) のみを使用します。したがって、私はしかし、それでも、この記事で説明した Move の主な利点はすべて、Aptos を含むすべての Move 統合 (Move バイトコードをネイティブにサポートする) に当てはまります。 Sui を選んだのは、Sui のほうが馴染みがあり、Sui のほうが直感的で、記事の形式で提示しやすいと考えたからです。
最初のレベルのタイトル
2. Solana プログラミング モデル
Solana では、プログラム (スマート コントラクト) はステートレスであり、トランザクション間で持続する状態にそれ自体でアクセス (読み取りまたは書き込み) することはできません。状態にアクセスしたり、状態を維持するには、プログラムはアカウントを使用する必要があります。各アカウントには一意のアドレス (Ed25519 キー ペアの公開キー) があり、任意のデータを保存できます。
Solana のアカウント スペースは、グローバル キーと値のストアとして考えることができます。キーはアカウント アドレス (公開キー)、値はアカウント データです。プログラムは、このキーと値のストアの値を読み取り、変更することによって操作します。
アカウントには所有権の概念があります。各アカウントは 1 つの (そして 1 つのみの) プログラムによって所有されます。アカウントがプログラムによって所有されている場合、そのプログラムはそのデータを変更することができます。プログラムは、所有していないアカウントを変更することはできません (ただし、読み取りは許可されています)。運用中は、プログラム実行前後の口座状況を比較することでこのような動的なチェックを行うことができ、不正な変更があった場合にはトランザクションは失敗します。
各アカウントには、関連付けられた秘密キーもあり (対応する公開キーはそのアドレスです)、この秘密キーにアクセスできるユーザーは、それを使用してトランザクションに署名できます。このメカニズムを使用して、Solana スマート コントラクトに権限と所有権の機能を実装します。たとえば、特定の資金を取得するために、スマート コントラクトはユーザーに必要な署名の提供を要求できます。
他のプログラムを呼び出す場合、顧客はプログラムが呼び出し時にアクセスするアカウントを指定する必要があります。このようにして、トランザクション処理ランタイムは、データの一貫性を確保しながら、重複しないトランザクションを並列実行するようにスケジュールできます。これは、Solana の高スループットを実現する設計上の特徴の 1 つです。
プログラムは、CPI 呼び出しを通じて他のプログラムを呼び出すことができます。これらの呼び出しは、基本的にクライアントからの呼び出しと同じように機能します。呼び出し元プログラムは、呼び出し先プログラムがアクセスするアカウントを指定する必要があり、呼び出し先プログラムは、クライアントからの呼び出しと同じように入力チェックを行います (信頼していないため)。呼び出し側プログラム)。
これらは、Solana 上の安全なスマート コントラクト プログラミングの基本的な構成要素です。ある程度、Solana プログラムはオペレーティング システム内のプログラム、アカウントはファイルと考えることができ、誰でも自由にプログラムを実行でき、独自のプログラムを展開することもできます。プログラム (スマート コントラクト) が実行されると、ファイル (アカウント) の読み取りと書き込みが行われます。すべてのファイルはすべてのプログラムで読み取ることができますが、ファイルに書き込むことができるのは、そのファイルに対する所有権を持つプログラムのみです。プログラムは他のプログラムを実行することもできますが、相互に信頼はありません。プログラムを実行する人は、入力が潜在的に悪意のあるものであると想定する必要があります。 OS は誰でもグローバルにアクセスできるため、ネイティブ署名検証サポートがプログラムに組み込まれ、ユーザーの権限と所有権の機能が実装されました。完璧なたとえではありませんが、興味深いものです。
最初のレベルのタイトル
3. Move のプログラミング モデル
Solana では、これはすべてのスマート コントラクトをプログラム内のモジュールとして公開することに相当します。これは、すべてのスマート コントラクト (モジュール) が同じクラス システムに含まれており、中間 API やインターフェイスを経由せずに相互に直接呼び出すことができることを意味します。これは非常に重要であり、その影響についてはこの記事で徹底的に説明します。
副題
3.1 オブジェクト
以下のオブジェクトの概念は、Move の Sui バリアントに固有のものであることに注意してください。 Move の他の統合 (Aptos や Diem/core Move など) では、状況が若干異なる場合があります。ただし、他の Move バリアントにも、同じこと (状態の永続化) を達成する同様のソリューションがあり、それらは大きく異なります。
ここで Sui バリアントを紹介する主な理由は、この記事で後述するコード サンプルはすべて Move の Sui バリアントに基づいており、コア Move のグローバル ストレージ メカニズムなどのオブジェクトがより直観的で理解しやすいためです。重要なのは、この記事で説明した Move の主な利点はすべて、Aptos を含むすべての Move 統合 (Move バイトコードをネイティブにサポートする) に当てはまります。
オブジェクトはランタイムによって保存される構造体インスタンスであり、トランザクション間で状態を保持します。
(Sui には) 3 つの異なるタイプのオブジェクトがあります。
所有オブジェクト
共有オブジェクト
不変オブジェクト
所有オブジェクトとは、ユーザーに属するオブジェクトです。オブジェクトを所有するユーザーのみがトランザクションでそれを使用できます。所有権メタデータは完全に透過的であり、ランタイムによって処理されます。これは公開キー暗号化を使用して実装されます。各オブジェクトは公開キー (実行時にオブジェクトのメタデータに保存される) に関連付けられ、トランザクションでオブジェクトを使用する場合は常に、対応する署名を提供する必要があります (現在サポートされています)。 Ed25519、まもなく ECDSA と K-of-N マルチ署名をサポートする予定です)。
共有オブジェクトは所有オブジェクトに似ていますが、所有者が関連付けられていません。したがって、トランザクションで使用するために秘密キーを所有する必要はありません (誰でも使用できます)。独自のオブジェクトはすべて (その所有者によって) 共有でき、一度共有されると、そのオブジェクトは永久に共有されたままとなり、再び譲渡したり、独自のオブジェクトになったりすることはありません。
Move プログラミング モデルは非常に直感的でシンプルです。各スマート コントラクトは、機能と構造の定義で構成されるモジュールです。構造体は関数内でインスタンス化され、関数呼び出しを通じて他のモジュールに渡すことができます。トランザクション間で構造を永続化するには、構造を所有、共有、または不変にできるオブジェクトにします (Sui のみ。他の Move バリアントでは若干異なります)。
最初のレベルのタイトル
4. 移動の安全性
Move で次のことがわかりました。
自分が所有(または共有)している任意のオブジェクトを、任意のモジュールの任意の関数に渡すことができます。
誰でも (敵対的な可能性がある) モジュールを公開できる
Solana アカウントの場合のように、所有者モジュールに構造を変更する唯一の権限を与えるモジュール所有構造の概念はありません。構造は他のモジュールに流入したり、他の構造に埋め込まれたりすることができます。
問題は、なぜこの行為が安全なのかということです。人々が悪意のあるモジュールを投稿し、共有オブジェクト (AMM プールなど) を取得し、それを悪意のあるモジュールに送信し、資金を流出し続けることを阻止しているのは何でしょうか?
それが Move の新規性です...リソースについて話しましょう。
副題
4.1 構造

構造体の型の定義は、ほぼ予想どおりです。
ここまではうまくいきました。これは、Rust で構造体を定義する方法でもあります。しかし、Move では構造体が独特で、従来のプログラミング言語と比較して、Move モジュールでは型の使用方法に余裕があります。上記のコード スニペットで定義された構造には、次の制限が適用されます。
構造体を定義するモジュール内でのみインスタンス化 (「パック」) および破棄 (「アンパック」) できます。つまり、他のモジュールの関数から構造体インスタンスをインスタンス化または破棄することはできません。
構造体インスタンスのフィールドは、そのモジュール内でのみアクセスできます (したがって変更できます)。
構造体インスタンスをそのモジュールの外部に複製またはコピーすることはできません
構造体インスタンスを別の構造体インスタンスのフィールドに格納することはできません
これは、別のモジュールの関数でこの構造体のインスタンスを扱う場合、そのフィールドを変更したり、クローンを作成したり、別の構造体のフィールドに保存したり、破棄したりすることはできないことを意味します (関数呼び出しを通じて他のモジュールに渡す必要があります)。場所)。ここで重要なのは、構造体のモジュールは、これらのことを行うためにモジュールから呼び出すことができる関数を実装することです。しかし、それ以外に、外部型に対してこれらのことを直接行うことはできません。これにより、モジュールはその型がどのように使用されるか、使用されないかを完全に制御できるようになります。
これらの制約により、私たちは柔軟性を大幅に失っているようです。これも当てはまります。従来のプログラミングでは、このような構造を扱うのは非常に面倒ですが、実際、これはまさにスマート コントラクトで求めていることです。スマート コントラクトの開発は、結局のところ、デジタル資産 (リソース) のプログラミングに関するものです。上で説明した構造を見ると、それはまさにリソースです。何もないところから自由に作成したり、コピーしたり、誤って破壊したりすることはできません。したがって、ここでは柔軟性がいくらか失われますが、リソースの操作が直感的かつ安全になるため、失われる柔軟性はまさに私たちが望んでいることです。

さらに、Move を使用すると、構造に機能を追加することで、これらの制限の一部を緩和できます。キー、保存、コピー、削除の 4 つの機能があります。これらの機能を任意に組み合わせて構造に追加できます。
彼らがやることは次のとおりです。
Keys - 構造体をオブジェクトにすることができます (Sui に限定されます。コアとなる Move のケースは少し異なります)。前述したように、オブジェクトは永続的であり、自己所有のオブジェクトの場合、スマート コントラクトの呼び出しで使用する前にユーザーが署名する必要があります。キー機能を使用する場合、構造体の最初のフィールドは UID タイプのオブジェクト ID である必要があります。これにより、参照できるグローバルに一意な ID が与えられます。
storage - 構造体をフィールドとして別の構造体に埋め込むことができます
copy - どこからでも構造を任意にコピー/クローン作成できます。
基本的に、Move のすべての構造はデフォルトのリソースです。機能により、これらの制約を細かく緩和して、従来の構造のように動作させることができます。
副題
4.2 コイン

完全なモジュール実装は、Sui コード リポジトリ (リンク)。
リンク
コインタイプには鍵と収納の機能が付いています。 key は、オブジェクトとして使用できることを意味します。これにより、ユーザーはコインを直接(トップレベルのオブジェクトとして)所有できるようになります。あなたがコインを所有している場合、あなた以外の誰もトランザクションでそのコインを参照することさえできません(使用することはおろか)。ストレージとは、コインを別の構造体のフィールドとして埋め込むことができることを意味し、これは構成可能性に役立ちます。
破棄機能がないため、機能内でコインを誤って破棄(破壊)することがありません。これは非常に優れた機能です。つまり、誤ってコインを失うことがなくなります。パラメータとしてコインを取る関数を実装している場合、関数の最後に明示的にコインを使って何かを行う必要があります。ユーザーにコインを転送するか、別のオブジェクトに埋め込むか、呼び出して別のオブジェクトに送信します。関数(これもまたそれを使って何かをする必要があります)。もちろん、coin モジュールの Coin::burn 関数を呼び出してコインを燃やすことは可能ですが、意図的に行う必要があります (偶然に行うことはできません)。
クローン作成機能がないということは、誰もコインを複製することができず、何もないところから新しい供給を生み出すことができないことを意味します。新しい供給の作成は、coin::mint 関数を使用して行うことができ、コインの財務機能オブジェクトの所有者のみが呼び出すことができます。
Move では、リソースのセキュリティはそのタイプによって定義されます。 Move にはグローバル型システムがあるため、信頼できないコードにリソースを直接受け渡しできる、より自然で安全なプログラミング モデルが実現します。
副題
4.3 バイトコードの検証
前述したように、モバイル スマート コントラクトはモジュールとしてリリースされます。誰でも任意のモジュールを作成し、ブロックチェーンにアップロードして実行することができます。また、Move には構造体の使用方法に関するルールがあることも確認しました。
では、モジュールがこれらのルールに従うことを保証するものは何でしょうか?コイン オブジェクトを受け入れ、その内部フィールドを直接変更してこれらのルールを回避するなど、特別に作成されたバイトコードを含むモジュールをアップロードすることを妨げているのは何でしょうか?これにより、すべてのコインの数を不正に増やすことができます。バイトコードの構文だけで確かにこれが可能になります。
バイトコード検証は、この種の悪用を防ぐのに役立ちます。 Move Verifier は、Move バイトコードを分析し、必要な型、メモリ、およびリソースの安全性ルールに従っているかどうかを判断する静的分析ツールです。チェーンにアップロードされるすべてのコードはバリデーターを通過する必要があります。 Move モジュールをチェーンにアップロードしようとすると、ノードとバリデーターは送信を許可する前にまずバリデーターを実行します。いずれかのモジュールが Move のセキュリティ ルールをバイパスしようとすると、バリデータによって拒否され、公開されません。
Move バイトコードとバリデーターは Move の中核となるイノベーションです。他では不可能な直感的なリソース中心のプログラミング モデルを実装します。最も重要なのは、構造化型が整合性を失うことなく信頼境界を越えることができることです。
Move では、型はモジュール全体に存在します。型システムはグローバルです。これは、CPI 呼び出し、アカウントのエンコード/デコード、アカウント所有権のチェックなどが必要ないことを意味します。別のモジュールから関数と引数を直接呼び出すだけです。スマート コントラクト全体の型とリソースの安全性は、コンパイル/リリース時のバイトコード検証によって保証され、Solana のようにスマート コントラクト レベルで実装して実行時にチェックする必要はありません。
最初のレベルのタイトル
これで、Move プログラミングがどのように機能するか、そしてそれが基本的に安全である理由がわかりました。それでは、これがスマート コントラクト プログラミングにどのような影響を与えるかを、構成可能性、人間工学、セキュリティの観点から詳しく見てみましょう。ここでは、Move のプログラミング モデルがもたらす利点を理解するために、EVM および Rust/Solana/Anchor を使用した Move/Sui の開発を比較します。
副題
5.1 フラッシュローン
フラッシュローンはDeFiのローンの一種で、借入と同じ取引でローン金額を返済する必要があります。この主な利点は、トランザクションがアトミックであるため、ローンを完全に無担保で実行できることです。これは、元本を持たずに資産間の裁定取引に使用できます。
これを達成する際の主な困難は、融資金額が同じ取引で返済されることをフラッシュ ローン スマート コントラクトからどのように保証するかということです。ローンを無担保にするためには、トランザクションがアトミックである必要があります。つまり、ローン金額が同じトランザクションで返済されない場合、トランザクション全体が失敗する必要があります。
EVM には動的スケジューリングがあるため、次のように再入可能を使用してこれを実現できます。
Lightning Loan ユーザーはカスタム スマート コントラクトを作成してアップロードします。コントラクトが呼び出されると、呼び出しによって制御が Lightning Loan スマート コントラクトに渡されます。
次に、フラッシュ ローン スマート コントラクトは、要求されたローン金額をカスタム スマート コントラクトに送信し、カスタム スマート コントラクトのexecuteOperation() コールバック関数を呼び出します。
カスタム スマート コントラクトは、受け取った融資金額を使用して、必要な操作 (裁定取引など) を実行します。
カスタム スマート コントラクトが操作を完了した後、融資金額をフラッシュ ローン スマート コントラクトに返す必要があります。
このようにして、カスタムスマートコントラクトのexecutionOperation()が完了し、制御がフラッシュローンスマートコントラクトに戻り、ローン金額が正しく返されたかどうかを確認します。
カスタム スマート コントラクトがローン金額を正しく返さない場合、トランザクション全体が失敗します。
これにより、目的の機能がうまく実現されますが、問題は、スマート コントラクト プログラミングでは望ましくない再入性に依存していることです。なぜなら、リエントランシーは本質的に危険であり、悪名高い DAO ハッキングを含む多くの脆弱性の根本原因だからです。
Solana は再入を許可しないため、これをより適切に実行します。しかし、リエントラントがなければ、フラッシュ ローン スマート コントラクトがカスタム スマート コントラクトにコールバックできない場合、どのようにしてフラッシュ ローンを Solana に実装できるのでしょうか?指導の内省のおかげです。 Solanaでは、各トランザクションは複数の命令(スマートコントラクト呼び出し)で構成されており、任意の命令から同じトランザクション内に存在する他の命令(プログラムID、命令データ、アカウント)を確認することができます。これにより、次のようなフラッシュ ローンの実装が可能になります。
ライトニングローンスマートコントラクトは借入と返済の指示を実装します
ユーザーは、同じトランザクション内で借入命令と返済命令の呼び出しを積み重ねることにより、フラッシュ ローン トランザクションを作成します。借入注文が実行されると、注文のイントロスペクションを使用して、同じ取引の後の段階で返済注文がスケジュールされているかどうかを確認します。償還命令の呼び出しが存在しないか無効である場合、トランザクションはこの段階で失敗します。
借用と返済の呼び出しの間に、借用した資金は、その間の他の指示で自由に使用できます。
トランザクションの終了時に、返済命令の呼び出しによって資金が Lightning Lender スマート コントラクトに返されます (この命令の存在は借用命令の反映でチェックされます)。
このソリューションは十分に優れていますが、まだ理想的ではありません。命令イントロスペクションはやや特殊なケースであり、Solana では一般的には使用されません。その使用には開発者が多数の概念を習得する必要があり、適切に考慮する必要のある微妙なニュアンスがあるため、その実装自体も非常に技術的です。また、技術的な制限もあります。返済命令はトランザクション内に静的に存在する必要があるため、トランザクション実行中に CPI 呼び出しを通じて動的に返済を呼び出すことはできません。これは大したことではありませんが、他のスマート コントラクトと統合するときにコードの柔軟性がある程度制限され、クライアントの複雑さが増します。
Move は動的なスケジューリングと再入も禁止していますが、Solana とは異なり、非常にシンプルで自然なフラッシュ ローン ソリューションを備えています。 Move の線形型システムを使用すると、トランザクションの実行中に 1 回だけ消費されることが保証される構造を作成できます。これは、いわゆる「ホット ポテト」パターンです。キー、保存、削除、またはクローン機能のない構造です。このパターンを実装するモジュールには通常、構造をインスタンス化する関数と構造を破棄する関数があります。 「ホット ポテト」構造体には破棄、キー、または保存機能がないため、それを消費するためにその destroy 関数が呼び出されることが保証されています。これを任意のモジュール内の他の関数に渡すこともできますが、最終的には destroy 関数に渡す必要があります。他に破棄する方法がなく、バリデータはトランザクションの終了時に破棄することを要求しているためです(破棄機能がないため、任意に破棄することはできません)。
これを利用してフラッシュ ローンを実装する方法を見てみましょう。
Lightning Loan スマート コントラクトは、「ホット ポテト」領収書 (領収書) 構造を実装します。
ローン関数を呼び出してローンを作成すると、要求された資金 (1 コイン) と返済が必要なローン金額の記録であるレシートという 2 つのオブジェクトが呼び出し元に送信されます。
借り手は受け取った資金を必要な操作(裁定取引など)に使用できます。
借り手は意図した操作を完了した後、返済関数を呼び出す必要があります。返済関数は、借入資金と領収書をパラメータとして受け取ります。この関数は、同じトランザクション内で呼び出されることが保証されています。これは、呼び出し元にはレシート インスタンスを削除する他の方法がないためです (バリデーターで必要とされる、レシート インスタンスを破棄したり、別のオブジェクトに埋め込んだりすることは許可されていません)。
返済機能は、レシートに埋め込まれた融資情報を読み取ることで、正しい金額が返済されたことを確認します。
Move のリソース安全機能により、再入またはイントロスペクションを使用せずに Move でフラッシュ ローンが可能になります。これらは、レシートが信頼できないコードによって変更できないこと、およびトランザクションの終了時に返金関数にレシートを返す必要があることを保証します。このようにして、同じトランザクションで正しい金額が返されることを保証できます。
Flash Loan は、Move の線形型システムとリソースの安全性の保証により、他のプログラミング言語ではできない方法で機能を表現できることを示す好例です。
副題
5.2 造幣局権限ロック
「鋳造機関ロック」スマート コントラクトは、トークン鋳造の機能を拡張し、ホワイトリストに登録された複数の当事者 (当局) がトークンを鋳造できるようにします。このスマート コントラクトに必要な機能は次のとおりです (Solana と Sui の両方の実装の場合)。
元のトークン鋳造機関は、スマート コントラクトが鋳造を監督できるようにする「鋳造ロック」を作成します。呼び出し元はミントロックの管理者になります。
管理者は、このロックに対する追加の鋳造承認を作成できます。これを他の当事者に委任して、いつでもこのロックを使用してトークンを鋳造できるようにすることができます。
各鋳造承認には、鋳造できるトークンの数に 1 日あたりの制限があります。
管理者は、いつでも鋳造機関を禁止 (およびブロック解除) できます。
管理者の機能を他の当事者に譲渡することができます。
このスマート コントラクトは、たとえば、元の鋳造機関 (管理者) が鋳造に対する制御を保持しながら、トークンの鋳造権限を他のユーザーまたはスマート コントラクトに譲渡するために使用できます。そうしないと、鋳造の完全な制御を別の当事者に引き渡さなければなりませんが、その権限を乱用しないように信頼する必要があるだけなので、これは理想的ではありません。また、複数の相手に許可を与えることはできません。
これらのスマート コントラクトの完全な実装は、ここ (Solana) とここ (Sui) で見つけることができます。
注: このコードを運用環境で使用しないでください! これは教育目的のみのサンプル コードです。私はその機能をテストしましたが、徹底的な監査やセキュリティ テストは行っていません。

次に、コードを見て、実装がどのように異なるかを見てみましょう。以下は、このスマート コントラクトの完全な Solana と Sui 実装のコードのスクリーンショットを並べて示したものです。
同じ機能に対して、Solana の実装のサイズは、Sui の 2 倍以上 (230 LOC 対 104) であることがわかります。通常、コードが減ればバグも減り、開発時間が短縮されるため、これは大きな問題です。
では、Solana はこれらの追加の行をどこで入手したのでしょうか? Solana のコードを詳しく見ると、命令の実装 (スマート コントラクト ロジック) とアカウント チェックの 2 つの部分に分けることができます。命令の実装は、Sui の実装に比較的近いです。Solana には 136 行があり、Sui には 104 行があります。余分な行数は、2 つの CPI 呼び出し (それぞれ約 10 LOC) の参照によるものです。最も重要な違いはアカウント チェック (上のスクリーンショットで赤でマークされている) です。これは Solana では必須 (実際には重要) ですが、Move では必要ありません。口座小切手はこのスマート コントラクトの約 40% (91 LOC) を占めます。
移動にはアカウントの確認は必要ありません。 LOC の削減は利点をもたらす可能性がありますが、口座チェックを排除することも必要です。なぜなら、これらのチェックを正しく実装することは非常に困難であり、1つでも間違えると、多くの場合、重大な違反やユーザー資金の損失につながる可能性があることが判明したためです。実際、(ユーザー資金の損失という点で)Solana スマート コントラクトの最大の脆弱性のいくつかは、不適切なアカウント チェックによって引き起こされるアカウント代替攻撃でした。
-ワームホール (3億3,600万ドル) - https://rekt.news/wormhole-rekt/
-カシオ (4,800万ドル) - https://rekt.news/casio-rekt/
- クレマ・ファイナンス(880万ドル) - https://rekt.news/crema-finance-rekt/

では、これらのチェックを行わずに、どのようにして Move を安全に保つことができるのでしょうか?これらのチェックが実際に何を行うのかを詳しく見てみましょう。 mint_to コマンドで必要なアカウント チェックは次のとおりです (権限保持者はこのコマンドを呼び出してトークンを作成します)。
チェックは 6 つあります (赤でマーク)。
1. 提供されたロック アカウントがスマート コントラクトによって所有されており、MintLock タイプであるかどうかを確認します。受信ロックは、ミント (権限を保存する) のためのトークン プログラムへの CPI 呼び出しに使用されるため、必要です。
2. 指定されたミント許可アカウントが指定されたロックに属しているかどうかを確認します。鋳造機関アカウントは、機関の状態 (公開鍵、禁止されているかどうかなど) を保持します。
3. 命令の呼び出し元が許可に必要なキーを持っていることを確認します (トランザクションに署名された必要な権限)。
4. トークン プログラムが CPI 呼び出しでトークン ターゲット アカウントを変更する (残高を増やす) ため、トークン ターゲット アカウントを渡す必要があります。間違ったアカウントが渡されると CPI 呼び出しが失敗するため、ミント チェックはここでは厳密には必要ありませんが、それでも良い習慣です。
5. 4と同様。
6. トークン プログラム アカウントが正しく渡されたかどうかを確認します。
(この例では) アカウントチェックが次の 5 つのカテゴリに分類されることがわかります。
アカウント所有権チェック (1、2、4、5)
口座種類チェック(1、2、4、5)
アカウント インスタンスのチェック (アカウント タイプの正しいインスタンスが渡されたかどうか) (2、5)
アカウント署名チェック(3)
プログラムアカウントアドレスチェック(6)

しかし、Move ではアカウントのチェックなどは行われず、関数の署名のみが行われます。
mint_balance 関数に必要なパラメータは 4 つだけです。これら 4 つのパラメータのうち、lock と cap だけがオブジェクト (アカウントに似ています) を表します。
Solana では 6 つのアカウントを宣言し、それらに対してさまざまなチェックを手動で実装する必要がありますが、Move では 2 つのオブジェクトを渡すだけでよく、明示的なチェックは必要ありません。
Move では、これらのチェックの一部は実行時に透過的に実行され、一部はコンパイル時にバリデータによって静的に実行され、一部は構築時にまったく必要ありません。
アカウント所有権のチェック - Move には型システムがあるため、この設計は不要です。 Move 構造は、直接ではなく、モジュール内で定義された関数によってのみ変更できます。バイトコード検証により、構造体インスタンスが不正に変更されることなく、信頼できないコード (他のモジュール) に自由に流入できることが保証されます。
アカウント タイプのチェック - 移動タイプはスマート コントラクト全体に存在するため、必要ありません。タイプ定義はモジュール バイナリに埋め込まれます (ブロックチェーン上に公開され、仮想マシンによって実行されます)。バリデーターは、コンパイル/パブリッシュ中に関数が呼び出されたときに、正しい型が渡されることをチェックします。
アカウント インスタンスのチェック - Move (Solana の場合もあります) では、これを関数本体で実行します。この特定のケースでは、ロック パラメータ タイプとキャップ パラメータ タイプのジェネリック タイプ パラメータ T によって、受信キャップ (鋳造能力/権限) オブジェクトがロックと正しく一致することが強制されるため、これは不要です (各コイン タイプ T はロックのみを持つことができます)。
アカウントの署名チェック - 署名は、Sui では直接処理されません。オブジェクトはユーザーが所有できます。 Minting 権限は、Minting Authority 能力オブジェクト (管理者によって作成された) の所有権によって付与されます。 mint_balance 関数でこのオブジェクトへの参照を渡すと、ミントが可能になります。所有されているオブジェクトは、その所有者によってのみトランザクションで使用できます。つまり、オブジェクトの署名チェックはランタイムによって透過的に行われます。
基本的に、Move はバイトコード検証を活用して、デジタル資産のプログラミング モデルをより自然にします。 Solana のモデルは、アカウントの所有権、署名、CPI コール、PDA などを中心に展開します。しかし、一歩下がって考えてみると、私たちはこれらの問題に取り組みたくないことがわかります。これらはデジタル資産自体とは何の関係もありません。むしろ、これにより Solana のプログラミング モデルに必要な機能を実装できるようになるため、これらを使用する必要があります。
Solana では、よりきめ細かい型やリソースの安全性を確保するためのバイトコード検証がなく、プログラムによるアカウントの変更を許可できないため、アカウント所有権の概念を導入する必要があります。同様の理由 (プログラム呼び出し全体で型やリソースの安全性がない) により、プログラムに出入りできるユーザー所有のオブジェクトという概念はなく、代わりにアカウントの署名を使用してアクセス許可を証明します。場合によってはプログラムでもアカウント署名を提供できる必要があるため、PDA を用意しています。
Move のリソースのネイティブ抽象化により、PDA などの低レベルのビルディング ブロックを導入せずにリソースを直接操作できます。スマート コントラクトの境界を越えた型とリソースの安全性の保証はバリデーターによって保証され、手動で実装する必要はありません。
副題
5.3 Solana コンポーザビリティの制限
Solana でのスマート コントラクトのコンポーザビリティの問題点を強調するために、もう 1 つの例を挙げたいと思います。
mint パーミッション ロックの例で、Sui と比較して Solana ではより多くの入力を宣言する必要があることがわかりました (mint_to は Solana では 6 つのアカウントを呼び出しますが、Sui では 2 つのオブジェクトを呼び出します)。明らかに、6 つのアカウントを処理することは、特にアカウントのアカウント チェックも実装する必要があることを考慮すると、2 つのオブジェクトを処理するよりも面倒です。理論的には、この部分は管理可能ですが、1 回の呼び出しで複数の異なるスマート コントラクトを一緒に構成し始めるとどうなるでしょうか?
次のことを実行できるスマート コントラクトを作成するとします。
鋳造権限ロックプログラムから特定のトークンを鋳造する権利を持ち、鋳造することができる
呼び出されると、その権限を使用してユーザーが指定した量のトークンを生成し、AMM を使用して別のトークンと交換し、同じコマンドでユーザーに送信します。

この例の焦点は、造幣局ロック スマート コントラクトと AMM スマート コントラクトがどのように組み合わされるかを示すことです。コマンドによって呼び出されるアカウント チェックは次のようになります。
17アカウント。 CPI コールごとに 5 ~ 6 個のプログラム (ミントとスワッピング)、およびプログラム アカウント。

Sui では、同等の関数のシグネチャは次のようになります。
オブジェクトは 3 つだけです。
Solana のアカウントと比較して、Sui で渡すオブジェクトの数がはるかに少ないのはなぜですか (3 対 17)?基本的に、Move ではそれらを埋め込む (ラップする) ことができるためです。型システムの安全性保証により、これが可能になります。

以下は、AMM プールの状態を保持する Solana アカウントと Sui オブジェクトの比較です。
Solana では、他のアカウントのアドレス (公開キー) が保存されていることがわかります。これはポインターのようなもので、実際のデータは保存されていません。これらのアカウントにアクセスするには、アカウントを個別に渡す必要があり、正しいアカウントが渡されていることを手動で確認する必要もあります。 Move では、構造を相互に埋め込み、その値に直接アクセスできます。 Move のグローバル型システムとリソースの安全性 (両方ともバイトコード検証によって駆動される) のおかげで、リソースと型の安全性の保証を維持しながら、任意のモジュールの型を組み合わせて一致させることができます。
ただし、複数のスマート コントラクトを構成する場合、多くのアカウントを渡す (したがってチェックする) 必要があるため、実装がかなり複雑になり、セキュリティに影響を及ぼします。これらのアカウント間の関係は非常に複雑になる可能性があり、必要なアカウント チェックをすべて追跡し、それらが正しく実装されているかどうかを追跡するのは困難です。
実際、それがカシオの侵害(4,800万ドル)で起こったことだと私は思います。以下は、脆弱性の原因となった(不適切な)アカウントチェックの内訳です。ご覧のとおり、これらのアカウントチェックは少し複雑になります。開発者は正しくチェックするという善意を持っていますが、ある時点で精神的なプレッシャーが大きくなりすぎて、非常にエラーが発生しやすくなります。アカウントが増えれば増えるほど、エラーが発生しやすくなります。
Move のグローバル型システムとより自然なプログラミング モデルにより、心理的ストレスの限界に達する前に、より安全にスマート コントラクトの構成を推進できるようになります。
Move は TCB の削減を念頭に置いて設計されました。Move は、TCB を可能な限り最小限に抑えるために多くの決定を下しました。バイトコード検証ツールは、Move コンパイラーによって実行されるチェックの多くを TCB から削除しますが、Rust/Anchor には信頼する必要があるコンポーネントがさらに多く存在するため、致命的なセキュリティ バグの表面積が大きくなります。
最初のレベルのタイトル
Solana で Move を使用できますか?またその方法は?
副題
6.1 グローバルにタイプセーフなアンカーはありますか?

始める前に、Anchor について簡単に見て、ちょっとした思考実験をしてみましょう。おそらく、何らかの方法でアンカーをアップグレードして、Move から得られる利点の一部を提供できるでしょうか?おそらく、プロシージャ間の呼び出しに対するタイプセーフなネイティブ サポートを取得できるでしょうか?結局のところ、Anchor 命令はすでに Move のエントリ関数と似ています。

おそらく、アカウントをコマンド パラメータに直接渡すことができるように、Anchor を拡張できるかもしれません。
口座調査を回避できるでしょうか?
この場合、プログラムではなく実行によって型チェックが行われるようにします。実行はアンカー アカウント識別子 (または同等のもの) を読み取り、渡されたアカウントが必要な識別子 (アンカー) に準拠していることを確認できます。アカウントの最初の 8 バイト)。
Solana は、同じプログラムの異なる命令呼び出しを区別しません。これはプログラムによって手動で実装されます (この場合、面倒な作業はアンカーによって行われます)。したがって、これを行うには、ランタイムがさまざまな命令、その署名、型情報を何らかの方法で認識している必要があります。
Solana プログラムは SBF (Solana Bytecode Format、eBPF の一種) にコンパイルされ、この方法でチェーンにアップロード (および実行) されます。 SBF 自体には、役立つような型や関数の情報は組み込まれていません。しかし、SBF を変更して、命令と型情報をバイナリに埋め込めるようにすることはできるでしょうか?このようにして、必要な命令と署名情報をランタイムによってバイナリから読み取ることができます。
確かにこれは可能です。特に古いプログラムとの下位互換性を維持する必要があることを考えると、これにはかなりの量のエンジニアリングが必要になりますが、得られる結果は次のとおりです。
- アカウントの所有権とタイプのチェックは、プログラムではなく実行によって行われます
- コンパイル時にアドレスがわかっているアカウント (プログラム アカウントなど) については、クライアントから渡すことを回避でき、実行によって渡すことができるようになりました。
- アカウント制約をバイナリに埋め込むことができれば、(埋め込まれた制約情報に基づいて) 実行時に動的に再ロードすることで、クライアントによって渡される必要があるアカウントの数をさらに減らすことができます。
まだわかりません:
- 埋め込みアカウント。他のアカウントを参照するには公開キーを使用する必要があり、直接埋め込むことはできません。これは、セクション 5.3 で説明したアカウントの肥大化の問題が解決されないことを意味します。
- プログラム間呼び出しを行う場合、アカウント タイプのチェックは、Move のようにコンパイル時に静的に行うのではなく、実行時に動的に行う必要があります。
注: これは単なる思考実験です。これは、安全に完了できるという意味でも、達成が難しいという意味でも、これらの利点がエンジニアリングの努力に値すると宣伝するものでもありません。
これらの利点は確かに素晴らしいものですが、スマート コントラクト開発の観点からは根本的に何も変わりません。プログラム内ではなく実行時に型チェックを行うと、パフォーマンス上の利点が得られる可能性があり、コンパイル時にクライアントからアドレス アカウントを手動で渡す必要がなくなり、人間工学がある程度改善されます (これはツールによって軽減することもできます)。しかし、私たちはまだ最終的には Solana のプログラミング モデルに取り組んでおり、それ自体がデジタル資産の処理にさらに役立つものです。私たちはまだネイティブ リソースのセキュリティがなく、アカウントを埋め込むことができないため、アカウントの肥大化が依然としてあり、まだ対処中です。アカウント署名と PDA を使用して...
私たちは自然なプログラミング モデルを望んでいますが、同時に信頼できないコードを扱っていることになります。 Solana では信頼できないコードを安全に処理できますが、プログラミング モデルには妥協があります。バイトコード検証により、両方を実現できるようになります。それがなければ、プログラミング モデルを改善することはできないようです。
副題
6.2 Solana バイトコード形式
前述したように、Solana スマート コントラクトのコンパイルおよびオンチェーン ストレージ形式である SBF (Solana Bytecode Format) は、eBPF に基づいています。他のバイトコード形式 (WASM など) の代わりに Solana で eBPF を使用するのは、主に、Solana の安全で高性能なスマート コントラクト実行の要件が、eBPF 設計のカーネル サンドボックス プログラムの実行要件と一致しているためです (セキュリティと高性能も必要です)。
一見すると、eBPF は確かな選択肢です。高性能、セキュリティを中心に設計されており、プログラムのサイズと命令数は制限されており、バイトコード検証機能を備えています...見た目は素晴らしいです。
しかし、これが実際に何を意味するのか見てみましょう。 eBPF バリデーターを何らかの方法で活用して、スマート コントラクトのセキュリティを向上させることができるでしょうか? eBPF バリデーターが行うことの一部を次に示します。
- 無限ループを許可しません
- プログラムが DAG (有向非巡回グラフ) であるかどうかを確認します。
・場外ジャンプは禁止
- さまざまなヘルパー関数呼び出しを行うときにパラメーターのタイプを確認します (ヘルパー関数は、ネットワーク パケットの変更などのためにカーネルで定義されます)。
そうですね、範囲外へのジャンプを禁止するのは便利そうですが、それ以外の場合は制限があります。実際、プログラムが DAG であり、無限ループを持たないことを義務付けることは、プログラムの操作性を大きく制限するため、問題があります (チューリング完全性はありません)。 eBPF プログラムでこれが必要な理由は、検証者がプログラムが特定の命令数以内に終了することを確認する必要があるためです (プログラムによってカーネルが終了しないようにします。これは有名な停止問題です)。メーターリングはパフォーマンスに大きく影響するため、オプションではありません。
このトレードオフは、高パフォーマンスのファイアウォールの実装には最適ですが、スマート コントラクトの開発にはあまり適していません。 eBPF バリデータの大部分は、Solana プログラムでは再利用できません。実際、Solana はオリジナルの eBPF バリデーターをまったく使用せず、基本的に正しい命令と範囲外のジャンプをチェックする (より基本的な) カスタム バリデーターを使用します。
同時に、eBPF は、呼び出しのために最大 5 つのパラメーターを関数に渡すことができるように設計されています。これは、Rust 標準ライブラリを eBPF に直接コンパイルできないことを意味します。または、スタック サイズが 512 バイトに制限されているため、ヒープ割り当てなしで関数に渡すことができる引数のサイズが減少します。
そのため、Rust が LLVM にコンパイルでき、LLVM 用の eBPF バックエンドがあり、eBPF 用の Rust コンパイラをサポートしていても、Solana スマート コントラクトを本来どおりに eBPF にコンパイルすることはできません。これが、Solana チームが Rust コードベースと eBPF LLVM バックエンドにいくつかの変更を加える必要があった理由です (スタック経由で引数を渡すなど)。
これらの変更の一部はアップストリーム (Rust または LLVM) のネイティブ サポートであるため、Solana チームは現在 Rust と LLVM の両方のフォークを維持しています。カーゴ build-bpf (Solana スマート コントラクトを構築するための一般的なコマンド) を実行すると、Cargo はこの Solana 固有のバージョンの Rustc (Rust プログラミング言語のコンパイラー) をプルしてスマート コントラクトをコンパイルします (元の Rustc は機能しません) )。
これが SBF の誕生です。Solana には eBPF と互換性のないいくつかの要件が必要です。 Solana チームは現在、別個の LLVM バックエンドとして SBF をアップストリームし、別個のフォークの維持を避けるためにそれを Rust ターゲットとして追加することに取り組んでいます。
したがって、eBPF はスマート コントラクトの形式として機能しますが、表面的に見えるほど理想的ではありません。いくつかの変更が必要で、元のバリデーターはあまり役に立ちませんでした。
Move と Solana/SBF に関する議論で誤解されているのは、Move の主な考え方が SBF にも適用できるはずだと考える人がいることです。これは、Move は eBPF に基づいており、おそらくそのバリデーターを使用して、静的なアカウント変更チェックを行うことができるからです。実行時に動的チェックを行います。
私の意見では、これは疑わしい主張です。たとえプログラムが所有していない eBPF 内のアカウントを変更しないことを証明できたとしても (まさに Move が行っていることです)、それが Move の主なアイデアではないことは確かです。
Move の主なアイデアは、信頼されていないコードと自然に対話できるリソース中心のプログラミング モデルを作成することです。
実際には、これは次のことを意味します。
グローバルタイプの安全性
リソースのセキュリティ (キー、クローン、隠し場所、破棄)
埋め込み可能なリソース
...
リソースは信頼できないコードとの間で安全にやり取りされます
Move の主要なアイデアを eBPF/SBF に導入するのは非常に困難です。 「この信頼できないコードは T を削除できない」などのプロパティを強制することは、eBPF に大きな変更を加えない限り不可能です。これには非常に多くの変更が必要になるため、最終的には eBPF というよりも Move に似た新しいバイトコードが作成されます。
実際、Move の誕生も同様の考え方から始まりました。 Move チーム (当時は Diem) は当初、WASM、JVM、CLR などの他の形式から始めることを検討していましたが、これを後から追加することは非常に困難でした。線形性/容量が型破りでした。そのため、Move は、軽量のバリデーター チャネルを通じてこれらのチェックを効率的に実行するという考えに基づいてゼロから設計されました。
考えてみれば、これは実際にはそれほど驚くべきことではありません。結局のところ、スマート コントラクト プログラミングは、システム プログラミングでも、バックエンド プログラミングでも、その他の従来のプログラミングでもありません。まったく異なる種類のプログラミングです。したがって、既存のバイトコードと命令形式の機能を活用できないのは当然のことです。これらはまったく異なる使用例を念頭に置いて設計されているためです。
私は Solana が eBPF を使用していることを批判しているわけではありません。実際、状況を考慮すると、これはかなり堅実な選択であり、チームによる適切な判断だと思います。今にして思えば、チームは eBPF ではなく WASM を選択した可能性があります。そうすれば、WASM は Rust で第一級のサポートを備えているため (ただし、WASM には他の問題がある可能性があります)、スマート コントラクトを eBPF にコンパイルするという前述の問題を回避できたでしょう。チームは、パフォーマンスを重視することを考えると、eBPF がより安全な選択であると感じるかもしれません。さらに、これらの設計の選択が行われた時点では、Move はまだ発表されておらず、新しい言語をゼロから作成することは、スタートアップにとって合理的な選択肢ではありませんでした。最終的に、Solana は高性能の L1 を成功裏に提供することができました。それが重要です。
Solana で Move を入手するには 3 つの方法があります。
Move VM をローカル ローダーとして (SBF VM とともに) 追加します。
仮想マシンの移動をプログラム (Neon など) として実行します。
コンパイル SBF に移動 (Solang など)
まず(3)について説明します。ここでのアイデアは、SBF にコンパイルできるように Move 用の LLVM フロントエンドを用意することです。 SBF にコンパイルされた Move スマート コントラクトは、Rust (または SBF にコンパイルできる他の言語) で構築されたスマート コントラクトと同様に透過的に実行され、実行時に Move の区別や知識は必要ありません。運用の観点から見ると、これは、セキュリティの前提条件を変更する必要がないため、非常に洗練されたソリューションとなります。
しかし、この方法でスマート コントラクトを開発するのは、Anchor を直接使用するよりも悪いと思います。 (3) で得られるのは、Solana プログラミング モデルの Move 構文です。これは、第 5 章で説明した Move の重要な利点 (グローバルな型の安全性、グローバルなリソースの安全性、埋め込み可能なオブジェクトなど) がすべて存在しなくなることを意味します。代わりに、Rust と同様に、アカウントチェック、CPI コール、PDA などに対処する必要があります。また、Move はマクロをサポートしていないため、この作業の一部を簡素化するために eDSL を使用して Anchor のようなフレームワークを実装することは不可能です。そのため、コードはバニラ Rust と似たものになります (ただし、おそらくそれよりも劣るでしょう)。 Rust の標準ライブラリとエコシステムも利用できないため、アカウントのシリアル化や逆シリアル化などは Move で再実装する必要があります。
Move は、他のプログラミング モデルでの使用にはあまり適していません。これは、バイトコードを移動してバリデーターを渡すようにコンパイルするように特別に設計されているためです。これは、能力チェッカーと借用チェッカーに関するカスタム ルールを考慮すると必要です。そのバイトコード検証は非常に具体的であるため、他の言語が Move バイトコードにコンパイルしてベリファイアを通過する可能性はほとんどありません。 Move は、この非常に特殊な種類のバイトコード検証を中心に構築されているため、Rust のような言語ほど柔軟性がありません。
バイトコードを削除すると、Move の主な利点がすべて失われます。 Move のタイプ、リソース、およびメモリの安全性機能はプログラム レベルで保持されますが、グローバルには保持されません。そして、プログラムレベルの安全性は多くの新しい結果をもたらしません - 私たちはすでに Rust でそれを達成しています。
Move のスマート コントラクト エコシステムも Solana では機能しません。プログラミング モデルが大きく異なるため、スマート コントラクトの重要な部分を書き直す必要があります。これらを考慮すると、(3) による Move の実装は受け入れられないと予測します。
(1) に関しては、ここでのアイデアは (SBF ローダーとともに) 実行時に Move ローダーのサポートを追加することです。 Move スマート コントラクトは、Move バイトコードとしてオンチェーンに保存され、Move VM によって実行されます (Sui と同様)。これは、SBF スマート コントラクトのエコシステムと Move スマート コントラクトのエコシステムが存在することを意味します。前者は現在の Solana プログラミング モデルで実行され、後者は (おそらくより高度な) Move モデルで実行されます。
このアプローチでは、Move スマート コントラクト間の相互作用のすべての利点を維持することが可能ですが、ここでの 1 つの課題は、Move スマート コントラクトと SBF スマート コントラクトとの相互作用、またはその逆を可能にすることです。Move と SBF スマート コントラクトのペアが必要です。 Solana を深く理解している場合は、バリデータも調整する必要があります。
実行時に 2 つの異なるローダーを維持する必要があるという欠点もあります。これは、攻撃対象領域が 2 倍になることを意味するため、セキュリティに影響を及ぼします。単一のローダー エラーがチェーン全体が悪用されることを意味する可能性があります。実際、MoveVM の初期サポートは 2019 年に Solana に追加されました (#5150) が、セキュリティ上の懸念により後に削除されました (#11184)。
(2) については、Move VM 全体を 1 つの Solana プログラム (スマート コントラクト) として実行するという考えです。 Move VM は Rust で実装されているため、おそらく (スレッドやその他のサポートされていない API を使用しない限り) SBF にコンパイルされます。これはクレイジーに聞こえるかもしれませんが、Neon は同様のアプローチを実装し、EVM を Solana プログラムとして実行しています。このアプローチの利点は、実行に変更を加える必要がなく、同じセキュリティ前提を維持できることです。
Move の主要な機能を Solana に直接導入する方法はありません。 LLVM フロントエンドを構築して Move to SBF をコンパイルすることは可能ですが、プログラミング モデルが同じままであるため、あまり役に立ちません。セクション 6.1 の思考実験が示すように、何らかのバイトコード検証がなければプログラミング モデルを改善することはできません。バイトコード検証をサポートするように eBPF/SBF を変更するのは非常に困難です。唯一の合理的な選択肢は、何らかの方法で MoveVM を実行することのようです。しかし、これは、異なるプログラミング モデルで動作する 2 つのエコシステムが存在することを意味し、それらを適切に相互運用することは非常に困難です。
副題
6.4. 移動の性能
Move のバイトコードは汎用バイトコード言語ではありません。これは非常に「アサーティブ」なタイプのシステムを備えており、必要なすべての検証を可能にする非常に高度なシステムです。これは、ネイティブ コードに近い eBPF/SBF などの他のバイトコード形式と比較してパフォーマンスが低いことを意味し、高パフォーマンス L1 での使用には問題があると考えられるかもしれません。
ただし、これまでのところ、スマート コントラクトの実行は、Solana (執筆時点で平均 3,000 TPS) でも、Sui (チームが行った最初の e2e ベンチマークに基づく) でもボトルネックになっていません。トランザクション処理のパフォーマンスを向上させる主な方法は、並列実行です。 Solana と Sui は両方とも、依存関係の事前宣言と、さまざまなオブジェクト/アカウントのセットに依存するトランザクションの実行の並行スケジューリングを要求することで、これを実装します。
これらすべてを念頭に置いて、Move のパフォーマンスが当面は大きな障害にならないことを願っています。
7. Moveのその他の機能
副題
7.1 バリデーター
Moveには、Move Proverと呼ばれるスマートコントラクトの正式な検証ツールがあります。このツールを使用すると、スマート コントラクトにさまざまな不変条件が適用されるかどうかを判断できます。バックグラウンドで検証条件が SMT 式に変換され、SMT ソルバーを使用してチェックされます。これは、たとえば入力空間を歩き回って試行錯誤するファジングとは大きく異なります。たとえば、ファジングや単体/統合テストでは、特定の入力または入力の組み合わせのテストに失敗した場合でも、プログラムのバグを示す偽陽性が発生する可能性があります。一方、検証者は基本的に、指定された不変条件が提供されたプログラムに対して保持する形式的な証明を提供します。これは、考えられるすべての入力に対してプログラムをチェックするようなものですが、その必要はありません。
以下はバリデーターの例です (ホワイトペーパー「Move Prover を使用したスマート コントラクトの高速かつ信頼性の高い正式検証」から抜粋)。

副題
7.2. ウォレットのセキュリティ
Sui では、トランザクションがアクセスするすべてのオブジェクトが関数の引数で渡されること (グローバル状態から動的にロードされない) が必要であり、move 関数の署名は型情報とともにバイトコード自体に格納されるため、ウォレットにユーザーが提供するものを送信させることができます。トランザクションの内容を説明する、より意味のある情報。

たとえば、次のシグネチャを持つ関数があるとします。
関数の署名から、このトランザクションがユーザーの 3 つの資産 (資産タイプ) にアクセスすることがわかります。それだけでなく、& および &mut キーワード (またはキーワードなし) に従って、アセット 1 は読み取り可能、アセット 2 は変更可能 (ただし、転送または破棄はできない)、アセット 3 は変更、転送、または破棄できることもわかります。 .破壊する。
ウォレットはこの情報をユーザーに表示することができ、ユーザーはトランザクションが資産に対してどのようなアクションを起こす可能性があるかをさらに認識できるようになります。何か異常な場合、たとえば、Web3 アプリケーションからのトランザクション呼び出しが、あるべきではない資産やコインに触れている場合、ユーザーはこれを観察して、トランザクションを続行しないことを決定できます。
Solana では、運用上の観点からアカウントには任意のデータが含まれるため、これは不可能です。アカウントを解釈するには、アカウントの外部説明 (アプリケーションに固有) が必要ですが、これは必ずしもスマート コントラクトの発行者によって提供されるわけではありません。さらに、資産所有権の概念は Solana ランタイムには存在せず、各スマート コントラクトはこのセマンティクスを手動で (通常はアカウントの署名と PDA を使用して) 実装する必要があります。これは、これを追跡する一般的な方法がないことを意味します。
副題
7.3 単純なトランザクションと複雑なトランザクション
特に、Sui では、コンセンサス レベルで興味深い最適化が行われており、特定の種類のトランザクションが完全なコンセンサスを放棄して、ビザンチン一貫性ブロードキャストに基づくより単純なアルゴリズムを採用できるようになります。この利点は、これらのトランザクションをコンセンサス レベルで並列化できることであり、行頭のブロッキングが排除され、ほぼ瞬時のファイナリティが実現され、本質的に web2 のスケーラビリティが実現されます。
これは、Sui の所有オブジェクトと共有オブジェクトの区別によるものです (セクション 3.1 を参照)。独自のオブジェクトのみが関与するトランザクション (単純トランザクションと呼ばれます) には、Sui での完全な合意は必要ありません。自身のオブジェクトは送信者以外のトランザクションでは使用できず、送信者は一度に 1 つのトランザクションしか送信できないため、これ自体、これらのトランザクションを他のトランザクションを参照して順序付けする必要がないことを意味します(合計順序付けと因果的順序付け)。 - トランザクションで参照されるオブジェクトは他のトランザクションの影響を受けることがなく、トランザクションは他のオブジェクトに影響を与えることができないことがわかっています。したがって、チェーン上で並行して発生する他のトランザクションに対するそのトランザクションの順序は気にしません。実際には無関係です。 Sui はこの事実を利用して単純なトランザクションの処理を大幅に最適化し、数百ミリ秒でファイナリティを達成します。ただし、送信者が一度に 1 つのトランザクションしか送信できないという欠点があります。一方、複雑なトランザクションと呼ばれる、任意の数の共有オブジェクトが関与するトランザクションには、常に完全な合意が必要です。
特定の種類のアプリケーションでは、独自のオブジェクトの作成、転送、および変更がすべて単純なトランザクションを通じて実行できるため、単純なトランザクションを有効に活用できます。良い例は、NFT (大量コインを含む) や Web3 ゲームです。これらのユースケースでは、低レイテンシのファイナリティとピア ブロッキングの排除から大きなメリットが得られ、より優れたユーザー エクスペリエンスとスケーラビリティが可能になります。
ただし、他の種類のアプリケーションは複雑なトランザクションに依存する必要があります。これには、ほとんどの DeFi アプリケーションが含まれます。たとえば、あらゆる種類の為替注文の実行には完全なコンセンサスと完全な注文が必要であるため、AMM 流動性プールは共有オブジェクトである必要があります。基本的に、複数の注文が異なるユーザーから同時に来た場合、誰の注文を最初に約定するかについて合意する必要があり、それによって各ユーザーが取得する約定価格が決まります。
単純なトランザクションと複雑なトランザクションについては、Sui のドキュメントに詳細が記載されています。
https://docs.sui.io/devnet/learn/sui-compared
https://docs.sui.io/devnet/learn/how-sui-works#system-overview
最初のレベルのタイトル
8. 結論
この記事では、Solana と Sui のプログラミング モデルを詳しく調査および比較し、Move プログラミング言語についても説明します。
第 2 章では Solana プログラミング モデルの概要を示し、第 3 章では、Sui Move とそのプログラミング モデルを紹介します。第 4 章では、Move における型とリソースの安全性がどのように機能するかについて説明します。スマート コントラクト開発における Move 機能の影響はすぐには明らかではないため、第 5 章では、実際の例を使用して Solana と SuiMove をより徹底的に比較します。第 6 章では eBPF/SBF について説明し、Move 機能や Move 自体を Solana 上で動作させるのが簡単ではないことを示しています。第 7 章では、Sui の Move 関連機能のいくつかについて説明します。
スマート コントラクト プログラミングとは、デジタル資産をプログラミングすることです。これは、これまで見てきた他のタイプのプログラミング (システム、バックエンドなど) とは大きく異なる、新しいタイプのプログラミングであると言えます。このため、既存のプログラミング言語とプログラミング モデルは、当然のことながら、このユースケースにはあまり適していません。
問題の核心は、リソースを自然に操作しながら、同時に信頼できないコードとも対話できるプログラミング モデルが必要であるということです。 Solana はここで妥協し、トラストレス環境でスマート コントラクトに必要なプログラム可能性を持たせることができますが、そのプログラミング モデルはリソースを使用したプログラミングには不自然です。バイトコード検証により、両方のプロパティを持つことが可能になります。ある意味、信頼できないコードを信頼できるコードに変えます。
Move は、スマート コントラクト開発用の新しいプログラミング言語です。その中心となるイノベーションは、検証可能になるように意図的に設計されたバイトコードです。バイトコード検証自体は新しい概念ではありませんが、Move が行う検証は確かに革新的です。 Move は、バイトコードと検証を通じて、リソースに対する最上級のサポートを備えたスマート コントラクト プログラミング モデルを実装し、信頼できない環境での安全なプログラミングを保証します。
スマート コントラクト開発における Move は、フロントエンド開発における React と同じだと思います。 「Move でできることは Rust でもできる」というのは、「React でできることは jQuery でもできる」と言っているのと同じです。もちろん、React アプリケーションと同等の jQuery ベースのアプリケーションを実装することは可能ですが、現実的ではありません。 React は、開発者にとって完全に理解できる仮想 DOM の概念を導入していますが、フロントエンドの開発をより速く、スケーラブルで、よりシンプルにします。同様に、Move のバイトコード検証も開発者にとって理解しやすい低レベルのテクノロジーですが、より人間工学的で構成可能で、より安全なスマート コントラクト開発を実現します。また、Move は、そのセキュリティとより直観的なプログラミング モデルにより、スマート コントラクト開発者の参入障壁を大幅に下げます。
もしMoveがなんとか牽引力を獲得できれば(そしてその兆候は初期段階からある)、ソラナにとって深刻な脅威となる可能性がある。理由は 2 つあります。
まず第一に、Move スマート コントラクトの開発時間ははるかに速くなります。 Move でスマート コントラクトをゼロから開発すると、Rust よりも 2 ~ 5 倍速くなります。したがって、Move エコシステムの発展は Solana の発展を超える可能性があります。ブロックチェーンのオープンで許可のない性質により、深刻なロックイン効果はなく、流動性は簡単に移動できます。 Solana 開発者は、純粋に経済的理由から Move の採用を余儀なくされる可能性があります。Move に切り替えるか、より安全なスマート コントラクトをより迅速に開発できる Move 開発者に追い越されるかのどちらかです。スマート コントラクト開発者を雇いたい場合は、1 つのスマート コントラクトを構築できる Rust 開発者を雇うことも、同じ時間内にさらに 2 つの安全なスマート コントラクトを構築できる Move 開発者を雇うこともできます。これは、React がフロントエンド開発に対して行ったことと似ています。
第二に、Move は Rust や Solidity よりも参入障壁がはるかに低いです。 Move 構文がより単純で、プログラミング モデルがより直観的であるためです。一部の開発者は、Rust や Solidity ではスマート コントラクト開発を行うことができませんが、Move ではできる場合があります。学ぶべき概念が少ないため、スマート コントラクト以外の開発者は、Rust (Rust 自体は複雑な言語であり、PDA などの Solana の概念と組み合わせると、初心者にとって大きな混乱を引き起こします) や Solidity (安全なスマート コントラクトを開発するには、リエントラントなどの言語の非常に細かい詳細に精通している必要があります) の方がはるかに簡単です。既存の Solana および Solidity 開発者が Move に切り替えなかったとしても、まだこの分野に参入していない開発者の市場は、その分野に存在する既存の開発者の数よりも桁違いに大きくなります。 Move は参入障壁が低く、開発が迅速であるため、Rust や Solidity よりも製品市場への適合性が高く、より大きなパイを占めることができます。新しい開発者が流入し始めたら、Rust や Solidity ではなく、Move から始めてほしいと思います。これは Web 業界における React のやり方と似ています。
このため、私は Solana が中長期的に Move に対する一流のサポートを追加することを強く期待しています。しかし、それは簡単ではありません。 Move の主な利点を得るには、Move バイトコードがネイティブにサポートされている必要があります。これは、Move を eBPF/SBF に単純にコンパイルすることは不可能であることを意味します (セクション 6.3 を参照)。既存のエコシステムを維持するには、両方の操作をサポートする必要があります。主な技術的課題は、操作間の適切な相互運用性をどのように実現するかです。これには、Move と Solana についての深い理解が必要です。そのため、Solana チームが Move チームのサポートを得て、これを直接推進してくれることを期待しています。
Move は Meta (旧姓 Facebook) の Diem プロジェクトから生まれました。 Sam Blackshear 氏が率いる Move チームは、スマート コントラクトを処理する方法を見つけるという任務を負っていました。問題を詳しく調べた結果、スマート コントラクトのプログラミングはすべてデジタル アセット (リソース) に関するものであることがわかりましたが、既存の言語はいずれもこのユースケースをサポートしていないため、新しいプログラミング言語をゼロから構築することにしました。


