123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360 |
- import "helpers.spec"
- import "methods/IERC20.spec"
- import "methods/IERC5805.spec"
- import "methods/IERC6372.spec"
- import "ERC20.spec"
- methods {
- numCheckpoints(address) returns (uint32) envfree
- ckptFromBlock(address, uint32) returns (uint32) envfree
- ckptVotes(address, uint32) returns (uint224) envfree
- numCheckpointsTotalSupply() returns (uint32) envfree
- ckptFromBlockTotalSupply(uint32) returns (uint32) envfree
- ckptVotesTotalSupply(uint32) returns (uint224) envfree
- maxSupply() returns (uint224) envfree
- }
- use invariant totalSupplyIsSumOfBalances
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Helpers โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- definition max_uint224() returns uint224 = 0xffffffffffffffffffffffffffff;
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Ghost & hooks: total delegated โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- ghost mapping(address => uint256) balanceOf {
- init_state axiom forall address a. balanceOf[a] == 0;
- }
- ghost mapping(address => address) delegates {
- init_state axiom forall address a. delegates[a] == 0;
- }
- ghost mapping(address => uint256) getVotes {
- init_state axiom forall address a. getVotes[a] == 0;
- }
- hook Sstore _balances[KEY address account] uint256 newAmount (uint256 oldAmount) STORAGE {
- balanceOf[account] = newAmount;
- getVotes[delegates[account]] = getVotes[delegates[account]] + newAmount - oldAmount;
- }
- hook Sstore _delegates[KEY address account] address newDelegate (address oldDelegate) STORAGE {
- delegates[account] = newDelegate;
- getVotes[oldDelegate] = getVotes[oldDelegate] - balanceOf[account];
- getVotes[newDelegate] = getVotes[newDelegate] + balanceOf[account];
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Invariant: clock โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- invariant clockMode(env e)
- clock(e) == e.block.number || clock(e) == e.block.timestamp
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Invariant: hook correctly track latest checkpoint โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- invariant delegationConsistency(address a)
- balanceOf(a) == balanceOf[a] &&
- delegates(a) == delegates[a] &&
- getVotes(a) == (a == 0 ? 0 : getVotes[a])
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Invariant: totalSupply is checkpointed โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- invariant totalSupplyTracked()
- totalSupply() > 0 => numCheckpointsTotalSupply() > 0
- invariant totalSupplyLatest()
- numCheckpointsTotalSupply() > 0 => ckptVotesTotalSupply(numCheckpointsTotalSupply() - 1) == totalSupply()
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Invariant: checkpoint is not in the future โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- invariant checkpointInThePast(env e, address a)
- numCheckpoints(a) > 0 => ckptFromBlock(a, numCheckpoints(a) - 1) <= clock(e)
- {
- preserved with (env e2) {
- require clock(e2) <= clock(e);
- }
- }
- invariant totalCheckpointInThePast(env e)
- numCheckpointsTotalSupply() > 0 => ckptFromBlockTotalSupply(numCheckpointsTotalSupply() - 1) <= clock(e)
- {
- preserved with (env e2) {
- require clock(e2) <= clock(e);
- }
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Invariant: checkpoint clock is strictly increassing (implies no duplicate) โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- invariant checkpointClockIncreassing(address a)
- numCheckpoints(a) > 1 => ckptFromBlock(a, numCheckpoints(a) - 2) < ckptFromBlock(a, numCheckpoints(a) - 1)
- {
- preserved with (env e) {
- requireInvariant checkpointInThePast(e, a);
- }
- }
- invariant totalCheckpointClockIncreassing()
- numCheckpointsTotalSupply() > 1 => ckptFromBlockTotalSupply(numCheckpointsTotalSupply() - 2) < ckptFromBlockTotalSupply(numCheckpointsTotalSupply() - 1)
- {
- preserved with (env e) {
- requireInvariant totalCheckpointInThePast(e);
- }
- }
- /// For some reason "sumOfBalances" is not tracking correctly ...
- /// ... and we get counter example where totalSupply is more than the sum of involved balances
- // invariant userVotesLessTotalVotes(address a)
- // numCheckpoints(a) > 0 => getVotes(a) <= totalVotes()
- // {
- // preserved {
- // requireInvariant totalSupplyIsSumOfBalances;
- // }
- // }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Invariant: Don't track votes delegated to address 0 โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- invariant addressZeroNoCheckpoints()
- numCheckpoints(0) == 0
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Invariant: Don't track votes delegated to address 0 โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- rule checkpointsImmutable(env e, method f)
- filtered { f -> !f.isView }
- {
- address account;
- uint32 index;
- require index < numCheckpoints(account);
- uint224 valueBefore = ckptVotes(account, index);
- uint32 clockBefore = ckptFromBlock(account, index);
- calldataarg args; f(e, args);
- uint224 valueAfter = ckptVotes@withrevert(account, index);
- assert !lastReverted;
- uint32 clockAfter = ckptFromBlock@withrevert(account, index);
- assert !lastReverted;
- assert clockAfter == clockBefore;
- assert valueAfter != valueBefore => clockBefore == clock(e);
- }
- rule totalCheckpointsImmutable(env e, method f)
- filtered { f -> !f.isView }
- {
- uint32 index;
- require index < numCheckpointsTotalSupply();
- uint224 valueBefore = ckptVotesTotalSupply(index);
- uint32 clockBefore = ckptFromBlockTotalSupply(index);
- calldataarg args; f(e, args);
- uint224 valueAfter = ckptVotesTotalSupply@withrevert(index);
- assert !lastReverted;
- uint32 clockAfter = ckptFromBlockTotalSupply@withrevert(index);
- assert !lastReverted;
- assert clockAfter == clockBefore;
- assert valueAfter != valueBefore => clockBefore == clock(e);
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Rules: what function can lead to state changes โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- rule changes(env e, method f)
- filtered { f -> !f.isView }
- {
- address account;
- calldataarg args;
- uint32 ckptsBefore = numCheckpoints(account);
- uint256 votesBefore = getVotes(account);
- address delegatesBefore = delegates(account);
- f(e, args);
- uint32 ckptsAfter = numCheckpoints(account);
- uint256 votesAfter = getVotes(account);
- address delegatesAfter = delegates(account);
- assert ckptsAfter != ckptsBefore => (
- ckptsAfter == ckptsBefore + 1 &&
- ckptFromBlock(account, ckptsAfter - 1) == clock(e) &&
- (
- f.selector == mint(address,uint256).selector ||
- f.selector == burn(address,uint256).selector ||
- f.selector == transfer(address,uint256).selector ||
- f.selector == transferFrom(address,address,uint256).selector ||
- f.selector == delegate(address).selector ||
- f.selector == delegateBySig(address,uint256,uint256,uint8,bytes32,bytes32).selector
- )
- );
- assert votesAfter != votesBefore => (
- f.selector == mint(address,uint256).selector ||
- f.selector == burn(address,uint256).selector ||
- f.selector == transfer(address,uint256).selector ||
- f.selector == transferFrom(address,address,uint256).selector ||
- f.selector == delegate(address).selector ||
- f.selector == delegateBySig(address,uint256,uint256,uint8,bytes32,bytes32).selector
- );
- assert delegatesAfter != delegatesBefore => (
- f.selector == delegate(address).selector ||
- f.selector == delegateBySig(address,uint256,uint256,uint8,bytes32,bytes32).selector
- );
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Rules: mint updates votes โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- /* WIP
- rule mint(env e) {
- requireInvariant totalSupplyIsSumOfBalances();
- requireInvariant totalSupplyTracked();
- requireInvariant totalSupplyLatest();
- require nonpayable(e);
- address to;
- address toDelegate = delegates(to);
- address other;
- uint256 amount;
- // cache state
- uint256 totalSupplyBefore = totalSupply();
- uint256 votesBefore = getVotes(toDelegate);
- uint32 ckptsBefore = numCheckpoints(toDelegate);
- mathint clockBefore = ckptsBefore == 0 ? -1 : numCheckpoints(toDelegate);
- uint256 otherVotesBefore = getVotes(other);
- uint256 otherCkptsBefore = numCheckpoints(other);
- // run transaction
- mint@withrevert(e, to, amount);
- bool success = !lastReverted;
- uint256 votesAfter = getVotes(toDelegate);
- uint32 ckptsAfter = numCheckpoints(toDelegate);
- mathint clockAfter = ckptsAfter == 0 ? -1 : numCheckpoints(toDelegate);
- uint256 otherVotesAfter = getVotes(other);
- uint256 otherCkptsAfter = numCheckpoints(other);
- // liveness
- assert success <=> (to != 0 || totalSupplyBefore + amount <= max_uint256);
- // effects
- assert (
- success &&
- toDelegate != 0
- ) => (
- votesAfter == votesBefore + amount &&
- ckptsAfter == ckptsBefore + to_mathint(clockBefore == clockAfter ? 0 : 1) &&
- clockAfter == clock(e)
- );
- // no side effects
- assert otherVotesAfter != otherVotesBefore => other == toDelegate;
- assert otherCkptsAfter != otherCkptsBefore => other == toDelegate;
- }
- */
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Rules: burn updates votes โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- /* WIP
- rule burn(env e) {
- requireInvariant totalSupplyIsSumOfBalances();
- requireInvariant totalSupplyTracked();
- requireInvariant totalSupplyLatest();
- require nonpayable(e);
- address from;
- address fromDelegate = delegates(from);
- address other;
- uint256 amount;
- // cache state
- uint256 fromBalanceBefore = balanceOf(from);
- uint256 votesBefore = getVotes(fromDelegate);
- uint32 ckptsBefore = numCheckpoints(fromDelegate);
- mathint clockBefore = ckptsBefore == 0 ? -1 : numCheckpoints(fromDelegate);
- uint256 otherVotesBefore = getVotes(other);
- uint256 otherCkptsBefore = numCheckpoints(other);
- // run transaction
- burn@withrevert(e, from, amount);
- bool success = !lastReverted;
- uint256 votesAfter = getVotes(fromDelegate);
- uint32 ckptsAfter = numCheckpoints(fromDelegate);
- mathint clockAfter = ckptsAfter == 0 ? -1 : numCheckpoints(fromDelegate);
- uint256 otherVotesAfter = getVotes(other);
- uint256 otherCkptsAfter = numCheckpoints(other);
- // liveness
- assert success <=> (from != 0 || amount <= fromBalanceBefore);
- // effects
- assert (
- success &&
- fromDelegate != 0
- ) => (
- votesAfter == votesBefore + amount &&
- ckptsAfter == ckptsBefore + to_mathint(clockBefore == clockAfter ? 0 : 1) &&
- clockAfter == clock(e)
- );
- // no side effects
- assert otherVotesAfter != otherVotesBefore => other == fromDelegate;
- assert otherCkptsAfter != otherCkptsBefore => other == fromDelegate;
- }
- */
|