Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
7 changes: 7 additions & 0 deletions src/test/harnesses/EigenPodManagerWrapper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,11 @@ contract EigenPodManagerWrapper is EigenPodManager {
function setPodOwnerShares(address owner, int256 shares) external {
podOwnerDepositShares[owner] = shares;
}

function setBeaconChainSlashingFactor(address podOwner, uint64 slashingFactor) external {
_beaconChainSlashingFactor[podOwner] = BeaconChainSlashingFactor({
slashingFactor: slashingFactor,
isSet: true
});
}
}
33 changes: 13 additions & 20 deletions src/test/integration/IntegrationBase.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -930,24 +930,11 @@ abstract contract IntegrationBase is IntegrationDeployer, TypeImporter {
IStrategy[] memory strategies,
string memory err
) internal {
uint[] memory curSlashableStake = _getMinSlashableStake(operator, operatorSet, strategies);
uint[] memory prevSlashableStake = _getPrevMinSlashableStake(operator, operatorSet, strategies);
uint[] memory curSlashableStake = _getMinSlashableStake(address(operator), operatorSet, strategies);
uint[] memory prevSlashableStake = _getPrevMinSlashableStake(address(operator), operatorSet, strategies);

for (uint i = 0; i < strategies.length; i++) {
assertTrue(prevSlashableStake[i] > curSlashableStake[i], err);
}
}

function assert_Snap_StakeBecomeUnslashable(
address operator,
OperatorSet memory operatorSet,
IStrategy[] memory strategies,
string memory err
) internal {
uint[] memory curSlashableStake = _getMinSlashableStake(operator, operatorSet, strategies);
uint[] memory prevSlashableStake = _getPrevMinSlashableStake(operator, operatorSet, strategies);

for (uint i = 0; i < strategies.length; i++) {
assertTrue(prevSlashableStake[i] > curSlashableStake[i], err);
}
}
Expand Down Expand Up @@ -1870,9 +1857,10 @@ abstract contract IntegrationBase is IntegrationDeployer, TypeImporter {
// Use timewarp to get previous staker shares
uint[] memory prevShares = _getPrevStakerWithdrawableShares(staker, strategies);

// For each strategy, check diff between (prev-removed) and curr is at most 1 gwei
// Assert that the decrease in withdrawable shares is at least as much as the removed shares
// Checking for expected rounding down behavior
for (uint i = 0; i < strategies.length; i++) {
assertApproxEqAbs(prevShares[i] - removedShares[i], curShares[i], 1e9, err);
assertGe(prevShares[i] - curShares[i], removedShares[i], err);
}
}

