GovernorBase.spec 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. // Governor.sol base definitions
  2. methods {
  3. proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart
  4. hashProposal(address[],uint256[],bytes[],bytes32) returns uint256 envfree
  5. // internal functions made public in harness:
  6. _quorumReached(uint256) returns bool envfree
  7. _voteSucceeded(uint256) returns bool envfree
  8. }
  9. ghost proposalVoteStart(uint256) returns uint64 {
  10. init_state axiom forall uint256 pId. proposalVoteStart(pId) == 0;
  11. }
  12. ghost proposalVoteEnd(uint256) returns uint64 {
  13. init_state axiom forall uint256 pId. proposalVoteEnd(pId) == 0;
  14. }
  15. ghost proposalExecuted(uint256) returns bool {
  16. init_state axiom forall uint256 pId. !proposalExecuted(pId);
  17. }
  18. ghost proposalCanceled(uint256) returns bool {
  19. init_state axiom forall uint256 pId. !proposalCanceled(pId);
  20. }
  21. definition mask_uint64() returns uint256 = max_uint64 - 1;
  22. hook Sstore _proposals[KEY uint256 pId] uint64 newValue STORAGE {
  23. havoc proposalVoteStart assuming (
  24. proposalVoteStart@new(pId) == newValue & mask_uint64()
  25. && (forall uint256 pId2. pId != pId2 => proposalVoteStart@new(pId2) == proposalVoteStart@old(pId2))
  26. );
  27. }
  28. hook Sload uint64 value _proposals[KEY uint256 pId] STORAGE {
  29. require proposalVoteStart(pId) == value & mask_uint64();
  30. }
  31. hook Sstore _proposals[KEY uint256 pId].(offset 32).(offset 0) uint64 newValue STORAGE {
  32. havoc proposalVoteEnd assuming (
  33. proposalVoteEnd@new(pId) == newValue & mask_uint64()
  34. && (forall uint256 pId2. pId != pId2 => proposalVoteEnd@new(pId2) == proposalVoteEnd@old(pId2))
  35. );
  36. }
  37. hook Sload uint64 value _proposals[KEY uint256 pId].(offset 32).(offset 0) STORAGE {
  38. require proposalVoteEnd(pId) == value & mask_uint64();
  39. }
  40. rule sanityCheckVoteStart(method f, uint256 pId) {
  41. uint64 preGhost = proposalVoteStart(pId);
  42. uint256 pre = proposalSnapshot(pId);
  43. env e;
  44. calldataarg arg;
  45. f(e, arg);
  46. uint64 postGhost = proposalVoteStart(pId);
  47. uint256 post = proposalSnapshot(pId);
  48. assert preGhost == postGhost <=> pre == post, "ghost changes are correlated with getter changes";
  49. assert pre == preGhost => post == postGhost, "if correlated at the beginning should be correlated at the end";
  50. }
  51. rule sanityCheckVoteEnd(method f, uint256 pId) {
  52. uint64 preGhost = proposalVoteEnd(pId);
  53. uint256 pre = proposalSnapshot(pId);
  54. env e;
  55. calldataarg arg;
  56. f(e, arg);
  57. uint64 postGhost = proposalVoteEnd(pId);
  58. uint256 post = proposalSnapshot(pId);
  59. assert preGhost == postGhost <=> pre == post, "ghost changes are correlated with getter changes";
  60. assert pre == preGhost => post == postGhost, "if correlated at the beginning should be correlated at the end";
  61. }
  62. /**
  63. * A proposal cannot end unless it started.
  64. */
  65. invariant voteStartBeforeVoteEnd(uint256 pId) proposalVoteStart(pId) < proposalVoteEnd(pId)
  66. /**
  67. * A proposal cannot be both executed and canceled.
  68. */
  69. invariant noBothExecutedAndCanceled(uint256 pId) !proposalExecuted(pId) || !proposalCanceled(pId)
  70. /**
  71. * A proposal cannot be executed nor canceled before it starts
  72. */
  73. invariant noExecuteOrCancelBeforeStarting(env e, uint256 pId) e.block.timestamp < proposalVoteStart(pId) => !proposalExecuted(pId) && !proposalCanceled(pId)
  74. /**
  75. * The voting must start not before the proposal’s creation time
  76. */
  77. rule noStartBeforeCreation(uint256 pId) {
  78. uint previousStart = proposalVoteStart(pId);
  79. require previousStart == 0;
  80. env e;
  81. calldataarg arg;
  82. propose(e, arg);
  83. uint newStart = proposalVoteStart(pId);
  84. // if created, start is after creation
  85. assert newStart != 0 => newStart > e.block.timestamp;
  86. }
  87. /**
  88. * Check hashProposal hashing is reliable (different inputs lead to different buffers hashed)
  89. */
  90. rule checkHashProposal {
  91. address[] t1;
  92. address[] t2;
  93. uint256[] v1;
  94. uint256[] v2;
  95. bytes[] c1;
  96. bytes[] c2;
  97. bytes32 d1;
  98. bytes32 d2;
  99. uint256 h1 = hashProposal(t1,v1,c1,d1);
  100. uint256 h2 = hashProposal(t2,v2,c2,d2);
  101. bool equalHashes = h1 == h2;
  102. assert equalHashes => t1.length == t2.length;
  103. assert equalHashes => v1.length == v2.length;
  104. assert equalHashes => c1.length == c2.length;
  105. assert equalHashes => d1 == d2;
  106. }
  107. /**
  108. * A proposal could be executed only if quorum was reached and vote succeeded
  109. */
  110. invariant executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId) proposalExecuted(pId) => _quorumReached(pId) && _voteSucceeded(pId)
  111. /**
  112. * Once a proposal is created, voteStart and voteEnd are immutable
  113. */
  114. rule immutableFieldsAfterProposalCreation(uint256 pId, method f) {
  115. uint _voteStart = proposalVoteStart(pId);
  116. uint _voteEnd = proposalVoteEnd(pId);
  117. require _voteStart > 0; // proposal was created
  118. env e;
  119. calldataarg arg;
  120. f(e, arg);
  121. uint voteStart_ = proposalVoteStart(pId);
  122. uint voteEnd_ = proposalVoteEnd(pId);
  123. assert _voteStart == voteStart_;
  124. assert _voteEnd == voteEnd_;
  125. }