Skip to content

Commit

Permalink
beacon chain tests in progress
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael authored and Michael committed Jan 9, 2025
1 parent 1590294 commit b2b3f7f
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 1 deletion.
5 changes: 4 additions & 1 deletion src/test/integration/IntegrationBase.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -591,8 +591,11 @@ abstract contract IntegrationBase is IntegrationDeployer {
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], 1, err);
assertApproxEqAbs(prevShares[i] - slashedShares, curShares[i], 1000, err);
}
}

Expand Down
50 changes: 50 additions & 0 deletions src/test/integration/IntegrationChecks.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,56 @@ 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
124 changes: 124 additions & 0 deletions src/test/integration/tests/WithdrawalSlashing_Combined.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;

import "src/test/integration/IntegrationChecks.t.sol";
import "src/test/integration/users/User.t.sol";

contract Integration_WithdrawalSlashing_Combined is IntegrationCheckUtils {
// Helper struct to reduce stack variables
struct TestContext {
User staker;
User operator;
IStrategy[] strategies;
uint256[] tokenBalances;
uint40[] validators;
bytes32[] withdrawalRoots;
IDelegationManagerTypes.Withdrawal[] withdrawals;
uint64 slashedGwei;
}
function testFuzz_deposit_delegate_queueWithdrawal_slashBeacon_checkpoint(uint24 _random) public {
TestContext memory ctx;

// Initial setup and configuration
_configRand({
_randomSeed: _random,
_assetTypes: HOLDS_ETH,
_userTypes: DEFAULT
});

_upgradeEigenLayerContracts();

// Initialize actors and store in context
(ctx.staker, ctx.strategies, ctx.tokenBalances) = _newRandomStaker();
(ctx.operator,,) = _newRandomOperator();

// Handle validator setup and delegation
_handleValidatorSetupAndDelegation(ctx);

// Queue withdrawal before slashing
_handleQueueWithdrawal(ctx);

// Execute slashing while withdrawal is in queue
_handleBeaconChainSlashing(ctx);

// Start a checkpoint to reflect slashing
_handlePostSlashingCheckpoint(ctx);

// Complete the withdrawal and verify slashing was applied
_handleWithdrawalCompletion(ctx);
}

function _handleValidatorSetupAndDelegation(TestContext memory ctx) internal {
// Create and verify validators
(ctx.validators,) = ctx.staker.startValidators();
beaconChain.advanceEpoch_NoRewards();
ctx.staker.verifyWithdrawalCredentials(ctx.validators);

// Delegate to operator
ctx.staker.delegateTo(ctx.operator);
check_Delegation_State(
ctx.staker,
ctx.operator,
ctx.strategies,
_getStakerDepositShares(ctx.staker, ctx.strategies)
);
}

function _handleQueueWithdrawal(TestContext memory ctx) internal {
// Queue withdrawal by undelegating
ctx.withdrawals = ctx.staker.undelegate();
ctx.withdrawalRoots = _getWithdrawalHashes(ctx.withdrawals);

// Verify withdrawal state
assert_AllWithdrawalsPending(ctx.withdrawalRoots, "withdrawals should be pending");
assert_ValidWithdrawalHashes(ctx.withdrawals, ctx.withdrawalRoots, "withdrawal hashes should be valid");
}

function _handleBeaconChainSlashing(TestContext memory ctx) internal {
// Choose subset of validators to slash
uint40[] memory slashedValidators = _chooseSubset(ctx.validators);

// Execute slashing on beacon chain
ctx.slashedGwei = beaconChain.slashValidators(slashedValidators);
beaconChain.advanceEpoch_NoRewards();

console.log("Slashed amount (gwei)", ctx.slashedGwei);
}

function _handlePostSlashingCheckpoint(TestContext memory ctx) internal {
// Start and complete checkpoint to reflect slashing
ctx.staker.startCheckpoint();
ctx.staker.completeCheckpoint();
console.log("Active validator count after completing checkpoint:", ctx.staker.pod().activeValidatorCount());

}

function _handleWithdrawalCompletion(TestContext memory ctx) internal {
// Advance blocks to complete withdrawal
_rollBlocksForCompleteWithdrawals(ctx.withdrawals);

// Complete each withdrawal and verify state
for (uint i = 0; i < ctx.withdrawals.length; i++) {
uint[] memory expectedTokens = _calculateExpectedTokens(
ctx.withdrawals[i].strategies,
ctx.withdrawals[i].scaledShares
);

IERC20[] memory tokens = ctx.staker.completeWithdrawalAsTokens(ctx.withdrawals[i]);

check_Withdrawal_AsTokens_State(
ctx.staker,
ctx.operator,
ctx.withdrawals[i],
ctx.withdrawals[i].strategies,
ctx.withdrawals[i].scaledShares,
tokens,
expectedTokens
);
}

// Final checks
assert_HasNoDelegatableShares(ctx.staker, "staker should have no shares after withdrawal");
assert_NoWithdrawalsPending(ctx.withdrawalRoots, "all withdrawals should be completed");
}
}

0 comments on commit b2b3f7f

Please sign in to comment.