55632 bc critical delegation submitted in the same period before a validator exit will be permanently frozen
Description
Brief/Intro
Vulnerability Details
1
Delegation added in Period 5
current, err := val.CurrentIteration(currentBlock)
if err != nil {
return nil, err
}
delegationID, err := s.delegationService.Add(validator, current+1, stake, multiplier)
if err != nil {
logger.Info("failed to add delegation", "validator", validator, "error", err)
return nil, err
}2
Validator signals exit in Period 5
func (s *Service) SignalExit(validator thor.Address, currentBlock uint32, minBlock uint32, maxTry int) error {
...
current, err := validation.CurrentIteration(currentBlock)
if err != nil {
return err
}
validation.ExitBlock = &exitBlock
// validator is going to exit after current iteration
validation.CompletedPeriods = current
return s.repo.updateValidation(validator, validation)
}3
Exit processed at start of Period 6
func (a *Aggregation) exit() *globalstats.Exit {
// Return these values to modify contract totals
exit := globalstats.Exit{
ExitedTVL: a.Locked.Clone(),
QueuedDecrease: a.Pending.VET,
}
// Reset the aggregation
a.Exiting = &stakes.WeightedStake{}
a.Locked = &stakes.WeightedStake{}
a.Pending = &stakes.WeightedStake{}
return &exit
}4
Delegator withdraws after exit — stuck due to underflow
func (d *Delegation) Started(val *validation.Validation, currentBlock uint32) (bool, error) {
if val.Status == validation.StatusQueued || val.Status == validation.StatusUnknown {
return false, nil // Delegation cannot start if the validation is not active
}
currentStakingPeriod, err := val.CurrentIteration(currentBlock)
if err != nil {
return false, err
}
return currentStakingPeriod >= d.FirstIteration, nil
}func (s *Staker) WithdrawDelegation(
delegationID *big.Int,
currentBlock uint32,
) (uint64, error) {
...
if !started {
weightedStake := stakes.NewWeightedStakeWithMultiplier(withdrawableStake, del.Multiplier)
if err = s.aggregationService.SubPendingVet(del.Validation, weightedStake); err != nil {
return 0, err
}
if err = s.globalStatsService.RemoveQueued(withdrawableStake); err != nil {
return 0, err
}
} else {
...
}Impact Details
Proof of Concept
Previous55711 sc insight redundant gas charge in native addvalidation function leads to unnecessary gas costsNext55524 bc insight null body transaction submission crashes rpc handler
Was this helpful?