Slow Misty Iguana
Medium
HandleRewarding's
loop skips unfinalized blocks, but the reward is not redistributed after the skipped block is Finalized.
The unfinalized block in theHandleRewarding
function is skipped and no further processing is performed.
- Multiple blocks are voting at the same time.
- from current block to current - FinalitySigTimeout are in voting state (unfinalized),
- Some FPs did not vote in many blocks, resulting in current block to current - FinalitySigTimeout are in voting state (unfinalized).
- gov removes the unvoted fp through
ResumeFinalityProposal
and reply the block. - FP that has already voted cannot get rewards.
In some blocks, fp cannot get reward, which is unfair reward calculation.
HandleRewarding
is used to distribute reward, called when each block Endblock:
x/finality/abci.go/EndBlocker() -> HandleRewarding()
// HandleRewarding calls the reward to stakers if the block is finalized
func (k Keeper) HandleRewarding(ctx context.Context, targetHeight int64) {
......
// targetHeight = heightToExamine = HeaderInfo().Height - k.GetParams(ctx).FinalitySigTimeout
for height := nextHeightToReward; height <= uint64(targetHeight); height++ {
block, err := k.GetBlock(ctx, height)
if err != nil {
panic(err)
}
-> if !block.Finalized {
-> continue
}
k.rewardBTCStaking(ctx, height)
nextHeightToReward = height + 1
}
if nextHeightToReward != copiedNextHeightToReward {
k.SetNextHeightToReward(ctx, nextHeightToReward)
}
}
The problem is that unfinalized blocks are skipped in this loop. After thecontinue
statement,nextHeightToReward
will be set after this block and the reward for this block will no longer be distributed.
This will happen if from current block to current - FinalitySigTimeout are in voting state (unfinalized),
This happens when many FPs do not vote in many blocks, and blocks that are not finalized will appear during HandleRewarding
.
At this time, gov may need to use ResumeFinalityProposal
to reply to the block and remove the unvoted fp, but the fp that has already voted will not receive any reward because HandleRewarding
has already been executed.
for height := nextHeightToReward; height <= uint64(targetHeight); height++ {
block, err := k.GetBlock(ctx, height)
if err != nil {
panic(err)
}
if !block.Finalized {
- continue
+ break
}
k.rewardBTCStaking(ctx, height)
nextHeightToReward = height + 1
}