diff --git a/certora/scripts/ERC20VotesRule.sh b/certora/scripts/ERC20VotesRule.sh index 64ff6a27d..0921d1df2 100644 --- a/certora/scripts/ERC20VotesRule.sh +++ b/certora/scripts/ERC20VotesRule.sh @@ -19,5 +19,5 @@ certoraRun \ --optimistic_loop \ --rule ${rule} \ --msg "${msg}" \ - --staging "Eyal/SanityWithoutCallTrace" \ - # --rule_sanity \ \ No newline at end of file + --staging "alex/new-dt-hashing-alpha" \ + --rule_sanity \ \ No newline at end of file diff --git a/certora/specs/ERC20Votes.spec b/certora/specs/ERC20Votes.spec index 33bef7384..5cccdeb9f 100644 --- a/certora/specs/ERC20Votes.spec +++ b/certora/specs/ERC20Votes.spec @@ -153,14 +153,18 @@ invariant no_delegate_no_checkpoints(address account) // converted from an invariant to a rule to slightly change the logic // if the fromBlock is the same as before, then the number of checkpoints stays the same // however if the fromBlock is new than the number of checkpoints increases +// passes, fails rule sanity because tautology check seems to be bugged rule unique_checkpoints_rule(method f) { env e; calldataarg args; - require e.block.number > 0; // we don't care about this exception + // require e.block.number > 0; // we don't care about this exception + address account; + // address delegates_pre = delegates(account); + - require unsafeNumCheckpoints(account) < 4294967295; // 2^32 // we don't want to deal with the checkpoint overflow error here + // require unsafeNumCheckpoints(account) < 1000000; // 2^32 // we don't want to deal with the checkpoint overflow error here uint32 num_ckpts_ = numCheckpoints(account); uint32 fromBlock_ = num_ckpts_ == 0 ? 0 : ckptFromBlock(account, num_ckpts_ - 1); @@ -171,8 +175,8 @@ rule unique_checkpoints_rule(method f) { uint32 _fromBlock = _num_ckpts == 0 ? 0 : ckptFromBlock(account, _num_ckpts - 1); - // assert fromBlock_ == _fromBlock => num_ckpts_ == _num_ckpts, "same fromBlock, new checkpoint"; - assert doubleFromBlock(account) => num_ckpts_ == _num_ckpts, "same fromBlock, new checkpoint"; + assert fromBlock_ == _fromBlock => num_ckpts_ == _num_ckpts || _num_ckpts == 1, "same fromBlock, new checkpoint"; + // assert doubleFromBlock(account) => num_ckpts_ == _num_ckpts, "same fromBlock, new checkpoint"; // this assert fails consistently // assert !doubleFromBlock(account) => ckpts_ != _ckpts, "new fromBlock but total checkpoints not being increased"; } @@ -180,16 +184,22 @@ rule unique_checkpoints_rule(method f) { // assumes neither account has delegated // currently fails due to this scenario. A has maxint number of checkpoints // an additional checkpoint is added which overflows and sets A's votes to 0 +// passes + rule sanity (- a bad tautology check) rule transfer_safe() { env e; uint256 amount; address a; address b; - require a != b; - require delegates(a) != delegates(b); // confirmed if they both delegate to the same person then transfer keeps the votes the sameƄ + // require a != b; + require delegates(a) != delegates(b); // confirmed if they both delegate to the same person then transfer keeps the votes the same // requireInvariant fromBlock_constrains_numBlocks(a); // requireInvariant fromBlock_constrains_numBlocks(b); // requireInvariant totalVotes_gte_accounts(a, b); + // require lastIndex(delegates(a)) < 1000000; + // require lastIndex(delegates(b)) < 1000000; + require numCheckpoints(delegates(a)) < 1000000; + require numCheckpoints(delegates(b)) < 1000000; + uint256 votesA_pre = getVotes(delegates(a)); uint256 votesB_pre = getVotes(delegates(b)); @@ -201,9 +211,9 @@ rule transfer_safe() { erc20votes.transferFrom(e, a, b, amount); - - require lastIndex(delegates(a)) < 1000000; - require lastIndex(delegates(b)) < 1000000; + + // require lastIndex(delegates(a)) < 1000000; + // require lastIndex(delegates(b)) < 1000000; mathint totalVotes_post = totalVotes(); uint256 votesA_post = getVotes(delegates(a)); @@ -236,24 +246,6 @@ rule delegates_safe(method f) filtered {f -> (f.selector != delegate(address).se assert pre == post, "invalid delegate change"; } - -rule delegator_votes_removed() { - env e; - address delegator; address delegatee; - - require delegator != delegatee; - require delegates(delegator) == 0; // has not delegated - - uint256 pre = getVotes(delegator); - - _delegate(e, delegator, delegatee); - - uint256 balance = balanceOf(e, delegator); - - uint256 post = getVotes(delegator); - assert post == pre - balance, "delegator retained votes"; -} - // delegates increases the delegatee's votes by the proper amount // passes + rule sanity rule delegatee_receives_votes() { @@ -263,6 +255,7 @@ rule delegatee_receives_votes() { require delegates(delegator) != delegatee; require delegatee != 0x0; + uint256 delegator_bal = balanceOf(e, delegator); uint256 votes_= getVotes(delegatee); @@ -275,14 +268,14 @@ rule delegatee_receives_votes() { assert _votes == votes_ + delegator_bal, "delegatee did not receive votes"; } -rule previous_delegatee_zeroed() { +// passes + rule sanity +rule previous_delegatee_votes_removed() { env e; address delegator; address delegatee; address third; require third != delegatee; - require third != delegator; require delegates(delegator) == third; - // for third to have been delegated to, it must have a checkpoint + require numCheckpoints(third) < 1000000; uint256 delegator_bal = balanceOf(e, delegator); uint256 votes_ = getVotes(third); @@ -291,7 +284,7 @@ rule previous_delegatee_zeroed() { uint256 _votes = getVotes(third); - assert _votes == votes_ - delegator_bal, "votes not removed from the previous delegatee"; + assert third != 0x0 => _votes == votes_ - delegator_bal, "votes not removed from the previous delegatee"; } // passes with rule sanity @@ -311,39 +304,40 @@ rule delegate_contained() { assert votes_ == _votes, "votes not contained"; } -// checks all of the above rules with front running +// checks all of the above delegate rules with front running rule delegate_no_frontrunning(method f) { env e; calldataarg args; address delegator; address delegatee; address third; address other; - require delegator != delegatee; require delegates(delegator) == third; + require third != delegatee; require other != third; + require other != delegatee; + require delegatee != 0x0; - uint256 delegator_bal = erc20votes.balanceOf(e, delegator); + require numCheckpoints(delegatee) < 1000000; + require numCheckpoints(third) < 1000000; - uint256 dr_ = getVotes(delegator); - uint256 de_ = getVotes(delegatee); - uint256 third_ = getVotes(third); - uint256 other_ = getVotes(other); + uint256 delegatee_votes_ = getVotes(delegatee); + uint256 third_votes_ = getVotes(third); + uint256 other_votes_ = getVotes(other); // require third is address for previous delegator f(e, args); + uint256 delegator_bal = erc20votes.balanceOf(e, delegator); _delegate(e, delegator, delegatee); - uint256 _dr = getVotes(delegator); - uint256 _de = getVotes(delegatee); - uint256 _third = getVotes(third); - uint256 _other = getVotes(other); + uint256 _delegatee_votes = getVotes(delegatee); + uint256 _third_votes = getVotes(third); + uint256 _other_votes = getVotes(other); - // delegator loses all of their votes + // previous delegatee loses all of their votes // delegatee gains that many votes // third loses any votes delegated to them - assert _dr == 0, "delegator retained votes"; - assert _de == de_ + delegator_bal, "delegatee not received votes"; - assert _third != 0 => _third == third_ - delegator_bal, "votes not removed from third"; - assert other_ == _other, "delegate not contained"; + assert _delegatee_votes == delegatee_votes_ + delegator_bal, "delegatee did not receive votes"; + assert third != 0 => _third_votes == third_votes_ - delegator_bal, "votes not removed from third"; + assert other_votes_ == _other_votes, "delegate not contained"; }