-
Notifications
You must be signed in to change notification settings - Fork 49
/
Health.spec
108 lines (85 loc) · 4.42 KB
/
Health.spec
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
// SPDX-License-Identifier: GPL-2.0-or-later
using Util as Util;
methods {
function extSloads(bytes32[]) external returns bytes32[] => NONDET DELETE;
function totalBorrowAssets(MorphoHarness.Id) external returns uint256 envfree;
function totalBorrowShares(MorphoHarness.Id) external returns uint256 envfree;
function virtualTotalBorrowAssets(MorphoHarness.Id) external returns uint256 envfree;
function virtualTotalBorrowShares(MorphoHarness.Id) external returns uint256 envfree;
function lastUpdate(MorphoHarness.Id) external returns uint256 envfree;
function borrowShares(MorphoHarness.Id, address) external returns uint256 envfree;
function collateral(MorphoHarness.Id, address) external returns uint256 envfree;
function isAuthorized(address, address user) external returns bool envfree;
function lastUpdate(MorphoHarness.Id) external returns uint256 envfree;
function Util.libId(MorphoHarness.MarketParams) external returns MorphoHarness.Id envfree;
function isHealthy(MorphoHarness.MarketParams, address user) external returns bool envfree;
function _.price() external => mockPrice() expect uint256;
function MathLib.mulDivDown(uint256 a, uint256 b, uint256 c) internal returns uint256 => summaryMulDivDown(a,b,c);
function MathLib.mulDivUp(uint256 a, uint256 b, uint256 c) internal returns uint256 => summaryMulDivUp(a,b,c);
function UtilsLib.min(uint256 a, uint256 b) internal returns uint256 => summaryMin(a,b);
}
persistent ghost uint256 lastPrice;
persistent ghost bool priceChanged;
function mockPrice() returns uint256 {
uint256 updatedPrice;
if (updatedPrice != lastPrice) {
priceChanged = true;
lastPrice = updatedPrice;
}
return updatedPrice;
}
function summaryMulDivUp(uint256 x, uint256 y, uint256 d) returns uint256 {
// Safe require 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 require because the reference implementation would revert.
require d != 0;
return require_uint256((x * y) / d);
}
function summaryMin(uint256 a, uint256 b) returns uint256 {
return a < b ? a : b;
}
// Check that users cannot lose collateral by unauthorized parties except in case of an unhealthy position.
rule healthyUserCannotLoseCollateral(env e, method f, calldataarg data)
filtered { f -> !f.isView }
{
MorphoHarness.MarketParams marketParams;
MorphoHarness.Id id = Util.libId(marketParams);
address user;
// Assume that the e.msg.sender is not authorized.
require !isAuthorized(user, e.msg.sender);
require user != e.msg.sender;
// Assumption to ensure that no interest is accumulated.
require lastUpdate(id) == e.block.timestamp;
// Assume that the user is healthy.
require isHealthy(marketParams, user);
mathint collateralBefore = collateral(id, user);
f(e, data);
mathint collateralAfter = collateral(id, user);
assert !priceChanged => collateralAfter >= collateralBefore;
}
// Check that users without collateral also have no debt.
// This invariant ensures that bad debt realization cannot be bypassed.
invariant alwaysCollateralized(MorphoHarness.Id id, address borrower)
borrowShares(id, borrower) != 0 => collateral(id, borrower) != 0;
// Checks that passing a seized amount input to liquidate leads to repaid shares S and repaid amount A such that liquidating instead with shares S also repays the amount A.
rule liquidateEquivalentInputDebtAndInputCollateral(env e, MorphoHarness.MarketParams marketParams, address borrower, uint256 seizedAssets, bytes data) {
MorphoHarness.Id id = Util.libId(marketParams);
// Assume no interest accrual to ease the verification.
require lastUpdate(id) == e.block.timestamp;
storage init = lastStorage;
uint256 sharesBefore = borrowShares(id, borrower);
uint256 repaidAssets1;
_, repaidAssets1 = liquidate(e, marketParams, borrower, seizedAssets, 0, data);
require !priceChanged;
// Omit the bad debt realization case.
require collateral(id, borrower) != 0;
uint256 sharesAfter = borrowShares(id, borrower);
uint256 repaidShares1 = assert_uint256(sharesBefore - sharesAfter);
uint256 repaidAssets2;
_, repaidAssets2 = liquidate(e, marketParams, borrower, 0, repaidShares1, data) at init;
require !priceChanged;
assert repaidAssets1 == repaidAssets2;
}