Skip to content

Commit

Permalink
chore: Refactor to start from the last finalized height (#257)
Browse files Browse the repository at this point in the history
Closes #253.

The poller should start from the `lastFinalizedHeight` other than
`lastVotedHeight`. This is to deal with cases where the gap between the
two heights is large so that the delay of catching up could cause
jailing.

This change will not give up many rewards because rewards are not
assigned to late votes due to
[ADR](https://github.com/babylonlabs-io/pm/blob/main/adr/adr-039-finality-voting-rewarding.md).
  • Loading branch information
gitferry authored Jan 3, 2025
1 parent d2258c0 commit 419645b
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 27 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)

* [#251](https://github.com/babylonlabs-io/finality-provider/pull/251) chore: nlreturn lint
* [#252](https://github.com/babylonlabs-io/finality-provider/pull/252) feat: rm interceptors and use context
* [#253](https://github.com/babylonlabs-io/finality-provider/issues/253) Refactor to start from the last finalized height

## v0.14.2

Expand Down
38 changes: 16 additions & 22 deletions finality-provider/service/fp_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -743,10 +743,10 @@ func (fp *FinalityProviderInstance) TestSubmitFinalitySignatureAndExtractPrivKey
// - Gets finalityActivationHeight from chain
// - Gets lastFinalizedHeight from chain
// - Gets lastVotedHeight from local state
// - If fp.GetLastVotedHeight() is 0, sets lastVotedHeight = lastFinalizedHeight
// - Gets highestVotedHeight from chain
// - Sets lastVotedHeight = max(lastVotedHeight, highestVotedHeight)
// - Returns max(finalityActivationHeight, lastVotedHeight + 1)
// - Sets startHeight = max(lastVotedHeight, highestVotedHeight, lastFinalizedHeight) + 1
// - Returns max(startHeight, finalityActivationHeight) to ensure startHeight is not
// lower than the finality activation height
//
// This ensures that:
// 1. The FP will not vote for heights below the finality activation height
Expand All @@ -765,38 +765,32 @@ func (fp *FinalityProviderInstance) DetermineStartHeight() (uint64, error) {
return fp.cfg.PollerConfig.StaticChainScanningStartHeight, nil
}

lastFinalizedHeight, err := fp.latestFinalizedHeightWithRetry()
highestVotedHeight, err := fp.highestVotedHeightWithRetry()
if err != nil {
return 0, fmt.Errorf("failed to get the last finalized height: %w", err)
}

// determine an effective lastVotedHeight
var lastVotedHeight uint64
if fp.GetLastVotedHeight() == 0 {
lastVotedHeight = lastFinalizedHeight
} else {
lastVotedHeight = fp.GetLastVotedHeight()
return 0, fmt.Errorf("failed to get the highest voted height: %w", err)
}

highestVotedHeight, err := fp.highestVotedHeightWithRetry()
lastFinalizedHeight, err := fp.latestFinalizedHeightWithRetry()
if err != nil {
return 0, fmt.Errorf("failed to get the highest voted height: %w", err)
return 0, fmt.Errorf("failed to get the last finalized height: %w", err)
}

// TODO: if highestVotedHeight > lastVotedHeight, using highestVotedHeight could lead
// to issues when there are missed blocks between the gap due to bugs.
// A proper solution is to check if the fp has voted for each block within the gap
lastVotedHeight = max(lastVotedHeight, highestVotedHeight)
// determine start height to be the max height among local last voted height, highest voted height
// from Babylon, and the last finalized height
// NOTE: if highestVotedHeight is selected, it could lead issues when there are missed blocks between
// the gap due to bugs. A potential solution is to check if the fp has voted for each block within
// the gap. This issue is not critical if we can assume the votes are sent in the monotonically
// increasing order.
startHeight := max(fp.GetLastVotedHeight(), highestVotedHeight, lastFinalizedHeight) + 1

finalityActivationHeight, err := fp.getFinalityActivationHeightWithRetry()
if err != nil {
return 0, fmt.Errorf("failed to get finality activation height: %w", err)
}

// determine the final starting height
startHeight := max(finalityActivationHeight, lastVotedHeight+1)
// ensure start height is not lower than the finality activation height
startHeight = max(startHeight, finalityActivationHeight)

// log how start height is determined
fp.logger.Info("determined poller starting height",
zap.String("pk", fp.GetBtcPkHex()),
zap.Uint64("start_height", startHeight),
Expand Down
6 changes: 1 addition & 5 deletions finality-provider/service/fp_instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,7 @@ func FuzzDetermineStartHeight(f *testing.F) {
startHeight, err := fpIns.DetermineStartHeight()
require.NoError(t, err)

if lastVotedHeight == 0 {
require.Equal(t, startHeight, max(finalityActivationHeight, highestVotedHeight+1, lastFinalizedHeight+1))
} else {
require.Equal(t, startHeight, max(finalityActivationHeight, highestVotedHeight+1, lastVotedHeight+1))
}
require.Equal(t, startHeight, max(finalityActivationHeight, highestVotedHeight+1, lastFinalizedHeight+1, lastVotedHeight+1))
})
}

Expand Down

0 comments on commit 419645b

Please sign in to comment.