123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425 |
- import "helpers/helpers.spec";
- import "methods/IERC20.spec";
- import "methods/IERC5805.spec";
- import "methods/IERC6372.spec";
- methods {
- function numCheckpoints(address) external returns (uint32) envfree;
- function ckptClock(address, uint32) external returns (uint32) envfree;
- function ckptVotes(address, uint32) external returns (uint224) envfree;
- function numCheckpointsTotalSupply() external returns (uint32) envfree;
- function ckptClockTotalSupply(uint32) external returns (uint32) envfree;
- function ckptVotesTotalSupply(uint32) external returns (uint224) envfree;
- function maxSupply() external returns (uint224) envfree;
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Invariant: clock โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- function clockSanity(env e) returns bool {
- return clock(e) <= max_uint32;
- }
- invariant clockMode(env e)
- assert_uint256(clock(e)) == e.block.number || assert_uint256(clock(e)) == e.block.timestamp;
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Ghost & hooks: total delegated โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- // copied from ERC20.spec (can't be imported because of hook conflicts)
- ghost mathint sumOfBalances {
- init_state axiom sumOfBalances == 0;
- }
- ghost mapping(address => mathint) balance {
- init_state axiom forall address a. balance[a] == 0;
- }
- ghost mapping(address => address) delegate {
- init_state axiom forall address a. delegate[a] == 0;
- }
- ghost mapping(address => mathint) votes {
- init_state axiom forall address a. votes[a] == 0;
- }
- hook Sload uint256 balance _balances[KEY address addr] STORAGE {
- require sumOfBalances >= to_mathint(balance);
- }
- hook Sstore _balances[KEY address addr] uint256 newValue (uint256 oldValue) STORAGE {
- balance[addr] = newValue;
- sumOfBalances = sumOfBalances - oldValue + newValue;
- votes[delegate[addr]] = votes[delegate[addr]] + newValue - oldValue;
- }
- hook Sstore _delegatee[KEY address addr] address newDelegate (address oldDelegate) STORAGE {
- delegate[addr] = newDelegate;
- votes[oldDelegate] = votes[oldDelegate] - balance[addr];
- votes[newDelegate] = votes[newDelegate] + balance[addr];
- }
- // all votes (total supply) minus the votes balances delegated to 0
- definition totalVotes() returns mathint = sumOfBalances - votes[0];
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Invariant: copied from ERC20.spec (can't be imported because of hook conflicts) โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- invariant totalSupplyIsSumOfBalances()
- to_mathint(totalSupply()) == sumOfBalances;
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Invariant: zero address has no delegate, no votes and no checkpoints โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- invariant zeroAddressConsistency()
- balanceOf(0) == 0 &&
- delegates(0) == 0 &&
- getVotes(0) == 0 &&
- numCheckpoints(0) == 0
- {
- preserved with (env e) {
- // we assume address 0 cannot perform any call
- require e.msg.sender != 0;
- }
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Invariant: hook correctly track latest checkpoint โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- // TODO: forall address a.
- invariant balanceDelegateAndVoteConsistency(address a)
- delegates(a) == delegate[a] &&
- to_mathint(balanceOf(a)) == balance[a] &&
- a != 0 => to_mathint(getVotes(a)) == votes[a];
- // TODO: forall address a.
- invariant voteBiggerThanDelegatedBalances(address a)
- getVotes(delegates(a)) >= balanceOf(a)
- {
- preserved {
- requireInvariant zeroAddressConsistency();
- }
- }
- // TODO: forall address a.
- invariant userVotesLessTotalVotes(address a)
- votes[a] <= totalVotes()
- {
- preserved {
- requireInvariant totalSupplyIsSumOfBalances;
- }
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Checkpoints: number, ordering and consistency with clock โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- // TODO: forall address a.
- invariant checkpointInThePast(env e, address a)
- forall uint32 i.
- numCheckpoints(a) > i => to_mathint(ckptClock(a, i)) <= to_mathint(clock(e))
- {
- preserved with (env e2) {
- require clock(e2) <= clock(e);
- }
- }
- invariant totalCheckpointInThePast(env e)
- forall uint32 i.
- numCheckpointsTotalSupply() > i => to_mathint(ckptClockTotalSupply(i)) <= to_mathint(clock(e))
- {
- preserved with (env e2) {
- require clock(e2) <= clock(e);
- }
- }
- // TODO: forall address a.
- invariant checkpointClockIncreassing(address a)
- forall uint32 i.
- forall uint32 j.
- (i < j && j < numCheckpoints(a)) => ckptClock(a, i) < ckptClock(a, j)
- {
- preserved with (env e) {
- requireInvariant checkpointInThePast(e, a);
- }
- }
- invariant totalCheckpointClockIncreassing()
- forall uint32 i.
- forall uint32 j.
- (i < j && j < numCheckpointsTotalSupply()) => ckptClockTotalSupply(i) < ckptClockTotalSupply(j)
- {
- preserved with (env e) {
- requireInvariant totalCheckpointInThePast(e);
- }
- }
- // TODO: forall address a.
- invariant checkpointCountLowerThanClock(env e, address a)
- numCheckpoints(a) <= assert_uint32(clock(e))
- {
- preserved {
- require clockSanity(e);
- requireInvariant checkpointInThePast(e, a);
- }
- }
- invariant totalCheckpointCountLowerThanClock(env e)
- numCheckpointsTotalSupply() <= assert_uint32(clock(e))
- {
- preserved {
- require clockSanity(e);
- requireInvariant totalCheckpointInThePast(e);
- }
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Invariant: totalSupply is checkpointed โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- invariant totalSupplyTracked()
- totalSupply() > 0 => numCheckpointsTotalSupply() > 0;
- invariant totalSupplyLatest()
- numCheckpointsTotalSupply() > 0 => totalSupply() == assert_uint256(ckptVotesTotalSupply(require_uint32(numCheckpointsTotalSupply() - 1)))
- {
- preserved {
- requireInvariant totalSupplyTracked();
- }
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Invariant: Delegate must have a checkpoint โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- // WIP
- // invariant delegateHasCheckpoint(address a)
- // (balanceOf(a) > 0 && delegates(a) != 0) => numCheckpoints(delegates(a)) > 0
- // {
- // preserved delegate(address delegatee) with (env e) {
- // require numCheckpoints(delegatee) < max_uint256;
- // }
- // preserved delegateBySig(address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s) with (env e) {
- // require numCheckpoints(delegatee) < max_uint256;
- // }
- // }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Invariant: Checkpoints are immutables โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- rule checkpointsImmutable(env e, method f)
- filtered { f -> !f.isView }
- {
- address account;
- uint32 index;
- require clockSanity(e);
- requireInvariant checkpointCountLowerThanClock(e, account);
- uint224 valueBefore = ckptVotes(account, index);
- uint32 clockBefore = ckptClock(account, index);
- calldataarg args; f(e, args);
- uint224 valueAfter = ckptVotes@withrevert(account, index);
- assert !lastReverted;
- uint32 clockAfter = ckptClock@withrevert(account, index);
- assert !lastReverted;
- assert clockAfter == clockBefore;
- assert valueAfter != valueBefore => clockBefore == assert_uint32(clock(e));
- }
- rule totalCheckpointsImmutable(env e, method f)
- filtered { f -> !f.isView }
- {
- uint32 index;
- require clockSanity(e);
- requireInvariant totalCheckpointCountLowerThanClock(e);
- uint224 valueBefore = ckptVotesTotalSupply(index);
- uint32 clockBefore = ckptClockTotalSupply(index);
- calldataarg args; f(e, args);
- uint224 valueAfter = ckptVotesTotalSupply@withrevert(index);
- assert !lastReverted;
- uint32 clockAfter = ckptClockTotalSupply@withrevert(index);
- assert !lastReverted;
- assert clockAfter == clockBefore;
- assert valueAfter != valueBefore => clockBefore == assert_uint32(clock(e));
- }
- /*
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- โ Rules: what function can lead to state changes โ
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- */
- rule changes(env e, method f)
- filtered { f -> !f.isView }
- {
- address account;
- require clockSanity(e);
- uint32 ckptsBefore = numCheckpoints(account);
- uint256 votesBefore = getVotes(account);
- address delegatesBefore = delegates(account);
- calldataarg args; f(e, args);
- uint32 ckptsAfter = numCheckpoints(account);
- uint256 votesAfter = getVotes(account);
- address delegatesAfter = delegates(account);
- assert ckptsAfter != ckptsBefore => (
- ckptsAfter == assert_uint32(ckptsBefore + 1) &&
- ckptClock(account, ckptsBefore) == assert_uint32(clock(e)) &&
- (
- f.selector == sig:mint(address,uint256).selector ||
- f.selector == sig:burn(address,uint256).selector ||
- f.selector == sig:transfer(address,uint256).selector ||
- f.selector == sig:transferFrom(address,address,uint256).selector ||
- f.selector == sig:delegate(address).selector ||
- f.selector == sig:delegateBySig(address,uint256,uint256,uint8,bytes32,bytes32).selector
- )
- );
- assert votesAfter != votesBefore => (
- f.selector == sig:mint(address,uint256).selector ||
- f.selector == sig:burn(address,uint256).selector ||
- f.selector == sig:transfer(address,uint256).selector ||
- f.selector == sig:transferFrom(address,address,uint256).selector ||
- f.selector == sig:delegate(address).selector ||
- f.selector == sig:delegateBySig(address,uint256,uint256,uint8,bytes32,bytes32).selector
- );
- assert delegatesAfter != delegatesBefore => (
- f.selector == sig:delegate(address).selector ||
- f.selector == sig: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;
- }
- */
|