LOADING

加载过慢请开启缓存 浏览器默认开启

Code review: Openzeppelin - AccessControlCrossChain

2023/6/26 Solidity Coding

概述:

这个名为 AccessControlCrossChain 的 Solidity 合约是一个 OpenZeppelin Contracts 库中 AccessControlCrossChainEnabled 两个合约的扩展,它提供了跨链访问管理的支持。它为每个角色实现了等效的“别名”角色,用于限制来自其他链的调用。该合约的主要目的是保护不同链上由不同实体控制的相同地址的多个合约之间的冲突。

完整代码:

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (access/AccessControlCrossChain.sol)

pragma solidity ^0.8.4;

import "./AccessControl.sol";
import "../crosschain/CrossChainEnabled.sol";

/**
 * @dev An extension to {AccessControl} with support for cross-chain access management.
 * For each role, is extension implements an equivalent "aliased" role that is used for
 * restricting calls originating from other chains.
 *
 * For example, if a function `myFunction` is protected by `onlyRole(SOME_ROLE)`, and
 * if an address `x` has role `SOME_ROLE`, it would be able to call `myFunction` directly.
 * A wallet or contract at the same address on another chain would however not be able
 * to call this function. In order to do so, it would require to have the role
 * `_crossChainRoleAlias(SOME_ROLE)`.
 *
 * This aliasing is required to protect against multiple contracts living at the same
 * address on different chains but controlled by conflicting entities.
 *
 * _Available since v4.6._
 */
abstract contract AccessControlCrossChain is AccessControl, CrossChainEnabled {
    bytes32 public constant CROSSCHAIN_ALIAS = keccak256("CROSSCHAIN_ALIAS");

    /**
     * @dev See {AccessControl-_checkRole}.
     */
    function _checkRole(bytes32 role) internal view virtual override {
        if (_isCrossChain()) {
            _checkRole(_crossChainRoleAlias(role), _crossChainSender());
        } else {
            super._checkRole(role);
        }
    }

    /**
     * @dev Returns the aliased role corresponding to `role`.
     */
    function _crossChainRoleAlias(bytes32 role) internal pure virtual returns (bytes32) {
        return role ^ CROSSCHAIN_ALIAS;
    }
}

代码分析:

pragma solidity ^0.8.4;

这行代码 pragma solidity ^0.8.4; 是 Solidity 合约中的一个指示器,它指示 Solidity 编译器使用版本为 0.8.4 或更高版本的 Solidity。它用于确保编译器可以正确地编译合约,以便与 Solidity 0.8.4 及更高版本兼容。

具体而言,这个指示器告诉 Solidity 编译器将 Solidity 合约代码编译为字节码,以便在区块链上部署和执行。它还指定 Solidity 编译器需要使用 0.8.4 或更高版本的 Solidity 编译器来编译这个合约,以确保合约能够正确地工作并且与其他 Solidity 合约兼容。

在代码实现方面,指示器只是在 Solidity 合约的开头添加一行 pragma solidity ^0.8.4;,告诉编译器使用指定的版本进行编译。

import "./AccessControl.sol";
import "../crosschain/CrossChainEnabled.sol";

这段代码导入了两个 Solidity 合约文件,分别是 AccessControl.solCrossChainEnabled.sol

AccessControl.sol 文件中定义了一个权限管理系统,可以控制合约中哪些地址具有哪些角色(Role),哪些角色具有哪些权限。开发人员可以使用这个系统来保证只有特定的地址或角色才能执行特定的操作,从而提高合约的安全性和可靠性。

CrossChainEnabled.sol 文件中提供了一些跨链交互相关的功能,可以方便地进行跨链交互,从而将智能合约从单链应用拓展到多链应用。这个文件中包含一些 Solidity 的库函数和一些存储变量,如 _chainId_crossChainSender 等。

这两个文件的实现细节在这里不详细介绍,但它们为 AccessControlCrossChain 合约提供了跨链访问管理的支持,提高了智能合约的安全性和灵活性。

abstract contract AccessControlCrossChain is AccessControl, CrossChainEnabled {
    bytes32 public constant CROSSCHAIN_ALIAS = keccak256("CROSSCHAIN_ALIAS");

这段代码定义了一个名为 AccessControlCrossChain 的抽象合约,它继承自 AccessControlCrossChainEnabled 两个合约。

这段代码还定义了一个名为 CROSSCHAIN_ALIAS 的常量,它是一个 bytes32 类型的值,其值为 "CROSSCHAIN_ALIAS" 的 keccak256 哈希值,用于为每个角色生成对应的别名角色。

合约的实现通过重写 _checkRole 函数来实现跨链访问管理。如果合约正在进行跨链访问,则在 _checkRole 函数中调用 _crossChainRoleAlias 函数获取给定角色的别名角色,并检查别名角色的调用者是否具有相应的角色。否则,它调用父合约 AccessControl 中的 _checkRole 函数来检查是否具有原始角色。在 _crossChainRoleAlias 函数中,给定角色的哈希值通过异或运算与 CROSSCHAIN_ALIAS 常量进行运算,生成一个新的哈希值,作为给定角色的别名角色。这样,不同链上的多个合约就可以共享同一地址,并使用别名角色来限制跨链访问,从而解决可能的冲突问题。

function _checkRole(bytes32 role) internal view virtual override {
        if (_isCrossChain()) {
            _checkRole(_crossChainRoleAlias(role), _crossChainSender());
        } else {
            super._checkRole(role);
        }
    }

这个代码块是一个名为 _checkRole 的内部函数,它是 AccessControl 合约中的一个重写函数。它接受一个 bytes32 类型的角色参数并检查当前调用者是否具有该角色。但是,与 AccessControl 合约中的原始实现不同,它还检查是否正在进行跨链调用。

如果合约正在跨链访问,则调用 _checkRole 函数来检查具有别名角色的调用者是否具有相应的角色。该函数通过 _crossChainRoleAlias 函数返回给定角色的别名角色。该别名角色通过对给定角色的哈希值执行异或运算生成,可以用于在不同链上识别相同角色。

如果合约没有在跨链访问,则它调用 AccessControl 合约中的 _checkRole 函数来检查是否具有原始角色。

该函数使用了 Solidity 中的关键字 virtualoverride,使它成为了一个内部虚函数,并且它是对父合约中 _checkRole 函数的重写。其中,virtual 声明了该函数可以被子合约重写,override 则表示该函数是对父合约中同名函数的重写。

function _crossChainRoleAlias(bytes32 role) internal pure virtual returns (bytes32) {
        return role ^ CROSSCHAIN_ALIAS;
    }

这段代码是一个名为 _crossChainRoleAlias 的内部纯函数,它返回给定角色的别名角色,具体实现是通过对给定角色的哈希值执行异或运算生成。作用是为每个角色生成一个对应的别名角色,以便限制来自其他链的调用。

该函数接受一个 bytes32 类型的角色参数,将该角色的哈希值与常量 CROSSCHAIN_ALIAS 执行异或运算,得到一个新的 bytes32 类型的值,作为对应角色的别名角色返回。具体来说,异或运算会将 CROSSCHAIN_ALIAS 中每个位置上的二进制位与给定角色中的对应位置上的二进制位进行异或运算,从而生成新的 bytes32 值。因为这里使用的是纯函数,所以不会修改任何状态,也不会产生任何副作用。

在跨链访问的情况下,别名角色可以确保调用者是具有相应原始角色的合法调用者,从而保护不同链上由不同实体控制的相同地址的多个合约之间的冲突。