Skip to content

Commit

Permalink
Revert "Slashing integration tests (#1138)"
Browse files Browse the repository at this point in the history
This reverts commit a61f5de.
  • Loading branch information
eigenmikem authored Feb 20, 2025
1 parent a61f5de commit 87103c9
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 435 deletions.
79 changes: 35 additions & 44 deletions src/test/integration/IntegrationBase.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -219,36 +219,6 @@ abstract contract IntegrationBase is IntegrationDeployer, TypeImporter {

return result;
}

/// @dev Choose a random subset of validators (selects AT LEAST ONE but NOT ALL)
function _chooseSubset(uint40[] memory validators) internal returns (uint40[] memory) {
require(validators.length >= 2, "Need at least 2 validators to choose subset");

uint40[] memory result = new uint40[](validators.length);
uint newLen;

uint rand = _randUint({ min: 1, max: validators.length ** 2 });
for (uint i = 0; i < validators.length; i++) {
if (rand >> i & 1 == 1) {
result[newLen] = validators[i];
newLen++;
}
}

// If we picked all, remove one random validator
if (newLen == validators.length) {
uint indexToRemove = _randUint({ min: 0, max: validators.length - 1 });
for (uint i = indexToRemove; i < newLen - 1; i++) {
result[i] = result[i + 1];
}
newLen--;
}

// Update array length
assembly { mstore(result, newLen) }

return result;
}

function _getTokenName(IERC20 token) internal view returns (string memory) {
if (token == NATIVE_ETH) {
Expand Down Expand Up @@ -1311,11 +1281,8 @@ abstract contract IntegrationBase is IntegrationDeployer, TypeImporter {
uint wadToSlash = slashingParams.wadsToSlash[slashingParams.strategies.indexOf(strat)];
slashedShares = prevShares[i].mulWadRoundUp(allocateParams.newMagnitudes[i].mulWadRoundUp(wadToSlash));
}
console.log(prevShares[i]);
console.log(slashedShares);
console.log(curShares[i]);

assertApproxEqAbs(prevShares[i] - slashedShares, curShares[i], 1000, err);
assertApproxEqAbs(prevShares[i] - slashedShares, curShares[i], 1, err);
}
}

