Web2 と Web3 を区別しようとするとき、両者を区別する重要な価値は所有権の概念です。
簡単に言えば、あなたが作成したものはあなたが所有し、収益化できるものです。あるべき姿で。それ以下でもそれ以上でもありません。実際、最初の NFT を作成した日は、ブロックチェーンの不変性のおかげで、所有者として Web3 で作成した日です。どちらかといえば、守られているという感覚はプライスレスです。
そういえば、この所有権の概念は、アクセス可能な機能と許可された状態の変更に関して、スマート コントラクトにとって重要です。
では、自分が作成したスマート コントラクトを「所有」していることが重要なのはなぜでしょうか。
考えてみてください。自転車を所有しているとしたら、所有権の記録を保持することを躊躇しますか?誰かがそれを盗んで悪用した場合に備えて?もちろん違います。
NFT やスマート コントラクトの所有権を証明したいのと同じ理由です。誰かが不正アクセスを取得し、それによって金銭的な利益を得ている可能性がありますよね?
あなたが Dunzo と協力して、このスマート コントラクトを作成したとしましょう。このスマート コントラクトでは、パッケージの価格と配送料、および購入者が支払ったかどうかを設定できます。
理想的なシナリオでは、購入者の名前、パッケージと配送の合計金額を特定の金額に設定し、setPackageDelivered を true に設定できます。もちろん、パッケージの配信が完了したら。
しかし、誰もが知っているように、計画通りに進むことはありません。
では、バイヤーがコントラクト機能にアクセスでき、packageDelivered 値を false にリセットしたり、packageDeliveryPrice 値をより低い値に変更したりできるとしたらどうでしょうか?製品に支払う金額が減るだけでなく、何も支払わなくても済むのです。
明らかに、Dunzo を除いて、誰もそのような変更を行うためのアクセス権を持つべきではありません。それが、アクセス制御の設定が重要である理由です。
それでは、このスマート コントラクトをデプロイして、侵入者が状態変数の値を簡単に変更できることを見てみましょう。
setPrice と setPackageDelivered の両方の機能にアクセスできるため、Dunzo の配送に金銭的損失をもたらす可能性のある変更を誰でも行うことができます。
Dunzo が商品の元の価格を 5 ETH に設定していた場合、顧客はその値を 3 ETH に変更できます。もちろん、顧客は setPackageDelivered 関数にアクセスしてブール値を false に変更することもできるため、同じアイテムの別の配達を受け取る可能性があります。したがって、どちらの場合も、Dunzo はお金を失う立場にあり、手遅れになるまでそれに気付かないかもしれません。
たとえば、以下に示すように、配送される製品が 5 ETH の価値があるとします。
導入後は、Dunzo 以外は契約条件を変更できないようにする必要があります。それでも、Remix でアドレスを切り替えて価格を 3 にリセットすると、保護が行われていないために可能になります。
setPackageDelivered ステータスを true から false に変更することもできます。パッケージがすでにお客様に配達されている場合でも。
お分かりのように、スマート コントラクトの所有者と他のユーザーを区別することが重要です。それでは、このスマート コントラクトに適切なアクセス制御を設定し、結果としてその安全性を確保する 4 つの修正を見てみましょう。
では、スマート コントラクトを作成するときに、所有者と他のユーザーをどのように区別すればよいでしょうか?
明らかに、これが必要なのは、無視すると経済的損失が発生する可能性があるためです。
方法 #1: require ステートメントを使用する
以下のコードでわかるように、アドレス型の新しい状態変数所有者が「require」ステートメントとともに追加されています。コンストラクターでは、所有者の状態変数に、コントラクトのデプロイ時に使用されるアドレスが格納されます。ご存じのように、スマート コントラクトのコンストラクターは 1 回しか呼び出されないため、アドレスを変更することはできません。
ここで、より少ない値と別のアドレスで setPrice 関数を呼び出すと、以下のメッセージのように、トランザクションは初期状態に戻ります。
ご覧のとおり、コントラクトによって提供された理由には、「require」ステートメントに追加したエラー メッセージが含まれています。とはいえ、契約の所有者 (この場合は Dunzo) 以外は、販売条件を変更することはできません。
方法 #2: 関数修飾子を使用する
適切な条件で「require」ステートメントを追加するのは簡単ですが、修飾子は繰り返し使用できるコードのブロックです。これは、そのようなチェックのために多数の 'require' ステートメントを記述するよりもはるかに理にかなっています。
前に共有したコードでは、setPrice 関数のみが「require」ステートメントによって保護されていますが、setPackageDelivered 関数については何も行われていません。以下のSolidityコードに示すように、修飾子を使用するとうまくいくはずです。
ここで、setPrice または setPackageDelivered 関数にアクセスしようとすると、前に取得したものと同じエラー メッセージが次のように表示されます。
方法 #3: 所有可能
この修正では、OpenZeppelin が提供する所有可能なスマート コントラクトを使用する必要があります。まず、Ownable スマート コントラクトをインポートしてから、保護する関数に onlyOwner 修飾子を追加する必要があります。したがって、以下に示すように、setPrice 関数と setPackageDelivered 関数の両方に、以下の onlyOwner 修飾子があります。
これで、dunzoDelivery スマート コントラクトは、Ownable.sol の関数の一部を使用するため、コントラクト名にも「is Ownable」が追加されます。
そうは言っても、Ownable スマート コントラクトを使用するときにアクセスできる関数が他に 2 つあります。renounceOwnership と transferOwnership です。通常、コントラクトのデプロイに使用されたアカウントが所有者と見なされますが、これはこれらの関数を使用して変更できます。
所有者の住所とその他の契約の詳細を表示する以外に、以下で使用する renounceOwnership および transferOwnership 関数を確認できます。
予想どおり、別のアドレスを使用して setPrice 関数を呼び出そうとすると、トランザクションは実行されません。代わりに、以下に示す理由により、元の状態に戻ります。
所有可能なスマート コントラクトを使用すると、他のユーザーの中で 1 人の所有者のみが必要な場合に適しています。さらに階層を探している場合は、AccessControl.sol スマート コントラクトを使用する必要があります。
方法 #4: AccessControl
この最後の修正では、Open Zeppelin によって AccessControl スマート コントラクトにアクセスする必要があります。
手始めに、Access Control ハイパーリンクをインポート ステートメントに追加し、「is AccessControl」を契約ステートメントに追加する必要があります。
前述のように、このスマート コントラクトを使用すると、以下に示すスマート コントラクトの最初の 2 行のように、宣言する必要がある階層内に多数のロールを追加できます。
このコントラクトには、Dunzo と Customer という 2 つの役割があります。ここで、コントラクトを展開するときに、誰が何にアクセスできるかを決定するために、前述の宣言されたロールを前述のアドレスに割り当てる必要があります。さらに、2 つの「require」ステートメントが setPrice および setPackageDelivered 関数に追加されました。
おわかりのように、Dunzo ロールが割り当てられたアカウントのみが、デプロイ時に言及された販売条件を変更できますが、それ以外は変更できません。つまり、AccessControl.sol を使用してスマート コントラクトの機能へのアクセス レベルが異なる多数のロールを割り当てることができますが、Ownable.sol はそれほど深くは入りません。
現在、スマート コントラクトでアクセス制御を実装するために利用できるソリューションはこれらだけではありません。 RBAC.solとWhitelistCrowdSale.solも過去に利用可能で、AccessControl.sol や Ownable.sol と同様に、役割をアドレスに関連付けることもできます。
したがって、アクセス制御の実装の進化について完全に理解したい場合は、これら 2 つのスマート コントラクトのコードについても検討する価値があります。
この記事のリード画像は、HackerNoon の AI Image Generatorによって、プロンプト「アクセスが拒否された生体認証スキャン」を介して生成されました。