diff --git a/CHANGELOG.md b/CHANGELOG.md index 696679d8..302772b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,8 @@ empty HD path to derive new key and use master private key. * [#154](https://github.com/babylonlabs-io/finality-provider/pull/154) Use sign schnorr instead of getting private key from EOTS manager * [#167](https://github.com/babylonlabs-io/finality-provider/pull/167) Remove last processed height * [#168](https://github.com/babylonlabs-io/finality-provider/pull/168) Remove key creation in `create-finality-provider` +* [#176](https://github.com/babylonlabs-io/finality-provider/pull/176) Refactor +determining start height based on [ADR-35](https://github.com/babylonlabs-io/pm/blob/main/adr/adr-035-slashing-protection.md) ### v0.12.1 diff --git a/clientcontroller/babylon.go b/clientcontroller/babylon.go index 855c7d19..7af65f6a 100644 --- a/clientcontroller/babylon.go +++ b/clientcontroller/babylon.go @@ -297,6 +297,17 @@ func (bc *BabylonController) QueryFinalityProviderVotingPower(fpPk *btcec.Public return res.VotingPower, nil } +// QueryFinalityProviderHighestVotedHeight queries the highest voted height of the given finality provider +func (bc *BabylonController) QueryFinalityProviderHighestVotedHeight(fpPk *btcec.PublicKey) (uint64, error) { + fpPubKey := bbntypes.NewBIP340PubKeyFromBTCPK(fpPk) + res, err := bc.bbnClient.QueryClient.FinalityProvider(fpPubKey.MarshalHex()) + if err != nil { + return 0, fmt.Errorf("failed to query highest voted height for finality provider %s: %w", fpPubKey.MarshalHex(), err) + } + + return uint64(res.FinalityProvider.HighestVotedHeight), nil +} + func (bc *BabylonController) QueryLatestFinalizedBlocks(count uint64) ([]*types.BlockInfo, error) { return bc.queryLatestBlocks(nil, count, finalitytypes.QueriedBlockStatus_FINALIZED, true) } diff --git a/clientcontroller/interface.go b/clientcontroller/interface.go index 6c1d1e2d..427b9b09 100644 --- a/clientcontroller/interface.go +++ b/clientcontroller/interface.go @@ -31,6 +31,9 @@ type ClientController interface { description []byte, ) (*types.TxResponse, error) + // EditFinalityProvider edits description and commission of a finality provider + EditFinalityProvider(fpPk *btcec.PublicKey, commission *math.LegacyDec, description []byte) (*btcstakingtypes.MsgEditFinalityProvider, error) + // CommitPubRandList commits a list of EOTS public randomness the consumer chain // it returns tx hash and error CommitPubRandList(fpPk *btcec.PublicKey, startHeight uint64, numPubRand uint64, commitment []byte, sig *schnorr.Signature) (*types.TxResponse, error) @@ -44,14 +47,18 @@ type ClientController interface { // UnjailFinalityProvider sends an unjail transaction to the consumer chain UnjailFinalityProvider(fpPk *btcec.PublicKey) (*types.TxResponse, error) + /* + The following methods are queries to the consumer chain + */ + // QueryFinalityProviderVotingPower queries the voting power of the finality provider at a given height QueryFinalityProviderVotingPower(fpPk *btcec.PublicKey, blockHeight uint64) (uint64, error) // QueryFinalityProviderSlashedOrJailed queries if the finality provider is slashed or jailed QueryFinalityProviderSlashedOrJailed(fpPk *btcec.PublicKey) (slashed bool, jailed bool, err error) - // EditFinalityProvider edits description and commission of a finality provider - EditFinalityProvider(fpPk *btcec.PublicKey, commission *math.LegacyDec, description []byte) (*btcstakingtypes.MsgEditFinalityProvider, error) + // QueryFinalityProviderHighestVotedHeight queries the highest voted height of the given finality provider + QueryFinalityProviderHighestVotedHeight(fpPk *btcec.PublicKey) (uint64, error) // QueryLatestFinalizedBlocks returns the latest finalized blocks QueryLatestFinalizedBlocks(count uint64) ([]*types.BlockInfo, error) diff --git a/finality-provider/service/app_test.go b/finality-provider/service/app_test.go index 41fba205..384c8571 100644 --- a/finality-provider/service/app_test.go +++ b/finality-provider/service/app_test.go @@ -186,6 +186,7 @@ func FuzzSyncFinalityProviderStatus(f *testing.F) { mockClientController.EXPECT().QueryActivatedHeight().Return(currentHeight, nil).AnyTimes() mockClientController.EXPECT().QueryFinalityProviderVotingPower(gomock.Any(), gomock.Any()).Return(uint64(2), nil).AnyTimes() } + mockClientController.EXPECT().QueryFinalityProviderHighestVotedHeight(gomock.Any()).Return(uint64(0), nil).AnyTimes() app, err := service.NewFinalityProviderApp(&fpCfg, mockClientController, em, fpdb, logger) require.NoError(t, err) @@ -266,6 +267,7 @@ func FuzzUnjailFinalityProvider(f *testing.F) { mockClientController.EXPECT().QueryFinalityProviderVotingPower(gomock.Any(), gomock.Any()).Return(uint64(0), nil).AnyTimes() mockClientController.EXPECT().QueryActivatedHeight().Return(uint64(1), nil).AnyTimes() mockClientController.EXPECT().QueryFinalityProviderSlashedOrJailed(gomock.Any()).Return(false, false, nil).AnyTimes() + mockClientController.EXPECT().QueryFinalityProviderHighestVotedHeight(gomock.Any()).Return(uint64(0), nil).AnyTimes() app, err := service.NewFinalityProviderApp(&fpCfg, mockClientController, em, fpdb, logger) require.NoError(t, err) diff --git a/finality-provider/service/fp_instance.go b/finality-provider/service/fp_instance.go index a4ec4fa6..f42ceab3 100644 --- a/finality-provider/service/fp_instance.go +++ b/finality-provider/service/fp_instance.go @@ -44,8 +44,6 @@ type FinalityProviderInstance struct { criticalErrChan chan<- *CriticalError isStarted *atomic.Bool - inSync *atomic.Bool - isLagging *atomic.Bool wg sync.WaitGroup quit chan struct{} @@ -97,8 +95,6 @@ func newFinalityProviderInstanceFromStore( cfg: cfg, logger: logger, isStarted: atomic.NewBool(false), - inSync: atomic.NewBool(false), - isLagging: atomic.NewBool(false), criticalErrChan: errChan, passphrase: passphrase, em: em, @@ -118,7 +114,7 @@ func (fp *FinalityProviderInstance) Start() error { fp.logger.Info("Starting finality-provider instance", zap.String("pk", fp.GetBtcPkHex())) - startHeight, err := fp.getPollerStartingHeight() + startHeight, err := fp.DetermineStartHeight() if err != nil { return fmt.Errorf("failed to get the start height: %w", err) } @@ -736,43 +732,75 @@ func (fp *FinalityProviderInstance) TestSubmitFinalitySignatureAndExtractPrivKey return res, privKey, nil } -// getPollerStartingHeight gets the starting height of the poller with -// max(lastVotedHeight+1, lastFinalizedHeight+1, params.FinalityActivationHeight) -// this ensures that: -// (1) the fp will not vote for a height lower than params.FinalityActivationHeight -// (2) the fp will not miss for any non-finalized blocks -// (3) the fp will not process any blocks that have been already voted -// Note: if the fp starting from the last finalized height with a gap to the last -// processed height, the fp might miss some rewards due to not sending the votes -// depending on the consumer chain's reward distribution mechanism -// TODO: provide an option to start from the last processed height in case -// the consumer chain distributes rewards for late voters -func (fp *FinalityProviderInstance) getPollerStartingHeight() (uint64, error) { +// DetermineStartHeight determines start height for block processing by: +// +// If AutoChainScanningMode is disabled: +// - Returns StaticChainScanningStartHeight from config +// +// If AutoChainScanningMode is enabled: +// - 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) +// +// This ensures that: +// 1. The FP will not vote for heights below the finality activation height +// 2. The FP will resume from its last voting position or the chain's last finalized height +// 3. The FP will not process blocks it has already voted on +// +// Note: Starting from lastFinalizedHeight when there's a gap to the last processed height +// may result in missed rewards, depending on the consumer chain's reward distribution mechanism. +func (fp *FinalityProviderInstance) DetermineStartHeight() (uint64, error) { + // start from a height from config if AutoChainScanningMode is disabled if !fp.cfg.PollerConfig.AutoChainScanningMode { + fp.logger.Info("using static chain scanning mode", + zap.String("pk", fp.GetBtcPkHex()), + zap.Uint64("start_height", fp.cfg.PollerConfig.StaticChainScanningStartHeight)) return fp.cfg.PollerConfig.StaticChainScanningStartHeight, nil } - // TODO: query last voted height and update local height - finalityActivationHeight, err := fp.getFinalityActivationHeightWithRetry() + lastFinalizedHeight, err := fp.latestFinalizedHeightWithRetry() if err != nil { - return 0, fmt.Errorf("failed to get finality activation height: %w", err) + return 0, fmt.Errorf("failed to get the last finalized height: %w", err) } - // start from finality activation height - startHeight := finalityActivationHeight + // determine an effective lastVotedHeight + var lastVotedHeight uint64 + if fp.GetLastVotedHeight() == 0 { + lastVotedHeight = lastFinalizedHeight + } else { + lastVotedHeight = fp.GetLastVotedHeight() + } - latestFinalisedBlocks, err := fp.latestFinalizedBlocksWithRetry(1) + highestVotedHeight, err := fp.highestVotedHeightWithRetry() if err != nil { - return 0, fmt.Errorf("failed to get the last finalized block: %w", err) + return 0, fmt.Errorf("failed to get the highest voted height: %w", err) } - // if we have finalized blocks, consider the height after the latest finalized block - if len(latestFinalisedBlocks) > 0 { - startHeight = max(startHeight, latestFinalisedBlocks[0].Height+1) + // 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) + + finalityActivationHeight, err := fp.getFinalityActivationHeightWithRetry() + if err != nil { + return 0, fmt.Errorf("failed to get finality activation height: %w", err) } - // consider the height after the last voted height - startHeight = max(startHeight, fp.GetLastVotedHeight()+1) + // determine the final starting height + startHeight := max(finalityActivationHeight, lastVotedHeight+1) + + // log how start height is determined + fp.logger.Info("determined poller starting height", + zap.String("pk", fp.GetBtcPkHex()), + zap.Uint64("start_height", startHeight), + zap.Uint64("finality_activation_height", finalityActivationHeight), + zap.Uint64("last_voted_height", fp.GetLastVotedHeight()), + zap.Uint64("last_finalized_height", lastFinalizedHeight), + zap.Uint64("highest_voted_height", highestVotedHeight)) return startHeight, nil } @@ -821,26 +849,54 @@ func (fp *FinalityProviderInstance) lastCommittedPublicRandWithRetry(count uint6 return response, nil } -func (fp *FinalityProviderInstance) latestFinalizedBlocksWithRetry(count uint64) ([]*types.BlockInfo, error) { - var response []*types.BlockInfo +func (fp *FinalityProviderInstance) latestFinalizedHeightWithRetry() (uint64, error) { + var height uint64 if err := retry.Do(func() error { - latestFinalisedBlock, err := fp.cc.QueryLatestFinalizedBlocks(count) + blocks, err := fp.cc.QueryLatestFinalizedBlocks(1) if err != nil { return err } - response = latestFinalisedBlock + if len(blocks) == 0 { + // no finalized block yet + return nil + } + height = blocks[0].Height return nil }, RtyAtt, RtyDel, RtyErr, retry.OnRetry(func(n uint, err error) { fp.logger.Debug( - "failed to query babylon for the latest finalised blocks", + "failed to query babylon for the latest finalised height", zap.Uint("attempt", n+1), zap.Uint("max_attempts", RtyAttNum), zap.Error(err), ) })); err != nil { - return nil, err + return 0, err } - return response, nil + + return height, nil +} + +func (fp *FinalityProviderInstance) highestVotedHeightWithRetry() (uint64, error) { + var height uint64 + if err := retry.Do(func() error { + h, err := fp.cc.QueryFinalityProviderHighestVotedHeight(fp.GetBtcPk()) + if err != nil { + return err + } + height = h + return nil + }, RtyAtt, RtyDel, RtyErr, retry.OnRetry(func(n uint, err error) { + fp.logger.Debug( + "failed to query babylon for the highest voted height", + zap.Uint("attempt", n+1), + zap.Uint("max_attempts", RtyAttNum), + zap.Error(err), + ) + })); err != nil { + return 0, err + } + + return height, nil } func (fp *FinalityProviderInstance) getFinalityActivationHeightWithRetry() (uint64, error) { diff --git a/finality-provider/service/fp_instance_test.go b/finality-provider/service/fp_instance_test.go index 113cb626..34d24814 100644 --- a/finality-provider/service/fp_instance_test.go +++ b/finality-provider/service/fp_instance_test.go @@ -36,7 +36,7 @@ func FuzzCommitPubRandList(f *testing.F) { mockClientController := testutil.PrepareMockedClientController(t, r, randomStartingHeight, currentHeight, 0) mockClientController.EXPECT().QueryFinalityProviderVotingPower(gomock.Any(), gomock.Any()). Return(uint64(0), nil).AnyTimes() - _, fpIns, cleanUp := startFinalityProviderAppWithRegisteredFp(t, r, mockClientController, randomStartingHeight) + _, fpIns, cleanUp := startFinalityProviderAppWithRegisteredFp(t, r, mockClientController, true, randomStartingHeight) defer cleanUp() expectedTxHash := testutil.GenRandomHexStr(r, 32) @@ -60,7 +60,7 @@ func FuzzSubmitFinalitySigs(f *testing.F) { startingBlock := &types.BlockInfo{Height: randomStartingHeight, Hash: testutil.GenRandomByteArray(r, 32)} mockClientController := testutil.PrepareMockedClientController(t, r, randomStartingHeight, currentHeight, 0) mockClientController.EXPECT().QueryLatestFinalizedBlocks(gomock.Any()).Return(nil, nil).AnyTimes() - _, fpIns, cleanUp := startFinalityProviderAppWithRegisteredFp(t, r, mockClientController, randomStartingHeight) + _, fpIns, cleanUp := startFinalityProviderAppWithRegisteredFp(t, r, mockClientController, true, randomStartingHeight) defer cleanUp() // commit pub rand @@ -99,7 +99,47 @@ func FuzzSubmitFinalitySigs(f *testing.F) { }) } -func startFinalityProviderAppWithRegisteredFp(t *testing.T, r *rand.Rand, cc clientcontroller.ClientController, startingHeight uint64) (*service.FinalityProviderApp, *service.FinalityProviderInstance, func()) { +func FuzzDetermineStartHeight(f *testing.F) { + testutil.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + // generate random heights + finalityActivationHeight := uint64(r.Int63n(1000) + 1) + lastVotedHeight := uint64(r.Int63n(1000)) + highestVotedHeight := uint64(r.Int63n(1000)) + lastFinalizedHeight := uint64(r.Int63n(1000) + 1) + + randomStartingHeight := uint64(r.Int63n(100) + 1) + currentHeight := randomStartingHeight + uint64(r.Int63n(10)+2) + mockClientController := testutil.PrepareMockedClientController(t, r, randomStartingHeight, currentHeight, finalityActivationHeight) + + // setup mocks + mockClientController.EXPECT(). + QueryFinalityProviderHighestVotedHeight(gomock.Any()). + Return(highestVotedHeight, nil). + AnyTimes() + finalizedBlocks := []*types.BlockInfo{{ + Height: lastFinalizedHeight, + }} + mockClientController.EXPECT().QueryLatestFinalizedBlocks(uint64(1)).Return(finalizedBlocks, nil).AnyTimes() + + _, fpIns, cleanUp := startFinalityProviderAppWithRegisteredFp(t, r, mockClientController, false, randomStartingHeight) + defer cleanUp() + fpIns.MustUpdateStateAfterFinalitySigSubmission(lastVotedHeight) + + 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)) + } + }) +} + +func startFinalityProviderAppWithRegisteredFp(t *testing.T, r *rand.Rand, cc clientcontroller.ClientController, isStaticStartHeight bool, startingHeight uint64) (*service.FinalityProviderApp, *service.FinalityProviderInstance, func()) { logger := zap.NewNop() // create an EOTS manager eotsHomeDir := filepath.Join(t.TempDir(), "eots-home") @@ -113,7 +153,7 @@ func startFinalityProviderAppWithRegisteredFp(t *testing.T, r *rand.Rand, cc cli fpHomeDir := filepath.Join(t.TempDir(), "fp-home") fpCfg := config.DefaultConfigWithHome(fpHomeDir) fpCfg.NumPubRand = testutil.TestPubRandNum - fpCfg.PollerConfig.AutoChainScanningMode = false + fpCfg.PollerConfig.AutoChainScanningMode = !isStaticStartHeight fpCfg.PollerConfig.StaticChainScanningStartHeight = startingHeight db, err := fpCfg.DatabaseConfig.GetDBBackend() require.NoError(t, err) diff --git a/finality-provider/service/fp_manager_test.go b/finality-provider/service/fp_manager_test.go index 623378e7..787586a1 100644 --- a/finality-provider/service/fp_manager_test.go +++ b/finality-provider/service/fp_manager_test.go @@ -56,6 +56,7 @@ func FuzzStatusUpdate(f *testing.F) { mockClientController.EXPECT().QueryBestBlock().Return(currentBlockRes, nil).AnyTimes() mockClientController.EXPECT().QueryActivatedHeight().Return(uint64(1), nil).AnyTimes() mockClientController.EXPECT().QueryFinalityActivationBlockHeight().Return(uint64(0), nil).AnyTimes() + mockClientController.EXPECT().QueryFinalityProviderHighestVotedHeight(gomock.Any()).Return(uint64(0), nil).AnyTimes() mockClientController.EXPECT().QueryBlock(gomock.Any()).Return(currentBlockRes, nil).AnyTimes() mockClientController.EXPECT().QueryLastCommittedPublicRand(gomock.Any(), uint64(1)).Return(nil, nil).AnyTimes() diff --git a/go.mod b/go.mod index cca84974..1008e530 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( cosmossdk.io/errors v1.0.1 cosmossdk.io/math v1.4.0 github.com/avast/retry-go/v4 v4.5.1 - github.com/babylonlabs-io/babylon v0.17.1 + github.com/babylonlabs-io/babylon v0.9.3-0.20241128025442-457909d8c43c github.com/btcsuite/btcd v0.24.2 github.com/btcsuite/btcd/btcec/v2 v2.3.2 github.com/btcsuite/btcd/btcutil v1.1.6 diff --git a/go.sum b/go.sum index 40bcdb41..f53442fd 100644 --- a/go.sum +++ b/go.sum @@ -1427,8 +1427,8 @@ github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX github.com/aws/aws-sdk-go v1.44.312 h1:llrElfzeqG/YOLFFKjg1xNpZCFJ2xraIi3PqSuP+95k= github.com/aws/aws-sdk-go v1.44.312/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= -github.com/babylonlabs-io/babylon v0.17.1 h1:lyWGdR7B49qDw5pllLyTW/HAM5uQWXXPZefjFzy/Xy0= -github.com/babylonlabs-io/babylon v0.17.1/go.mod h1:sT+KG2U+M0tDMNZZ2L5CwlXX0OpagGEs56BiWXqaZFw= +github.com/babylonlabs-io/babylon v0.9.3-0.20241128025442-457909d8c43c h1:BfanV3d5TsQa/8xyj3mOlquW5ooRNMoK1Y8HDqcyAzk= +github.com/babylonlabs-io/babylon v0.9.3-0.20241128025442-457909d8c43c/go.mod h1:sT+KG2U+M0tDMNZZ2L5CwlXX0OpagGEs56BiWXqaZFw= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= diff --git a/itest/container/config.go b/itest/container/config.go index d2ddb5d2..ef62622b 100644 --- a/itest/container/config.go +++ b/itest/container/config.go @@ -1,8 +1,6 @@ package container import ( - "github.com/babylonlabs-io/finality-provider/testutil" - "github.com/stretchr/testify/require" "testing" ) @@ -20,10 +18,11 @@ const ( // NewImageConfig returns ImageConfig needed for running e2e test. func NewImageConfig(t *testing.T) ImageConfig { - babylondVersion, err := testutil.GetBabylonVersion() - require.NoError(t, err) + // TODO: currently use specific commit, should uncomment after having a new release + // babylondVersion, err := testutil.GetBabylonVersion() + // require.NoError(t, err) return ImageConfig{ BabylonRepository: dockerBabylondRepository, - BabylonVersion: babylondVersion, + BabylonVersion: "457909d8c43c8483655c2d3a3a01cd2190344fd4", } } diff --git a/testutil/mocks/babylon.go b/testutil/mocks/babylon.go index b4bc43fa..4dddee18 100644 --- a/testutil/mocks/babylon.go +++ b/testutil/mocks/babylon.go @@ -98,21 +98,6 @@ func (mr *MockClientControllerMockRecorder) QueryActivatedHeight() *gomock.Call return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryActivatedHeight", reflect.TypeOf((*MockClientController)(nil).QueryActivatedHeight)) } -// QueryFinalityActivationBlockHeight mocks base method. -func (m *MockClientController) QueryFinalityActivationBlockHeight() (uint64, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "QueryFinalityActivationBlockHeight") - ret0, _ := ret[0].(uint64) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// QueryFinalityActivationBlockHeight indicates an expected call of QueryFinalityActivationBlockHeight. -func (mr *MockClientControllerMockRecorder) QueryFinalityActivationBlockHeight() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryFinalityActivationBlockHeight", reflect.TypeOf((*MockClientController)(nil).QueryFinalityActivationBlockHeight)) -} - // QueryBestBlock mocks base method. func (m *MockClientController) QueryBestBlock() (*types1.BlockInfo, error) { m.ctrl.T.Helper() @@ -158,6 +143,36 @@ func (mr *MockClientControllerMockRecorder) QueryBlocks(startHeight, endHeight, return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryBlocks", reflect.TypeOf((*MockClientController)(nil).QueryBlocks), startHeight, endHeight, limit) } +// QueryFinalityActivationBlockHeight mocks base method. +func (m *MockClientController) QueryFinalityActivationBlockHeight() (uint64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "QueryFinalityActivationBlockHeight") + ret0, _ := ret[0].(uint64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// QueryFinalityActivationBlockHeight indicates an expected call of QueryFinalityActivationBlockHeight. +func (mr *MockClientControllerMockRecorder) QueryFinalityActivationBlockHeight() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryFinalityActivationBlockHeight", reflect.TypeOf((*MockClientController)(nil).QueryFinalityActivationBlockHeight)) +} + +// QueryFinalityProviderHighestVotedHeight mocks base method. +func (m *MockClientController) QueryFinalityProviderHighestVotedHeight(fpPk *btcec.PublicKey) (uint64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "QueryFinalityProviderHighestVotedHeight", fpPk) + ret0, _ := ret[0].(uint64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// QueryFinalityProviderHighestVotedHeight indicates an expected call of QueryFinalityProviderHighestVotedHeight. +func (mr *MockClientControllerMockRecorder) QueryFinalityProviderHighestVotedHeight(fpPk interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryFinalityProviderHighestVotedHeight", reflect.TypeOf((*MockClientController)(nil).QueryFinalityProviderHighestVotedHeight), fpPk) +} + // QueryFinalityProviderSlashedOrJailed mocks base method. func (m *MockClientController) QueryFinalityProviderSlashedOrJailed(fpPk *btcec.PublicKey) (bool, bool, error) { m.ctrl.T.Helper()