Michael.W liest intensiv die 37. Ausgabe von Openzeppelin basierend auf Foundry – AccessControl.sol

0. Version

[openzeppelin]: v4.8.3, [forge-std]: v1.5.6

0.1 AccessControl.sol

Github: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.8.3/contracts/access/AccessControl.sol

Die AccessControl-Bibliothek wird zum Verwalten von Funktionsaufrufberechtigungen verwendet. Alle Unterverträge, die AccessControl erben, können Aufrufberechtigungen für ihre eigenen Geschäftsfunktionen festlegen. AccessControl ist eine einfache Basisbibliothek und jede Rolle unterstützt keinen iterativen Export kompilierter Mitglieder, sodass die Vorgänge zum Autorisieren und Widerrufen von Rollenmitgliedern Ereignisse auslösen. Rolle stellt die Art der Rollenberechtigungen dar, und verschiedene Bytes32 stellen unterschiedliche Rollenberechtigungen dar. Jede Rolle kann die Personalliste für diese Rolle über ihre eigene Administratorrollenadresse verwalten, und die am Vertrag beteiligten Rollen sollten bytes32 public constantnach außen hin offengelegt werden.

1. Zielvertrag

Erben Sie AccessControl und werden Sie zu einem kündbaren Vertrag:

Github: https://github.com/RevelationOfTuring/foundry-openzeppelin-contracts/blob/master/src/access/MockAccessControl.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import "openzeppelin-contracts/contracts/access/AccessControl.sol";

contract MockAccessControl is AccessControl {
    
    
    constructor(){
    
    
        // set msg.sender into admin role
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
    }

    function setRoleAdmin(bytes32 role, bytes32 adminRole) external {
    
    
        _setRoleAdmin(role, adminRole);
    }

    function doSomethingWithAccessControl(bytes32 role) onlyRole(role) external {
    
    }
}

Alle Prüfverträge für Gießereien:

Github: https://github.com/RevelationOfTuring/foundry-openzeppelin-contracts/blob/master/test/access/AccessControl.t.sol

2. Intensives Codelesen

2.1 unterstütztInterface(bytes4 interfaceId)

Geben Sie extern an, ob dieser Vertrag die Abfragefunktion der durch die Eingabeschnittstellen-ID identifizierten Schnittstelle implementiert.

Hinweis: Hier wird ERC165.supportsInterface() neu geschrieben. Dies ist die Schnittstellen-ID, die zu allen unterstützten Schnittstellen-IDs hinzugefügt wird IAccessControl. Einzelheiten zu ERC165.supportsInterface() finden Sie unter: https://learnblockchain.cn/article/6286

    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
    
    
    	// 如果输入的interfaceId为IAccessControl或IERC165的interface id,返回true。否则返回false
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

Überprüfung des Gießereicodes

contract AccessControlTest is Test {
    
    
    MockAccessControl private _testing = new MockAccessControl();

    function test_SupportsInterface() external {
    
    
        // support IERC165 and IAccessControl
        assertTrue(_testing.supportsInterface(type(IERC165).interfaceId));
        assertTrue(_testing.supportsInterface(type(IAccessControl).interfaceId));
    }
}
2.2 hasRole(bytes32-Rolle, Adresskonto) && getRoleAdmin(bytes32-Rolle) && _setRoleAdmin(bytes32-Rolle, bytes32 adminRole)
  • hasRole(bytes32 role, address account): Gibt zurück, ob der eingegebenen Kontoadresse Rollenberechtigungen erteilt wurden;
  • getRoleAdmin(bytes32 role): Gibt die adminRole in der Eingaberolle zurück. adminRole wird verwendet, um die Gewährung und den Widerruf von Berechtigungen für die entsprechende Rolle zu verwalten;
  • _setRoleAdmin(bytes32 role, bytes32 adminRole): Legen Sie die entsprechende Administratorrolle für die Eingaberolle fest.
 	// 每个role(bytes32)都对应唯一的RoleData
    struct RoleData {
    
    
    	// 该role中用于记录授权地址的mapping
        mapping(address => bool) members;
        // 可管理上述mappings的管理role,默认为bytes32(0),即DEFAULT_ADMIN_ROLE
        // 注:具体地说,_roles[adminRole].members中value为true的地址具有修改本RoleData.members的权限
        bytes32 adminRole;
    }

	// 用于存放role与对应RoleData的mapping
    mapping(bytes32 => RoleData) private _roles;
	// 每个RoleData中默认的adminRole
    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
    
    
    	// role作为key找到对应的RoleData.members,再用account作为key去得到value值
        return _roles[role].members[account];
    }
    
    function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
    
    
    	// role作为key找到对应的RoleData.adminRole并返回
        return _roles[role].adminRole;
    }
   
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
    
    
        // 获取目前该role的admin role
        bytes32 previousAdminRole = getRoleAdmin(role);
        // 更新该role的admin role为输入的adminRole
        _roles[role].adminRole = adminRole;
        // 抛出事件
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