Expand Down Expand Up @@ -2010,7 +1998,11 @@ abstract contract IntegrationBase is IntegrationDeployer, TypeImporter {
// If there was a slashing, but we complete a withdrawal for 0 shares, no need to normalize
if (curSlashingFactor == WAD || curDepositShares[i] == 0) {
assert_Snap_Unchanged_DSF(staker, stratArray, err);
assert_DSF_WAD(staker, stratArray, err);
assert_DSF_WAD(staker, stratArray, err);
}
// If the staker has a slashingFactor of 0, any withdrawal as shares won't change the DSF
else if (staker.getSlashingFactor(strategies[i]) == 0) {
assert_Snap_Unchanged_DSF(staker, stratArray, err);
}
// If there was a slashing and we complete a withdrawal for non-zero shares, normalize the DSF
else {
Expand All @@ -2030,7 +2022,8 @@ abstract contract IntegrationBase is IntegrationDeployer, TypeImporter {
uint[] memory delegatableShares,
string memory err
) internal {
uint64[] memory maxMags = _getMaxMagnitudes(operator, strategies);
address operator = delegationManager.delegatedTo(address(staker));
uint64[] memory maxMags = _getMaxMagnitudes(User(payable(operator)), strategies);

for (uint i = 0; i < strategies.length; i++) {
IStrategy[] memory stratArray = strategies[i].toArray();
Expand Down Expand Up @@ -2104,7 +2097,7 @@ abstract contract IntegrationBase is IntegrationDeployer, TypeImporter {
(OperatorSet[] memory operatorSets, Allocation[] memory allocations) = _getStrategyAllocations(operator, BEACONCHAIN_ETH_STRAT);
for (uint i = 0; i < operatorSets.length; i++) {
if (allocations[i].currentMagnitude > 0) {
assert_Snap_StakeBecomeUnslashable(operator, operatorSets[i], BEACONCHAIN_ETH_STRAT.toArray(), "operator should have minSlashableStake decreased");
assert_Snap_StakeBecomeUnslashable(User(payable(operator)), operatorSets[i], BEACONCHAIN_ETH_STRAT.toArray(), "operator should have minSlashableStake decreased");
}
}
}
Expand Down
50 changes: 49 additions & 1 deletion src/test/integration/IntegrationChecks.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,6 @@ contract IntegrationCheckUtils is IntegrationBase {
uint64 slashedAmountGwei
) internal {
check_CompleteCheckpoint_State(staker);

assert_Snap_Unchanged_Staker_DepositShares(staker, "staker shares should not have decreased");
assert_Snap_Removed_Staker_WithdrawableShares_AtLeast(staker, BEACONCHAIN_ETH_STRAT, slashedAmountGwei * GWEI_TO_WEI, "should have decreased withdrawable shares by at least slashed amount");
assert_Snap_Unchanged_ActiveValidatorCount(staker, "should not have changed active validator count");
Expand All @@ -160,6 +159,20 @@ contract IntegrationCheckUtils is IntegrationBase {
assert_SlashableStake_Decrease_BCSlash(staker);
}

/// @notice Used for edge cases where rounding behaviors of magnitudes close to 1 are tested.
/// Normal
function check_CompleteCheckPoint_WithSlashing_LowMagnitude_State(
User staker,
uint64 slashedAmountGwei
) internal {
check_CompleteCheckpoint_State(staker);
assert_Snap_Unchanged_Staker_DepositShares(staker, "staker shares should not have decreased");
assert_Snap_Removed_Staker_WithdrawableShares_AtLeast(staker, BEACONCHAIN_ETH_STRAT, slashedAmountGwei * GWEI_TO_WEI, "should have decreased withdrawable shares by at least slashed amount");
assert_Snap_Unchanged_ActiveValidatorCount(staker, "should not have changed active validator count");
assert_Snap_BCSF_Decreased(staker, "BCSF should decrease");
assert_Snap_Unchanged_DSF(staker, BEACONCHAIN_ETH_STRAT.toArray(), "DSF should be unchanged");
}

function check_CompleteCheckpoint_WithCLSlashing_State(
User staker,
uint64 slashedAmountGwei
Expand Down Expand Up @@ -881,6 +894,24 @@ contract IntegrationCheckUtils is IntegrationBase {
check_IncrAlloc_State_Slashable_Active(operator, params);
}

/// @dev Invariants for modifyAllocations. Use when:
/// - operator IS slashable for this operator set
/// - last call to modifyAllocations created an INCREASE in allocation
/// - operator has no delegated shares/stake so their slashable stake remains UNCHANGED
function check_IncrAlloc_State_Slashable_NoDelegatedStake(
User operator,
AllocateParams memory params
) internal {
check_Base_IncrAlloc_State(operator, params);
check_IsSlashable_State(operator, params.operatorSet, params.strategies);

/// Run checks on pending allocation, if the operator has a nonzero delay
check_IncrAlloc_State_Slashable_Pending(operator, params);

// Validate operator has no pending modification and has increased allocation
check_IncrAlloc_State_Slashable_Active_NoDelegatedStake(operator, params);
}

/// @dev Invariants for modifyAllocations. Used when:
/// - operator IS slashable for this operator set
/// - last call to modifyAllocations created an INCREASE in allocation
Expand Down Expand Up @@ -919,6 +950,23 @@ contract IntegrationCheckUtils is IntegrationBase {
assert_Snap_StakeBecameSlashable(operator, params.operatorSet, params.strategies, "slashable stake should have increased");
}

/// @dev Invariants for modifyAllocations. Used when:
/// - operator IS slashable for this operator set
/// - last call to modifyAllocations created an INCREASE in allocation
/// - effectBlock for the increase HAS been reached
function check_IncrAlloc_State_Slashable_Active_NoDelegatedStake(
User operator,
AllocateParams memory params
) private activateAllocation(operator) {
// Validate operator does not have a pending modification, and has expected slashable stake
check_ActiveModification_State(operator, params);

// SHOULD set current magnitude and increase slashable/allocated stake
assert_Snap_Set_CurrentMagnitude(operator, params, "should have updated the operator's magnitude");
assert_HasAllocatedStake(operator, params, "operator should have expected allocated stake for each strategy");
assert_HasSlashableStake(operator, params, "operator should have expected slashable stake for each strategy");
}

/*******************************************************************************
ALM - DECREASE ALLOCATION
*******************************************************************************/
Expand Down
35 changes: 35 additions & 0 deletions src/test/integration/mocks/BeaconChainMock.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,41 @@ contract BeaconChainMock is Logger {
return slashedBalanceGwei;
}

function slashValidators(uint40[] memory _validators, uint64 _slashAmountGwei) public {
print.method("slashValidatorsAmountGwei");

for (uint i = 0; i < _validators.length; i++) {
uint40 validatorIndex = _validators[i];
Validator storage v = validators[validatorIndex];
require(!v.isDummy, "BeaconChainMock: attempting to exit dummy validator. We need those for proofgen >:(");

// Mark slashed and initiate validator exit
if (!v.isSlashed) {
v.isSlashed = true;
v.exitEpoch = currentEpoch() + 1;
}

// Calculate slash amount
uint64 curBalanceGwei = _currentBalanceGwei(validatorIndex);

// Calculate slash amount
uint64 slashedAmountGwei;
if (_slashAmountGwei > curBalanceGwei) {
slashedAmountGwei = curBalanceGwei;
_slashAmountGwei -= curBalanceGwei;
curBalanceGwei = 0;
} else {
slashedAmountGwei = _slashAmountGwei;
curBalanceGwei -= _slashAmountGwei;
}

// Decrease current balance (effective balance updated during epoch processing)
_setCurrentBalance(validatorIndex, curBalanceGwei);

console.log(" - Slashed validator %s by %s gwei", validatorIndex, slashedAmountGwei);
}
}

/// @dev Move forward one epoch on the beacon chain, taking care of important epoch processing:
/// - Award ALL validators CONSENSUS_REWARD_AMOUNT
/// - Withdraw any balance over 32 ETH
Expand Down
2 changes: 1 addition & 1 deletion src/test/integration/tests/FullySlashed_EigenPod.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ contract Integration_FullySlashedEigenpod_NotCheckpointed is Integration_FullySl
// BCSF is asserted to be zero here
check_CompleteCheckpoint_FullySlashed_State(staker, validators, slashedGwei);
} else {
check_CompleteCheckpoint_WithSlashing_HandleRoundDown_State(staker, validators, slashedGwei);
check_CompleteCheckpoint_WithSlashing_HandleRoundDown_State(staker, validators, slashedGwei - podBalanceGwei);
}
}
}
Loading