Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Certora] Improve require documentation #468

Merged
merged 1 commit into from
Sep 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions certora/specs/AccrueInterest.spec
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ ghost ghostTransferFrom(address, address, uint256) returns bool;
// Check that calling accrueInterest first has no effect.
// This is because supply should call accrueInterest itself.
rule supplyAccruesInterest(env e, MorphoHarness.MarketParams marketParams, uint256 assets, uint256 shares, address onBehalf, bytes data) {
// Safe require because timestamps cannot realistically be that large.
require e.block.timestamp < 2^128;

storage init = lastStorage;
Expand All @@ -49,6 +50,7 @@ rule supplyAccruesInterest(env e, MorphoHarness.MarketParams marketParams, uint2
// Check that calling accrueInterest first has no effect.
// This is because withdraw should call accrueInterest itself.
rule withdrawAccruesInterest(env e, MorphoHarness.MarketParams marketParams, uint256 assets, uint256 shares, address onBehalf, address receiver) {
// Safe require because timestamps cannot realistically be that large.
require e.block.timestamp < 2^128;

storage init = lastStorage;
Expand All @@ -66,6 +68,7 @@ rule withdrawAccruesInterest(env e, MorphoHarness.MarketParams marketParams, uin
// Check that calling accrueInterest first has no effect.
// This is because borrow should call accrueInterest itself.
rule borrowAccruesInterest(env e, MorphoHarness.MarketParams marketParams, uint256 assets, uint256 shares, address onBehalf, address receiver) {
// Safe require because timestamps cannot realistically be that large.
require e.block.timestamp < 2^128;

storage init = lastStorage;
Expand All @@ -83,6 +86,7 @@ rule borrowAccruesInterest(env e, MorphoHarness.MarketParams marketParams, uint2
// Check that calling accrueInterest first has no effect.
// This is because repay should call accrueInterest itself.
rule repayAccruesInterest(env e, MorphoHarness.MarketParams marketParams, uint256 assets, uint256 shares, address onBehalf, bytes data) {
// Safe require because timestamps cannot realistically be that large.
require e.block.timestamp < 2^128;

storage init = lastStorage;
Expand Down Expand Up @@ -111,9 +115,9 @@ filtered {
env e2;
MorphoHarness.MarketParams marketParams;

// Require interactions to happen at the same block.
// Assume interactions to happen at the same block.
require e1.block.timestamp == e2.block.timestamp;
// Assumption required to cast a timestamp to uint128.
// Safe require because timestamps cannot realistically be that large.
require e1.block.timestamp < 2^128;

storage init = lastStorage;
Expand Down
22 changes: 15 additions & 7 deletions certora/specs/ConsistentState.spec
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ methods {
function _.borrowRate(MorphoHarness.MarketParams, MorphoHarness.Market) external => HAVOC_ECF;

function maxFee() external returns uint256 envfree;
function wad() external returns uint256 envfree;

function SafeTransferLib.safeTransfer(address token, address to, uint256 value) internal => summarySafeTransferFrom(token, currentContract, to, value);
function SafeTransferLib.safeTransferFrom(address token, address from, address to, uint256 value) internal => summarySafeTransferFrom(token, from, to, value);
Expand Down Expand Up @@ -79,9 +80,11 @@ hook Sstore market[KEY MorphoHarness.Id id].totalBorrowAssets uint128 newAmount

function summarySafeTransferFrom(address token, address from, address to, uint256 amount) {
if (from == currentContract) {
// Safe require because the reference implementation would revert.
myBalances[token] = require_uint256(myBalances[token] - amount);
}
if (to == currentContract) {
// Safe require because the reference implementation would revert.
myBalances[token] = require_uint256(myBalances[token] + amount);
}
}
Expand Down Expand Up @@ -116,6 +119,7 @@ invariant marketInvariant(MorphoHarness.MarketParams marketParams)
invariant isLiquid(address token)
sumAmount[token] <= myBalances[token]
{
// Safe requires on the sender because the contract cannot call the function itself.
preserved supply(MorphoHarness.MarketParams marketParams, uint256 assets, uint256 shares, address onBehalf, bytes data) with (env e) {
requireInvariant marketInvariant(marketParams);
require e.msg.sender != currentContract;
Expand Down Expand Up @@ -150,6 +154,9 @@ invariant isLiquid(address token)
invariant onlyEnabledLltv(MorphoHarness.MarketParams marketParams)
isCreated(libId(marketParams)) => isLltvEnabled(marketParams.lltv);

invariant lltvSmallerThanWad(uint256 lltv)
isLltvEnabled(lltv) => lltv < wad();

// Check that a market can only exist if its IRM is enabled.
invariant onlyEnabledIrm(MorphoHarness.MarketParams marketParams)
isCreated(libId(marketParams)) => isIrmEnabled(marketParams.irm);
Expand All @@ -159,7 +166,7 @@ rule libIdUnique() {
MorphoHarness.MarketParams marketParams1;
MorphoHarness.MarketParams marketParams2;

// Require the same arguments.
// Assume that arguments are the same.
require libId(marketParams1) == libId(marketParams2);

assert marketParams1.borrowableToken == marketParams2.borrowableToken;
Expand All @@ -178,7 +185,7 @@ filtered {
address user;
address someone;

// Require a different user to interact with Morpho.
// Assume that it is another user that is interacting with Morpho.
require user != e.msg.sender;

bool authorizedBefore = isAuthorized(user, someone);
Expand All @@ -197,7 +204,7 @@ filtered { f -> !f.isView }
MorphoHarness.Id id;
address user;

// Require that the e.msg.sender is not authorized.
// Assume that the e.msg.sender is not authorized.
require !isAuthorized(user, e.msg.sender);
require user != e.msg.sender;

Expand All @@ -217,7 +224,7 @@ filtered { f -> !f.isView }
MorphoHarness.Id id;
address user;

// Require that the e.msg.sender is not authorized.
// Assume that the e.msg.sender is not authorized.
require !isAuthorized(user, e.msg.sender);
require user != e.msg.sender;

Expand All @@ -240,7 +247,7 @@ filtered {
MorphoHarness.Id id;
address user;

// Require that the e.msg.sender is not authorized.
// Assume that the e.msg.sender is not authorized.
require !isAuthorized(user, e.msg.sender);
require user != e.msg.sender;

Expand All @@ -260,10 +267,10 @@ filtered { f -> !f.isView }
MorphoHarness.Id id;
address user;

// Require that the e.msg.sender is not authorized.
// Assume that the e.msg.sender is not authorized.
require !isAuthorized(user, e.msg.sender);
require user != e.msg.sender;
// Require that the user has no outstanding debt.
// Assume that the user has no outstanding debt.
require borrowShares(id, user) == 0;

mathint collateralBefore = collateral(id, user);
Expand All @@ -280,6 +287,7 @@ rule noTimeTravel(method f, env e, calldataarg args)
filtered { f -> !f.isView }
{
MorphoHarness.Id id;
// Assume the property before the interaction.
require lastUpdate(id) <= e.block.timestamp;
f(e, args);
assert lastUpdate(id) <= e.block.timestamp;
Expand Down
14 changes: 10 additions & 4 deletions certora/specs/ExactMath.spec
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,32 @@ methods {
}

function summaryMulDivUp(uint256 x, uint256 y, uint256 d) returns uint256 {
// Safe require because the reference implementation would revert.
return require_uint256((x * y + (d - 1)) / d);
}

function summaryMulDivDown(uint256 x, uint256 y, uint256 d) returns uint256 {
// Safe require because the reference implementation would revert.
return require_uint256((x * y) / d);
}

// Check that when not accruing interest, and when repaying all, the borrow ratio is at least reset to the initial ratio.
// More details on the purpose of this rule in RatioMath.spec.
rule repayAllResetsBorrowRatio(env e, MorphoHarness.MarketParams marketParams, uint256 assets, uint256 shares, address onbehalf, bytes data) {
MorphoHarness.Id id = libId(marketParams);
// Safe require because this invariant is checked in ConsistentState.spec
require fee(id) <= maxFee();

mathint assetsBefore = virtualTotalBorrowAssets(id);
mathint sharesBefore = virtualTotalBorrowShares(id);

// Assume no interest as it would increase the borrowed assets.
require lastUpdate(id) == e.block.timestamp;

mathint repaidAssets;
repaidAssets, _ = repay(e, marketParams, assets, shares, onbehalf, data);

// Check the case where the market is fully repaid.
require repaidAssets >= assetsBefore;

mathint assetsAfter = virtualTotalBorrowAssets(id);
Expand All @@ -60,9 +66,9 @@ rule supplyWithdraw() {
env e1;
env e2;

// Require interactions to happen at the same block.
// Assume that interactions to happen at the same block.
require e1.block.timestamp == e2.block.timestamp;
// Assumption required to cast timestamps to uint128.
// Safe require because timestamps cannot realistically be that large.
require e1.block.timestamp < 2^128;

uint256 suppliedAssets;
Expand Down Expand Up @@ -93,9 +99,9 @@ rule withdrawSupply() {
env e1;
env e2;

// Require interactions to happen at the same block.
// Assume interactions to happen at the same block.
require e1.block.timestamp == e2.block.timestamp;
// Assumption required to cast timestamps to uint128.
// Safe require because timestamps cannot realistically be that large.
require e1.block.timestamp < 2^128;

uint256 withdrawnAssets;
Expand Down
3 changes: 3 additions & 0 deletions certora/specs/ExitLiquidity.spec
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ rule withdrawLiquidity(MorphoHarness.MarketParams marketParams, uint256 assets,
env e;
MorphoHarness.Id id = libId(marketParams);

// Assume no interest as it would increase the total supply assets.
require lastUpdate(id) == e.block.timestamp;

uint256 initialShares = supplyShares(id, onBehalf);
Expand Down Expand Up @@ -51,6 +52,7 @@ rule repayLiquidity(MorphoHarness.MarketParams marketParams, uint256 assets, uin
env e;
MorphoHarness.Id id = libId(marketParams);

// Assume no interest as it would increase the total borrowed assets.
require lastUpdate(id) == e.block.timestamp;

uint256 initialShares = borrowShares(id, onBehalf);
Expand All @@ -61,6 +63,7 @@ rule repayLiquidity(MorphoHarness.MarketParams marketParams, uint256 assets, uin
uint256 repaidAssets;
repaidAssets, _ = repay(e, marketParams, assets, shares, onBehalf, data);

// Assume a full repay.
require borrowShares(id, onBehalf) == 0;

assert repaidAssets >= assetsDue;
Expand Down
14 changes: 8 additions & 6 deletions certora/specs/Health.spec
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@ function mockPrice() returns uint256 {
}

function summaryMulDivUp(uint256 x, uint256 y, uint256 d) returns uint256 {
// Safe requires because the reference implementation would revert.
require d != 0;
return require_uint256((x * y + (d - 1)) / d);
}

function summaryMulDivDown(uint256 x, uint256 y, uint256 d) returns uint256 {
// Safe requires because the reference implementation would revert.
require d != 0;
return require_uint256((x * y) / d);
}
Expand All @@ -55,12 +57,12 @@ filtered {
MorphoHarness.Id id = libId(marketParams);
address user;

// Require that the position is healthy before the interaction.
// Assume that the position is healthy before the interaction.
require isHealthy(marketParams, user);
// Require that the LLTV takes coherent values.
// Safe require because of the invariants onlyEnabledLltv and lltvSmallerThanWad in ConsistentState.spec.
require marketParams.lltv < 10^18;
require marketParams.lltv > 0;
// Ensure that no interest is accumulated.
// Assumption to ensure that no interest is accumulated.
require lastUpdate(id) == e.block.timestamp;

priceChanged = false;
Expand All @@ -81,12 +83,12 @@ filtered { f -> !f.isView }
MorphoHarness.Id id = libId(marketParams);
address user;

// Require that the e.msg.sender is not authorized.
// Assume that the e.msg.sender is not authorized.
require !isAuthorized(user, e.msg.sender);
require user != e.msg.sender;
// Ensure that no interest is accumulated.
// Assumption to ensure that no interest is accumulated.
require lastUpdate(id) == e.block.timestamp;
// Require that the user is healthy.
// Assume that the user is healthy.
require isHealthy(marketParams, user);

mathint collateralBefore = collateral(id, user);
Expand Down
Loading