Überprüfung des Gießereicodes

contract AccessControlTest is Test {
    
    
    MockAccessControl private _testing = new MockAccessControl();

    bytes32 immutable private ROLE_DEFAULT = 0;
    bytes32 immutable private ROLE_1 = keccak256("ROLE_1");

    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    function test_HasRole_GetRoleAdmin_SetRoleAdmin() external {
    
    
        // deployer was granted default role
        assertTrue(_testing.hasRole(ROLE_DEFAULT, address(this)));
        // default admin of any role is bytes32(0)
        assertEq(_testing.getRoleAdmin(ROLE_DEFAULT), 0);
        assertEq(_testing.getRoleAdmin(ROLE_1), 0);

        // change admin role by _setRoleAdmin()
        bytes32 newAdminRole = keccak256("new admin role");
        vm.expectEmit(true, true, true, false, address(_testing));
        emit RoleAdminChanged(ROLE_1, 0, newAdminRole);
        _testing.setRoleAdmin(ROLE_1, newAdminRole);
        assertEq(_testing.getRoleAdmin(ROLE_1), newAdminRole);
    }
}
2.3 grantRole(bytes32-Rolle, Adresskonto) && _setupRole(bytes32-Rolle, Adresskonto)
  • grantRole(bytes32 role, address account): Erteilen Sie dem Adresskonto die Berechtigung, die Rolle einzugeben. Nur Adressen mit adminRole-Berechtigungen für die Eingaberolle können diese Methode aufrufen, andernfalls wird sie wiederhergestellt. Hinweis: Wenn das eingegebene Konto ursprünglich nicht über die Berechtigung zum Betreten der Rolle verfügt, wird es autorisiert und das Ereignis RoleGranted wird ausgelöst.
  • _setupRole(bytes32 role, address account): Erteilen Sie dem Adresskonto die Berechtigung, die Rolle einzugeben. Wenn das eingegebene Konto ursprünglich keine Rollenberechtigungen hat, wird es autorisiert und das Ereignis RoleGranted ausgelöst. Hinweis: Diese Funktion unterscheidet sich von der Funktion grantRole() darin, dass sie die Identität des Aufrufers nicht überprüft. Daher wird diese Funktion nur in der Konstruktorfunktion verwendet, um die Berechtigungen der entsprechenden Rolle für das System zu initialisieren. Bitte rufen Sie diese Funktion nicht an anderer Stelle im Vertrag auf. Derzeit 已弃用verwenden Sie für diese Funktion bitte _grantRole(bytes32-Rolle, Adresskonto).
    function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
    
    
    	// onlyRole(getRoleAdmin(role))是对_msgSender()具有role的adminRole的权限进行检查,而后直接调用_grantRole()方法为account授予输入role的权限
        _grantRole(role, account);
    }
    
    function _setupRole(bytes32 role, address account) internal virtual {
    
    
    	// 直接调用_grantRole(),所以本internal函数等价于_grantRole()
        _grantRole(role, account);
    }

    // 将输入role的权限授予给地址account。如果输入的account原先不具备role的权限,会授权并抛出事件RoleGranted,否则不做任何操作。
    function _grantRole(bytes32 role, address account) internal virtual {
    
    
        if (!hasRole(role, account)) {
    
    
        	// 如果输入account不具有role的权限,在该role对应roleData.member中对account标记为true
            _roles[role].members[account] = true;
            // 抛出事件
            emit RoleGranted(role, account, _msgSender());
        }
    }

Überprüfung des Gießereicodes

contract AccessControlTest is Test {
    
    
    MockAccessControl private _testing = new MockAccessControl();

    bytes32 immutable private ROLE_DEFAULT = 0;
    bytes32 immutable private ROLE_1 = keccak256("ROLE_1");

    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    function test_GrantRole() external {
    
    
        address account = address(1024);
        assertFalse(_testing.hasRole(ROLE_DEFAULT, account));

        vm.expectEmit(true, true, true, false, address(_testing));
        emit RoleGranted(ROLE_DEFAULT, account, address(this));
        _testing.grantRole(ROLE_DEFAULT, account);
        assertTrue(_testing.hasRole(ROLE_DEFAULT, account));

        // grant role for ROLE_1
        assertFalse(_testing.hasRole(ROLE_1, account));
        vm.expectEmit(true, true, true, false, address(_testing));
        emit RoleGranted(ROLE_1, account, address(this));
        _testing.grantRole(ROLE_1, account);
        assertTrue(_testing.hasRole(ROLE_1, account));
    }

    function testFail_GrantRole_NoEventWhenGrantAgain() external {
    
    
        address account = address(1024);
        _testing.grantRole(ROLE_DEFAULT, account);

        // no emit event if grant again
        vm.expectEmit(true, true, true, false, address(_testing));
        emit RoleGranted(ROLE_DEFAULT, account, address(this));
        _testing.grantRole(ROLE_DEFAULT, account);
    }
}
2.4 revokeRole (Bytes32-Rolle, Adresskonto)

