Skip to content

Commit 5ebed67

Browse files
committed
test: slash invariants first pass
1 parent 319d232 commit 5ebed67

File tree

5 files changed

+394
-46
lines changed

5 files changed

+394
-46
lines changed

src/test/integration/IntegrationBase.t.sol

Lines changed: 252 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import "src/test/integration/users/User_M1.t.sol";
1818
abstract contract IntegrationBase is IntegrationDeployer, TypeImporter {
1919
using StdStyle for *;
2020
using SlashingLib for *;
21+
using Math for uint256;
2122
using Strings for *;
2223
using print for *;
2324

@@ -68,6 +69,35 @@ abstract contract IntegrationBase is IntegrationDeployer, TypeImporter {
6869
return (staker, strategies, tokenBalances);
6970
}
7071

72+
function _newBasicStaker() internal returns (User, IStrategy[] memory, uint[] memory) {
73+
string memory stakerName;
74+
75+
User staker;
76+
IStrategy[] memory strategies;
77+
uint[] memory tokenBalances;
78+
79+
if (!isUpgraded) {
80+
stakerName = string.concat("M2Staker", cheats.toString(numStakers));
81+
82+
(staker, strategies, tokenBalances) = _randUser(stakerName);
83+
84+
stakersToMigrate.push(staker);
85+
} else {
86+
stakerName = string.concat("staker", cheats.toString(numStakers));
87+
88+
(staker, strategies, tokenBalances) = _randUser(stakerName);
89+
}
90+
91+
assert_HasUnderlyingTokenBalances(staker, strategies, tokenBalances, "_newRandomStaker: failed to award token balances");
92+
93+
numStakers++;
94+
assembly { // TODO HACK
95+
mstore(strategies, 1)
96+
mstore(tokenBalances, 1)
97+
}
98+
return (staker, strategies, tokenBalances);
99+
}
100+
71101
/**
72102
* @dev Create a new operator according to configured random variants.
73103
* This user will immediately deposit their randomized assets into eigenlayer.
@@ -835,6 +865,29 @@ abstract contract IntegrationBase is IntegrationDeployer, TypeImporter {
835865
}
836866
}
837867

868+
function assert_Snap_Slashed_SlashableStake(
869+
User operator,
870+
OperatorSet memory operatorSet,
871+
SlashingParams memory params,
872+
string memory err
873+
) internal {
874+
uint[] memory curSlashableStake = _getMinSlashableStake(operator, operatorSet, params.strategies);
875+
uint[] memory prevSlashableStake = _getPrevMinSlashableStake(operator, operatorSet, params.strategies);
876+
877+
Magnitudes[] memory curMagnitudes = _getMagnitudes(operator, params.strategies);
878+
Magnitudes[] memory prevMagnitudes = _getPrevMagnitudes(operator, params.strategies);
879+
880+
for (uint i = 0; i < params.strategies.length; i++) {
881+
uint expectedSlashed = SlashingLib.calcSlashedAmount({
882+
operatorShares: prevSlashableStake[i],
883+
prevMaxMagnitude: prevMagnitudes[i].max,
884+
newMaxMagnitude: curMagnitudes[i].max
885+
});
886+
887+
assertEq(curSlashableStake[i], prevSlashableStake[i] - expectedSlashed, err);
888+
}
889+
}
890+
838891
function assert_Snap_StakeBecameAllocated(
839892
User operator,
840893
OperatorSet memory operatorSet,
@@ -877,6 +930,29 @@ abstract contract IntegrationBase is IntegrationDeployer, TypeImporter {
877930
}
878931
}
879932

933+
function assert_Snap_Slashed_AllocatedStake(
934+
User operator,
935+
OperatorSet memory operatorSet,
936+
SlashingParams memory params,
937+
string memory err
938+
) internal {
939+
uint[] memory curAllocatedStake = _getAllocatedStake(operator, operatorSet, params.strategies);
940+
uint[] memory prevAllocatedStake = _getPrevAllocatedStake(operator, operatorSet, params.strategies);
941+
942+
Magnitudes[] memory curMagnitudes = _getMagnitudes(operator, params.strategies);
943+
Magnitudes[] memory prevMagnitudes = _getPrevMagnitudes(operator, params.strategies);
944+
945+
for (uint i = 0; i < curAllocatedStake.length; i++) {
946+
uint expectedSlashed = SlashingLib.calcSlashedAmount({
947+
operatorShares: prevAllocatedStake[i],
948+
prevMaxMagnitude: prevMagnitudes[i].max,
949+
newMaxMagnitude: curMagnitudes[i].max
950+
});
951+
952+
assertEq(curAllocatedStake[i], prevAllocatedStake[i] - expectedSlashed, err);
953+
}
954+
}
955+
880956
function assert_Snap_Added_EncumberedMagnitude(
881957
User operator,
882958
IStrategy[] memory strategies,
@@ -918,6 +994,20 @@ abstract contract IntegrationBase is IntegrationDeployer, TypeImporter {
918994
}
919995
}
920996

997+
function assert_Snap_Slashed_EncumberedMagnitude(
998+
User operator,
999+
SlashingParams memory params,
1000+
string memory err
1001+
) internal {
1002+
Magnitudes[] memory curMagnitudes = _getMagnitudes(operator, params.strategies);
1003+
Magnitudes[] memory prevMagnitudes = _getPrevMagnitudes(operator, params.strategies);
1004+
1005+
for (uint i = 0; i < params.strategies.length; i++) {
1006+
uint expectedSlashed = prevMagnitudes[i].encumbered.mulWadRoundUp(params.wadsToSlash[i]);
1007+
assertEq(curMagnitudes[i].encumbered, prevMagnitudes[i].encumbered - expectedSlashed, err);
1008+
}
1009+
}
1010+
9211011
function assert_Snap_Added_AllocatableMagnitude(
9221012
User operator,
9231013
IStrategy[] memory strategies,
@@ -948,14 +1038,14 @@ abstract contract IntegrationBase is IntegrationDeployer, TypeImporter {
9481038
function assert_Snap_Removed_AllocatableMagnitude(
9491039
User operator,
9501040
IStrategy[] memory strategies,
951-
uint64[] memory magnitudeAllocated,
1041+
uint64[] memory magnitudeRemoved,
9521042
string memory err
9531043
) internal {
9541044
Magnitudes[] memory curMagnitudes = _getMagnitudes(operator, strategies);
9551045
Magnitudes[] memory prevMagnitudes = _getPrevMagnitudes(operator, strategies);
9561046

9571047
for (uint i = 0; i < strategies.length; i++) {
958-
assertEq(curMagnitudes[i].allocatable, prevMagnitudes[i].allocatable - magnitudeAllocated[i], err);
1048+
assertEq(curMagnitudes[i].allocatable, prevMagnitudes[i].allocatable - magnitudeRemoved[i], err);
9591049
}
9601050
}
9611051

@@ -1009,6 +1099,21 @@ abstract contract IntegrationBase is IntegrationDeployer, TypeImporter {
10091099
}
10101100
}
10111101

1102+
function assert_Snap_Slashed_Allocation(
1103+
User operator,
1104+
OperatorSet memory operatorSet,
1105+
SlashingParams memory params,
1106+
string memory err
1107+
) internal {
1108+
Allocation[] memory curAllocations = _getAllocations(operator, operatorSet, params.strategies);
1109+
Allocation[] memory prevAllocations = _getPrevAllocations(operator, operatorSet, params.strategies);
1110+
1111+
for (uint i = 0; i < params.strategies.length; i++) {
1112+
uint expectedSlashed = prevAllocations[i].currentMagnitude.mulWadRoundUp(params.wadsToSlash[i]);
1113+
assertEq(curAllocations[i].currentMagnitude, prevAllocations[i].currentMagnitude - expectedSlashed, err);
1114+
}
1115+
}
1116+
10121117
function assert_Snap_Unchanged_MaxMagnitude(
10131118
User operator,
10141119
IStrategy[] memory strategies,
@@ -1022,6 +1127,20 @@ abstract contract IntegrationBase is IntegrationDeployer, TypeImporter {
10221127
}
10231128
}
10241129

1130+
function assert_Snap_Slashed_MaxMagnitude(
1131+
User operator,
1132+
SlashingParams memory params,
1133+
string memory err
1134+
) internal {
1135+
Magnitudes[] memory curMagnitudes = _getMagnitudes(operator, params.strategies);
1136+
Magnitudes[] memory prevMagnitudes = _getPrevMagnitudes(operator, params.strategies);
1137+
1138+
for (uint i = 0; i < params.strategies.length; i++) {
1139+
uint expectedSlashed = prevMagnitudes[i].max.mulWadRoundUp(params.wadsToSlash[i]);
1140+
assertEq(curMagnitudes[i].max, prevMagnitudes[i].max - expectedSlashed, err);
1141+
}
1142+
}
1143+
10251144
function assert_Snap_Allocations_Slashed(
10261145
SlashingParams memory slashingParams,
10271146
OperatorSet memory operatorSet,
@@ -1237,6 +1356,47 @@ abstract contract IntegrationBase is IntegrationDeployer, TypeImporter {
12371356
}
12381357
}
12391358

1359+
function assert_Snap_Slashed_OperatorShares(
1360+
User operator,
1361+
SlashingParams memory params,
1362+
string memory err
1363+
) internal {
1364+
uint[] memory curShares = _getOperatorShares(operator, params.strategies);
1365+
uint[] memory prevShares = _getPrevOperatorShares(operator, params.strategies);
1366+
1367+
Magnitudes[] memory curMagnitudes = _getMagnitudes(operator, params.strategies);
1368+
Magnitudes[] memory prevMagnitudes = _getPrevMagnitudes(operator, params.strategies);
1369+
1370+
for (uint i = 0; i < params.strategies.length; i++) {
1371+
uint expectedSlashed = SlashingLib.calcSlashedAmount({
1372+
operatorShares: prevShares[i],
1373+
prevMaxMagnitude: prevMagnitudes[i].max,
1374+
newMaxMagnitude: curMagnitudes[i].max
1375+
});
1376+
1377+
assertEq(curShares[i], prevShares[i] - expectedSlashed, err);
1378+
}
1379+
}
1380+
1381+
function assert_Snap_Increased_BurnableShares(
1382+
User operator,
1383+
SlashingParams memory params,
1384+
string memory err
1385+
) internal {
1386+
uint[] memory curBurnable = _getBurnableShares(params.strategies);
1387+
uint[] memory prevBurnable = _getPrevBurnableShares(params.strategies);
1388+
1389+
uint[] memory curShares = _getOperatorShares(operator, params.strategies);
1390+
uint[] memory prevShares = _getPrevOperatorShares(operator, params.strategies);
1391+
1392+
for (uint i = 0; i < params.strategies.length; i++) {
1393+
uint slashedAtLeast = prevShares[i] - curShares[i];
1394+
1395+
// Not factoring in slashable shares in queue here, because that gets more complex (TODO)
1396+
assertTrue(curBurnable[i] >= (prevBurnable[i] + slashedAtLeast), err);
1397+
}
1398+
}
1399+
12401400
/*******************************************************************************
12411401
SNAPSHOT ASSERTIONS: STAKER SHARES
12421402
*******************************************************************************/
@@ -1787,12 +1947,34 @@ abstract contract IntegrationBase is IntegrationDeployer, TypeImporter {
17871947
Magnitudes[] memory magnitudes = _getMagnitudes(operator, strategies);
17881948

17891949
for (uint i = 0; i < params.strategies.length; i++) {
1790-
IStrategy strategy = params.strategies[i];
17911950
uint64 halfAvailable = uint64(magnitudes[i].allocatable) / 2;
17921951
params.newMagnitudes[i] = allocations[i].currentMagnitude + halfAvailable;
17931952
}
17941953
}
17951954

1955+
/// @dev Generate params to allocate a random portion of available magnitude to each strategy
1956+
/// in the operator set. All strategies will have a nonzero allocation, and the minimum allocation
1957+
/// will be 10% of available magnitude
1958+
function _genAllocation_Rand(
1959+
User operator,
1960+
OperatorSet memory operatorSet
1961+
) internal returns (AllocateParams memory params) {
1962+
params.operatorSet = operatorSet;
1963+
params.strategies = allocationManager.getStrategiesInOperatorSet(operatorSet);
1964+
params.newMagnitudes = new uint64[](params.strategies.length);
1965+
1966+
Allocation[] memory allocations = _getAllocations(operator, operatorSet, params.strategies);
1967+
Magnitudes[] memory magnitudes = _getMagnitudes(operator, params.strategies);
1968+
1969+
for (uint i = 0; i < params.strategies.length; i++) {
1970+
// minimum of 10%, maximum of 100%. increments of 10%.
1971+
uint r = _randUint({min: 1, max: 10});
1972+
uint64 allocation = uint64(magnitudes[i].allocatable) / uint64(r);
1973+
1974+
params.newMagnitudes[i] = allocations[i].currentMagnitude + allocation;
1975+
}
1976+
}
1977+
17961978
/// @dev Generates params for a half deallocation from all strategies the operator is allocated to in the operator set
17971979
function _genDeallocation_HalfRemaining(
17981980
User operator,
@@ -1838,12 +2020,46 @@ abstract contract IntegrationBase is IntegrationDeployer, TypeImporter {
18382020
User operator,
18392021
OperatorSet memory operatorSet,
18402022
IStrategy[] memory strategies
1841-
) internal view returns (AllocateParams memory params) {
2023+
) internal pure returns (AllocateParams memory params) {
18422024
params.operatorSet = operatorSet;
18432025
params.strategies = strategies;
18442026
params.newMagnitudes = new uint64[](params.strategies.length);
18452027
}
18462028

2029+
/// Generate random slashing between 1 and 99%
2030+
function _genSlashing_Rand(
2031+
User operator,
2032+
OperatorSet memory operatorSet
2033+
) internal returns (SlashingParams memory params) {
2034+
params.operator = address(operator);
2035+
params.operatorSetId = operatorSet.id;
2036+
params.description = "genSlashing_Half";
2037+
params.strategies = allocationManager.getStrategiesInOperatorSet(operatorSet).sort();
2038+
params.wadsToSlash = new uint[](params.strategies.length);
2039+
2040+
/// 1% * rand(1, 99)
2041+
uint slashWad = 1e16 * _randUint({min: 1, max: 99});
2042+
2043+
for (uint i = 0; i < params.wadsToSlash.length; i++) {
2044+
params.wadsToSlash[i] = slashWad;
2045+
}
2046+
}
2047+
2048+
function _genSlashing_Half(
2049+
User operator,
2050+
OperatorSet memory operatorSet
2051+
) internal view returns (SlashingParams memory params) {
2052+
params.operator = address(operator);
2053+
params.operatorSetId = operatorSet.id;
2054+
params.description = "genSlashing_Half";
2055+
params.strategies = allocationManager.getStrategiesInOperatorSet(operatorSet).sort();
2056+
params.wadsToSlash = new uint[](params.strategies.length);
2057+
2058+
for (uint i = 0; i < params.wadsToSlash.length; i++) {
2059+
params.wadsToSlash[i] = 1e17;
2060+
}
2061+
}
2062+
18472063
function _randWadToSlash() internal returns (uint) {
18482064
return _randUint({ min: 0.01 ether, max: 1 ether });
18492065
}
@@ -2371,6 +2587,38 @@ abstract contract IntegrationBase is IntegrationDeployer, TypeImporter {
23712587
return allocationManager.isMemberOfOperatorSet(address(operator), operatorSet);
23722588
}
23732589

2590+
function _getPrevBurnableShares(IStrategy[] memory strategies) internal timewarp() returns (uint[] memory) {
2591+
return _getBurnableShares(strategies);
2592+
}
2593+
2594+
function _getBurnableShares(IStrategy[] memory strategies) internal view returns (uint[] memory) {
2595+
uint[] memory burnableShares = new uint[](strategies.length);
2596+
2597+
for (uint i = 0; i < strategies.length; i++) {
2598+
if (strategies[i] == beaconChainETHStrategy) {
2599+
burnableShares[i] = eigenPodManager.burnableETHShares();
2600+
} else {
2601+
burnableShares[i] = strategyManager.getBurnableShares(strategies[i]);
2602+
}
2603+
}
2604+
2605+
return burnableShares;
2606+
}
2607+
2608+
function _getPrevSlashableSharesInQueue(User operator, IStrategy[] memory strategies) internal timewarp() returns (uint[] memory) {
2609+
return _getSlashableSharesInQueue(operator, strategies);
2610+
}
2611+
2612+
function _getSlashableSharesInQueue(User operator, IStrategy[] memory strategies) internal view returns (uint[] memory) {
2613+
uint[] memory slashableShares = new uint[](strategies.length);
2614+
2615+
for (uint i = 0; i < strategies.length; i++) {
2616+
slashableShares[i] = delegationManager.getSlashableSharesInQueue(address(operator), strategies[i]);
2617+
}
2618+
2619+
return slashableShares;
2620+
}
2621+
23742622
/// @dev Uses timewarp modifier to get operator shares at the last snapshot
23752623
function _getPrevOperatorShares(
23762624
User operator,

0 commit comments

Comments
 (0)