スマートコントラクトのアップグレード可能性はどのように実現されますか?

Hans-Helmut Kraus
Hans-Helmut Kraus
Ethereum smart contract auditor and security expert; 以太坊智能合约审计师与安全专家。

友よ!その質問は非常に良いですね。スマートコントラクト開発を始めたばかりの多くの人が最も戸惑う点の一つです。例え話を使って、完全に理解できるようお手伝いしましょう。

核心的な矛盾点:ブロックチェーンの「不変性」

まず、ブロックチェーンの最大の特徴の一つが改ざん不可能であることです。一度スマートコントラクトがイーサリアムにデプロイされると、そのコードは石に刻まれたように、誰にも修正できません。これは利点(安全、信頼性)であると同時に、大きな欠点でもあります。

考えてみてください。もしコードにバグがあったり、将来的に製品に新機能を追加したくなったりしたら、どうしますか?古いコントラクトを放棄し、すべてのユーザーに新しいコントラクトへ移行してもらうのでしょうか?それはあまりにも手間がかかります。ユーザーの資産やデータはすべて古いコントラクトにあり、移行は困難かつ高価です。

そこで、コミュニティの識者たちはこの問題を解決するための素晴らしい方法を考案しました。それが**プロキシパターン(Proxy Pattern)**です。


核心となる考え方:アドレスとロジックの分離

「プロキシパターン」という言葉に怖がらないでください。その考え方は実は非常にシンプルで、身近な例を使って説明します。

あなたがネットショップを開いていると想像してください。あなたのウェブサイトのアドレスは www.my-awesome-shop.com です。

すべての顧客はこのURLだけを認識しています。最初、このURLはあなたのサーバーAを指していました。サーバーAではあなたのネットショップのV1.0バージョンコードが実行されています。

その後、ネットショップをアップグレードして、タイムセール機能を追加したいと考え、V2.0バージョンを開発し、新しいサーバーBにデプロイしました。

このとき、すべての顧客に「新しいURLに変わりました!」と通知する必要はありません。 ドメイン管理画面で、www.my-awesome-shop.com のドメインの指し先をサーバーAからサーバーBに変更するだけで良いのです。

顧客にとっては、アクセスするアドレスは常に変わらないURLですが、彼らが体験するのはすでに最新の機能です。

この例で言うと:

  • 変わらないURL (www.my-awesome-shop.com) -> これがプロキシコントラクト (Proxy Contract) です。
  • 交換可能なサーバー (サーバーA, サーバーB) -> これがロジックコントラクト (Logic/Implementation Contract) です。

スマートコントラクトのアップグレード可能性は、この考え方をブロックチェーン上に持ち込んだものです。


2つの主要な役割とそれらの連携

アップグレード可能なコントラクトのアーキテクチャでは、通常少なくとも2つのコントラクトをデプロイします。

  1. プロキシコントラクト (Proxy)

    • これはユーザーが実際にやり取りする入り口であり、そのアドレスは永久不変です。
    • それ自体にはほとんどビジネスロジックがなく、非常にシンプルです。
    • それはすべてのデータと状態を保存する役割を担います(例:ユーザーの残高、NFTの所有権など)。
    • その最も重要な機能は、「伝言役」のように、受け取ったすべてのリクエスト(ユーザーが関数を呼び出すなど)を「ロジックコントラクト」に転送して処理させることです。
  2. ロジックコントラクト (Implementation)

    • ここには、私たちのプロジェクトのすべての実際のビジネスロジックコード(例:送金、NFTのミントなど)が含まれています。
    • それはいかなる状態データも保存せず、「ステートレス」なツール役です。
    • このコントラクトは置き換えることができます。V2、V3バージョンのロジックコントラクトをデプロイできます。

神秘的な delegatecall

プロキシコントラクトはどのようにしてリクエストをロジックコントラクトに転送するのでしょうか?それは delegatecall と呼ばれる非常に特殊なオペコードを使用します。

delegatecall の魔法のような点は次のとおりです。

プロキシコントラクトがロジックコントラクトのコードを「借りて」実行することを可能にしますが、コード実行のコンテキスト(context)は依然としてプロキシコントラクト自身の環境です。

簡単に言うと: プロキシコントラクトはロジックコントラクトにこう言います。「おい、お前の頭脳(ロジックコントラクトのコード)を使って、俺の家の机の上にある書類(プロキシコントラクトのデータと状態)を処理してくれ。」

このようにして、すべての状態変更(例えばユーザー残高の増減)は、ロジックコントラクトではなくプロキシコントラクトのストレージスペースで発生します。そのため、将来ロジックコントラクトを置き換えたとしても(より賢い「頭脳」に交換したとしても)、データはプロキシコントラクトという「家」の中に完全に保存されたままになります。


完全なアップグレードプロセスはどのようなものですか?

  1. 初回デプロイ

    • ロジックコントラクトV1 (Logic_V1) をデプロイします。
    • プロキシコントラクト (Proxy) をデプロイし、デプロイ時にロジックコントラクトのアドレスが Logic_V1 のアドレスであることを伝えます。
  2. 日常的な使用

    • すべてのユーザーは Proxy コントラクトのアドレスのみとやり取りします。
    • Proxy はリクエストを受け取ると、delegatecall を通じて Logic_V1 のコードを実行させ、すべてのデータは Proxy に保存されます。
  3. アップグレードの開始

    • バグが見つかった、または新機能を追加したい場合、新しいロジックコントラクトV2 (Logic_V2) を開発しデプロイします。
    • コントラクトの管理者として、Proxy コントラクトの特別な管理関数(例えば upgradeTo() と呼ばれるもの)を呼び出します。
    • Proxy コントラクトに「今から、あなたの新しいロジックコントラクトのアドレスは Logic_V2 のアドレスです」と伝えます。
  4. アップグレードの完了

    • Proxy コントラクト内部に記録されているロジックコントラクトのアドレスが Logic_V1 から Logic_V2 に更新されます。
    • ユーザーは引き続き同じ Proxy アドレスとやり取りしますが、今度は Proxy がリクエストを Logic_V2 に転送して実行するようになります。
    • このプロセス全体はユーザーには意識されず、彼らが使用するコントラクトアドレスは一切変わっていませんが、コントラクトの機能は一新されています!

まとめ

特性例え役割
永続的なアドレス & データ保存ウェブサイトのドメイン / あなたの家の番地プロキシコントラクト (Proxy)
変更可能なビジネスロジックウェブサイトの裏側のサーバー / あなたの家の内装スタイルロジックコントラクト (Implementation)
両者をつなぐ魔法ドメイン解決 / 「お前の頭脳で俺の家の仕事をしろ」delegatecall

これがスマートコントラクトがアップグレード可能性を実現する核心的な秘密です。不変のアドレス/データ変更可能なロジックを分離することで、コントラクトの入り口を変えたり、データを移行したりすることなく、コントラクト機能の修正とイテレーションを実現できます。現在、最も主流な実装標準はOpenZeppelinが提唱するUUPSとTransparent Proxy Patternです。

この説明がお役に立てば幸いです!