Expand Down Expand Up @@ -2148,18 +2115,42 @@ abstract contract IntegrationBase is IntegrationDeployer, TypeImporter {

function _strategiesAndWadsForFullSlash(
OperatorSet memory operatorSet
) internal view returns (IStrategy[] memory strategies, uint[] memory wadsToSlash) {
// Get list of all strategies in an operator set.
strategies = allocationManager.getStrategiesInOperatorSet(operatorSet);

wadsToSlash = new uint[](strategies.length);

for (uint i; i < strategies.length; ++i) {
wadsToSlash[i] = 1 ether;
}
) internal view returns (IStrategy[] memory strategies, uint[] memory wadsToSlash) {
// Get list of all strategies in an operator set.
strategies = allocationManager.getStrategiesInOperatorSet(operatorSet);

return (strategies.sort(), wadsToSlash);
wadsToSlash = new uint[](strategies.length);

for (uint i; i < strategies.length; ++i) {
wadsToSlash[i] = 1 ether;
}

return(strategies.sort(), wadsToSlash);
}

function _strategiesAndWadsForRandFullSlash(
OperatorSet memory operatorSet
) internal returns (IStrategy[] memory strategies, uint[] memory wadsToSlash) {
// Get list of all strategies in an operator set.
strategies = allocationManager.getStrategiesInOperatorSet(operatorSet);

// Randomly select a subset of strategies to slash.
uint len = _randUint({ min: 1, max: strategies.length });

// Update length of strategies array.
assembly {
mstore(strategies, len)
}

wadsToSlash = new uint[](len);

// Fully slash each selected strategy
for (uint i; i < len; ++i) {
wadsToSlash[i] = 1 ether;
}

return (strategies.sort(), wadsToSlash);
}

function _randMagnitudes(uint64 sum, uint256 len) internal returns (uint64[] memory magnitudes) {
magnitudes = new uint64[](len);
Expand Down
50 changes: 0 additions & 50 deletions src/test/integration/IntegrationChecks.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -971,56 +971,6 @@ contract IntegrationCheckUtils is IntegrationBase {
}
}

function check_Withdrawal_AsTokens_State_AfterBeaconSlash(
User staker,
User operator,
IDelegationManagerTypes.Withdrawal memory withdrawal,
IAllocationManagerTypes.AllocateParams memory allocateParams,
IAllocationManagerTypes.SlashingParams memory slashingParams,
uint[] memory expectedTokens
) internal {
IERC20[] memory tokens = new IERC20[](withdrawal.strategies.length);

for (uint i; i < withdrawal.strategies.length; i++) {
IStrategy strat = withdrawal.strategies[i];

bool isBeaconChainETHStrategy = strat == beaconChainETHStrategy;

tokens[i] = isBeaconChainETHStrategy ? NATIVE_ETH : withdrawal.strategies[i].underlyingToken();

if (slashingParams.strategies.contains(strat)) {
uint wadToSlash = slashingParams.wadsToSlash[slashingParams.strategies.indexOf(strat)];

expectedTokens[i] -= expectedTokens[i]
.mulWadRoundUp(allocateParams.newMagnitudes[i].mulWadRoundUp(wadToSlash));

uint256 max = allocationManager.getMaxMagnitude(address(operator), strat);

withdrawal.scaledShares[i] -= withdrawal.scaledShares[i].calcSlashedAmount(WAD, max);

// Round down to the nearest gwei for beaconchain ETH strategy.
if (isBeaconChainETHStrategy) {
expectedTokens[i] -= expectedTokens[i] % 1 gwei;
}
}
}

// Common checks
assert_WithdrawalNotPending(delegationManager.calculateWithdrawalRoot(withdrawal), "staker withdrawal should no longer be pending");

// assert_Snap_Added_TokenBalances(staker, tokens, expectedTokens, "staker should have received expected tokens");
assert_Snap_Unchanged_StakerDepositShares(staker, "staker shares should not have changed");
assert_Snap_Removed_StrategyShares(withdrawal.strategies, withdrawal.scaledShares, "strategies should have total shares decremented");

// Checks specific to an operator that the Staker has delegated to
if (operator != User(payable(0))) {
if (operator != staker) {
assert_Snap_Unchanged_TokenBalances(operator, "operator token balances should not have changed");
}
assert_Snap_Unchanged_OperatorShares(operator, "operator shares should not have changed");
}
}

function check_Withdrawal_AsShares_State_AfterSlash(
User staker,
User operator,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,41 @@ import "src/test/integration/IntegrationChecks.t.sol";
import "src/test/integration/users/User.t.sol";
import {console} from "forge-std/console.sol";

contract Integration_Deposit_Delegate_Allocate_Slash_Queue_Redeposit is IntegrationCheckUtils, IDelegationManagerTypes {
contract Integration_Deposit_Delegate_Allocate_Slash_Queue_Redeposit is IntegrationCheckUtils {

function testFuzz_deposit_delegate_allocate_fullSlash_queue_complete_redeposit(
uint24 _random
) public {
_configRand({_randomSeed: _random, _assetTypes: HOLDS_LST, _userTypes: DEFAULT});
_upgradeEigenLayerContracts();

(User staker, IStrategy[] memory strategies, uint256[] memory tokenBalances) = _newRandomStaker();
(User operator,,) = _newRandomOperator();
(AVS avs,) = _newRandomAVS();

uint256[] memory tokensToDeposit = new uint256[](tokenBalances.length);
uint256[] memory numTokensRemaining = new uint256[](tokenBalances.length);
for (uint256 i = 0; i < tokenBalances.length; i++) {
tokensToDeposit[i] = tokenBalances[i]/2;
numTokensRemaining[i] = tokenBalances[i] - tokensToDeposit[i];
AVS avs;
OperatorSet operatorSet;

User operator;
AllocateParams allocateParams;

User staker;
IStrategy[] strategies;
uint[] initTokenBalances;
uint[] initDepositShares;

uint[] numTokensRemaining;

function _init() internal override {
// TODO: Partial deposits don't work when beacon chain eth balance is initialized to < 64 ETH, need to write _newRandomStaker variant that ensures beacon chain ETH balance
// greater than or equal to 64
_configAssetTypes(HOLDS_LST);
_configUserTypes(DEFAULT);

(staker, strategies, initTokenBalances) = _newRandomStaker();
(operator,,) = _newRandomOperator();
(avs,) = _newRandomAVS();

uint256[] memory tokensToDeposit = new uint256[](initTokenBalances.length);
numTokensRemaining = new uint256[](initTokenBalances.length);
for (uint256 i = 0; i < initTokenBalances.length; i++) {
if (strategies[i] == BEACONCHAIN_ETH_STRAT) {
tokensToDeposit[i] = initTokenBalances[i];
continue;
}

tokensToDeposit[i] = initTokenBalances[i]/2;
numTokensRemaining[i] = initTokenBalances[i] - tokensToDeposit[i];
}

uint256[] memory shares = _calculateExpectedShares(strategies, tokensToDeposit);
Expand All @@ -35,63 +53,127 @@ contract Integration_Deposit_Delegate_Allocate_Slash_Queue_Redeposit is Integrat
check_Delegation_State(staker, operator, strategies, shares);

// Create operator set and register operator
OperatorSet memory operatorSet = avs.createOperatorSet(strategies);
operatorSet = avs.createOperatorSet(strategies);
operator.registerForOperatorSet(operatorSet);
check_Registration_State_NoAllocation(operator, operatorSet, allStrats);

// 3. Allocate to operator set
IAllocationManagerTypes.AllocateParams memory allocateParams =
operator.modifyAllocations(operatorSet, _maxMagnitudes(operatorSet, operator));

assert_Snap_Allocations_Modified(
operator,
allocateParams,
false,
"operator allocations should be updated before delay"
);
allocateParams = _genAllocation_AllAvailable(operator, operatorSet);
operator.modifyAllocations(allocateParams);
check_IncrAlloc_State_Slashable(operator, allocateParams);

_rollBlocksForCompleteAllocation(operator, operatorSet, strategies);

assert_Snap_Allocations_Modified(
operator,
allocateParams,
true,
"operator allocations should be updated after delay"
);

}

function testFuzz_fullSlash_queue_complete_redeposit(
uint24 _random
) public rand(_random) {
// 4. Fully slash operator
IAllocationManagerTypes.SlashingParams memory slashingParams;
SlashingParams memory slashingParams;
{
(IStrategy[] memory strategiesToSlash, uint256[] memory wadsToSlash) =
_strategiesAndWadsForFullSlash(operatorSet);

slashingParams = avs.slashOperator(operator, operatorSet.id, strategiesToSlash, wadsToSlash);
assert_Snap_Allocations_Slashed(slashingParams, operatorSet, true, "operator allocations should be slashed");
assert_Snap_Unchanged_StakerDepositShares(staker, "staker deposit shares should be unchanged after slashing");
assert_Snap_Unchanged_Staker_DepositShares(staker, "staker deposit shares should be unchanged after slashing");
assert_Snap_StakerWithdrawableShares_AfterSlash(staker, allocateParams, slashingParams, "staker deposit shares should be slashed");
}

// 5. Undelegate from an operator
IDelegationManagerTypes.Withdrawal[] memory withdrawals = staker.undelegate();
Withdrawal[] memory withdrawals = staker.undelegate();
bytes32[] memory withdrawalRoots = _getWithdrawalHashes(withdrawals);

// 6. Complete withdrawal
_rollBlocksForCompleteWithdrawals(withdrawals);
for (uint256 i = 0; i < withdrawals.length; ++i) {
uint256[] memory expectedTokens =
_calculateExpectedTokens(withdrawals[i].strategies, withdrawals[i].scaledShares);
for (uint256 i = 0; i < expectedTokens.length; i++) {
}
staker.completeWithdrawalAsTokens(withdrawals[i]);
check_Withdrawal_AsTokens_State_AfterSlash(staker, operator, withdrawals[i], allocateParams, slashingParams, expectedTokens);
}

// 7. Redeposit
shares = _calculateExpectedShares(strategies, numTokensRemaining);
uint[] memory shares = _calculateExpectedShares(strategies, numTokensRemaining);
staker.depositIntoEigenlayer(strategies, numTokensRemaining);
check_Deposit_State(staker, strategies, shares);

// Final state checks
assert_HasExpectedShares(staker, strategies, shares, "staker should have expected shares after redeposit");
assert_NoWithdrawalsPending(withdrawalRoots, "all withdrawals should be removed from pending");
}

function testFuzz_undelegate_fullSlash_complete_redeposit(
uint24 _random
) public rand(_random) {
// 4. Undelegate from an operator
Withdrawal[] memory withdrawals = staker.undelegate();
bytes32[] memory withdrawalRoots = _getWithdrawalHashes(withdrawals);

// 5. Fully slash operator
SlashingParams memory slashingParams;
{
(IStrategy[] memory strategiesToSlash, uint256[] memory wadsToSlash) =
_strategiesAndWadsForFullSlash(operatorSet);

slashingParams = avs.slashOperator(operator, operatorSet.id, strategiesToSlash, wadsToSlash);
assert_Snap_Allocations_Slashed(slashingParams, operatorSet, true, "operator allocations should be slashed");
assert_Snap_Unchanged_Staker_DepositShares(staker, "staker deposit shares should be unchanged after slashing");
assert_Snap_StakerWithdrawableShares_AfterSlash(staker, allocateParams, slashingParams, "staker deposit shares should be slashed");
}

// 6. Complete withdrawal
_rollBlocksForCompleteWithdrawals(withdrawals);
for (uint256 i = 0; i < withdrawals.length; ++i) {
uint256[] memory expectedTokens =
_calculateExpectedTokens(withdrawals[i].strategies, withdrawals[i].scaledShares);
staker.completeWithdrawalAsTokens(withdrawals[i]);
check_Withdrawal_AsTokens_State_AfterSlash(staker, operator, withdrawals[i], allocateParams, slashingParams, expectedTokens);
}

// 7. Redeposit
uint[] memory shares = _calculateExpectedShares(strategies, numTokensRemaining);
staker.depositIntoEigenlayer(strategies, numTokensRemaining);
check_Deposit_State(staker, strategies, shares);

// Final state checks
assert_HasExpectedShares(staker, strategies, shares, "staker should have expected shares after redeposit");
assert_NoWithdrawalsPending(withdrawalRoots, "all withdrawals should be removed from pending");
}

function testFuzz_depositFull_fullSlash_undelegate_completeAsShares(
uint24 _random
) public rand(_random) {
uint[] memory shares = _calculateExpectedShares(strategies, numTokensRemaining);
staker.depositIntoEigenlayer(strategies, numTokensRemaining);
check_Deposit_State(staker, strategies, shares);

// 4. Fully slash random proper subset of operators strategies
SlashingParams memory slashingParams;
{
(IStrategy[] memory strategiesToSlash, uint256[] memory wadsToSlash) =
_strategiesAndWadsForRandFullSlash(operatorSet);
slashingParams = avs.slashOperator(operator, operatorSet.id, strategiesToSlash, wadsToSlash);
assert_Snap_Allocations_Slashed(slashingParams, operatorSet, true, "operator allocations should be slashed");
assert_Snap_Unchanged_Staker_DepositShares(staker, "staker deposit shares should be unchanged after slashing");
assert_Snap_StakerWithdrawableShares_AfterSlash(staker, allocateParams, slashingParams, "staker deposit shares should be slashed");
}

// 5. Undelegate from an operator
Withdrawal[] memory withdrawals = staker.undelegate();
bytes32[] memory withdrawalRoots = _getWithdrawalHashes(withdrawals);

// 6. Complete withdrawal as shares
// Fast forward to when we can complete the withdrawal
_rollBlocksForCompleteWithdrawals(withdrawals);

for (uint256 i = 0; i < withdrawals.length; ++i) {
staker.completeWithdrawalAsShares(withdrawals[i]);
check_Withdrawal_AsShares_State_AfterSlash(staker, operator, withdrawals[i], allocateParams, slashingParams);
}

// Check final state:
assert_HasNoUnderlyingTokenBalance(staker, slashingParams.strategies, "staker not have any underlying tokens");
assert_NoWithdrawalsPending(withdrawalRoots, "all withdrawals should be removed from pending");
}
}
Loading

0 comments on commit 87103c9

Please sign in to comment.