Michael.W基于Foundry精读Openzeppelin第38期——AccessControlEnumerable.sol

Michael.W基于Foundry精读Openzeppelin第38期——AccessControlEnumerable.sol

      • 0. 版本
        • 0.1 AccessControlEnumerable.sol
      • 1. 目标合约
      • 2. 代码精读
        • 2.1 supportsInterface(bytes4 interfaceId)
        • 2.2 _grantRole(bytes32 role, address account)
        • 2.3 _revokeRole(bytes32 role, address account)
        • 2.4 getRoleMember(bytes32 role, uint256 index) && getRoleMemberCount(bytes32 role)

0. 版本

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

0.1 AccessControlEnumerable.sol

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

AccessControlEnumerable库用于管理函数的调用权限,是AccessControl库的拓展版。与AccessControl库相比,AccessControlEnumerable支持在编成员的迭代导出,这大大方便了各个角色权限的统计查询(不用通过扫块追溯events来统计目前各角色的在编权限人员的地址)。

1. 目标合约

继承AccessControlEnumerable成为一个可调用合约:

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

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;import "openzeppelin-contracts/contracts/access/AccessControlEnumerable.sol";contract MockAccessControlEnumerable is AccessControlEnumerable {constructor(){// set msg.sender into admin role_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);}function doSomethingWithAccessControl(bytes32 role) onlyRole(role) external {}
}

全部foundry测试合约:

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

2. 代码精读

2.1 supportsInterface(bytes4 interfaceId)

对外提供本合约是否实现了输入interfaceId标识的interface的查询功能。

注:此处重写了AccessControl.supportsInterface(),即在全部支持的interface ids中加入IAccessControlEnumerable的interface id。AccessControl.supportsInterface()的细节参见:

    using EnumerableSet for EnumerableSet.AddressSet;// 用于迭代各role的在编成员地址的set。这里借用了openzeppelin的EnumerableSet库中的AddressSet结构体mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers;function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {// 如果输入的interfaceId为IAccessControlEnumerable或IAccessControl或IERC165的interface id,返回true。否则返回falsereturn interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);}

foundry代码验证

contract AccessControlEnumerableTest is Test {MockAccessControlEnumerable private _testing = new MockAccessControlEnumerable();function test_SupportsInterface() external {// support IERC165 && IAccessControl && IAccessControlEnumerableassertTrue(_testing.supportsInterface(type(IERC165).interfaceId));assertTrue(_testing.supportsInterface(type(IAccessControl).interfaceId));assertTrue(_testing.supportsInterface(type(IAccessControlEnumerable).interfaceId));}
}
2.2 _grantRole(bytes32 role, address account)

授予地址account关于输入role的权限。只有具有输入role的adminRole权限的地址才可调用该方法,否则revert。

注:该方法重写了父类AccessControl的同名方法,在AccessControl._grantRole()的基础上增加了在EnumerableSet.AddressSet中注册account的逻辑。同时,父类AccessControl.grantRole()方法的内在逻辑也会改变。

    function _grantRole(bytes32 role, address account) internal virtual override {// 调用父类AccessControl._grantRole()super._grantRole(role, account);// 在输入role对应的EnumerableSet.AddressSet中注册account地址_roleMembers[role].add(account);}

foundry代码验证

contract AccessControlEnumerableTest is Test {MockAccessControlEnumerable private _testing = new MockAccessControlEnumerable();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 {// case 1: grant role for ROLE_DEFAULTaddress account = address(1024);assertFalse(_testing.hasRole(ROLE_DEFAULT, account));// deployer (address of AccessControlEnumerableTest) is already inassertEq(_testing.getRoleMemberCount(ROLE_DEFAULT), 1);vm.expectEmit(true, true, true, false, address(_testing));emit RoleGranted(ROLE_DEFAULT, account, address(this));_testing.grantRole(ROLE_DEFAULT, account);assertEq(_testing.getRoleMemberCount(ROLE_DEFAULT), 2);assertTrue(_testing.hasRole(ROLE_DEFAULT, account));// grant more accounts for ROLE_DEFAULT_testing.grantRole(ROLE_DEFAULT, address(2048));_testing.grantRole(ROLE_DEFAULT, address(4096));assertEq(_testing.getRoleMemberCount(ROLE_DEFAULT), 4);// revert if msg.sender is not the admin of the rolevm.prank(address(0));vm.expectRevert("AccessControl: account 0x0000000000000000000000000000000000000000 is missing role 0x0000000000000000000000000000000000000000000000000000000000000000");_testing.grantRole(ROLE_DEFAULT, account);// case 2: grant role for ROLE_1assertEq(_testing.getRoleMemberCount(ROLE_1), 0);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));assertEq(_testing.getRoleMemberCount(ROLE_1), 1);// grant more accounts for ROLE_1_testing.grantRole(ROLE_1, address(2048));_testing.grantRole(ROLE_1, address(4096));assertEq(_testing.getRoleMemberCount(ROLE_1), 3);// revert if msg.sender is not the admin of the rolevm.prank(address(0));vm.expectRevert("AccessControl: account 0x0000000000000000000000000000000000000000 is missing role 0x0000000000000000000000000000000000000000000000000000000000000000");_testing.grantRole(ROLE_1, account);}
}
2.3 _revokeRole(bytes32 role, address account)

撤销地址account关于输入role的权限。只有具有输入role的adminRole权限的地址才可调用该方法,否则revert。

注:该方法重写了父类AccessControl的同名方法,在AccessControl._revokeRole()的基础上增加了在EnumerableSet.AddressSet中删除account的逻辑。同时,父类AccessControl.revokeRole()方法的内在逻辑也会改变。

    function _revokeRole(bytes32 role, address account) internal virtual override {// 调用父类AccessControl._revokeRole()super._revokeRole(role, account);// 在输入role对应的EnumerableSet.AddressSet中删除account地址_roleMembers[role].remove(account);}

foundry代码验证

contract AccessControlEnumerableTest is Test {MockAccessControlEnumerable private _testing = new MockAccessControlEnumerable();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 {// case 1: revoke role for ROLE_DEFAULTaddress account = address(1024);_testing.grantRole(ROLE_DEFAULT, account);_testing.grantRole(ROLE_DEFAULT, address(2048));_testing.grantRole(ROLE_DEFAULT, address(4096));assertEq(_testing.getRoleMemberCount(ROLE_DEFAULT), 4);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));assertEq(_testing.getRoleMemberCount(ROLE_DEFAULT), 3);_testing.revokeRole(ROLE_DEFAULT, address(2048));_testing.revokeRole(ROLE_DEFAULT, address(4096));assertEq(_testing.getRoleMemberCount(ROLE_DEFAULT), 1);// revert if msg.sender is not the admin of the rolevm.prank(address(1));vm.expectRevert("AccessControl: account 0x0000000000000000000000000000000000000001 is missing role 0x0000000000000000000000000000000000000000000000000000000000000000");_testing.revokeRole(ROLE_DEFAULT, address(this));// case 2: revoke role for ROLE_1_testing.grantRole(ROLE_1, account);_testing.grantRole(ROLE_1, address(2048));_testing.grantRole(ROLE_1, address(4096));assertEq(_testing.getRoleMemberCount(ROLE_1), 3);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));assertEq(_testing.getRoleMemberCount(ROLE_1), 2);_testing.revokeRole(ROLE_1, address(2048));_testing.revokeRole(ROLE_1, address(4096));assertEq(_testing.getRoleMemberCount(ROLE_1), 0);// revert if msg.sender is not the admin of the rolevm.prank(address(1));vm.expectRevert("AccessControl: account 0x0000000000000000000000000000000000000001 is missing role 0x0000000000000000000000000000000000000000000000000000000000000000");_testing.revokeRole(ROLE_1, address(this));}
}
2.4 getRoleMember(bytes32 role, uint256 index) && getRoleMemberCount(bytes32 role)
  • getRoleMember(bytes32 role, uint256 index):获得输入role中索引为index的在编权限地址。输入的index应该介于[0, getRoleMemberCount(role))之内。注:1. 返回的在编权限地址的index顺序与其被添加的顺序无关;2. 严格的讲,该方法与getRoleMemberCount(role)应该保证在同一个区块高度被调用,这样才能保证数据状态的一致性;
  • getRoleMemberCount(bytes32 role):返回输入role的在编权限地址的个数。注:该函数与getRoleMember()配合使用可以迭代出该role的全部在编权限地址。

注:openzeppelin中EnumerableSet库的相关细节参见:https://learnblockchain.cn/article/6272

    function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) {// 直接调用role对应的EnumerableSet.AddressSet的at()方法,获取role中索引为index的在编权限地址return _roleMembers[role].at(index);}function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) {// 直接调用role对应的EnumerableSet.AddressSet的length()方法,获取该role的在编权限地址的个数return _roleMembers[role].length();}

foundry代码验证

contract AccessControlEnumerableTest is Test {MockAccessControlEnumerable private _testing = new MockAccessControlEnumerable();bytes32 immutable private ROLE_DEFAULT = 0;bytes32 immutable private ROLE_1 = keccak256("ROLE_1");function test_GetRoleMemberAndGetRoleMemberCount() external {// case 1: for ROLE_DEFAULT_testing.grantRole(ROLE_DEFAULT, address(1024));_testing.grantRole(ROLE_DEFAULT, address(2048));_testing.grantRole(ROLE_DEFAULT, address(4096));assertEq(_testing.getRoleMemberCount(ROLE_DEFAULT), 4);assertEq(_testing.getRoleMember(ROLE_DEFAULT, 0), address(this));assertEq(_testing.getRoleMember(ROLE_DEFAULT, 1), address(1024));assertEq(_testing.getRoleMember(ROLE_DEFAULT, 2), address(2048));assertEq(_testing.getRoleMember(ROLE_DEFAULT, 3), address(4096));// revoke_testing.revokeRole(ROLE_DEFAULT, address(1024));// index of account are not sorted when #revoke()assertEq(_testing.getRoleMemberCount(ROLE_DEFAULT), 3);assertEq(_testing.getRoleMember(ROLE_DEFAULT, 0), address(this));assertEq(_testing.getRoleMember(ROLE_DEFAULT, 1), address(4096));assertEq(_testing.getRoleMember(ROLE_DEFAULT, 2), address(2048));// case 2: for ROLE_1_testing.grantRole(ROLE_1, address(1024));_testing.grantRole(ROLE_1, address(2048));_testing.grantRole(ROLE_1, address(4096));assertEq(_testing.getRoleMemberCount(ROLE_1), 3);assertEq(_testing.getRoleMember(ROLE_1, 0), address(1024));assertEq(_testing.getRoleMember(ROLE_1, 1), address(2048));assertEq(_testing.getRoleMember(ROLE_1, 2), address(4096));// revoke_testing.revokeRole(ROLE_1, address(1024));// index of account are not sorted when #revoke()assertEq(_testing.getRoleMemberCount(ROLE_1), 2);assertEq(_testing.getRoleMember(ROLE_1, 0), address(4096));assertEq(_testing.getRoleMember(ROLE_1, 1), address(2048));}function test_onlyRole() external {// test for modifier onlyRoleaddress account = address(1024);// test for ROLE_DEFAULT// passassertTrue(_testing.hasRole(ROLE_DEFAULT, address(this)));_testing.doSomethingWithAccessControl(ROLE_DEFAULT);// case 1: revertassertFalse(_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: revertassertFalse(_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:
本人热爱图灵,热爱中本聪,热爱V神。
以下是我个人的公众号,如果有技术问题可以关注我的公众号来跟我交流。
同时我也会在这个公众号上每周更新我的原创文章,喜欢的小伙伴或者老伙计可以支持一下!
如果需要转发,麻烦注明作者。十分感谢!

在这里插入图片描述

公众号名称:后现代泼痞浪漫主义奠基人

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/195184.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android SmartTable根据int状态格式化文字及颜色

private void initData() {List<UserInfo> list new ArrayList<>();list.add(new UserInfo("一年级", "李同学", 6, 1, 120, 1100, 450, 0));list.add(new UserInfo("一年级", "张同学", 6, 2, 120, 1100, 450, 1));list…

(八)五种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB

​ 一、五种算法&#xff08;DBO、LO、SWO、COA、GRO&#xff09;简介 1、蜣螂优化算法DBO 蜣螂优化算法&#xff08;Dung beetle optimizer&#xff0c;DBO&#xff09;由Jiankai Xue和Bo Shen于2022年提出&#xff0c;该算法主要受蜣螂的滚球、跳舞、觅食、偷窃和繁殖行为…

大数据Doris(二十四):数据导入(Stream Load)介绍

文章目录 数据导入(Stream Load)介绍 一、适用场景 二、基本原理

柯桥外语学校|西班牙语中关于金钱的俚语

01 Estar forrado(a) “Forrado(a)”源自动词“forrar”&#xff0c;该动词本意为“包&#xff1b;裹”的动作。 在口语中&#xff0c;则是形容一个人被金钱所包裹&#xff0c;可见这个人是多么地有钱&#xff08;有点类似于我们的成语“腰缠万贯”所描绘的画面&#xff09;。…

Java8Stream快速使用

将List集合存入流中 List<String> list new ArrayList<>();list.add("张一");list.add("张二");list.add("张三");list.add("李四");list.add("赵五");list.add("张六");list.add("王八"…

Git 分支管理

目录 列出分支 删除分支 分支合并 合并冲突 几乎每一种版本控制系统都以某种形式支持分支&#xff0c;一个分支代表一条独立的开发线。 使用分支意味着你可以从开发主线上分离开来&#xff0c;然后在不影响主线的同时继续工作。 Git 分支实际上是指向更改快照的指针。 有…

石原子科技亮相2023成都市信息领域新产品发布会

2023年11月13日至15日&#xff0c;由成都市互联网信息办公室、四川天府新区管委会、成都市经信局市新经济委、成都市农业农村局指导的以“信息创造价值 创新引领未来”为主题的成都市信息领域新产品发布会在科创生态岛1号馆举行。围绕人工智能、区块链、数字化绿色化、数字乡村…

12v24v60v高校同步降压转换芯片推荐

12V/24V/60V 高校同步降压转换芯片推荐&#xff1a; 对于需要高效、稳定、低噪音的降压转换芯片&#xff0c;推荐使用WD5030E和WD5105。这两款芯片都是采用同步整流技术&#xff0c;具有高效率、低噪音、低功耗等优点&#xff0c;适用于各种电子设备。 WD5030E是一款高效率…

CUDA安装

在cmd中输入nvidia-smi。显示CUDA Version&#xff1a;12.3&#xff0c;所以只能下载小于等于12.3的版本。如下图&#xff1a; 进这个网址&#xff1a;https://developer.nvidia.com/cuda-toolkit-archive 选择一个版本下载。 选择完后之后这样选择&#xff1a; 最后点击下载即…

【智能家居】4、智能家居框架设计和代码文件工程建立

目录 一、智能家居项目框架 二、智能家居工厂模式示意 三、代码文件工程建立 SourceInsight创建新工程步骤 一、智能家居项目框架 二、智能家居工厂模式示意 三、代码文件工程建立 创建一个名为si的文件夹用于保存SourceInsight生成的文件信息&#xff0c;然后在SourceInsig…

WPF小知识

在编写WPF程序遇到一些小问题&#xff0c;所以记录起来&#xff0c;查其他方便。 Label自动换行 网上搜的都不能自动换行&#xff0c;发现使用Run 就可以。在脚本中直接调用labTip.Text进行赋值就可以了。 <Label Foreground"#FF9E9E9E" FontSize"16"…

day22_mysql

今日内容 零、 复习昨日 一、MySQL 一、约束 1.1 约束 是什么? 约束,即限制,就是通过设置约束,可以限制对数据表数据的插入,删除,更新 怎么做? 约束设置的语法,大部分是 create table 表名( 字段 数据类型(长度) 约束, 字段 数据类型(长度) 约束 );1.1 数据类型 其实数据类型…

易点易动RFID管理系统:年终固定资产盘点的革命

随着现代企业规模的扩大和固定资产数量的增加&#xff0c;年终固定资产盘点成为了一项繁琐而耗时的任务。传统的手工盘点方法不仅效率低下&#xff0c;还容易出现错误和遗漏。为了解决这一难题&#xff0c;易点易动RFID管理系统应运而生。本文将重点介绍易点易动RFID管理系统在…

Word或者WPS批量调整文中图片大小的快捷方法

文章目录 0、前言1、编写宏代码2、在文档中调用宏实现一键批量调整3、就这么简单&#xff01; 0、前言 不知道大家是不是也和我一样&#xff0c;经常需要在编写的Word&#xff08;或者WPS&#xff09;文档里插入大量的图片&#xff0c;但是这些图片的尺寸大小一般都不一样&…

SpringBoot整合Redis使用基于注解的缓存

环境准备 注解 EnableCaching CacheConfig CacheConfig 提供了一种在类级别共享公共缓存相关设置的机制。 | 参数 | 作用 | | | — | — | — | | cacheNames | 使用在类上的默认缓存名称 | | | keyGenerator | 用于类的默认KeyGenerator的bean名称 | | | cacheManager | 自定…

Heidenhain海德汉触摸屏数控面板维修MC 7522

海德汉HEIDENHAIN系统触摸屏维修/海德汉HEIDENHAIN系统操作面板维修。 数控系统维修范围&#xff1a; 海德汉数控系统维修范围&#xff1a;iTNC530系统、TNC620系统、Hi800-A系统、Hi800-E系统、Hi800-M系统、Hi800-D系统、Hi800-S系统、Hi200-S系统等&#xff1b; 发格数控系…

时间序列预测(6) — ARIMA实现单输入单输出负荷预测

目录 1 数据准备与可视化 2 简单数据探索与清洗 3 差分处理 4 绘制ACF与PACF图像&#xff0c;完成模型选择 5 建立ARIMA和SARIMA模型 5.1 初步建模 5.2 精细化建模 5.3 最终的模型 ARIMA作为成熟的统计学模型已被各种软件以各种方式实现&#xff0c;在Python中我们最常使…

麦克纳姆轮x运动学分析

麦克纳姆轮介绍 A轮 B轮 我们以俯视图的中心为坐标系原点 A轮轴向方向过2 4象限 B轮轴向方向过1 3象限 这里相当于从下往上看仰视图相当于B轮的俯视图 辊子转动方向是不会产生给机器人运动的力,轴向方向会产生运动的摩擦力。 车如果向前运动则A轮要产生45斜向上的力 车如果向…

暖阳脚本_ 定制企业软件开发的4个趋势:AI、RPA、云应用、边缘计算

根据 Statista 的统计数据显示&#xff0c;企业级软件市场在全球范围内占据了领先地位&#xff0c;预测到2028年&#xff0c;市场规模将接近3760亿美元。企业应用软件市场的稳健增长&#xff0c;甚至在经济不景气的时候也能持续&#xff0c;这充分表明软件解决方案对于提升企业…

Molecular Plant | ChIP-seq+RNA-seq解析E2F转录因子在植物复制胁迫响应中的独特和互补作用

生物体的生存完全依赖于它们对基因组完整性的维持&#xff0c;而基因组完整性受到增殖细胞复制胁迫的永久威胁。尽管植物DNA损伤反应&#xff08;DDR&#xff09;调节因子SOG1已被证明能够应对复制缺陷&#xff0c;但越来越多的证据表明&#xff0c;还有其他途径独立于SOG1发挥…