Entziehen Sie dem Adresskonto die Berechtigung, die Rolle einzugeben. Nur Adressen mit adminRole-Berechtigungen für die Eingaberolle können diese Methode aufrufen, andernfalls wird sie wiederhergestellt.

Hinweis: Wenn das eingegebene Konto ursprünglich über Rollenberechtigungen verfügte, werden die Berechtigungen widerrufen und das Ereignis RoleRevoked ausgelöst.

    function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
    
    
    	// onlyRole(getRoleAdmin(role))是对_msgSender()具有role的adminRole的权限进行检查,而后直接调用_revokeRole()方法为account撤销输入role的权限
        _revokeRole(role, account);
    }

    // 撤销已授予地址account关于输入role的权限。如果输入的account原先已具备role的权限,会撤销权限并抛出事件RoleRevoked,否则不做任何操作。
    function _revokeRole(bytes32 role, address account) internal virtual {
    
    
        if (hasRole(role, account)) {
    
    
        	// 如果输入account具有role的权限,在该role对应roleData.member中对account标记为false
            _roles[role].members[account] = false;
            // 抛出事件
            emit RoleRevoked(role, account, _msgSender());
        }
    }

Überprüfung des Gießereicodes

contract AccessControlTest is Test {
    
    
    MockAccessControl private _testing = new MockAccessControl();

    bytes32 immutable private ROLE_DEFAULT = 0;
    bytes32 immutable private ROLE_1 = keccak256("ROLE_1");

    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    function test_RevokeRole() external {
    
    
        address account = address(1024);
        _testing.grantRole(ROLE_DEFAULT, account);
        assertTrue(_testing.hasRole(ROLE_DEFAULT, account));
        vm.expectEmit(true, true, true, false, address(_testing));
        emit RoleRevoked(ROLE_DEFAULT, account, address(this));
        _testing.revokeRole(ROLE_DEFAULT, account);
        assertFalse(_testing.hasRole(ROLE_DEFAULT, account));

        // revoke role for ROLE_1
        _testing.grantRole(ROLE_1, account);
        assertTrue(_testing.hasRole(ROLE_1, account));
        vm.expectEmit(true, true, true, false, address(_testing));
        emit RoleRevoked(ROLE_1, account, address(this));
        _testing.revokeRole(ROLE_1, account);
        assertFalse(_testing.hasRole(ROLE_1, account));
    }

    function testFail_RevokeRole_NoEventWhenRevokeAgain() external {
    
    
        address account = address(1024);
        _testing.grantRole(ROLE_DEFAULT, account);
        _testing.revokeRole(ROLE_DEFAULT, account);

        // no emit event if revoke again
        vm.expectEmit(true, true, true, false, address(_testing));
        emit RoleRevoked(ROLE_DEFAULT, account, address(this));
        _testing.revokeRole(ROLE_DEFAULT, account);
    }
}
2.5 renounceRole (Bytes32-Rolle, Adresskonto)

Das Kontokonto selbst verzichtet freiwillig auf die Berechtigung, die Rolle einzugeben. Diese Funktion muss von der Kontoadresse aus aufgerufen werden.

Hinweis: Wenn das eingegebene Konto ursprünglich über Rollenberechtigungen verfügte, werden die Berechtigungen widerrufen und das Ereignis RoleRevoked ausgelöst.

    function renounceRole(bytes32 role, address account) public virtual override {
    
    
        // 要求_msgSender()为输入account地址
        require(account == _msgSender(), "AccessControl: can only renounce roles for self");
		// 撤销account关于输入role的权限
        _revokeRole(role, account);
    }

Überprüfung des Gießereicodes

