AccessControl.spec 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. import "helpers.spec"
  2. import "methods/IAccessControl.spec"
  3. /*
  4. ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
  5. │ Identify entrypoints: only grantRole, revokeRole and renounceRole can alter permissions │
  6. └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
  7. */
  8. rule onlyGrantCanGrant(env e, bytes32 role, address account) {
  9. method f; calldataarg args;
  10. bool hasRoleBefore = hasRole(role, account);
  11. f(e, args);
  12. bool hasRoleAfter = hasRole(role, account);
  13. assert (
  14. !hasRoleBefore &&
  15. hasRoleAfter
  16. ) => (
  17. f.selector == grantRole(bytes32, address).selector
  18. );
  19. assert (
  20. hasRoleBefore &&
  21. !hasRoleAfter
  22. ) => (
  23. f.selector == revokeRole(bytes32, address).selector ||
  24. f.selector == renounceRole(bytes32, address).selector
  25. );
  26. }
  27. /*
  28. ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
  29. │ Function correctness: grantRole only affects the specified user/role combo │
  30. └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
  31. */
  32. rule grantRoleEffect(env e) {
  33. require nonpayable(e);
  34. bytes32 role;
  35. bytes32 otherRole;
  36. address account;
  37. address otherAccount;
  38. bool isCallerAdmin = hasRole(getRoleAdmin(role), e.msg.sender);
  39. bool hasOtherRoleBefore = hasRole(otherRole, otherAccount);
  40. grantRole@withrevert(e, role, account);
  41. bool success = !lastReverted;
  42. bool hasOtherRoleAfter = hasRole(otherRole, otherAccount);
  43. // liveness
  44. assert success <=> isCallerAdmin;
  45. // effect
  46. assert success => hasRole(role, account);
  47. // no side effect
  48. assert hasOtherRoleBefore != hasOtherRoleAfter => (role == otherRole && account == otherAccount);
  49. }
  50. /*
  51. ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
  52. │ Function correctness: revokeRole only affects the specified user/role combo │
  53. └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
  54. */
  55. rule revokeRoleEffect(env e) {
  56. require nonpayable(e);
  57. bytes32 role;
  58. bytes32 otherRole;
  59. address account;
  60. address otherAccount;
  61. bool isCallerAdmin = hasRole(getRoleAdmin(role), e.msg.sender);
  62. bool hasOtherRoleBefore = hasRole(otherRole, otherAccount);
  63. revokeRole@withrevert(e, role, account);
  64. bool success = !lastReverted;
  65. bool hasOtherRoleAfter = hasRole(otherRole, otherAccount);
  66. // liveness
  67. assert success <=> isCallerAdmin;
  68. // effect
  69. assert success => !hasRole(role, account);
  70. // no side effect
  71. assert hasOtherRoleBefore != hasOtherRoleAfter => (role == otherRole && account == otherAccount);
  72. }
  73. /*
  74. ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
  75. │ Function correctness: renounceRole only affects the specified user/role combo │
  76. └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
  77. */
  78. rule renounceRoleEffect(env e) {
  79. require nonpayable(e);
  80. bytes32 role;
  81. bytes32 otherRole;
  82. address account;
  83. address otherAccount;
  84. bool hasOtherRoleBefore = hasRole(otherRole, otherAccount);
  85. renounceRole@withrevert(e, role, account);
  86. bool success = !lastReverted;
  87. bool hasOtherRoleAfter = hasRole(otherRole, otherAccount);
  88. // liveness
  89. assert success <=> account == e.msg.sender;
  90. // effect
  91. assert success => !hasRole(role, account);
  92. // no side effect
  93. assert hasOtherRoleBefore != hasOtherRoleAfter => (role == otherRole && account == otherAccount);
  94. }