60125 sc high moving delegations from one validator to another validator will not be possible in exit case for validator 1
Description
Brief/Intro
Vulnerability Details
5
Step
if (
currentValidatorStatus == VALIDATOR_STATUS_EXITED ||
status == DelegationStatus.PENDING
) {
// get the completed periods of the previous validator
(, , , uint32 oldCompletedPeriods) = $
.protocolStakerContract
.getValidationPeriodDetails(currentValidator);
// decrease the effective stake of the previous validator
_updatePeriodEffectiveStake(
$,
currentValidator,
_tokenId,
oldCompletedPeriods + 2,
false // decrease
);
}function _delegate(StargateStorage storage $, uint256 _tokenId, address _validator) private {
// ensure token is not already delegated
DelegationStatus status = _getDelegationStatus($, _tokenId);
if (status == DelegationStatus.ACTIVE) {
revert TokenAlreadyDelegated(_tokenId);
}
// ensure validator is in valid state
(, , , , uint8 validatorStatus, ) = $.protocolStakerContract.getValidation(_validator); // e.g 2 for active
(, , uint32 validatorExitBlock, ) = $.protocolStakerContract.getValidationPeriodDetails(
_validator
); // e.g type(uint256).max for no exit
if (
(validatorStatus != VALIDATOR_STATUS_ACTIVE &&
validatorStatus != VALIDATOR_STATUS_QUEUED) ||
// if the validator has requested to exit, we cannot delegate to it
validatorExitBlock != type(uint32).max
) {
revert ValidatorNotActiveOrQueued(_validator);
}
// Tokens under matutiry period cannot be delegated
if ($.stargateNFTContract.isUnderMaturityPeriod(_tokenId)) {
revert TokenUnderMaturityPeriod(_tokenId);
}
// get the token details
DataTypes.Token memory token = $.stargateNFTContract.getToken(_tokenId);
if (token.levelId == 0) {
revert InvalidToken(_tokenId);
}
uint256 currentDelegationId = $.delegationIdByTokenId[_tokenId];
// If the token was previously exited or pending it means that the VET is still held in the protocol,
// so we need to withdraw it and deposit again for the new delegation
if (status == DelegationStatus.EXITED || status == DelegationStatus.PENDING) {
// get the current validator
(address currentValidator, , , ) = $.protocolStakerContract.getDelegation(
currentDelegationId
);
// withdraw the delegation
$.protocolStakerContract.withdrawDelegation(currentDelegationId);
// emit the event to signal that the delegation was withdrawn
emit DelegationWithdrawn(
_tokenId,
currentValidator,
currentDelegationId,
token.vetAmountStaked,
token.levelId
);
// get the validator status
(, , , , uint8 currentValidatorStatus, ) = $.protocolStakerContract.getValidation(
currentValidator
);
// if the delegation is pending or the validator is exited or unknown
// decrease the effective stake of the previous validator
if (
currentValidatorStatus == VALIDATOR_STATUS_EXITED ||
status == DelegationStatus.PENDING
) {
// get the completed periods of the previous validator
(, , , uint32 oldCompletedPeriods) = $
.protocolStakerContract
.getValidationPeriodDetails(currentValidator);
// decrease the effective stake of the previous validator
_updatePeriodEffectiveStake(
$,
currentValidator,
_tokenId,
oldCompletedPeriods + 2,
false // decrease
);
}
if (status == DelegationStatus.PENDING) {
// If the current delegation is pending, it means that the owner is changing the validator,
// without requesting to exit first (which is allowed since the exit is not active yet)
// so we emit an event to signal this action to the indexers
emit DelegationExitRequested(
_tokenId,
currentValidator,
currentDelegationId,
Clock.clock()
);
}
}
// ... rest of code omitted for brevity ...
}Impact Details
References
Proof of Concept
Previous60102 sc high exited delegator could keep claiming rewards stealing them from active delegators which would then lead to freeze of fundsNext60150 sc high off by one in claim window lets exited delegations harvest post exit rewards
Was this helpful?