mirror of
https://github.com/logos-co/staking.git
synced 2025-01-11 03:06:24 +00:00
bb31a4c80d
We've introduced a rule that finds counter examples for all functions that changes balances. This rule will always fail by definition, so we're commenting it out to get CI green again.
155 lines
4.2 KiB
Ruby
155 lines
4.2 KiB
Ruby
using ERC20A as staked;
|
|
methods {
|
|
function staked.balanceOf(address) external returns (uint256) envfree;
|
|
function totalSupplyBalance() external returns (uint256) envfree;
|
|
function totalSupplyMP() external returns (uint256) envfree;
|
|
function oldManager() external returns (address) envfree;
|
|
function _.migrateFrom(address, bool, StakeManager.Account) external => NONDET;
|
|
function _.increaseMPFromMigration(uint256) external => NONDET;
|
|
function _.migrationInitialize(uint256,uint256,uint256,uint256) external => NONDET;
|
|
|
|
function accounts(address) external returns(address, uint256, uint256, uint256, uint256, uint256, uint256) envfree;
|
|
}
|
|
|
|
function getAccountMultiplierPoints(address addr) returns uint256 {
|
|
uint256 multiplierPoints;
|
|
_, _, _, multiplierPoints, _, _, _ = accounts(addr);
|
|
|
|
return multiplierPoints;
|
|
}
|
|
|
|
function getAccountBalance(address addr) returns uint256 {
|
|
uint256 balance;
|
|
_, balance, _, _, _, _, _ = accounts(addr);
|
|
|
|
return balance;
|
|
}
|
|
|
|
function isMigrationfunction(method f) returns bool {
|
|
return
|
|
f.selector == sig:migrateFrom(address,bool,StakeManager.Account).selector ||
|
|
f.selector == sig:migrateTo(bool).selector;
|
|
}
|
|
|
|
/* assume that migration is zero, causing the verification to take into account only
|
|
cases where it is zero. specifically no externall call to the migration contract */
|
|
function simplification() {
|
|
require currentContract.migration == 0;
|
|
}
|
|
|
|
ghost mathint sumOfEpochRewards
|
|
{
|
|
init_state axiom sumOfEpochRewards == 0;
|
|
}
|
|
|
|
ghost mathint sumOfBalances { /* sigma account[u].balance forall u */
|
|
init_state axiom sumOfBalances == 0;
|
|
}
|
|
|
|
ghost mathint sumOfMultipliers /* sigma account[u].multiplier forall u */
|
|
{
|
|
init_state axiom sumOfMultipliers == 0;
|
|
}
|
|
|
|
hook Sstore epochs[KEY uint256 epochId].epochReward uint256 newValue (uint256 oldValue) STORAGE {
|
|
sumOfEpochRewards = sumOfEpochRewards - oldValue + newValue;
|
|
}
|
|
|
|
hook Sstore accounts[KEY address addr].balance uint256 newValue (uint256 oldValue) STORAGE {
|
|
sumOfBalances = sumOfBalances - oldValue + newValue;
|
|
}
|
|
|
|
hook Sstore accounts[KEY address addr].currentMP uint256 newValue (uint256 oldValue) STORAGE {
|
|
sumOfMultipliers = sumOfMultipliers - oldValue + newValue;
|
|
}
|
|
|
|
invariant sumOfBalancesIsStakeSupply()
|
|
sumOfBalances == to_mathint(totalSupplyBalance());
|
|
|
|
invariant sumOfMultipliersIsMultiplierSupply()
|
|
sumOfMultipliers == to_mathint(totalSupplyMP())
|
|
{ preserved with () {
|
|
simplification();
|
|
}
|
|
}
|
|
|
|
invariant sumOfEpochRewardsIsPendingRewards()
|
|
sumOfEpochRewards == to_mathint(currentContract.pendingReward)
|
|
{ preserved {
|
|
requireInvariant highEpochsAreNull(currentContract.currentEpoch);
|
|
}
|
|
}
|
|
|
|
invariant highEpochsAreNull(uint256 epochNumber)
|
|
epochNumber >= currentContract.currentEpoch => currentContract.epochs[epochNumber].epochReward == 0;
|
|
|
|
|
|
rule reachability(method f)
|
|
{
|
|
calldataarg args;
|
|
env e;
|
|
f(e,args);
|
|
satisfy true;
|
|
}
|
|
|
|
/**
|
|
@title when there is no migration - some functions must revert.
|
|
Other function should have non reverting cases
|
|
**/
|
|
rule revertsWhenNoMigration(method f) {
|
|
calldataarg args;
|
|
env e;
|
|
require currentContract.migration == 0;
|
|
f@withrevert(e,args);
|
|
bool reverted = lastReverted;
|
|
if (!isMigrationfunction(f))
|
|
satisfy !reverted;
|
|
assert isMigrationfunction(f) => reverted;
|
|
}
|
|
|
|
// This rule is commented out as it's just a helper rule to easily see which
|
|
// functions change the balance of the `StakeManager` contract.
|
|
//
|
|
// rule whoChangeERC20Balance( method f ) filtered { f -> f.contract != staked }
|
|
// {
|
|
// address user;
|
|
// uint256 before = staked.balanceOf(user);
|
|
// calldataarg args;
|
|
// env e;
|
|
// f(e,args);
|
|
// assert before == staked.balanceOf(user);
|
|
// }
|
|
|
|
rule epochOnlyIncreases {
|
|
method f;
|
|
env e;
|
|
calldataarg args;
|
|
|
|
uint256 epochBefore = currentContract.currentEpoch;
|
|
|
|
f(e, args);
|
|
|
|
assert currentContract.currentEpoch >= epochBefore;
|
|
}
|
|
|
|
|
|
//TODO codehash / isVault
|
|
/*
|
|
ghost mapping(address => bytes32) codehash;
|
|
|
|
hook EXTCODEHASH(address a) bytes32 hash {
|
|
require hash == codehash[a];
|
|
}
|
|
|
|
rule checksCodeHash(method f) filtered {
|
|
f -> requiresVault(f)
|
|
} {
|
|
env e;
|
|
|
|
bool isWhitelisted = isVault(codehash[e.msg.sender]);
|
|
f(e);
|
|
|
|
assert isWhitelisted;
|
|
}
|
|
*/
|