contract AccessControlTest is Test {
    
    
    MockAccessControl private _testing = new MockAccessControl();

    bytes32 immutable private ROLE_DEFAULT = 0;
    bytes32 immutable private ROLE_1 = keccak256("ROLE_1");

    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
    
    function test_RenounceRole() external {
    
    
        address account = address(1024);
        _testing.grantRole(ROLE_DEFAULT, account);
        assertTrue(_testing.hasRole(ROLE_DEFAULT, account));
        vm.prank(account);
        vm.expectEmit(true, true, true, false, address(_testing));
        emit RoleRevoked(ROLE_DEFAULT, account, account);
        _testing.renounceRole(ROLE_DEFAULT, account);
        assertFalse(_testing.hasRole(ROLE_DEFAULT, account));

        // renounce role for ROLE_1
        _testing.grantRole(ROLE_1, account);
        assertTrue(_testing.hasRole(ROLE_1, account));
        vm.prank(account);
        vm.expectEmit(true, true, true, false, address(_testing));
        emit RoleRevoked(ROLE_1, account, account);
        _testing.renounceRole(ROLE_1, account);
        assertFalse(_testing.hasRole(ROLE_1, account));

        // case 1: revert if account != _msgSender()
        vm.expectRevert("AccessControl: can only renounce roles for self");
        _testing.renounceRole(ROLE_DEFAULT, account);
    }

    function testFail_RenounceRole_NoEventWhenRenounceAgain() external {
    
    
        _testing.renounceRole(ROLE_DEFAULT, address(this));

        // no emit event if renounce again
        vm.expectEmit(true, true, true, false, address(_testing));
        emit RoleRevoked(ROLE_DEFAULT, address(this), address(this));
        _testing.renounceRole(ROLE_DEFAULT, address(this));
    }
}
2.6 Modifikator onlyRole(bytes32-Rolle)

Der Modifikator onlyRole(bytes32 Role) wird verwendet, um zu bestätigen, dass dem aktuellen _msgSender() Rollenberechtigungen erteilt wurden. Andernfalls kehren Sie direkt mit den formatierten Zeichenfolgeninformationen zurück.

    modifier onlyRole(bytes32 role) {
    
    
    	// 调用_checkRole()进行_msgSender()权限检查
        _checkRole(role);
        _;
    }

	// 确认_msgSender()已被授予了角色role的权限。否则,直接以格式化字符串信息revert
    function _checkRole(bytes32 role) internal view virtual {
    
    
    	// 调用_checkRole()来确认_msgSender()已被授予了角色role的权限
        _checkRole(role, _msgSender());
    }

    // 确认输入account地址已被授予了角色role的权限。否则,直接以格式化字符串信息revert
    function _checkRole(bytes32 role, address account) internal view virtual {
    
    
        if (!hasRole(role, account)) {
    
    
        	// 如果account未被授予角色role的权限,则以格式化字符串信息revert
            revert(
                string(
                    abi.encodePacked(
                        "AccessControl: account ",
                        Strings.toHexString(account),
                        " is missing role ",
                        Strings.toHexString(uint256(role), 32)
                    )
                )
            );
        }
    }

Überprüfung des Gießereicodes

contract AccessControlTest is Test {
    
    
    MockAccessControl private _testing = new MockAccessControl();

    bytes32 immutable private ROLE_DEFAULT = 0;
    bytes32 immutable private ROLE_1 = keccak256("ROLE_1");

    function test_onlyRole() external {
    
    
        // test for modifier onlyRole
        address account = address(1024);
        // test for default role
        // pass
        assertTrue(_testing.hasRole(ROLE_DEFAULT, address(this)));
        _testing.doSomethingWithAccessControl(ROLE_DEFAULT);
        // case 1: revert
        assertFalse(_testing.hasRole(ROLE_DEFAULT, account));
        vm.expectRevert("AccessControl: account 0x0000000000000000000000000000000000000400 is missing role 0x0000000000000000000000000000000000000000000000000000000000000000");
        vm.prank(account);
        _testing.doSomethingWithAccessControl(ROLE_DEFAULT);

        // test for role_1
        // case 2: revert
        assertFalse(_testing.hasRole(ROLE_1, account));
        vm.expectRevert("AccessControl: account 0x0000000000000000000000000000000000000400 is missing role 0x00e1b9dbbc5c12d9bbd9ed29cbfd10bab1e01c5e67a7fc74a02f9d3edc5ad0a8");
        vm.prank(account);
        _testing.doSomethingWithAccessControl(ROLE_1);
        // grant role_1 to account
        _testing.grantRole(ROLE_1,account);
        vm.prank(account);
        _testing.doSomethingWithAccessControl(ROLE_1);
    }
}

PS:
Ich liebe Turing, ich liebe Satoshi Nakamoto und ich liebe Buterin.
Das Folgende ist mein persönlicher offizieller Account. Wenn Sie technische Fragen haben, können Sie meinem offiziellen Account folgen, um mit mir zu kommunizieren.
Gleichzeitig werde ich jede Woche meine Originalartikel auf diesem offiziellen Konto aktualisieren. Freunde oder alte Freunde, denen es gefällt, können es unterstützen!
Wenn Sie erneut posten müssen, geben Sie bitte den Autor an. Vielen Dank!

Fügen Sie hier eine Bildbeschreibung ein

Name des öffentlichen Kontos: Begründer der postmodernen Schurkenromantik

Guess you like

Origin blog.csdn.net/michael_wgy_/article/details/133386759