はじめに
初めまして。
CryptoGamesというブロックチェーンゲーム企業でエンジニアをしている cardene(かるでね) です!
スマートコントラクトを書いたり、フロントエンド・バックエンド・インフラと幅広く触れています。
代表的なゲームはクリプトスペルズというブロックチェーンゲームです。
今回は、データを保持しつつコントラクトのコードの置換を行うSETCODE
(0xfc
)を追加し、アドレスの変更を行うことなくコントラクトをアップグレードする仕組みを提案しているEIP6913についてまとめていきます!
以下にまとめられているものを翻訳・要約・補足しながらまとめていきます。
他にも様々なEIPについてまとめています。
概要
この規格では、SETCODE (0xfc)
という実行中のアカウントのコードをメモリから置き換える命令の導入を提案しています。
動機
スマートコントラクトのアップグレードは、コントラクトの機能向上や設計上の決定を後回しにすることを可能にするため重要です。
これまでのアップグレードの方法とSETCODE
の利点を順を追って解説します。
コントラクトのアップグレードについては以下の記事を参考にしてください。
既存のアップグレード方法
CALLを使用する方法
最も古い方法で、実装を別のコントラクトにCALLすることでアップグレードします。
この方法の限界は、すべての将来の実装が内部状態を変更できる必要がある点です。
DELEGATECALLを使用する方法
代理のコントラクトであるプロキシコントラクトが、実装コントラクトを実行する方法。
この方法は、アカウントのコードサイズ制限を回避できる利点があります。
ただし、多くの異なる実装アカウントへの分岐があるプロキシも存在します。
SELFDESTRUCTとCREATE2を使用する方法
コードをその場で置き換えるために、SELFDESTRUCT
で自己破壊し、CREATE2
で新しいコードを生成します。
外部コントラクトへの呼び出しを必要としない点で以前の方法よりも改善されています。
ただし、SELFDESTRUCT
によって内部状態が削除されるという制限があります。
また、SELFDESTRUCT
はトランザクションの終了時までコードを削除しないため、CREATE2
がアップグレードを完了するまで利用できないという問題もあります。
SETCODEの導入
SETCODE
は上記の方法に対してさらに改良された手法を提供します。
これにより、コントラクトのコードをその場で直接置き換えることができます。
この命令は、SELFDESTRUCT
の廃止が予定されていることを受けて導入されています。
SETCODE
を使用することで、内部状態を保持しつつコードの即時更新が可能になり、利用可能性の低下を防ぎながら安全にコードを更新できます。
SETCODE
の導入により、開発者はより柔軟にコントラクトを管理し、アップグレードすることができるようになります。
これは特に長期にわたってサービスを維持し、運用しなければならないプロジェクトにとって大きなメリットです。
ただし、新しいコードの安全性と正確性を確保するための厳格なテストと検証が必要になります。
仕様
この命令はスマートコントラクトのコードを動的にアップグレードするために使用されますが、特定の実行環境下や条件下での挙動にはいくつかの重要な制約があります。
制約と例外処理
読み取り専用実行スコープ中の制約
STATICCALL
などによって作成される再帰的な読み取り専用実行スコープ中で SETCODE
を実行すると、例外的な中断が発生します。
これは、読み取り専用のスコープ内で状態変更操作(コードの書き換え含む)を行うことができないためです。
DELEGATECALLやCREATEの中での制約
現在実行中のコードが実行アカウントのコードと異なる場合(例:DELEGATECALL
やCREATE
を通じて)、SETCODE
命令を実行すると例外的な中断が生じます。
これは、これらのコンテキストでは他のアカウントのコードを実行しているため、自アカウントのコードを変更することは許されていません。
実行と検証
SETCODE
はスタックから2つの値(オフセットと長さ)を消費します。
これらは新しいコードが含まれるメモリ範囲を指定します。
新しいコードに対する検証が即座に実施され、CREATE
や CREATE2
の結果として行われる検証と同様のものが行われます。
これにより、検証失敗時には例外的な中断が発生する可能性があります。
EXTCODESIZE
や EXTCODECOPY
のオペレーションは更新されたコードを問い合わせます。
DELEGATECALL
、CALLCODE
、CALL
、STATICCALL
といったメッセージコールは更新されたコードを実行します。
すでに置き換えられたコードを実行中の実行スコープは、そのスコープ内で SETCODE
を行ったものも含め、以前のコードの実行を継続します。
このようなスコープ内では CODESIZE
と CODECOPY
が実行中のコードを問い合わせ続けます。
例外とロールバック
SETCODE
によるアカウントの変更は、現在のスコープまたは任意の親スコープがrevert
または中断した場合にはロールバックされます。
これは SSTORE
命令の動作と同様です。
SELFDESTRUCTとの違い
SETCODE
は SELFDESTRUCT
と異なり、アカウントの残高、nonce
、またはストレージをクリアしません。
ガス消費
SETCODE
のガスコストは Gselfdestruct
のコストと、新しいコードのバイト数に Gcodedeposit
を乗じた値の合計です。
これにより、コードの置き換えにかかる計算資源のコストが考慮されます。
この仕様は、動的なコードアップグレードを安全に行うための厳格な条件を設定しており、エラーハンドリングとセキュリティの観点から設計されています。
補足
挙動の一致性
CODECOPY
、CODESIZE
、EXTCODESIZE
、EXTCODECOPY
が DELEGATECALL
や CREATE
での挙動と一致する理由は、実行中のコードが実行アカウントのコードと異なる可能性があるからです。
このような状況では、これらの命令は実際に実行中のコード(実行コンテキストに依存する)を参照する必要があります。
ガス消費の理由
SETCODE
のガスコストは CREATE
と比較可能ですが、Gcreate
(新しい実行コンテキストや新しいアカウントの生成に関連するコスト)は含まれません。
これは、SETCODE
が新しいコンテキストやアカウントを生成するわけではなく、既存のアカウントのコードを単に置き換えるためです。
その他のアカウント変更コストは実行ガスの外で計算されます。
SELFDESTRUCTとの比較
SELFDESTRUCT
と異なり、SETCODE
の実行後は通常通りの処理が続行され、これにより検証や返り値の提供が可能です。
SELFDESTRUCT
は実行を終了し、関連アカウントを即座に削除しますが、SETCODE
は更新後の検証を可能にし、必要に応じて REVERT
や再帰的な SETCODE
で操作を元に戻すことができます。
REVERT
を使用する場合は、ガスを節約しつつ SETCODE
操作を取り消すことができます。
DELEGATECALL内でのSETCODEの禁止の理由
ほとんどの DELEGATECALL
内で SETCODE
を禁止することにより、静的解析を用いて可変コードを容易に識別することが可能になります。
SETCODE
操作を含まないアカウントコードは、不変であると安全に仮定できます。
revert
しないコンテキストで観察されたコードが不変である場合、そのコードは引き続き不変であると見なされ、オンチェーンの静的解析によって不変性を検証できるようになります。
この設計は、スマートコントラクトの動的なアップグレードを可能にしつつ、セキュリティと効率性を高めるための配慮がなされています。
これにより、開発者はコードの安全性を保ちつつ、必要に応じてコントラクトの挙動を更新することができます。
互換性
SELFDESTRUCTの挙動
SELFDESTRUCT
命令は、スマートコントラクトを自己破壊し、その残高を指定されたアドレスに送金するものです。
この操作はコントラクトを即座に無効化するものではなく、コントラクトのコードと状態はトランザクションの終了時まで削除されません。
これにより、SELFDESTRUCT
が実行された後もそのトランザクション内での他の命令実行が可能となりますが、トランザクションの完了と共に最終的にコントラクトは削除されます。
SETCODEとの関連性
SETCODE
はスマートコントラクトのコードをその場で置き換える命令です。
この命令により、新しいコードが即時に有効化され、旧コードは置き換えられます。
SELFDESTRUCT
によるコードの削除がトランザクションの終了まで遅延されるため、同一トランザクション内で SELFDESTRUCT
と SETCODE
が同時に使用された場合、SETCODE
によって新しいコードが設置されても、トランザクションの終了時にはそのコントラクトは完全に削除されます。
定義された相互作用
SELFDESTRUCT
と SETCODE
の相互作用が明確に定義されているとは、これらの命令が同一トランザクション内でどのように機能するか、そしてそれがどのような結果をもたらすかが予測可能であることを意味します。
例えば、SELFDESTRUCT
が先に呼び出された後に SETCODE
が実行された場合、SETCODE
で設定された新しいコードはトランザクションの残りの期間中は有効ですが、トランザクション終了時には SELFDESTRUCT
による全体の削除が行われるため、その影響は一時的なものになります。
このように、SELFDESTRUCT
と SETCODE
の操作はそれぞれ独自の用途と効果を持ちながら、同一トランザクション内での使用においては予測可能で整合性のある結果を生み出します。
この明確な定義により、開発者はこれらの命令をより安全に、かつ効果的に使用することができます。
セキュリティ
アップグレード可能性のリスク
アップグレードの永続的なリスク
アップグレード機能が存在すること自体が、永続的なリスクを引き起こす可能性があります。
不適切なアップグレードはコントラクトの永続的な失敗を引き起こす可能性があります。
アップグレードの制限
多くのコントラクトはアップグレード可能であるべきではなく、変更されるべきではありません。
固定された機能や不変のロジックが求められる場面では、アップグレード機能を排除することが望ましいです。
アップグレード操作へのアクセス制限
アクセスの制限
アップグレード操作にアクセスできるのは限られたユーザーや条件のみにすることが推奨されます。
アクセス制御を厳格にすることで、権限のない者による不正なアップグレードを防ぎます。
慎重なアップグレード
アップグレードは慌てて行うべきではなく、疲れた状態でも避けるべきです。
不注意や急ぎの判断がセキュリティ事故につながる可能性があります。
テストの徹底
アップグレードは本番環境とできるだけ類似した条件下でテストを行うべきです。
環境の違いは予期せぬ結果を引き起こす原因となり得ます。
複数のエンジニアによる確認
可能であれば、複数のエンジニアがアップグレード手順を事前に確認し、独立して検証することが望ましいです。
これにより、ミスや見落としが減少します。
ソフトウェアとインターフェースの役割
ブロックエクスプローラー、ウォレット、その他のインターフェース
これらのツールはアップグレード可能なコードを明示的に表示し、ユーザーが識別できるようにするべきです。
クライアントソフトウェアの警告
ERC20やERC721トークンをアップグレード可能なアカウントに承認する時には、クライアントソフトウェアから警告が出されるべきです。
これにより、ユーザーはトークンのセキュリティリスクを意識できるようになります。
ERC20については以下の記事を参考にしてください。
ERC721については以下の記事を参考にしてください。
このセクションでは、アップグレード機能がもたらす潜在的なリスクを最小限に抑え、安全性を確保するための具体的な戦略と手法が提示されています。
これにより、開発者や利用者はスマートコントラクトをより安全に運用するためのガイドラインを得ることができます。
引用
William Morriss (@wjmelements), "EIP-6913: SETCODE instruction [DRAFT]," Ethereum Improvement Proposals, no. 6913, April 2023. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-6913.
最後に
今回は「データを保持しつつコントラクトのコードの置換を行うSETCODE
(0xfc
)を追加し、アドレスの変更を行うことなくコントラクトをアップグレードする仕組みを提案しているEIP6913」についてまとめてきました!
いかがだったでしょうか?
質問などがある方は以下のTwitterのDMなどからお気軽に質問してください!
他の媒体でも情報発信しているのでぜひ他も見ていってください!