Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

slashing: revert timestamps delegation #861

Merged
merged 2 commits into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
9 changes: 5 additions & 4 deletions script/deploy/devnet/deploy_from_scratch.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -365,10 +365,11 @@ contract DeployFromScratch is Script, Test {
_verifyInitializationParams();

// Check DM and AM have same withdrawa/deallocation delay
require(
delegation.MIN_WITHDRAWAL_DELAY() == allocationManager.DEALLOCATION_DELAY(),
"DelegationManager and AllocationManager have different withdrawal/deallocation delays"
);
// TODO: Update after AllocationManager is converted to timestamps as well
// require(
// delegation.MIN_WITHDRAWAL_DELAY_BLOCKS() == allocationManager.DEALLOCATION_DELAY(),
// "DelegationManager and AllocationManager have different withdrawal/deallocation delays"
// );
require(
allocationManager.DEALLOCATION_DELAY() == 1 days
);
Expand Down
31 changes: 5 additions & 26 deletions src/contracts/core/DelegationManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -506,13 +506,15 @@ contract DelegationManager is
bytes32 withdrawalRoot = calculateWithdrawalRoot(withdrawal);
require(pendingWithdrawals[withdrawalRoot], WithdrawalNotQueued());

uint32 completableTimestamp = getCompletableTimestamp(withdrawal.startTimestamp);
uint32 completableBlock = withdrawal.startBlock + MIN_WITHDRAWAL_DELAY_BLOCKS;
require(completableBlock <= uint32(block.number), WithdrawalDelayNotElapsed());

// read delegated operator's maxMagnitudes at the earliest time that the withdrawal could be completed
// to convert the delegatedShares to shares factoring in slashing that occured during withdrawal delay
uint64[] memory maxMagnitudes = allocationManager.getMaxMagnitudesAtTimestamp({
operator: withdrawal.delegatedTo,
strategies: withdrawal.strategies,
timestamp: completableTimestamp
timestamp: completableBlock //TODO: update ALM to use blocks
});

for (uint256 i = 0; i < withdrawal.strategies.length; i++) {
Expand Down Expand Up @@ -672,7 +674,7 @@ contract DelegationManager is
delegatedTo: operator,
withdrawer: staker,
nonce: nonce,
startTimestamp: uint32(block.timestamp),
startBlock: uint32(block.number),
strategies: strategies,
scaledShares: scaledShares
});
Expand Down Expand Up @@ -826,29 +828,6 @@ contract DelegationManager is
return (strategies, shares);
}

/// @inheritdoc IDelegationManager
function getCompletableTimestamp(
uint32 startTimestamp
) public view returns (uint32 completableTimestamp) {
if (startTimestamp < LEGACY_WITHDRAWAL_CHECK_VALUE) {
// this is a legacy M2 withdrawal using blocknumbers.
// It would take 370+ years for the blockNumber to reach the LEGACY_WITHDRAWAL_CHECK_VALUE, so this is a safe check.
require(startTimestamp + LEGACY_MIN_WITHDRAWAL_DELAY_BLOCKS <= block.number, WithdrawalDelayNotElapsed());
// sourcing the magnitudes from time=0, will always give us WAD, which doesn't factor in slashing
completableTimestamp = 0;
} else {
// this is a post Slashing release withdrawal using timestamps
require(startTimestamp + MIN_WITHDRAWAL_DELAY <= block.timestamp, WithdrawalDelayNotElapsed());
// source magnitudes from the time of completability
completableTimestamp = startTimestamp + MIN_WITHDRAWAL_DELAY;
}
}

/// @inheritdoc IDelegationManager
function minWithdrawalDelayBlocks() public view returns (uint256) {
return LEGACY_MIN_WITHDRAWAL_DELAY_BLOCKS;
}

