From 2633ed9659d19549b12a4eac74d0757846bc8164 Mon Sep 17 00:00:00 2001 From: Fangyu Gai Date: Thu, 5 Dec 2024 14:56:05 +0800 Subject: [PATCH 1/2] not block unjailing --- finality-provider/service/app.go | 56 ++++++------ finality-provider/service/app_test.go | 6 +- finality-provider/service/event_loops.go | 86 +++++++++++++++++++ finality-provider/service/fp_instance.go | 2 +- finality-provider/service/fp_store_adapter.go | 23 ----- finality-provider/service/rpcserver.go | 4 +- 6 files changed, 124 insertions(+), 53 deletions(-) diff --git a/finality-provider/service/app.go b/finality-provider/service/app.go index d2ac193..9105794 100644 --- a/finality-provider/service/app.go +++ b/finality-provider/service/app.go @@ -49,6 +49,7 @@ type FinalityProviderApp struct { metrics *metrics.FpMetrics createFinalityProviderRequestChan chan *CreateFinalityProviderRequest + unjailFinalityProviderRequestChan chan *UnjailFinalityProviderRequest criticalErrChan chan *CriticalError } @@ -114,6 +115,7 @@ func NewFinalityProviderApp( eotsManager: em, metrics: fpMetrics, quit: make(chan struct{}), + unjailFinalityProviderRequestChan: make(chan *UnjailFinalityProviderRequest), createFinalityProviderRequestChan: make(chan *CreateFinalityProviderRequest), criticalErrChan: make(chan *CriticalError), }, nil @@ -251,12 +253,13 @@ func (app *FinalityProviderApp) Start() error { app.startOnce.Do(func() { app.logger.Info("Starting FinalityProviderApp") - app.wg.Add(5) + app.wg.Add(6) go app.syncChainFpStatusLoop() - go app.registrationLoop() go app.metricsUpdateLoop() go app.monitorCriticalErr() go app.monitorStatusUpdate() + go app.registrationLoop() + go app.unjailFpLoop() }) return startErr @@ -374,34 +377,39 @@ func (app *FinalityProviderApp) CreateFinalityProvider( } // UnjailFinalityProvider sends a transaction to unjail a finality-provider -func (app *FinalityProviderApp) UnjailFinalityProvider(fpPk *bbntypes.BIP340PubKey) (string, error) { - _, err := app.fps.GetFinalityProvider(fpPk.MustToBTCPK()) - if err != nil { - return "", fmt.Errorf("failed to get finality provider from db: %w", err) +func (app *FinalityProviderApp) UnjailFinalityProvider(fpPk *bbntypes.BIP340PubKey) (*UnjailFinalityProviderResponse, error) { + // send request to the loop to avoid blocking the main thread + request := &UnjailFinalityProviderRequest{ + btcPubKey: fpPk, + errResponse: make(chan error, 1), + successResponse: make(chan *UnjailFinalityProviderResponse, 1), } - // Send unjail transaction - res, err := app.cc.UnjailFinalityProvider(fpPk.MustToBTCPK()) - if err != nil { - return "", fmt.Errorf("failed to send unjail transaction: %w", err) - } + app.unjailFinalityProviderRequestChan <- request - // Update finality-provider status in the local store - // set it to INACTIVE for now and it will be updated to - // ACTIVE if the fp has voting power - err = app.fps.SetFpStatus(fpPk.MustToBTCPK(), proto.FinalityProviderStatus_INACTIVE) - if err != nil { - return "", fmt.Errorf("failed to update finality-provider status after unjailing: %w", err) - } + select { + case err := <-request.errResponse: + return nil, err + case successResponse := <-request.successResponse: + _, err := app.fps.GetFinalityProvider(fpPk.MustToBTCPK()) + if err != nil { + return nil, fmt.Errorf("failed to get finality provider from db: %w", err) + } - app.metrics.RecordFpStatus(fpPk.MarshalHex(), proto.FinalityProviderStatus_INACTIVE) + // Update finality-provider status in the local store + // set it to INACTIVE for now and it will be updated to + // ACTIVE if the fp has voting power + err = app.fps.SetFpStatus(fpPk.MustToBTCPK(), proto.FinalityProviderStatus_INACTIVE) + if err != nil { + return nil, fmt.Errorf("failed to update finality-provider status after unjailing: %w", err) + } - app.logger.Info("successfully unjailed finality-provider", - zap.String("btc_pk", fpPk.MarshalHex()), - zap.String("txHash", res.TxHash), - ) + app.metrics.RecordFpStatus(fpPk.MarshalHex(), proto.FinalityProviderStatus_INACTIVE) - return res.TxHash, nil + return successResponse, nil + case <-app.quit: + return nil, fmt.Errorf("finality-provider app is shutting down") + } } func (app *FinalityProviderApp) CreatePop(fpAddress sdk.AccAddress, fpPk *bbntypes.BIP340PubKey, passphrase string) (*bstypes.ProofOfPossessionBTC, error) { diff --git a/finality-provider/service/app_test.go b/finality-provider/service/app_test.go index f45d910..78ab04e 100644 --- a/finality-provider/service/app_test.go +++ b/finality-provider/service/app_test.go @@ -214,7 +214,7 @@ func FuzzUnjailFinalityProvider(f *testing.F) { // set voting power to be positive so that the fp should eventually become ACTIVE 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().QueryFinalityProviderSlashedOrJailed(gomock.Any()).Return(false, true, nil).AnyTimes() mockClientController.EXPECT().QueryFinalityProviderHighestVotedHeight(gomock.Any()).Return(uint64(0), nil).AnyTimes() // Create fp app @@ -223,9 +223,9 @@ func FuzzUnjailFinalityProvider(f *testing.F) { expectedTxHash := datagen.GenRandomHexStr(r, 32) mockClientController.EXPECT().UnjailFinalityProvider(fpPk.MustToBTCPK()).Return(&types.TxResponse{TxHash: expectedTxHash}, nil) - txHash, err := app.UnjailFinalityProvider(fpPk) + res, err := app.UnjailFinalityProvider(fpPk) require.NoError(t, err) - require.Equal(t, expectedTxHash, txHash) + require.Equal(t, expectedTxHash, res.TxHash) fpInfo, err := app.GetFinalityProviderInfo(fpPk) require.NoError(t, err) require.Equal(t, proto.FinalityProviderStatus_INACTIVE.String(), fpInfo.GetStatus()) diff --git a/finality-provider/service/event_loops.go b/finality-provider/service/event_loops.go index abf5c7b..b0ce820 100644 --- a/finality-provider/service/event_loops.go +++ b/finality-provider/service/event_loops.go @@ -2,13 +2,48 @@ package service import ( "errors" + "fmt" "time" + sdkmath "cosmossdk.io/math" + bbntypes "github.com/babylonlabs-io/babylon/types" + btcstakingtypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "go.uber.org/zap" "github.com/babylonlabs-io/finality-provider/finality-provider/proto" ) +type CreateFinalityProviderRequest struct { + fpAddr sdk.AccAddress + btcPubKey *bbntypes.BIP340PubKey + pop *btcstakingtypes.ProofOfPossessionBTC + description *stakingtypes.Description + commission *sdkmath.LegacyDec + errResponse chan error + successResponse chan *RegisterFinalityProviderResponse +} + +type RegisterFinalityProviderResponse struct { + txHash string +} + +type CreateFinalityProviderResult struct { + FpInfo *proto.FinalityProviderInfo + TxHash string +} + +type UnjailFinalityProviderRequest struct { + btcPubKey *bbntypes.BIP340PubKey + errResponse chan error + successResponse chan *UnjailFinalityProviderResponse +} + +type UnjailFinalityProviderResponse struct { + TxHash string +} + // monitorStatusUpdate periodically check the status of the running finality provider and update // it accordingly. We update the status by querying the latest voting power and the slashed_height. // In particular, we perform the following status transitions (REGISTERED, ACTIVE, INACTIVE, SLASHED): @@ -201,6 +236,57 @@ func (app *FinalityProviderApp) registrationLoop() { } } +// event loop for unjailing fp +func (app *FinalityProviderApp) unjailFpLoop() { + defer app.wg.Done() + for { + select { + case req := <-app.unjailFinalityProviderRequestChan: + pkHex := req.btcPubKey.MarshalHex() + isSlashed, isJailed, err := app.cc.QueryFinalityProviderSlashedOrJailed(req.btcPubKey.MustToBTCPK()) + if err != nil { + req.errResponse <- fmt.Errorf("failed to query jailing status of the finality provider %s: %w", pkHex, err) + continue + } + if isSlashed { + req.errResponse <- fmt.Errorf("the finality provider %s is already slashed", pkHex) + continue + } + if !isJailed { + req.errResponse <- fmt.Errorf("the finality provider %s is not jailed", pkHex) + continue + } + + res, err := app.cc.UnjailFinalityProvider( + req.btcPubKey.MustToBTCPK(), + ) + + if err != nil { + app.logger.Error( + "failed to unjail finality-provider", + zap.String("pk", req.btcPubKey.MarshalHex()), + zap.Error(err), + ) + req.errResponse <- err + continue + } + + app.logger.Info( + "successfully unjailed finality-provider on babylon", + zap.String("btc_pk", req.btcPubKey.MarshalHex()), + zap.String("txHash", res.TxHash), + ) + + req.successResponse <- &UnjailFinalityProviderResponse{ + TxHash: res.TxHash, + } + case <-app.quit: + app.logger.Info("exiting unjailing fp loop") + return + } + } +} + // event loop for metrics update func (app *FinalityProviderApp) metricsUpdateLoop() { defer app.wg.Done() diff --git a/finality-provider/service/fp_instance.go b/finality-provider/service/fp_instance.go index 6b7883c..45fee92 100644 --- a/finality-provider/service/fp_instance.go +++ b/finality-provider/service/fp_instance.go @@ -152,7 +152,7 @@ func (fp *FinalityProviderInstance) Stop() error { close(fp.quit) fp.wg.Wait() - fp.logger.Info("the finality-provider instance %s is successfully stopped", zap.String("pk", fp.GetBtcPkHex())) + fp.logger.Info("the finality-provider instance is successfully stopped", zap.String("pk", fp.GetBtcPkHex())) return nil } diff --git a/finality-provider/service/fp_store_adapter.go b/finality-provider/service/fp_store_adapter.go index 18290af..da1c019 100644 --- a/finality-provider/service/fp_store_adapter.go +++ b/finality-provider/service/fp_store_adapter.go @@ -3,37 +3,14 @@ package service import ( "sync" - sdkmath "cosmossdk.io/math" bbntypes "github.com/babylonlabs-io/babylon/types" - btcstakingtypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" "github.com/btcsuite/btcd/btcec/v2" - sdk "github.com/cosmos/cosmos-sdk/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "go.uber.org/zap" "github.com/babylonlabs-io/finality-provider/finality-provider/proto" "github.com/babylonlabs-io/finality-provider/finality-provider/store" ) -type CreateFinalityProviderRequest struct { - fpAddr sdk.AccAddress - btcPubKey *bbntypes.BIP340PubKey - pop *btcstakingtypes.ProofOfPossessionBTC - description *stakingtypes.Description - commission *sdkmath.LegacyDec - errResponse chan error - successResponse chan *RegisterFinalityProviderResponse -} - -type RegisterFinalityProviderResponse struct { - txHash string -} - -type CreateFinalityProviderResult struct { - FpInfo *proto.FinalityProviderInfo - TxHash string -} - type fpState struct { mu sync.Mutex fp *store.StoredFinalityProvider diff --git a/finality-provider/service/rpcserver.go b/finality-provider/service/rpcserver.go index 0269c13..9cb68c1 100644 --- a/finality-provider/service/rpcserver.go +++ b/finality-provider/service/rpcserver.go @@ -201,7 +201,7 @@ func (r *rpcServer) UnjailFinalityProvider(_ context.Context, req *proto.UnjailF return nil, err } - txHash, err := r.app.UnjailFinalityProvider(fpPk) + res, err := r.app.UnjailFinalityProvider(fpPk) if err != nil { return nil, fmt.Errorf("failed to unjail the finality-provider: %w", err) } @@ -211,7 +211,7 @@ func (r *rpcServer) UnjailFinalityProvider(_ context.Context, req *proto.UnjailF return nil, fmt.Errorf("failed to start the finality provider instance after unjailing: %w", err) } - return &proto.UnjailFinalityProviderResponse{TxHash: txHash}, nil + return &proto.UnjailFinalityProviderResponse{TxHash: res.TxHash}, nil } // QueryFinalityProvider queries the information of the finality-provider From 6d37e24a3cd9c13ad49f26aabc0d9bcb54999b53 Mon Sep 17 00:00:00 2001 From: Fangyu Gai Date: Thu, 5 Dec 2024 14:59:52 +0800 Subject: [PATCH 2/2] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81ec088..c7bb4a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) * [#184](https://github.com/babylonlabs-io/finality-provider/pull/184) eots manager sign record store * [#189](https://github.com/babylonlabs-io/finality-provider/pull/189) Remove `fpd register-finality-provider` cmd * [#190](https://github.com/babylonlabs-io/finality-provider/pull/190) Benchmark pub rand +* [#195](https://github.com/babylonlabs-io/finality-provider/pull/195) Not block unjailing ### Bug Fixes