Skip to content

Commit

Permalink
test: beacon chain invariants
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael authored and Michael committed Feb 20, 2025
1 parent 889996e commit 73a6855
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 1 deletion.
69 changes: 68 additions & 1 deletion src/test/integration/IntegrationBase.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ abstract contract IntegrationBase is IntegrationDeployer, TypeImporter {

/// @dev Choose a random subset of validators (selects AT LEAST ONE)
function _choose(uint40[] memory validators) internal returns (uint40[] memory) {
uint _rand = _randUint({ min: 1, max: validators.length ** 2 });
uint _rand = _randUint({ min: 1, max: (2**validators.length) - 1 });

uint40[] memory result = new uint40[](validators.length);
uint newLen;
Expand Down Expand Up @@ -833,6 +833,20 @@ abstract contract IntegrationBase is IntegrationDeployer, TypeImporter {
}
}

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);
}
}

function assert_Snap_Added_SlashableStake(
User operator,
OperatorSet memory operatorSet,
Expand Down Expand Up @@ -1658,6 +1672,20 @@ abstract contract IntegrationBase is IntegrationDeployer, TypeImporter {
}
}

function assert_SlashableStake_Decrease_BCSlash(
User staker
) internal {
if (delegationManager.isDelegated(address(staker))) {
address operator = delegationManager.delegatedTo(address(staker));
(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");
}
}
}
}

/*******************************************************************************
SNAPSHOT ASSERTIONS: UNDERLYING TOKEN
*******************************************************************************/
Expand Down Expand Up @@ -1904,6 +1932,16 @@ abstract contract IntegrationBase is IntegrationDeployer, TypeImporter {
assertEq(prevExitedBalanceGwei + addedGwei, curExitedBalanceGwei, err);
}

function assert_Snap_BCSF_Decreased(
User staker,
string memory err
) internal {
uint64 curBCSF = _getBeaconChainSlashingFactor(staker);
uint64 prevBCSF = _getPrevBeaconChainSlashingFactor(staker);

assertLt(curBCSF, prevBCSF, err);
}

/*******************************************************************************
UTILITY METHODS
*******************************************************************************/
Expand Down Expand Up @@ -2551,6 +2589,14 @@ abstract contract IntegrationBase is IntegrationDeployer, TypeImporter {
return _getMinSlashableStake(operator, operatorSet, strategies);
}

function _getPrevMinSlashableStake(
address operator,
OperatorSet memory operatorSet,
IStrategy[] memory strategies
) internal timewarp() returns (uint[] memory) {
return _getMinSlashableStake(operator, operatorSet, strategies);
}

function _getMinSlashableStake(
User operator,
OperatorSet memory operatorSet,
Expand All @@ -2564,6 +2610,19 @@ abstract contract IntegrationBase is IntegrationDeployer, TypeImporter {
})[0];
}

function _getMinSlashableStake(
address operator,
OperatorSet memory operatorSet,
IStrategy[] memory strategies
) internal view returns (uint[] memory) {
return allocationManager.getMinimumSlashableStake({
operatorSet: operatorSet,
operators: address(operator).toArray(),
strategies: strategies,
futureBlock: uint32(block.number)
})[0];
}

function _getPrevAllocatedStake(
User operator,
OperatorSet memory operatorSet,
Expand Down Expand Up @@ -2591,6 +2650,14 @@ abstract contract IntegrationBase is IntegrationDeployer, TypeImporter {
(operatorSets, allocations) = allocationManager.getStrategyAllocations(address(operator), strategy);
}

function _getStrategyAllocations(
address operator,
IStrategy strategy
) internal view returns (OperatorSet[] memory operatorSets, Allocation[] memory allocations) {
(operatorSets, allocations) = allocationManager.getStrategyAllocations(operator, strategy);
}


function _getPrevIsSlashable(
User operator,
OperatorSet memory operatorSet
Expand Down
12 changes: 12 additions & 0 deletions src/test/integration/IntegrationChecks.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ contract IntegrationCheckUtils is IntegrationBase {
assert_Snap_Removed_Staker_WithdrawableShares(staker, BEACONCHAIN_ETH_STRAT, slashedAmountGwei * GWEI_TO_WEI, "should have decreased withdrawable shares by slashed amount");
assert_Snap_Removed_ActiveValidatorCount(staker, slashedValidators.length, "should have decreased active validator count");
assert_Snap_Removed_ActiveValidators(staker, slashedValidators, "exited validators should each be WITHDRAWN");
assert_Snap_BCSF_Decreased(staker, "BCSF should decrease");
assert_Snap_Unchanged_DSF(staker, BEACONCHAIN_ETH_STRAT.toArray(), "DSF should be unchanged");
assert_SlashableStake_Decrease_BCSlash(staker);
}

function check_CompleteCheckpoint_WithSlashing_HandleRoundDown_State(
Expand All @@ -105,6 +108,9 @@ contract IntegrationCheckUtils is IntegrationBase {

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_BCSF_Decreased(staker, "BCSF should decrease");
assert_Snap_Unchanged_DSF(staker, BEACONCHAIN_ETH_STRAT.toArray(), "DSF should be unchanged");
assert_SlashableStake_Decrease_BCSlash(staker);
// TODO - currently only used after a `NoWithdrawNoRewards` action. Investigate re-adding in future.
// assert_Snap_Removed_ActiveValidatorCount(staker, slashedValidators.length, "should have decreased active validator count");
// assert_Snap_Removed_ActiveValidators(staker, slashedValidators, "exited validators should each be WITHDRAWN");
Expand All @@ -119,6 +125,9 @@ contract IntegrationCheckUtils is IntegrationBase {
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");
assert_SlashableStake_Decrease_BCSlash(staker);
}

function check_CompleteCheckpoint_WithCLSlashing_State(
Expand All @@ -130,6 +139,9 @@ contract IntegrationCheckUtils is IntegrationBase {
assert_Snap_Unchanged_Staker_DepositShares(staker, "staker shares should not have decreased");
assert_Snap_Removed_Staker_WithdrawableShares(staker, BEACONCHAIN_ETH_STRAT, slashedAmountGwei * GWEI_TO_WEI, "should have decreased withdrawable shares by 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");
assert_SlashableStake_Decrease_BCSlash(staker);
}

function check_CompleteCheckpoint_WithExits_State(
Expand Down
1 change: 1 addition & 0 deletions src/test/integration/tests/Slashed_Eigenpod.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ contract Integration_SlashedEigenpod is IntegrationCheckUtils {
check_Deposit_State(staker, strategies, shares);

uint40[] memory slashedValidators = _choose(validators);
console.log(slashedValidators.length);
slashedGwei = beaconChain.slashValidators(slashedValidators);
console.log(slashedGwei);
beaconChain.advanceEpoch_NoWithdrawNoRewards();
Expand Down

0 comments on commit 73a6855

Please sign in to comment.