{"author":{"address":null,"user":"https://learnblockchain.cn/people/19797"},"content":{"body":"```javascript\r\n\r\n// SPDX-License-Identifier: MIT\r\n\r\npragma solidity ^0.8.0;\r\n\r\n  \r\n  \r\n\r\nimport \"@openzeppelin/contracts/utils/structs/EnumerableSet.sol\";\r\n\r\n  \r\n\r\n/// @title RoleAccessControl\r\n\r\n/// @dev Library for managing role-based access control.\r\n\r\ncontract ComplexStorage {\r\n\r\nusing EnumerableSet for EnumerableSet.Bytes32Set;\r\n\r\n  \r\n\r\n  \r\n\r\nbytes32 constant public ROLE_ADMIN = \"ADMIN\";\r\n\r\nbytes32 constant public ROLE_UPGRADE = \"UPGRADE\";\r\n\r\nbytes32 constant public ROLE_CONFIG = \"CONFIG\";\r\n\r\nbytes32 constant public ROLE_KEEPER = \"KEEPER\";\r\n\r\n  \r\n\r\n/// @dev Error thrown when an account does not have the required role.\r\n\r\nerror InvalidRoleAccess(address account, bytes32 role);\r\n\r\n  \r\n\r\n/// @dev Error thrown when an invalid role name is provided.\r\n\r\nerror InvalidRoleName(bytes32 role);\r\n\r\n  \r\n\r\nmapping(address =\u003e EnumerableSet.Bytes32Set) accountRoles;\r\n\r\nmodifier onlyRoleAdmin() {\r\n\r\nif (!hasRole(msg.sender, ROLE_ADMIN)) {\r\n\r\nrevert InvalidRoleAccess(msg.sender, ROLE_ADMIN);\r\n\r\n}\r\n\r\n_;\r\n\r\n}\r\n\r\n  \r\n\r\nconstructor() {\r\n\r\ngrantRole(msg.sender, ROLE_ADMIN);\r\n\r\n}\r\n\r\n  \r\n\r\n/// @dev Checks if the caller has the specified role.\r\n\r\n/// @param role The role to check.\r\n\r\nfunction checkRole(bytes32 role) public view {\r\n\r\nif (!hasRole(msg.sender, role)) {\r\n\r\nrevert InvalidRoleAccess(msg.sender, role);\r\n\r\n}\r\n\r\n}\r\n\r\n  \r\n\r\n/// @dev Checks if the caller has the specified role.\r\n\r\n/// @param role The role to check.\r\n\r\n/// @return True if the caller has the role, false otherwise.\r\n\r\nfunction hasRole(bytes32 role) public view returns (bool) {\r\n\r\nreturn hasRole(msg.sender, role);\r\n\r\n}\r\n\r\n  \r\n\r\n/// @dev Checks if an account has the specified role.\r\n\r\n/// @param account The account to check.\r\n\r\n/// @param role The role to check.\r\n\r\n/// @return True if the account has the role, false otherwise.\r\n\r\nfunction hasRole(address account, bytes32 role) public view returns (bool) {\r\n\r\nreturn accountRoles[account].contains(role);\r\n\r\n}\r\n\r\n  \r\n\r\n/// @dev Grants a role to an account. public for test\r\n\r\n/// @param account The account to grant the role to.\r\n\r\n/// @param role The role to grant.\r\n\r\nfunction grantRole(address account, bytes32 role) public {\r\n\r\naccountRoles[account].add(role);\r\n\r\n}\r\n\r\n  \r\n\r\n/// @dev Revokes a role from an account. public for test\r\n\r\n/// @param account The account to revoke the role from.\r\n\r\n/// @param role The role to revoke.\r\n\r\nfunction revokeRole(address account, bytes32 role) public {\r\n\r\nif (accountRoles[account].contains(role)) {\r\n\r\naccountRoles[account].remove(role);\r\n\r\n}\r\n\r\n}\r\n\r\n  \r\n\r\n/// @dev Revokes all roles from an account. public for test\r\n\r\n/// @param account The account to revoke all roles from.\r\n\r\nfunction revokeAllRole(address account) public {\r\n\r\ndelete accountRoles[account];//@audit 问题在这里，这个slot的值本身就是0，比如是slot111222,它只起到计算后面的EnumerableSet.Bytes32Set的slot的作用\r\n\r\n}\r\n\r\n}\r\n```\r\n\r\n以上面的代码为例：\r\n变量 accountRoles 占用的 slot = 0, slot0里面存储的值也是0. \r\n因此删除 `delete accountRoles` 并不会删除整个的storage（即把整个的storage置0）\r\n\r\n同理：\r\naccountRoles[account] 占用的slot = keccak256(abi.encode(account, 0)) 假设为slotX, slotX里面存储的值也是0.\r\n因此删除 `accountRoles[account]` 也不会删除 里面 EnumerableSet.Bytes32Set结构体的值。它只是 让 slotX这个slot里面存储值=0，而已。而这个值本身就是0.\r\nslotX的作用是让结构体 来寻址用的  keccak256(abi.encode(Value, slotX))，Value为account或者动态数组的下标如1，2，3\r\n\r\n测试代码：\r\n```javascript\r\n// SPDX-License-Identifier: MIT\r\n\r\npragma solidity 0.8.20;\r\n\r\n  \r\n\r\nimport {Test, console2} from \"forge-std/Test.sol\";\r\n\r\nimport {ComplexStorage} from \"../../src/mapping-storage/ComplexStorage.sol\";\r\n\r\n  \r\n\r\ncontract ComplexStorageTest is Test{\r\n\r\n  \r\n\r\nbytes32 constant public ROLE_ADMIN = \"ADMIN\";\r\n\r\nbytes32 constant public ROLE_UPGRADE = \"UPGRADE\";\r\n\r\nbytes32 constant public ROLE_CONFIG = \"CONFIG\";\r\n\r\nbytes32 constant public ROLE_KEEPER = \"KEEPER\";\r\n\r\n  \r\n\r\naddress public user1;\r\n\r\nComplexStorage public cs;\r\n\r\nfunction setUp() public {\r\n\r\ncs = new ComplexStorage();\r\n\r\nuser1 = makeAddr(\"user1\");\r\n\r\n}\r\n\r\n  \r\n\r\nfunction testHasAdminRole() public{\r\n\r\nassertEq(cs.hasRole(ROLE_ADMIN), true);\r\n\r\nassertEq(cs.hasRole(ROLE_UPGRADE), false);\r\n\r\n}\r\n\r\n  \r\n\r\nfunction testAddAndRemoveRole() public{\r\n\r\ncs.grantRole(user1, ROLE_UPGRADE);\r\n\r\ncs.grantRole(user1, ROLE_CONFIG);\r\n\r\ncs.grantRole(user1, ROLE_KEEPER);\r\n\r\n  \r\n  \r\n\r\nassertEq(true, cs.hasRole(user1, ROLE_UPGRADE));\r\n\r\nassertEq(false, cs.hasRole(user1, ROLE_ADMIN));\r\n\r\n  \r\n\r\ncs.revokeRole(user1, ROLE_UPGRADE);\r\n\r\n//remove role_upgrade\r\n\r\nassertEq(true, cs.hasRole(user1, ROLE_KEEPER));\r\n\r\nassertEq(false, cs.hasRole(user1, ROLE_UPGRADE));\r\n\r\n  \r\n\r\ncs.revokeAllRole(user1);\r\n\r\n  \r\n\r\n//nothing change\r\n\r\nassertEq(true, cs.hasRole(user1, ROLE_KEEPER));\r\n\r\nassertEq(true, cs.hasRole(user1, ROLE_CONFIG));\r\n\r\n  \r\n  \r\n\r\n}\r\n\r\n  \r\n\r\n  \r\n\r\n}\r\n```","title":"安全审计中 复杂Storage的结构体删除常见错误"},"history":null,"timestamp":1719462600,"version":1}