diff --git a/CHANGELOG.md b/CHANGELOG.md index ea9eff91..c7832d75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [#374](https://github.com/babylonlabs-io/babylon/pull/374) Fix non-consecutive finalization of the block in `TallyBlocks` function +- [#378](https://github.com/babylonlabs-io/babylon/pull/378) Fix give out rewards +with gaps of unfinalized blocks ## v1.0.0-rc2 diff --git a/x/finality/keeper/rewarding.go b/x/finality/keeper/rewarding.go index adcedad9..0a375552 100644 --- a/x/finality/keeper/rewarding.go +++ b/x/finality/keeper/rewarding.go @@ -9,6 +9,7 @@ import ( "github.com/babylonlabs-io/babylon/x/finality/types" ) +// HandleRewarding calls the reward to stakers if the block is finalized func (k Keeper) HandleRewarding(ctx context.Context, targetHeight int64) { // rewarding is executed in a range of [nextHeightToReward, heightToExamine] // this is we don't know when a block will be finalized and we need ensure @@ -30,7 +31,7 @@ func (k Keeper) HandleRewarding(ctx context.Context, targetHeight int64) { panic(err) } if !block.Finalized { - break + continue } k.rewardBTCStaking(ctx, height) nextHeightToReward = height + 1 diff --git a/x/finality/keeper/rewarding_test.go b/x/finality/keeper/rewarding_test.go index 741b4e68..32bfb3ce 100644 --- a/x/finality/keeper/rewarding_test.go +++ b/x/finality/keeper/rewarding_test.go @@ -3,6 +3,7 @@ package keeper_test import ( "math/rand" "testing" + "time" "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" @@ -57,8 +58,8 @@ func FuzzHandleRewarding(f *testing.F) { fKeeper.HandleRewarding(ctx, int64(targetHeight)) nextHeight := fKeeper.GetNextHeightToReward(ctx) - require.Equal(t, uint64(0), nextHeight, - "next height is not updated when no blocks finalized") + require.Zero(t, nextHeight, + "next height is not updated when no blocks finalized. Act: %d", nextHeight) // Second phase: Finalize some blocks firstBatchFinalized := datagen.RandomInt(r, 5) + 1 @@ -81,7 +82,7 @@ func FuzzHandleRewarding(f *testing.F) { nextHeight = fKeeper.GetNextHeightToReward(ctx) expectedNextHeight := activatedHeight + firstBatchFinalized require.Equal(t, expectedNextHeight, nextHeight, - "next height should be after first batch of finalized blocks") + "next height should be after first batch of finalized blocks. Exp: %d, Act: %d", expectedNextHeight, nextHeight) // Third phase: Finalize more blocks secondBatchFinalized := datagen.RandomInt(r, int(totalBlocks-firstBatchFinalized)) + 1 @@ -108,3 +109,59 @@ func FuzzHandleRewarding(f *testing.F) { "next height should be after second batch of finalized blocks") }) } + +func TestHandleRewardingWithGapsOfUnfinalizedBlocks(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + r := rand.New(rand.NewSource(time.Now().Unix())) + + // Setup keepers + bsKeeper := types.NewMockBTCStakingKeeper(ctrl) + iKeeper := types.NewMockIncentiveKeeper(ctrl) + cKeeper := types.NewMockCheckpointingKeeper(ctrl) + fKeeper, ctx := keepertest.FinalityKeeper(t, bsKeeper, iKeeper, cKeeper) + + fpPK, err := datagen.GenRandomBIP340PubKey(r) + require.NoError(t, err) + fKeeper.SetVotingPower(ctx, fpPK.MustMarshal(), 1, 1) + + // starts rewarding at block 1 + fKeeper.SetNextHeightToReward(ctx, 1) + + fKeeper.SetBlock(ctx, &types.IndexedBlock{ + Height: 1, + AppHash: datagen.GenRandomByteArray(r, 32), + Finalized: true, + }) + + fKeeper.SetBlock(ctx, &types.IndexedBlock{ + Height: 2, + AppHash: datagen.GenRandomByteArray(r, 32), + Finalized: false, + }) + + // adds the latest finalized block + fKeeper.SetBlock(ctx, &types.IndexedBlock{ + Height: 3, + AppHash: datagen.GenRandomByteArray(r, 32), + Finalized: true, + }) + dc := types.NewVotingPowerDistCache() + dc.AddFinalityProviderDistInfo(&types.FinalityProviderDistInfo{ + BtcPk: fpPK, + TotalBondedSat: 1, + }) + fKeeper.SetVotingPowerDistCache(ctx, 1, dc) + fKeeper.SetVotingPowerDistCache(ctx, 3, dc) + + iKeeper.EXPECT(). + RewardBTCStaking(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + Return(). + Times(2) // number of finalized blocks processed + + fKeeper.HandleRewarding(ctx, 3) + + actNextBlockToBeRewarded := fKeeper.GetNextHeightToReward(ctx) + require.Equal(t, uint64(4), actNextBlockToBeRewarded) +}