Rustスマートコントラクト開発日記(9)
BlockSec
2022-04-01 11:39
本文约2024字,阅读全文需要约8分钟
プログラムのメンテナンスにおける根本的な問題は、バグ修正によって常に (20 ~ 50)% の確率で新しいバグが発生することです。

副題

1. 契約アップグレードの必要性

副題

2. Solidity 契約の一般的なアップグレード方法

イーサリアムでは、スマート コントラクトは不変であり、一度チェーンにデプロイされると、誰もそれを変更することはできません。

では、契約に抜け穴がある場合、または契約に新しい機能を追加する必要がある場合、契約のコードをどのように変更すればよいのでしょうか?解決策は、新しいコントラクトをブロックチェーンにデプロイすることです。

この方法の課題は、Solidity がコントラクトを展開するたびに、コントラクトに一意のアドレスが割り当てられることです。したがって、このコントラクトを使用するすべての DApp は、新しいコントラクトに適応するようにコントラクト アドレスを変更する必要があります。さらに、古いバージョンの契約の状態を新しいバージョンの契約に移行する必要があります。より複雑な状態を持つ契約の移行には多くの作業が必要で、エラーが発生しやすく、データのコピーにかかるガスコストがかかります。は高い。

そのため、通常はデータとロジックを分離し、ロジックを一切処理しないコントラクト(ステートコントラクト)にデータを格納し、全てのロジックを別のコントラクト(ロジックコントラクト)に実装するアーキテクチャを採用しています。通常、コントラクトのアップグレードではロジックが変更されますが、このアーキテクチャを使用すると、状態の移行を気にせずにロジック コントラクトをアップグレードするだけで済みます。

この問題を解決するには代理契約(Proxy Contract)を利用することができ、具体的な仕組みは下図のとおりです。

プロキシ コントラクトはデータの保存に使用され、delegatecall はロジック コントラクト A の呼び出しに使用されます。これにより、コントラクト A によって読み書きされたデータはプロキシ コントラクトに保存されます。ロジック コントラクトをアップグレードする必要がある場合は、新しいコントラクト B をデプロイし、プロキシ コントラクトにトランザクションを送信して、プロキシ コントラクトが新しいロジック コントラクト B を指すようにします。

3. NEAR契約のアップグレードの一般的な方法

次に、StatusMessage プロジェクトを例として、NEAR コントラクトの一般的なアップグレード方法を紹介します。StatusMessage のコントラクト コードは次のとおりです。

まず、コンパイルされたコントラクトをテストネットにデプロイします。

取引内容は以下の通りです

次に、set_status メソッドを呼び出してコントラクトにデータを保存します。

取引内容は以下の通りです

次に、2 つの異なる契約アップグレード シナリオについて詳しく説明します。

3.1 契約データ構造は変更されていない

たとえば、次の関数を追加します。

コンパイル後、deploy を使用して再デプロイします。

コンパイル後、deploy を使用して再デプロイします。

次に、get_status メソッドを呼び出して、以前に書き込まれたデータを読み取ります。

元のコントラクト内のデータは正常に読み取ることができます。

これは、NEAR コントラクトは繰り返しデプロイできるためであり、アカウント (アドレス) がすでにコントラクトをデプロイしている場合、neardeploy コマンドを再度呼び出すと、新しいコントラクト コードをアカウントにデプロイできます。データ構造を変更せずにコントラクト ロジックのみを変更する場合は、neardeploy を直接使用して新しいコードをデプロイできます。

3.2 契約データ構造の変更

契約をアップグレードし、元のデータ構造を変更し、記録を削除し、キャッチフレーズと略歴を追加しました。

もう一度再デプロイしてみます。

コントラクトは引き続き正常にデプロイされています。

ただし、 get_tagline メソッドを呼び出して、保存されたデータを読み取ります。

何か問題が発生し、次のようなエラー メッセージが表示されることがわかります。

Cannot deserialize the contract state.

特定のトランザクションを参照してください。

https://explorer.testnet.near.org/transactions/4hQQ1zAwU5bsbfb6tA6DQDqjmFcHsBwaBctdHaPiCKHu

これは、コントラクトの状態がシリアル化されたデータの形式で永続的に保存されるためです。コントラクトが再デプロイされた後、コード内のデータ構造は変更されますが、状態は変更されません。新しいデータ構造が古い状態と一致しない場合、エラーが発生します。

3.3 アップグレード スマート コントラクトの移行

NEAR は、コントラクトのアップグレードに役立つ Migrate メソッドを提供しています。3.2 のエラーについては、新しいコントラクトに mitigrate メソッドを追加します。

コード内の #[init(ignore_state)] は、移行関数が実行される前に状態をロードしないことを意味します。次に、コントラクトを再デプロイしますが、デプロイ中に移行メソッドを呼び出します。

以下に示すように、コントラクトは正常にデプロイされています。

新しいデータのタグラインを取得するために、コントラクトの新しいメソッド get_tagline を呼び出します。

メソッドが正常に呼び出され、古いコントラクト データも新しいコントラクトに移行されていることがわかります。

4. 契約アップグレードのセキュリティに関する考慮事項

コントラクトのセキュリティ アップグレードでは、最初に権限制御を考慮する必要があります。通常、コントラクトは開発者または DAO によってのみアップグレードできます。前期Rustスマートコントラクト育成日記(8) コントラクトセキュリティの権限制御特権機能のアクセス制御を導入し、一般契約のアップグレード機能が唯一の所有者機能となり、所有者のみが呼び出せるようになります。

契約の所有者は可能な限り DAO とし、提案や投票を通じて共同で契約を管理することをお勧めします。所有者が個人アカウントとして設定されるため、契約は高度に一元化され、所有者は契約データを自由に変更でき、所有者の秘密鍵を失うリスクもあります。

さらに、開発者はコントラクトの移行を行う際に次の提案を考慮することもできます。

  • 移行関数が実行される前に状態が読み込まれないようにするには、移行関数の前に #[init(ignore_state)] を追加します。

  • 移行が完了したら、移行関数が 1 回だけ呼び出されるように、移行関数をできる限り削除します。

  • 新しく追加されたデータ構造は移行中に初期化されます。

BlockSec
作者文库