Slashed finality provider retaining voting power
Description
Finality providers are ranked based on their staked Satoshi, and only those within maxActiveFps
are eligible to participate in voting. In the ProcessAllPowerDistUpdateEvents
function (source here↗), an FP that has been slashed is intended to be excluded from newDc
, ensuring that it no longer contributes to voting power. However, if an FP is both slashed and receives new delegations within the same block, it may still be added back to newDc
through another code path (source here↗), effectively restoring its voting power despite the slashing event.
The issue arises because slashed FPs are correctly skipped in one part of the function but can still be included in newDc
through another logic path. Additionally, when the FP is re-added, its IsJailed
flag is set to false
, which may not directly impact its slash status when calling CreateBTCDelegation
(source here↗), but it allows the FP to continue influencing finality votes despite having been slashed.
Impact
A slashed FP that should have been removed from the VotingPowerDistCache
can retain its voting power and continue participating in finality votes. This weakens the integrity of the slashing mechanism and could lead to security risks, as validators who should be penalized may still exert influence over consensus.
Recommendations
Ensure that slashed FPs are consistently removed from newDc
, regardless of delegation events occurring in the same block. The logic at ProcessAllPowerDistUpdateEvents
↗ should be updated to verify whether the FP has been slashed before re-adding it to the active voting set. Additionally, a check should be implemented to prevent the IsJailed
flag from being reset incorrectly.
Remediation
This issue has been acknowledged by Babylon Labs, and fixes were implemented in the following commits:
This was remediated by ensuring that the NewFinalityProviderDistInfo
function correctly includes the slash and jail status when returning FP information, and by preventing issues that occur when Slash or Jail events are processed in the same block as an Active event.