/// @inheritdoc IDelegationManager
function calculateWithdrawalRoot(
Withdrawal memory withdrawal
Expand Down
18 changes: 4 additions & 14 deletions src/contracts/core/DelegationManagerStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,6 @@ abstract contract DelegationManagerStorage is IDelegationManager {
/// @dev Index for flag that pauses completing existing withdrawals when set.
uint8 internal constant PAUSED_EXIT_WITHDRAWAL_QUEUE = 2;

/// @notice The minimum number of blocks to complete a withdrawal of a strategy. 50400 * 12 seconds = 1 week
uint256 public constant LEGACY_MIN_WITHDRAWAL_DELAY_BLOCKS = 50_400;

/// @notice Check against the blockNumber/timestamps to determine if the withdrawal is a legacy or slashing withdrawl.
// Legacy withdrawals use block numbers. We expect block number 1 billion in ~370 years
// Slashing withdrawals use timestamps. The UTC timestmap as of Jan 1st, 2024 is 1_704_067_200 . Thus, when deployed, all
// withdrawal timestamps are AFTER the `LEGACY_WITHDRAWAL_CHECK_VALUE` timestamp.
// This below value is the UTC timestamp at Sunday, September 9th, 2001.
uint32 public constant LEGACY_WITHDRAWAL_CHECK_VALUE = 1_000_000_000;

/// @notice Canonical, virtual beacon chain ETH strategy
IStrategy public constant beaconChainETHStrategy = IStrategy(0xbeaC0eeEeeeeEEeEeEEEEeeEEeEeeeEeeEEBEaC0);

Expand All @@ -63,8 +53,8 @@ abstract contract DelegationManagerStorage is IDelegationManager {
/// @notice The AllocationManager contract for EigenLayer
IAllocationManager public immutable allocationManager;

/// @notice Minimum withdrawal delay in seconds until a queued withdrawal can be completed.
uint32 public immutable MIN_WITHDRAWAL_DELAY;
/// @notice Minimum withdrawal delay in blocks until a queued withdrawal can be completed.
uint32 public immutable MIN_WITHDRAWAL_DELAY_BLOCKS;

// Mutatables

Expand Down Expand Up @@ -121,13 +111,13 @@ abstract contract DelegationManagerStorage is IDelegationManager {
IStrategyManager _strategyManager,
IEigenPodManager _eigenPodManager,
IAllocationManager _allocationManager,
uint32 _MIN_WITHDRAWAL_DELAY
uint32 _MIN_WITHDRAWAL_DELAY_BLOCKS
) {
avsDirectory = _avsDirectory;
strategyManager = _strategyManager;
eigenPodManager = _eigenPodManager;
allocationManager = _allocationManager;
MIN_WITHDRAWAL_DELAY = _MIN_WITHDRAWAL_DELAY;
MIN_WITHDRAWAL_DELAY_BLOCKS = _MIN_WITHDRAWAL_DELAY_BLOCKS;
}

/**
Expand Down
26 changes: 8 additions & 18 deletions src/contracts/interfaces/IDelegationManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ interface IDelegationManagerErrors {

/// @dev Thrown when attempting to execute an action that was not queued.
error WithdrawalNotQueued();
/// @dev Thrown when provided delay exceeds maximum.
error AllocationDelaySet();
/// @dev Thrown when caller cannot undelegate on behalf of a staker.
error CallerCannotUndelegate();
/// @dev Thrown when two array parameters have mismatching lengths.
Expand All @@ -40,9 +38,6 @@ interface IDelegationManagerErrors {
/// @dev Thrown when caller is neither the StrategyManager or EigenPodManager contract.
error OnlyStrategyManagerOrEigenPodManager();

/// @dev Thrown when provided delay exceeds maximum.
error WithdrawalDelayExceedsMax();

/// Slashing

/// @dev Thrown when an operator has been fully slashed(maxMagnitude is 0) for a strategy.
Expand Down Expand Up @@ -133,11 +128,8 @@ interface IDelegationManagerTypes {
address withdrawer;
// Nonce used to guarantee that otherwise identical withdrawals have unique hashes
uint256 nonce;
// Timestamp when the Withdrawal was created.
// NOTE this used to be `startBlock` but changedto timestamps in the Slashing release. This has no effect
// on the hash of this struct but we do need to know when to handle blocknumbers vs timestamps depending on
// if the withdrawal was created before or after the Slashing release.
uint32 startTimestamp;
// Blocknumber when the Withdrawal was created.
uint32 startBlock;
// Array of strategies that the Withdrawal contains
IStrategy[] strategies;
// Array containing the amount of staker's scaledShares for withdrawal in each Strategy in the `strategies` array
Expand Down Expand Up @@ -542,14 +534,12 @@ interface IDelegationManager is ISignatureUtils, IDelegationManagerErrors, IDele
address staker
) external view returns (IStrategy[] memory, uint256[] memory);

/// @notice Returns a completable timestamp given a start timestamp for a withdrawal
/// @dev check whether the withdrawal delay has elapsed (handles both legacy and post-slashing-release withdrawals) and returns the completable timestamp
function getCompletableTimestamp(
uint32 startTimestamp
) external view returns (uint32 completableTimestamp);

/// @notice Return the M2 minimum withdrawal delay in blocks for backwards compatability
function minWithdrawalDelayBlocks() external view returns (uint256);
/**
* @notice Returns the minimum withdrawal delay in blocks to pass for withdrawals queued to be completable.
* Also applies to legacy withdrawals so any withdrawals not completed prior to the slashing upgrade will be subject
* to this longer delay.
*/
function MIN_WITHDRAWAL_DELAY_BLOCKS() external view returns (uint32);

/// @notice Returns the keccak256 hash of `withdrawal`.
function calculateWithdrawalRoot(
Expand Down
2 changes: 1 addition & 1 deletion src/test/DepositWithdraw.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ contract DepositWithdrawTests is EigenLayerTestHelper {
withdrawer: withdrawer,
nonce: delegation.cumulativeWithdrawalsQueued(staker),
delegatedTo: delegation.delegatedTo(staker),
startTimestamp: uint32(block.timestamp),
startBlock: uint32(block.number),
scaledShares: shareAmounts
});

Expand Down
10 changes: 5 additions & 5 deletions src/test/DevnetLifecycle.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ contract Devnet_Lifecycle_Test is Test {
cheats.prank(operator);
delegationManager.registerAsOperator(operatorDetails, 1, emptyStringForMetadataURI);
// Warp passed configuration delay
cheats.warp(block.timestamp + delegationManager.MIN_WITHDRAWAL_DELAY());
cheats.roll(block.number + delegationManager.MIN_WITHDRAWAL_DELAY_BLOCKS());

// Validate storage
assertTrue(delegationManager.isOperator(operator));
Expand Down Expand Up @@ -202,10 +202,10 @@ contract Devnet_Lifecycle_Test is Test {
IAllocationManagerTypes.MagnitudeInfo[] memory infos = allocationManager.getAllocationInfo(operator, wethStrategy, _getOperatorSetsArray());
assertEq(infos[0].currentMagnitude, 0);
assertEq(infos[0].pendingDiff, int128(uint128(magnitudeToSet)));
assertEq(infos[0].effectTimestamp, block.timestamp + 1);
assertEq(infos[0].effectTimestamp, block.number + 1);

// Warp to effect timestamp
cheats.warp(block.timestamp + 1);
cheats.roll(block.number + 1);

// Check allocation
infos = allocationManager.getAllocationInfo(operator, wethStrategy, _getOperatorSetsArray());
Expand Down Expand Up @@ -253,7 +253,7 @@ contract Devnet_Lifecycle_Test is Test {
delegatedTo: operator,
withdrawer: staker,
nonce: delegationManager.cumulativeWithdrawalsQueued(staker),
startTimestamp: uint32(block.timestamp),
startBlock: uint32(block.number),
strategies: strategies,
scaledShares: scaledShares
});
Expand All @@ -264,7 +264,7 @@ contract Devnet_Lifecycle_Test is Test {
delegationManager.queueWithdrawals(queuedWithdrawals);

// Roll passed withdrawal delay
cheats.warp(block.timestamp + delegationManager.MIN_WITHDRAWAL_DELAY());
cheats.roll(block.number + delegationManager.MIN_WITHDRAWAL_DELAY_BLOCKS());

// Complete withdrawal
IERC20[] memory tokens = new IERC20[](1);
Expand Down
6 changes: 3 additions & 3 deletions src/test/EigenLayerTestHelper.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ contract EigenLayerTestHelper is EigenLayerDeployer {
withdrawer: withdrawer,
nonce: delegation.cumulativeWithdrawalsQueued(staker),
delegatedTo: delegation.delegatedTo(staker),
startTimestamp: uint32(block.timestamp)
startBlock: uint32(block.number)
});

{
Expand Down Expand Up @@ -402,7 +402,7 @@ contract EigenLayerTestHelper is EigenLayerDeployer {
staker: depositor,
withdrawer: withdrawer,
nonce: nonce,
startTimestamp: withdrawalStartTimestamp,
startBlock: withdrawalStartTimestamp,
delegatedTo: delegatedTo
});

Expand Down Expand Up @@ -452,7 +452,7 @@ contract EigenLayerTestHelper is EigenLayerDeployer {
staker: depositor,
withdrawer: withdrawer,
nonce: nonce,
startTimestamp: withdrawalStartTimestamp,
startBlock: withdrawalStartTimestamp,
delegatedTo: delegatedTo,
scaledShares: shareAmounts
});
Expand Down
6 changes: 2 additions & 4 deletions src/test/integration/IntegrationDeployer.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -367,11 +367,9 @@ abstract contract IntegrationDeployer is ExistingDeploymentParser {

// Create time machine and beacon chain. Set block time to beacon chain genesis time
// TODO: update if needed to sane timestamp
// cheats.warp(GENESIS_TIME_LOCAL);
cheats.warp(delegationManager.LEGACY_WITHDRAWAL_CHECK_VALUE());
cheats.warp(GENESIS_TIME_LOCAL);
timeMachine = new TimeMachine();
// beaconChain = new BeaconChainMock(eigenPodManager, GENESIS_TIME_LOCAL);
beaconChain = new BeaconChainMock(eigenPodManager, delegationManager.LEGACY_WITHDRAWAL_CHECK_VALUE());
beaconChain = new BeaconChainMock(eigenPodManager, GENESIS_TIME_LOCAL);
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/test/integration/users/User.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ contract User is PrintUtils {
delegatedTo: operator,
withdrawer: withdrawer,
nonce: nonce,
startTimestamp: uint32(block.timestamp),
startBlock: uint32(block.number),
strategies: strategies,
scaledShares: shares
});
Expand Down Expand Up @@ -486,7 +486,7 @@ contract User is PrintUtils {
delegatedTo: delegatedTo,
withdrawer: staker,
nonce: (nonce + i),
startTimestamp: uint32(block.timestamp),
startBlock: uint32(block.number),
strategies: singleStrategy,
scaledShares: singleShares
});
Expand Down
Loading
Loading