123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314 |
- using ERC20VotesHarness as erc20votes
- methods {
- // functions
- checkpoints(address, uint32) envfree
- numCheckpoints(address) returns (uint32) envfree
- delegates(address) returns (address) envfree
- getVotes(address) returns (uint256) envfree
- getPastVotes(address, uint256) returns (uint256)
- getPastTotalSupply(uint256) returns (uint256)
- delegate(address)
- _delegate(address, address)
- delegateBySig(address, uint256, uint256, uint8, bytes32, bytes32)
- totalSupply() returns (uint256) envfree
- _maxSupply() returns (uint224) envfree
- // harnesss functions
- ckptFromBlock(address, uint32) returns (uint32) envfree
- ckptVotes(address, uint32) returns (uint224) envfree
- mint(address, uint256)
- burn(address, uint256)
- // solidity generated getters
- _delegates(address) returns (address) envfree
- // external functions
- }
- // gets the most recent votes for a user
- ghost userVotes(address) returns uint224;
- // sums the total votes for all users
- ghost totalVotes() returns mathint {
- axiom totalVotes() > 0;
- }
- hook Sstore _checkpoints[KEY address account][INDEX uint32 index].votes uint224 newVotes (uint224 oldVotes) STORAGE {
- havoc userVotes assuming
- userVotes@new(account) == newVotes;
- havoc totalVotes assuming
- totalVotes@new() == totalVotes@old() + to_mathint(newVotes) - to_mathint(userVotes(account));
- }
- ghost lastFromBlock(address) returns uint32;
- ghost doubleFromBlock(address) returns bool {
- init_state axiom forall address a. doubleFromBlock(a) == false;
- }
- hook Sstore _checkpoints[KEY address account][INDEX uint32 index].fromBlock uint32 newBlock (uint32 oldBlock) STORAGE {
- havoc lastFromBlock assuming
- lastFromBlock@new(account) == newBlock;
-
- havoc doubleFromBlock assuming
- doubleFromBlock@new(account) == (newBlock == oldBlock);
- }
- rule sanity(method f) {
- env e;
- calldataarg arg;
- f(e, arg);
- assert false;
- }
- // something stupid just to see
- invariant sanity_invariant()
- totalSupply() >= 0
- // sum of user balances is >= total amount of delegated votes
- invariant votes_solvency()
- to_mathint(totalSupply()) >= totalVotes()
- // for some checkpoint, the fromBlock is less than the current block number
- // passes but fails rule sanity from hash on delegate by sig
- invariant timestamp_constrains_fromBlock(address account, uint32 index, env e)
- ckptFromBlock(account, index) < e.block.number
- {
- preserved {
- require index < numCheckpoints(account);
- }
- }
- // numCheckpoints are less than maxInt
- // passes
- invariant maxInt_constrains_numBlocks(address account)
- numCheckpoints(account) <= 4294967295 // 2^32
- // can't have more checkpoints for a given account than the last from block
- invariant fromBlock_constrains_numBlocks(address account)
- numCheckpoints(account) <= lastFromBlock(account)
- invariant unique_checkpoints(address account)
- !doubleFromBlock(account)
- rule transfer_safe() {
- env e;
- uint256 amount;
- address a; address b;
- require a != b;
- uint256 votesA_pre = getVotes(a);
- uint256 votesB_pre = getVotes(b);
- require votesA_pre == erc20votes.balanceOf(e, a);
- require votesB_pre == erc20votes.balanceOf(e, b);
- mathint totalVotes_pre = totalVotes();
- erc20votes.transferFrom(e, a, b, amount);
-
- mathint totalVotes_post = totalVotes();
- uint256 votesA_post = getVotes(a);
- uint256 votesB_post = getVotes(b);
- assert totalVotes_pre == totalVotes_post, "transfer changed total supply";
- assert votesA_pre - votesA_post == amount, "a lost the proper amount of votes";
- assert votesB_post - votesB_pre == amount, "b lost the proper amount of votes";
- }
- 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";
- }
- rule delegatee_receives_votes() {
- env e;
- address delegator; address delegatee;
- require delegator != delegatee;
- uint256 delegator_bal = balanceOf(e, delegator);
- uint256 votes_= getVotes(delegatee);
- _delegate(e, delegator, delegatee);
- uint256 _votes = getVotes(delegatee);
- assert _votes == votes_ + delegator_bal, "delegatee did not receive votes";
- }
- rule previous_delegatee_zerod() {
- env e;
- address delegator; address delegatee; address third;
- require delegator != delegatee;
- require third != delegatee;
- require third != delegator;
- uint256 delegator_bal = balanceOf(e, delegator);
- uint256 votes_ = getVotes(third);
- _delegate(e, delegator, delegatee);
- uint256 _votes = getVotes(third);
- assert votes_ == votes_ - delegator_bal, "votes not removed from the previous delegatee";
- }
- // passes
- rule delegate_contained() {
- env e;
- address delegator; address delegatee; address other;
- require delegator != delegatee;
- require other != delegator;
- require other != delegatee;
- require other != delegates(delegator);
- uint256 votes_ = getVotes(other);
- _delegate(e, delegator, delegatee);
- uint256 _votes = getVotes(other);
- assert votes_ == _votes, "votes not contained";
- }
- // checks all of the above 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 other != third;
- uint256 delegator_bal = erc20votes.balanceOf(e, delegator);
- uint256 dr_ = getVotes(delegator);
- uint256 de_ = getVotes(delegatee);
- uint256 third_ = getVotes(third);
- uint256 other_ = getVotes(other);
- // require third is address for previous delegator
- f(e, args);
- _delegate(e, delegator, delegatee);
- uint256 _dr = getVotes(delegator);
- uint256 _de = getVotes(delegatee);
- uint256 _third = getVotes(third);
- uint256 _other = getVotes(other);
- // delegator 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";
- }
- // passes
- rule mint_increases_totalSupply() {
- env e;
- uint256 amount; address account;
- uint256 fromBlock = e.block.number;
- uint256 totalSupply_ = totalSupply();
- mint(e, account, amount);
- uint256 _totalSupply = totalSupply();
- require _totalSupply < _maxSupply();
- assert _totalSupply == totalSupply_ + amount, "totalSupply not increased properly";
- assert getPastTotalSupply(e, fromBlock) == totalSupply_ , "previous total supply not saved properly";
- }
- // passes
- rule burn_decreases_totalSupply() {
- env e;
- uint256 amount; address account;
- uint256 fromBlock = e.block.number;
- uint256 totalSupply_ = totalSupply();
- require totalSupply_ > balanceOf(e, account);
- burn(e, account, amount);
- uint256 _totalSupply = totalSupply();
- require _totalSupply < _maxSupply();
- assert _totalSupply == totalSupply_ - amount, "totalSupply not decreased properly";
- assert getPastTotalSupply(e, fromBlock) == totalSupply_ , "previous total supply not saved properly";
- }
- // passes
- rule mint_doesnt_increase_totalVotes() {
- env e;
- uint256 amount; address account;
- mathint totalVotes_ = totalVotes();
- mint(e, account, amount);
- assert totalVotes() == totalVotes_, "totalVotes increased";
- }
- // passes
- rule burn_doesnt_decrease_totalVotes() {
- env e;
- uint256 amount; address account;
- mathint totalVotes_ = totalVotes();
- burn(e, account, amount);
- assert totalVotes() == totalVotes_, "totalVotes decreased";
- }
- // // fails
- // rule mint_increases_totalVotes() {
- // env e;
- // uint256 amount; address account;
- // mathint totalVotes_ = totalVotes();
- // mint(e, account, amount);
- // assert totalVotes() == totalVotes_ + to_mathint(amount), "totalVotes not increased";
- // }
- // // fails
- // rule burn_decreases_totalVotes() {
- // env e;
- // uint256 amount; address account;
- // mathint totalVotes_ = totalVotes();
- // burn(e, account, amount);
- // assert totalVotes() == totalVotes_ - to_mathint(amount), "totalVotes not decreased";
- // }
|