From 27a503bbe214f3998c529b456262bc8731d6332d Mon Sep 17 00:00:00 2001 From: Cirrus Gai Date: Tue, 3 Sep 2024 14:56:50 +0800 Subject: [PATCH 01/10] feat: accomodate BTC-timestamping (#40) This PR closes #4. The [change](https://github.com/babylonlabs-io/babylon/pull/23) in Babylon removed voting power from a finality provider if it has no timestamped pub rand for a certain height. Therefore, from the finality provider side, as long as it has voting power, the existence of a timestamped pub rand is ensured. No need to do an extra query to check it. This PR accommodates this change --- clientcontroller/babylon.go | 27 ++++ clientcontroller/interface.go | 1 + eotsmanager/proto/eotsmanager.pb.go | 2 +- eotsmanager/proto/eotsmanager_grpc.pb.go | 37 +++-- finality-provider/config/config.go | 6 +- .../proto/finality_providers.pb.go | 2 +- .../proto/finality_providers_grpc.pb.go | 42 +++-- finality-provider/service/fastsync.go | 8 - finality-provider/service/fastsync_test.go | 11 +- finality-provider/service/fp_instance.go | 115 ------------- go.mod | 2 +- go.sum | 4 +- itest/babylon_node_handler.go | 1 + itest/e2e_test.go | 47 +----- itest/test_manager.go | 152 +++++++++++++++++- tools/go.mod | 2 +- tools/go.sum | 4 +- 17 files changed, 254 insertions(+), 209 deletions(-) diff --git a/clientcontroller/babylon.go b/clientcontroller/babylon.go index dc3d0ef4..f7c57837 100644 --- a/clientcontroller/babylon.go +++ b/clientcontroller/babylon.go @@ -493,6 +493,15 @@ func (bc *BabylonController) QueryBtcLightClientTip() (*btclctypes.BTCHeaderInfo return res.Header, nil } +func (bc *BabylonController) QueryCurrentEpoch() (uint64, error) { + res, err := bc.bbnClient.QueryClient.CurrentEpoch() + if err != nil { + return 0, fmt.Errorf("failed to query BTC tip: %v", err) + } + + return res.CurrentEpoch, nil +} + func (bc *BabylonController) QueryVotesAtHeight(height uint64) ([]bbntypes.BIP340PubKey, error) { res, err := bc.bbnClient.QueryClient.VotesAtHeight(height) if err != nil { @@ -589,3 +598,21 @@ func (bc *BabylonController) SubmitCovenantSigs( return &types.TxResponse{TxHash: res.TxHash, Events: res.Events}, nil } + +func (bc *BabylonController) GetBBNClient() *bbnclient.Client { + return bc.bbnClient +} + +func (bc *BabylonController) InsertSpvProofs(submitter string, proofs []*btcctypes.BTCSpvProof) (*provider.RelayerTxResponse, error) { + msg := &btcctypes.MsgInsertBTCSpvProof{ + Submitter: submitter, + Proofs: proofs, + } + + res, err := bc.reliablySendMsg(msg, emptyErrs, emptyErrs) + if err != nil { + return nil, err + } + + return res, nil +} diff --git a/clientcontroller/interface.go b/clientcontroller/interface.go index a705a3f0..a7100068 100644 --- a/clientcontroller/interface.go +++ b/clientcontroller/interface.go @@ -10,6 +10,7 @@ import ( "go.uber.org/zap" finalitytypes "github.com/babylonlabs-io/babylon/x/finality/types" + fpcfg "github.com/babylonlabs-io/finality-provider/finality-provider/config" "github.com/babylonlabs-io/finality-provider/types" ) diff --git a/eotsmanager/proto/eotsmanager.pb.go b/eotsmanager/proto/eotsmanager.pb.go index 9a512e34..eaf5ac07 100644 --- a/eotsmanager/proto/eotsmanager.pb.go +++ b/eotsmanager/proto/eotsmanager.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.32.0 +// protoc-gen-go v1.31.0 // protoc (unknown) // source: eotsmanager.proto diff --git a/eotsmanager/proto/eotsmanager_grpc.pb.go b/eotsmanager/proto/eotsmanager_grpc.pb.go index 75b1e5cf..0a8814ad 100644 --- a/eotsmanager/proto/eotsmanager_grpc.pb.go +++ b/eotsmanager/proto/eotsmanager_grpc.pb.go @@ -1,4 +1,8 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc (unknown) +// source: eotsmanager.proto package proto @@ -14,6 +18,15 @@ import ( // Requires gRPC-Go v1.32.0 or later. const _ = grpc.SupportPackageIsVersion7 +const ( + EOTSManager_Ping_FullMethodName = "/proto.EOTSManager/Ping" + EOTSManager_CreateKey_FullMethodName = "/proto.EOTSManager/CreateKey" + EOTSManager_CreateRandomnessPairList_FullMethodName = "/proto.EOTSManager/CreateRandomnessPairList" + EOTSManager_KeyRecord_FullMethodName = "/proto.EOTSManager/KeyRecord" + EOTSManager_SignEOTS_FullMethodName = "/proto.EOTSManager/SignEOTS" + EOTSManager_SignSchnorrSig_FullMethodName = "/proto.EOTSManager/SignSchnorrSig" +) + // EOTSManagerClient is the client API for EOTSManager service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. @@ -41,7 +54,7 @@ func NewEOTSManagerClient(cc grpc.ClientConnInterface) EOTSManagerClient { func (c *eOTSManagerClient) Ping(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*PingResponse, error) { out := new(PingResponse) - err := c.cc.Invoke(ctx, "/proto.EOTSManager/Ping", in, out, opts...) + err := c.cc.Invoke(ctx, EOTSManager_Ping_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -50,7 +63,7 @@ func (c *eOTSManagerClient) Ping(ctx context.Context, in *PingRequest, opts ...g func (c *eOTSManagerClient) CreateKey(ctx context.Context, in *CreateKeyRequest, opts ...grpc.CallOption) (*CreateKeyResponse, error) { out := new(CreateKeyResponse) - err := c.cc.Invoke(ctx, "/proto.EOTSManager/CreateKey", in, out, opts...) + err := c.cc.Invoke(ctx, EOTSManager_CreateKey_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -59,7 +72,7 @@ func (c *eOTSManagerClient) CreateKey(ctx context.Context, in *CreateKeyRequest, func (c *eOTSManagerClient) CreateRandomnessPairList(ctx context.Context, in *CreateRandomnessPairListRequest, opts ...grpc.CallOption) (*CreateRandomnessPairListResponse, error) { out := new(CreateRandomnessPairListResponse) - err := c.cc.Invoke(ctx, "/proto.EOTSManager/CreateRandomnessPairList", in, out, opts...) + err := c.cc.Invoke(ctx, EOTSManager_CreateRandomnessPairList_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -68,7 +81,7 @@ func (c *eOTSManagerClient) CreateRandomnessPairList(ctx context.Context, in *Cr func (c *eOTSManagerClient) KeyRecord(ctx context.Context, in *KeyRecordRequest, opts ...grpc.CallOption) (*KeyRecordResponse, error) { out := new(KeyRecordResponse) - err := c.cc.Invoke(ctx, "/proto.EOTSManager/KeyRecord", in, out, opts...) + err := c.cc.Invoke(ctx, EOTSManager_KeyRecord_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -77,7 +90,7 @@ func (c *eOTSManagerClient) KeyRecord(ctx context.Context, in *KeyRecordRequest, func (c *eOTSManagerClient) SignEOTS(ctx context.Context, in *SignEOTSRequest, opts ...grpc.CallOption) (*SignEOTSResponse, error) { out := new(SignEOTSResponse) - err := c.cc.Invoke(ctx, "/proto.EOTSManager/SignEOTS", in, out, opts...) + err := c.cc.Invoke(ctx, EOTSManager_SignEOTS_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -86,7 +99,7 @@ func (c *eOTSManagerClient) SignEOTS(ctx context.Context, in *SignEOTSRequest, o func (c *eOTSManagerClient) SignSchnorrSig(ctx context.Context, in *SignSchnorrSigRequest, opts ...grpc.CallOption) (*SignSchnorrSigResponse, error) { out := new(SignSchnorrSigResponse) - err := c.cc.Invoke(ctx, "/proto.EOTSManager/SignSchnorrSig", in, out, opts...) + err := c.cc.Invoke(ctx, EOTSManager_SignSchnorrSig_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -156,7 +169,7 @@ func _EOTSManager_Ping_Handler(srv interface{}, ctx context.Context, dec func(in } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/proto.EOTSManager/Ping", + FullMethod: EOTSManager_Ping_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(EOTSManagerServer).Ping(ctx, req.(*PingRequest)) @@ -174,7 +187,7 @@ func _EOTSManager_CreateKey_Handler(srv interface{}, ctx context.Context, dec fu } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/proto.EOTSManager/CreateKey", + FullMethod: EOTSManager_CreateKey_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(EOTSManagerServer).CreateKey(ctx, req.(*CreateKeyRequest)) @@ -192,7 +205,7 @@ func _EOTSManager_CreateRandomnessPairList_Handler(srv interface{}, ctx context. } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/proto.EOTSManager/CreateRandomnessPairList", + FullMethod: EOTSManager_CreateRandomnessPairList_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(EOTSManagerServer).CreateRandomnessPairList(ctx, req.(*CreateRandomnessPairListRequest)) @@ -210,7 +223,7 @@ func _EOTSManager_KeyRecord_Handler(srv interface{}, ctx context.Context, dec fu } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/proto.EOTSManager/KeyRecord", + FullMethod: EOTSManager_KeyRecord_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(EOTSManagerServer).KeyRecord(ctx, req.(*KeyRecordRequest)) @@ -228,7 +241,7 @@ func _EOTSManager_SignEOTS_Handler(srv interface{}, ctx context.Context, dec fun } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/proto.EOTSManager/SignEOTS", + FullMethod: EOTSManager_SignEOTS_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(EOTSManagerServer).SignEOTS(ctx, req.(*SignEOTSRequest)) @@ -246,7 +259,7 @@ func _EOTSManager_SignSchnorrSig_Handler(srv interface{}, ctx context.Context, d } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/proto.EOTSManager/SignSchnorrSig", + FullMethod: EOTSManager_SignSchnorrSig_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(EOTSManagerServer).SignSchnorrSig(ctx, req.(*SignSchnorrSigRequest)) diff --git a/finality-provider/config/config.go b/finality-provider/config/config.go index 3fa4b51f..fabce0b1 100644 --- a/finality-provider/config/config.go +++ b/finality-provider/config/config.go @@ -25,9 +25,9 @@ const ( defaultFinalityProviderKeyName = "finality-provider" DefaultRPCPort = 12581 defaultConfigFileName = "fpd.conf" - defaultNumPubRand = 100 - defaultNumPubRandMax = 200 - defaultMinRandHeightGap = 20 + defaultNumPubRand = 70000 // support running of 1 week with block production time as 10s + defaultNumPubRandMax = 100000 + defaultMinRandHeightGap = 35000 defaultStatusUpdateInterval = 20 * time.Second defaultRandomInterval = 30 * time.Second defaultSubmitRetryInterval = 1 * time.Second diff --git a/finality-provider/proto/finality_providers.pb.go b/finality-provider/proto/finality_providers.pb.go index e36c1b1f..7aa726ad 100644 --- a/finality-provider/proto/finality_providers.pb.go +++ b/finality-provider/proto/finality_providers.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.32.0 +// protoc-gen-go v1.31.0 // protoc (unknown) // source: finality_providers.proto diff --git a/finality-provider/proto/finality_providers_grpc.pb.go b/finality-provider/proto/finality_providers_grpc.pb.go index 6480c381..e2d7bdca 100644 --- a/finality-provider/proto/finality_providers_grpc.pb.go +++ b/finality-provider/proto/finality_providers_grpc.pb.go @@ -1,4 +1,8 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc (unknown) +// source: finality_providers.proto package proto @@ -14,6 +18,16 @@ import ( // Requires gRPC-Go v1.32.0 or later. const _ = grpc.SupportPackageIsVersion7 +const ( + FinalityProviders_GetInfo_FullMethodName = "/proto.FinalityProviders/GetInfo" + FinalityProviders_CreateFinalityProvider_FullMethodName = "/proto.FinalityProviders/CreateFinalityProvider" + FinalityProviders_RegisterFinalityProvider_FullMethodName = "/proto.FinalityProviders/RegisterFinalityProvider" + FinalityProviders_AddFinalitySignature_FullMethodName = "/proto.FinalityProviders/AddFinalitySignature" + FinalityProviders_QueryFinalityProvider_FullMethodName = "/proto.FinalityProviders/QueryFinalityProvider" + FinalityProviders_QueryFinalityProviderList_FullMethodName = "/proto.FinalityProviders/QueryFinalityProviderList" + FinalityProviders_SignMessageFromChainKey_FullMethodName = "/proto.FinalityProviders/SignMessageFromChainKey" +) + // FinalityProvidersClient is the client API for FinalityProviders service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. @@ -46,7 +60,7 @@ func NewFinalityProvidersClient(cc grpc.ClientConnInterface) FinalityProvidersCl func (c *finalityProvidersClient) GetInfo(ctx context.Context, in *GetInfoRequest, opts ...grpc.CallOption) (*GetInfoResponse, error) { out := new(GetInfoResponse) - err := c.cc.Invoke(ctx, "/proto.FinalityProviders/GetInfo", in, out, opts...) + err := c.cc.Invoke(ctx, FinalityProviders_GetInfo_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -55,7 +69,7 @@ func (c *finalityProvidersClient) GetInfo(ctx context.Context, in *GetInfoReques func (c *finalityProvidersClient) CreateFinalityProvider(ctx context.Context, in *CreateFinalityProviderRequest, opts ...grpc.CallOption) (*CreateFinalityProviderResponse, error) { out := new(CreateFinalityProviderResponse) - err := c.cc.Invoke(ctx, "/proto.FinalityProviders/CreateFinalityProvider", in, out, opts...) + err := c.cc.Invoke(ctx, FinalityProviders_CreateFinalityProvider_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -64,7 +78,7 @@ func (c *finalityProvidersClient) CreateFinalityProvider(ctx context.Context, in func (c *finalityProvidersClient) RegisterFinalityProvider(ctx context.Context, in *RegisterFinalityProviderRequest, opts ...grpc.CallOption) (*RegisterFinalityProviderResponse, error) { out := new(RegisterFinalityProviderResponse) - err := c.cc.Invoke(ctx, "/proto.FinalityProviders/RegisterFinalityProvider", in, out, opts...) + err := c.cc.Invoke(ctx, FinalityProviders_RegisterFinalityProvider_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -73,7 +87,7 @@ func (c *finalityProvidersClient) RegisterFinalityProvider(ctx context.Context, func (c *finalityProvidersClient) AddFinalitySignature(ctx context.Context, in *AddFinalitySignatureRequest, opts ...grpc.CallOption) (*AddFinalitySignatureResponse, error) { out := new(AddFinalitySignatureResponse) - err := c.cc.Invoke(ctx, "/proto.FinalityProviders/AddFinalitySignature", in, out, opts...) + err := c.cc.Invoke(ctx, FinalityProviders_AddFinalitySignature_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -82,7 +96,7 @@ func (c *finalityProvidersClient) AddFinalitySignature(ctx context.Context, in * func (c *finalityProvidersClient) QueryFinalityProvider(ctx context.Context, in *QueryFinalityProviderRequest, opts ...grpc.CallOption) (*QueryFinalityProviderResponse, error) { out := new(QueryFinalityProviderResponse) - err := c.cc.Invoke(ctx, "/proto.FinalityProviders/QueryFinalityProvider", in, out, opts...) + err := c.cc.Invoke(ctx, FinalityProviders_QueryFinalityProvider_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -91,7 +105,7 @@ func (c *finalityProvidersClient) QueryFinalityProvider(ctx context.Context, in func (c *finalityProvidersClient) QueryFinalityProviderList(ctx context.Context, in *QueryFinalityProviderListRequest, opts ...grpc.CallOption) (*QueryFinalityProviderListResponse, error) { out := new(QueryFinalityProviderListResponse) - err := c.cc.Invoke(ctx, "/proto.FinalityProviders/QueryFinalityProviderList", in, out, opts...) + err := c.cc.Invoke(ctx, FinalityProviders_QueryFinalityProviderList_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -100,7 +114,7 @@ func (c *finalityProvidersClient) QueryFinalityProviderList(ctx context.Context, func (c *finalityProvidersClient) SignMessageFromChainKey(ctx context.Context, in *SignMessageFromChainKeyRequest, opts ...grpc.CallOption) (*SignMessageFromChainKeyResponse, error) { out := new(SignMessageFromChainKeyResponse) - err := c.cc.Invoke(ctx, "/proto.FinalityProviders/SignMessageFromChainKey", in, out, opts...) + err := c.cc.Invoke(ctx, FinalityProviders_SignMessageFromChainKey_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -178,7 +192,7 @@ func _FinalityProviders_GetInfo_Handler(srv interface{}, ctx context.Context, de } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/proto.FinalityProviders/GetInfo", + FullMethod: FinalityProviders_GetInfo_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(FinalityProvidersServer).GetInfo(ctx, req.(*GetInfoRequest)) @@ -196,7 +210,7 @@ func _FinalityProviders_CreateFinalityProvider_Handler(srv interface{}, ctx cont } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/proto.FinalityProviders/CreateFinalityProvider", + FullMethod: FinalityProviders_CreateFinalityProvider_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(FinalityProvidersServer).CreateFinalityProvider(ctx, req.(*CreateFinalityProviderRequest)) @@ -214,7 +228,7 @@ func _FinalityProviders_RegisterFinalityProvider_Handler(srv interface{}, ctx co } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/proto.FinalityProviders/RegisterFinalityProvider", + FullMethod: FinalityProviders_RegisterFinalityProvider_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(FinalityProvidersServer).RegisterFinalityProvider(ctx, req.(*RegisterFinalityProviderRequest)) @@ -232,7 +246,7 @@ func _FinalityProviders_AddFinalitySignature_Handler(srv interface{}, ctx contex } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/proto.FinalityProviders/AddFinalitySignature", + FullMethod: FinalityProviders_AddFinalitySignature_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(FinalityProvidersServer).AddFinalitySignature(ctx, req.(*AddFinalitySignatureRequest)) @@ -250,7 +264,7 @@ func _FinalityProviders_QueryFinalityProvider_Handler(srv interface{}, ctx conte } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/proto.FinalityProviders/QueryFinalityProvider", + FullMethod: FinalityProviders_QueryFinalityProvider_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(FinalityProvidersServer).QueryFinalityProvider(ctx, req.(*QueryFinalityProviderRequest)) @@ -268,7 +282,7 @@ func _FinalityProviders_QueryFinalityProviderList_Handler(srv interface{}, ctx c } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/proto.FinalityProviders/QueryFinalityProviderList", + FullMethod: FinalityProviders_QueryFinalityProviderList_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(FinalityProvidersServer).QueryFinalityProviderList(ctx, req.(*QueryFinalityProviderListRequest)) @@ -286,7 +300,7 @@ func _FinalityProviders_SignMessageFromChainKey_Handler(srv interface{}, ctx con } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/proto.FinalityProviders/SignMessageFromChainKey", + FullMethod: FinalityProviders_SignMessageFromChainKey_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(FinalityProvidersServer).SignMessageFromChainKey(ctx, req.(*SignMessageFromChainKeyRequest)) diff --git a/finality-provider/service/fastsync.go b/finality-provider/service/fastsync.go index fd2b97ab..3cf114e5 100644 --- a/finality-provider/service/fastsync.go +++ b/finality-provider/service/fastsync.go @@ -62,14 +62,6 @@ func (fp *FinalityProviderInstance) FastSync(startHeight, endHeight uint64) (*Fa fp.metrics.IncrementFpTotalBlocksWithoutVotingPower(fp.GetBtcPkHex()) continue } - // check whether the randomness has been committed - hasRand, err := fp.hasRandomness(b) - if err != nil { - return nil, err - } - if !hasRand { - break - } // all good, add the block for catching up catchUpBlocks = append(catchUpBlocks, b) } diff --git a/finality-provider/service/fastsync_test.go b/finality-provider/service/fastsync_test.go index 2ef082e8..240c1fac 100644 --- a/finality-provider/service/fastsync_test.go +++ b/finality-provider/service/fastsync_test.go @@ -88,10 +88,17 @@ func FuzzFastSync_NoRandomness(f *testing.F) { _, err := fpIns.CommitPubRand(randomStartingHeight) require.NoError(t, err) - mockClientController.EXPECT().QueryFinalityProviderVotingPower(fpIns.GetBtcPk(), gomock.Any()). - Return(uint64(1), nil).AnyTimes() // the last height with pub rand is a random value inside [finalizedHeight+1, currentHeight] lastHeightWithPubRand := uint64(rand.Intn(int(currentHeight)-int(finalizedHeight))) + finalizedHeight + 1 + for i := randomStartingHeight; i <= currentHeight; i++ { + if i <= lastHeightWithPubRand { + mockClientController.EXPECT().QueryFinalityProviderVotingPower(fpIns.GetBtcPk(), i). + Return(uint64(1), nil).AnyTimes() + } else { + mockClientController.EXPECT().QueryFinalityProviderVotingPower(fpIns.GetBtcPk(), i). + Return(uint64(0), nil).AnyTimes() + } + } lastCommittedPubRandMap := make(map[uint64]*ftypes.PubRandCommitResponse) lastCommittedPubRandMap[lastHeightWithPubRand-10] = &ftypes.PubRandCommitResponse{ NumPubRand: 10 + 1, diff --git a/finality-provider/service/fp_instance.go b/finality-provider/service/fp_instance.go index 89b4933a..e5b30ba7 100644 --- a/finality-provider/service/fp_instance.go +++ b/finality-provider/service/fp_instance.go @@ -203,22 +203,6 @@ func (fp *FinalityProviderInstance) finalitySigSubmissionLoop() { fp.metrics.IncrementFpTotalBlocksWithoutVotingPower(fp.GetBtcPkHex()) continue } - // check whether the randomness has been committed - // the retry will end if max retry times is reached - // or the target block is finalized - isFinalized, err := fp.retryCheckRandomnessUntilBlockFinalized(b) - if err != nil { - if !errors.Is(err, ErrFinalityProviderShutDown) { - fp.reportCriticalErr(err) - } - break - } - // the block is finalized, no need to submit finality signature - if isFinalized { - fp.MustSetLastProcessedHeight(b.Height) - continue - } - // use the copy of the block to avoid the impact to other receivers nextBlock := *b res, err := fp.retrySubmitFinalitySignatureUntilBlockFinalized(&nextBlock) @@ -431,24 +415,6 @@ func (fp *FinalityProviderInstance) hasVotingPower(b *types.BlockInfo) (bool, er return true, nil } -func (fp *FinalityProviderInstance) hasRandomness(b *types.BlockInfo) (bool, error) { - lastCommittedHeight, err := fp.GetLastCommittedHeight() - if err != nil { - return false, err - } - if b.Height > lastCommittedHeight { - fp.logger.Debug( - "the finality provider has not committed public randomness for the height", - zap.String("pk", fp.GetBtcPkHex()), - zap.Uint64("block_height", b.Height), - zap.Uint64("last_committed_height", lastCommittedHeight), - ) - return false, nil - } - - return true, nil -} - func (fp *FinalityProviderInstance) reportCriticalErr(err error) { fp.criticalErrChan <- &CriticalError{ err: err, @@ -461,77 +427,6 @@ func (fp *FinalityProviderInstance) checkLagging(currentBlock *types.BlockInfo) return currentBlock.Height >= fp.GetLastProcessedHeight()+fp.cfg.FastSyncGap } -// retryQueryingRandomnessUntilBlockFinalized periodically checks whether -// the randomness has been committed to the target block until the block is -// finalized -// error will be returned if maximum retries have been reached or the query to -// the consumer chain fails -func (fp *FinalityProviderInstance) retryCheckRandomnessUntilBlockFinalized(targetBlock *types.BlockInfo) (bool, error) { - var numRetries uint32 - - // we break the for loop if the block is finalized or the randomness is successfully committed - // error will be returned if maximum retries have been reached or the query to the consumer chain fails - for { - fp.logger.Debug( - "checking randomness", - zap.String("pk", fp.GetBtcPkHex()), - zap.Uint64("target_block_height", targetBlock.Height), - ) - hasRand, err := fp.hasRandomness(targetBlock) - if err != nil { - fp.logger.Debug( - "failed to check last committed randomness", - zap.String("pk", fp.GetBtcPkHex()), - zap.Uint32("current_failures", numRetries), - zap.Uint64("target_block_height", targetBlock.Height), - zap.Error(err), - ) - - numRetries += 1 - if numRetries > uint32(fp.cfg.MaxSubmissionRetries) { - return false, fmt.Errorf("reached max failed cycles with err: %w", err) - } - } else if !hasRand { - fp.logger.Debug( - "randomness does not exist", - zap.String("pk", fp.GetBtcPkHex()), - zap.Uint32("current_retries", numRetries), - zap.Uint64("target_block_height", targetBlock.Height), - ) - - numRetries += 1 - if numRetries > uint32(fp.cfg.MaxSubmissionRetries) { - return false, fmt.Errorf("reached max retries but randomness still not existed") - } - } else { - // the randomness has been successfully committed - return false, nil - } - select { - case <-time.After(fp.cfg.SubmissionRetryInterval): - // periodically query the index block to be later checked whether it is Finalized - finalized, err := fp.checkBlockFinalization(targetBlock.Height) - if err != nil { - return false, fmt.Errorf("failed to query block finalization at height %v: %w", targetBlock.Height, err) - } - if finalized { - fp.logger.Debug( - "the block is already finalized, skip checking randomness", - zap.String("pk", fp.GetBtcPkHex()), - zap.Uint64("target_height", targetBlock.Height), - ) - // TODO: returning nil here is to safely break the loop - // the error still exists - return true, nil - } - - case <-fp.quit: - fp.logger.Debug("the finality-provider instance is closing", zap.String("pk", fp.GetBtcPkHex())) - return false, ErrFinalityProviderShutDown - } - } -} - // retrySubmitFinalitySignatureUntilBlockFinalized periodically tries to submit finality signature until success or the block is finalized // error will be returned if maximum retries have been reached or the query to the consumer chain fails func (fp *FinalityProviderInstance) retrySubmitFinalitySignatureUntilBlockFinalized(targetBlock *types.BlockInfo) (*types.TxResponse, error) { @@ -815,16 +710,6 @@ func (fp *FinalityProviderInstance) SubmitBatchFinalitySignatures(blocks []*type // this API is the same as SubmitFinalitySignature except that we don't constraint the voting height and update status // Note: this should not be used in the submission loop func (fp *FinalityProviderInstance) TestSubmitFinalitySignatureAndExtractPrivKey(b *types.BlockInfo) (*types.TxResponse, *btcec.PrivateKey, error) { - // check last committed height - lastCommittedHeight, err := fp.GetLastCommittedHeight() - if err != nil { - return nil, nil, err - } - if lastCommittedHeight < b.Height { - return nil, nil, fmt.Errorf("the finality-provider's last committed height %v is lower than the current block height %v", - lastCommittedHeight, b.Height) - } - // get public randomness prList, err := fp.getPubRandList(b.Height, 1) if err != nil { diff --git a/go.mod b/go.mod index 8c43992c..f1804407 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( cosmossdk.io/errors v1.0.1 cosmossdk.io/math v1.3.0 github.com/avast/retry-go/v4 v4.5.1 - github.com/babylonlabs-io/babylon v0.9.0 + github.com/babylonlabs-io/babylon v0.9.3-0.20240903035004-5c732382e9a4 github.com/btcsuite/btcd v0.24.2 github.com/btcsuite/btcd/btcec/v2 v2.3.2 github.com/btcsuite/btcd/btcutil v1.1.5 diff --git a/go.sum b/go.sum index 13ee8509..96bf4680 100644 --- a/go.sum +++ b/go.sum @@ -281,8 +281,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.9.0 h1:dHZ9wUrI5XLaO4UIwJRgiCdnzFdi5yv7dpibbu6TDv0= -github.com/babylonlabs-io/babylon v0.9.0/go.mod h1:t7B4e+ooD2oYvAxkegtNKDL9bXe+vU29a8xnCQh+UKo= +github.com/babylonlabs-io/babylon v0.9.3-0.20240903035004-5c732382e9a4 h1:WKR81WDZnf9tWi5sQTx9JN3k4254UjteCg6VRUEFT8w= +github.com/babylonlabs-io/babylon v0.9.3-0.20240903035004-5c732382e9a4/go.mod h1:9VUUAwVaalXiDdPZT65SPoawKWpp6ple6tBr8Vw0NI8= 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/babylon_node_handler.go b/itest/babylon_node_handler.go index 57919e8b..716b117c 100644 --- a/itest/babylon_node_handler.go +++ b/itest/babylon_node_handler.go @@ -133,6 +133,7 @@ func NewBabylonNodeHandler(t *testing.T, covenantQuorum int, covenantPks []*type "--keyring-backend=test", "--chain-id=chain-test", "--additional-sender-account", + fmt.Sprintf("--epoch-interval=%d", 5), fmt.Sprintf("--slashing-address=%s", slashingAddr), fmt.Sprintf("--covenant-quorum=%d", covenantQuorum), fmt.Sprintf("--covenant-pks=%s", strings.Join(covenantPksStr, ",")), diff --git a/itest/e2e_test.go b/itest/e2e_test.go index 0dec0dc1..257705ee 100644 --- a/itest/e2e_test.go +++ b/itest/e2e_test.go @@ -13,12 +13,11 @@ import ( "github.com/stretchr/testify/require" "github.com/babylonlabs-io/finality-provider/finality-provider/proto" - "github.com/babylonlabs-io/finality-provider/finality-provider/service" "github.com/babylonlabs-io/finality-provider/types" ) var ( - stakingTime = uint16(100) + stakingTime = uint16(1000) stakingAmount = int64(20000) ) @@ -33,7 +32,7 @@ func TestFinalityProviderLifeCycle(t *testing.T) { fpIns := fpInsList[0] // check the public randomness is committed - tm.WaitForFpPubRandCommitted(t, fpIns) + tm.WaitForFpPubRandTimestamped(t, fpIns) // send a BTC delegation _ = tm.InsertBTCDelegation(t, []*btcec.PublicKey{fpIns.GetBtcPk()}, stakingTime, stakingAmount) @@ -65,7 +64,7 @@ func TestDoubleSigning(t *testing.T) { fpIns := fpInsList[0] // check the public randomness is committed - tm.WaitForFpPubRandCommitted(t, fpIns) + tm.WaitForFpPubRandTimestamped(t, fpIns) // send a BTC delegation _ = tm.InsertBTCDelegation(t, []*btcec.PublicKey{fpIns.GetBtcPk()}, stakingTime, stakingAmount) @@ -115,44 +114,6 @@ func TestDoubleSigning(t *testing.T) { require.Equal(t, false, fps[0].IsRunning) } -// TestMultipleFinalityProviders tests starting with multiple finality providers -func TestMultipleFinalityProviders(t *testing.T) { - n := 3 - tm, fpInstances := StartManagerWithFinalityProvider(t, n) - defer tm.Stop(t) - - // submit BTC delegations for each finality-provider - for _, fpIns := range fpInstances { - tm.Wg.Add(1) - go func(fpi *service.FinalityProviderInstance) { - defer tm.Wg.Done() - // check the public randomness is committed - tm.WaitForFpPubRandCommitted(t, fpi) - // send a BTC delegation - _ = tm.InsertBTCDelegation(t, []*btcec.PublicKey{fpi.GetBtcPk()}, stakingTime, stakingAmount) - }(fpIns) - } - tm.Wg.Wait() - - // check the BTC delegations are pending - delsResp := tm.WaitForNPendingDels(t, n) - require.Equal(t, n, len(delsResp)) - - // send covenant sigs to each of the delegations - for _, delResp := range delsResp { - d, err := ParseRespBTCDelToBTCDel(delResp) - require.NoError(t, err) - // send covenant sigs - tm.InsertCovenantSigForDelegation(t, d) - } - - // check the BTC delegations are active - _ = tm.WaitForNActiveDels(t, n) - - // check if there's a block finalized - _ = tm.WaitForNFinalizedBlocks(t, 1) -} - // TestFastSync tests the fast sync process where the finality-provider is terminated and restarted with fast sync func TestFastSync(t *testing.T) { tm, fpInsList := StartManagerWithFinalityProvider(t, 1) @@ -161,7 +122,7 @@ func TestFastSync(t *testing.T) { fpIns := fpInsList[0] // check the public randomness is committed - tm.WaitForFpPubRandCommitted(t, fpIns) + tm.WaitForFpPubRandTimestamped(t, fpIns) // send a BTC delegation _ = tm.InsertBTCDelegation(t, []*btcec.PublicKey{fpIns.GetBtcPk()}, stakingTime, stakingAmount) diff --git a/itest/test_manager.go b/itest/test_manager.go index 984d547d..3ddee10c 100644 --- a/itest/test_manager.go +++ b/itest/test_manager.go @@ -13,22 +13,26 @@ import ( sdkmath "cosmossdk.io/math" "github.com/babylonlabs-io/babylon/btcstaking" + txformat "github.com/babylonlabs-io/babylon/btctxformatter" asig "github.com/babylonlabs-io/babylon/crypto/schnorr-adaptor-signature" "github.com/babylonlabs-io/babylon/testutil/datagen" bbntypes "github.com/babylonlabs-io/babylon/types" btcctypes "github.com/babylonlabs-io/babylon/x/btccheckpoint/types" btclctypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" bstypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" - "github.com/babylonlabs-io/finality-provider/clientcontroller" + ckpttypes "github.com/babylonlabs-io/babylon/x/checkpointing/types" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/wire" sdk "github.com/cosmos/cosmos-sdk/types" + sdkquerytypes "github.com/cosmos/cosmos-sdk/types/query" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/stretchr/testify/require" "go.uber.org/zap" + "github.com/babylonlabs-io/finality-provider/clientcontroller" + fpcc "github.com/babylonlabs-io/finality-provider/clientcontroller" "github.com/babylonlabs-io/finality-provider/eotsmanager/client" eotsconfig "github.com/babylonlabs-io/finality-provider/eotsmanager/config" @@ -100,6 +104,16 @@ func StartManager(t *testing.T) *TestManager { bc, err := fpcc.NewBabylonController(cfg.BabylonConfig, &cfg.BTCNetParams, logger) require.NoError(t, err) + var currentEpoch uint64 + require.Eventually(t, func() bool { + currentEpoch, err = bc.QueryCurrentEpoch() + if err != nil { + return false + } + return currentEpoch > 0 + }, eventuallyWaitTimeOut, eventuallyPollTime) + t.Logf("current epoch is %d", currentEpoch) + // 3. prepare EOTS manager eotsHomeDir := filepath.Join(testDir, "eots-home") eotsCfg := eotsconfig.DefaultConfigWithHomePath(eotsHomeDir) @@ -236,16 +250,31 @@ func (tm *TestManager) Stop(t *testing.T) { tm.EOTSServerHandler.Stop() } -func (tm *TestManager) WaitForFpPubRandCommitted(t *testing.T, fpIns *service.FinalityProviderInstance) { +func (tm *TestManager) WaitForFpPubRandTimestamped(t *testing.T, fpIns *service.FinalityProviderInstance) { + var lastCommittedHeight uint64 + var err error + require.Eventually(t, func() bool { - lastCommittedHeight, err := fpIns.GetLastCommittedHeight() + lastCommittedHeight, err = fpIns.GetLastCommittedHeight() if err != nil { return false } return lastCommittedHeight > 0 }, eventuallyWaitTimeOut, eventuallyPollTime) - t.Logf("public randomness is successfully committed") + t.Logf("public randomness is successfully committed, last committed height: %d", lastCommittedHeight) + + // wait until the last registered epoch is finalised + currentEpoch, err := tm.BBNClient.QueryCurrentEpoch() + require.NoError(t, err) + + tm.FinalizeUntilEpoch(t, currentEpoch) + + res, err := tm.BBNClient.GetBBNClient().LatestEpochFromStatus(ckpttypes.Finalized) + require.NoError(t, err) + t.Logf("last finalized epoch: %d", res.RawCheckpoint.EpochNum) + + t.Logf("public randomness is successfully timestamped, last finalized epoch: %d", currentEpoch) } func (tm *TestManager) WaitForNPendingDels(t *testing.T, n int) []*bstypes.BTCDelegationResponse { @@ -518,6 +547,117 @@ func (tm *TestManager) InsertCovenantSigForDelegation(t *testing.T, btcDel *bsty require.NoError(t, err) } +func (tm *TestManager) InsertWBTCHeaders(t *testing.T, r *rand.Rand) { + params, err := tm.BBNClient.QueryStakingParams() + require.NoError(t, err) + btcTipResp, err := tm.BBNClient.QueryBtcLightClientTip() + require.NoError(t, err) + tipHeader, err := bbntypes.NewBTCHeaderBytesFromHex(btcTipResp.HeaderHex) + require.NoError(t, err) + kHeaders := datagen.NewBTCHeaderChainFromParentInfo(r, &btclctypes.BTCHeaderInfo{ + Header: &tipHeader, + Hash: tipHeader.Hash(), + Height: btcTipResp.Height, + Work: &btcTipResp.Work, + }, uint32(params.FinalizationTimeoutBlocks)) + _, err = tm.BBNClient.InsertBtcBlockHeaders(kHeaders.ChainToBytes()) + require.NoError(t, err) +} + +func (tm *TestManager) FinalizeUntilEpoch(t *testing.T, epoch uint64) { + bbnClient := tm.BBNClient.GetBBNClient() + + // wait until the checkpoint of this epoch is sealed + require.Eventually(t, func() bool { + lastSealedCkpt, err := bbnClient.LatestEpochFromStatus(ckpttypes.Sealed) + if err != nil { + return false + } + return epoch <= lastSealedCkpt.RawCheckpoint.EpochNum + }, eventuallyWaitTimeOut, 1*time.Second) + + t.Logf("start finalizing epochs till %d", epoch) + // Random source for the generation of BTC data + r := rand.New(rand.NewSource(time.Now().Unix())) + + // get all checkpoints of these epochs + pagination := &sdkquerytypes.PageRequest{ + Key: ckpttypes.CkptsObjectKey(0), + Limit: epoch, + } + resp, err := bbnClient.RawCheckpoints(pagination) + require.NoError(t, err) + require.Equal(t, int(epoch), len(resp.RawCheckpoints)) + + submitter := tm.BBNClient.GetKeyAddress() + + for _, checkpoint := range resp.RawCheckpoints { + currentBtcTipResp, err := tm.BBNClient.QueryBtcLightClientTip() + require.NoError(t, err) + tipHeader, err := bbntypes.NewBTCHeaderBytesFromHex(currentBtcTipResp.HeaderHex) + require.NoError(t, err) + + rawCheckpoint, err := checkpoint.Ckpt.ToRawCheckpoint() + require.NoError(t, err) + + btcCheckpoint, err := ckpttypes.FromRawCkptToBTCCkpt(rawCheckpoint, submitter) + require.NoError(t, err) + + babylonTagBytes, err := hex.DecodeString("01020304") + require.NoError(t, err) + + p1, p2, err := txformat.EncodeCheckpointData( + babylonTagBytes, + txformat.CurrentVersion, + btcCheckpoint, + ) + require.NoError(t, err) + + tx1 := datagen.CreatOpReturnTransaction(r, p1) + + opReturn1 := datagen.CreateBlockWithTransaction(r, tipHeader.ToBlockHeader(), tx1) + tx2 := datagen.CreatOpReturnTransaction(r, p2) + opReturn2 := datagen.CreateBlockWithTransaction(r, opReturn1.HeaderBytes.ToBlockHeader(), tx2) + + // insert headers and proofs + _, err = tm.BBNClient.InsertBtcBlockHeaders([]bbntypes.BTCHeaderBytes{ + opReturn1.HeaderBytes, + opReturn2.HeaderBytes, + }) + require.NoError(t, err) + + _, err = tm.BBNClient.InsertSpvProofs(submitter.String(), []*btcctypes.BTCSpvProof{ + opReturn1.SpvProof, + opReturn2.SpvProof, + }) + require.NoError(t, err) + + // wait until this checkpoint is submitted + require.Eventually(t, func() bool { + ckpt, err := bbnClient.RawCheckpoint(checkpoint.Ckpt.EpochNum) + if err != nil { + return false + } + return ckpt.RawCheckpoint.Status == ckpttypes.Submitted + }, eventuallyWaitTimeOut, eventuallyPollTime) + } + + // insert w BTC headers + tm.InsertWBTCHeaders(t, r) + + // wait until the checkpoint of this epoch is finalised + require.Eventually(t, func() bool { + lastFinalizedCkpt, err := bbnClient.LatestEpochFromStatus(ckpttypes.Finalized) + if err != nil { + t.Logf("failed to get last finalized epoch: %v", err) + return false + } + return epoch <= lastFinalizedCkpt.RawCheckpoint.EpochNum + }, eventuallyWaitTimeOut, 1*time.Second) + + t.Logf("epoch %d is finalised", epoch) +} + func (tm *TestManager) InsertBTCDelegation(t *testing.T, fpPks []*btcec.PublicKey, stakingTime uint16, stakingAmount int64) *TestDelegationData { params := tm.StakingParams r := rand.New(rand.NewSource(time.Now().UnixNano())) @@ -660,6 +800,10 @@ func (tm *TestManager) InsertBTCDelegation(t *testing.T, fpPks []*btcec.PublicKe func defaultFpConfig(keyringDir, homeDir string) *fpcfg.Config { cfg := fpcfg.DefaultConfigWithHome(homeDir) + cfg.NumPubRand = 1000 + cfg.NumPubRandMax = 1000 + cfg.MinRandHeightGap = 500 + cfg.BitcoinNetwork = "simnet" cfg.BTCNetParams = chaincfg.SimNetParams diff --git a/tools/go.mod b/tools/go.mod index 30a04a46..026ccab7 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -4,7 +4,7 @@ go 1.21 toolchain go1.21.4 -require github.com/babylonlabs-io/babylon v0.9.0 +require github.com/babylonlabs-io/babylon v0.9.3-0.20240903035004-5c732382e9a4 require ( cloud.google.com/go v0.112.0 // indirect diff --git a/tools/go.sum b/tools/go.sum index 9707fffa..5f4f5bb4 100644 --- a/tools/go.sum +++ b/tools/go.sum @@ -268,8 +268,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.9.0 h1:dHZ9wUrI5XLaO4UIwJRgiCdnzFdi5yv7dpibbu6TDv0= -github.com/babylonlabs-io/babylon v0.9.0/go.mod h1:t7B4e+ooD2oYvAxkegtNKDL9bXe+vU29a8xnCQh+UKo= +github.com/babylonlabs-io/babylon v0.9.3-0.20240903035004-5c732382e9a4 h1:WKR81WDZnf9tWi5sQTx9JN3k4254UjteCg6VRUEFT8w= +github.com/babylonlabs-io/babylon v0.9.3-0.20240903035004-5c732382e9a4/go.mod h1:9VUUAwVaalXiDdPZT65SPoawKWpp6ple6tBr8Vw0NI8= 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= From dba360183f7091d9df0344ae08955e35caf55b34 Mon Sep 17 00:00:00 2001 From: Cirrus Gai Date: Tue, 3 Sep 2024 15:12:48 +0800 Subject: [PATCH 02/10] Add contributing and release process docs (#46) Pre-step to switching to trunk-based development --- .github/workflows/publish.yml | 1 - CONTRIBUTING.md | 5 +++++ RELEASE_PROCESS.md | 5 +++++ 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 CONTRIBUTING.md create mode 100644 RELEASE_PROCESS.md diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 9fa69f46..d8e3d1bb 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -4,7 +4,6 @@ on: push: branches: - 'main' - - 'dev' tags: - '*' diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..a756a685 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,5 @@ +# Contributing + +Finality-provider repository follows the same contributing rules as +[Babylon node](https://github.com/babylonlabs-io/babylon/blob/main/CONTRIBUTING.md) +repository. diff --git a/RELEASE_PROCESS.md b/RELEASE_PROCESS.md new file mode 100644 index 00000000..52b41086 --- /dev/null +++ b/RELEASE_PROCESS.md @@ -0,0 +1,5 @@ +# Release Process + +Finality-provider repository follows the same release process rules as +[Babylon node](https://github.com/babylonlabs-io/babylon/blob/main/RELEASE_PROCESS.md) +repository. From 25ab5db1e527d3906d950c7d4c453c153bd124f6 Mon Sep 17 00:00:00 2001 From: Cirrus Gai Date: Thu, 5 Sep 2024 22:40:06 +0800 Subject: [PATCH 03/10] chore: Bump babylon version (#48) This PR bumps Babylon version to accommodate changes about parameters --- clientcontroller/babylon.go | 8 ++------ go.mod | 2 +- go.sum | 4 ++-- itest/babylon_node_handler.go | 14 ++++++++++++-- itest/e2e_test.go | 2 +- itest/test_manager.go | 14 +++++++------- tools/go.mod | 2 +- tools/go.sum | 4 ++-- types/stakingparams.go | 5 +++-- 9 files changed, 31 insertions(+), 24 deletions(-) diff --git a/clientcontroller/babylon.go b/clientcontroller/babylon.go index f7c57837..eb2fa382 100644 --- a/clientcontroller/babylon.go +++ b/clientcontroller/babylon.go @@ -556,20 +556,16 @@ func (bc *BabylonController) QueryStakingParams() (*types.StakingParams, error) } covenantPks = append(covenantPks, covPk) } - slashingAddress, err := btcutil.DecodeAddress(stakingParamRes.Params.SlashingAddress, bc.btcParams) - if err != nil { - return nil, err - } return &types.StakingParams{ ComfirmationTimeBlocks: ckptParamRes.Params.BtcConfirmationDepth, FinalizationTimeoutBlocks: ckptParamRes.Params.CheckpointFinalizationTimeout, MinSlashingTxFeeSat: btcutil.Amount(stakingParamRes.Params.MinSlashingTxFeeSat), CovenantPks: covenantPks, - SlashingAddress: slashingAddress, + SlashingPkScript: stakingParamRes.Params.SlashingPkScript, CovenantQuorum: stakingParamRes.Params.CovenantQuorum, SlashingRate: stakingParamRes.Params.SlashingRate, - MinUnbondingTime: stakingParamRes.Params.MinUnbondingTime, + MinUnbondingTime: stakingParamRes.Params.MinUnbondingTimeBlocks, }, nil } diff --git a/go.mod b/go.mod index f1804407..349600b5 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( cosmossdk.io/errors v1.0.1 cosmossdk.io/math v1.3.0 github.com/avast/retry-go/v4 v4.5.1 - github.com/babylonlabs-io/babylon v0.9.3-0.20240903035004-5c732382e9a4 + github.com/babylonlabs-io/babylon v0.9.3-0.20240904154239-00330e49b177 github.com/btcsuite/btcd v0.24.2 github.com/btcsuite/btcd/btcec/v2 v2.3.2 github.com/btcsuite/btcd/btcutil v1.1.5 diff --git a/go.sum b/go.sum index 96bf4680..83df9ab9 100644 --- a/go.sum +++ b/go.sum @@ -281,8 +281,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.9.3-0.20240903035004-5c732382e9a4 h1:WKR81WDZnf9tWi5sQTx9JN3k4254UjteCg6VRUEFT8w= -github.com/babylonlabs-io/babylon v0.9.3-0.20240903035004-5c732382e9a4/go.mod h1:9VUUAwVaalXiDdPZT65SPoawKWpp6ple6tBr8Vw0NI8= +github.com/babylonlabs-io/babylon v0.9.3-0.20240904154239-00330e49b177 h1:rcbSkxPZVl5AdWt40prUUzj7Rpuv3jD6PvlLh1dQ0DM= +github.com/babylonlabs-io/babylon v0.9.3-0.20240904154239-00330e49b177/go.mod h1:9VUUAwVaalXiDdPZT65SPoawKWpp6ple6tBr8Vw0NI8= 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/babylon_node_handler.go b/itest/babylon_node_handler.go index 716b117c..4bb79267 100644 --- a/itest/babylon_node_handler.go +++ b/itest/babylon_node_handler.go @@ -2,16 +2,22 @@ package e2etest import ( "bytes" + "encoding/hex" "fmt" "log" + "math/rand" "os" "os/exec" "path/filepath" "runtime" "strings" "testing" + "time" + "github.com/babylonlabs-io/babylon/testutil/datagen" "github.com/babylonlabs-io/babylon/types" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/txscript" "github.com/stretchr/testify/require" ) @@ -117,7 +123,11 @@ func NewBabylonNodeHandler(t *testing.T, covenantQuorum int, covenantPks []*type walletName := "node0" nodeDataDir := filepath.Join(testDir, walletName, "babylond") - slashingAddr := "SZtRT4BySL3o4efdGLh3k7Kny8GAnsBrSW" + r := rand.New(rand.NewSource(time.Now().Unix())) + slashingAddress, err := datagen.GenRandomBTCAddress(r, &chaincfg.SigNetParams) + require.NoError(t, err) + slashingPkScript, err := txscript.PayToAddrScript(slashingAddress) + require.NoError(t, err) var covenantPksStr []string for _, pk := range covenantPks { @@ -134,7 +144,7 @@ func NewBabylonNodeHandler(t *testing.T, covenantQuorum int, covenantPks []*type "--chain-id=chain-test", "--additional-sender-account", fmt.Sprintf("--epoch-interval=%d", 5), - fmt.Sprintf("--slashing-address=%s", slashingAddr), + fmt.Sprintf("--slashing-pk-script=%s", hex.EncodeToString(slashingPkScript)), fmt.Sprintf("--covenant-quorum=%d", covenantQuorum), fmt.Sprintf("--covenant-pks=%s", strings.Join(covenantPksStr, ",")), ) diff --git a/itest/e2e_test.go b/itest/e2e_test.go index 257705ee..32432ad1 100644 --- a/itest/e2e_test.go +++ b/itest/e2e_test.go @@ -18,7 +18,7 @@ import ( var ( stakingTime = uint16(1000) - stakingAmount = int64(20000) + stakingAmount = int64(500000) ) // TestFinalityProviderLifeCycle tests the whole life cycle of a finality-provider diff --git a/itest/test_manager.go b/itest/test_manager.go index 3ddee10c..9aff6817 100644 --- a/itest/test_manager.go +++ b/itest/test_manager.go @@ -78,10 +78,10 @@ type TestDelegationData struct { DelegatorSig *bbntypes.BIP340Signature FpPks []*btcec.PublicKey - SlashingAddr string - ChangeAddr string - StakingTime uint16 - StakingAmount int64 + SlashingPkScript []byte + ChangeAddr string + StakingTime uint16 + StakingAmount int64 } func StartManager(t *testing.T) *TestManager { @@ -677,7 +677,7 @@ func (tm *TestManager) InsertBTCDelegation(t *testing.T, fpPks []*btcec.PublicKe params.CovenantQuorum, stakingTime, stakingAmount, - params.SlashingAddress.String(), + params.SlashingPkScript, params.SlashingRate, unbondingTime, ) @@ -742,7 +742,7 @@ func (tm *TestManager) InsertBTCDelegation(t *testing.T, fpPks []*btcec.PublicKe wire.NewOutPoint(&stakingTxHash, 0), unbondingTime, unbondingValue, - params.SlashingAddress.String(), + params.SlashingPkScript, params.SlashingRate, unbondingTime, ) @@ -791,7 +791,7 @@ func (tm *TestManager) InsertBTCDelegation(t *testing.T, fpPks []*btcec.PublicKe SlashingTx: testStakingInfo.SlashingTx, StakingTxInfo: txInfo, DelegatorSig: delegatorSig, - SlashingAddr: params.SlashingAddress.String(), + SlashingPkScript: params.SlashingPkScript, StakingTime: stakingTime, StakingAmount: stakingAmount, } diff --git a/tools/go.mod b/tools/go.mod index 026ccab7..2ce0cca6 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -4,7 +4,7 @@ go 1.21 toolchain go1.21.4 -require github.com/babylonlabs-io/babylon v0.9.3-0.20240903035004-5c732382e9a4 +require github.com/babylonlabs-io/babylon v0.9.3-0.20240904154239-00330e49b177 require ( cloud.google.com/go v0.112.0 // indirect diff --git a/tools/go.sum b/tools/go.sum index 5f4f5bb4..a0af02a0 100644 --- a/tools/go.sum +++ b/tools/go.sum @@ -268,8 +268,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.9.3-0.20240903035004-5c732382e9a4 h1:WKR81WDZnf9tWi5sQTx9JN3k4254UjteCg6VRUEFT8w= -github.com/babylonlabs-io/babylon v0.9.3-0.20240903035004-5c732382e9a4/go.mod h1:9VUUAwVaalXiDdPZT65SPoawKWpp6ple6tBr8Vw0NI8= +github.com/babylonlabs-io/babylon v0.9.3-0.20240904154239-00330e49b177 h1:rcbSkxPZVl5AdWt40prUUzj7Rpuv3jD6PvlLh1dQ0DM= +github.com/babylonlabs-io/babylon v0.9.3-0.20240904154239-00330e49b177/go.mod h1:9VUUAwVaalXiDdPZT65SPoawKWpp6ple6tBr8Vw0NI8= 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/types/stakingparams.go b/types/stakingparams.go index 4c71c227..835835a5 100644 --- a/types/stakingparams.go +++ b/types/stakingparams.go @@ -18,8 +18,9 @@ type StakingParams struct { // Bitcoin public keys of the covenant committee CovenantPks []*btcec.PublicKey - // Address to which slashing transactions are sent - SlashingAddress btcutil.Address + // The pk_script expected in slashing output i.e., the first + // output of slashing transaction + SlashingPkScript []byte // Minimum number of signatures needed for the covenant multisignature CovenantQuorum uint32 From f9c8130eb36f30281493d32858fcd8f9dde6448b Mon Sep 17 00:00:00 2001 From: Rafael Tenfen Date: Wed, 11 Sep 2024 21:16:15 -0300 Subject: [PATCH 04/10] chore: add retry for consumer chain to become available (#50) The goal here was to make life easier for finality provider operators to be able to turn on the finality provider before the consumer node is available and wait for that node to become available for `MinutesToWaitForConsumer` minutes --- finality-provider/config/config.go | 2 ++ finality-provider/service/app.go | 25 ++++++++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/finality-provider/config/config.go b/finality-provider/config/config.go index fabce0b1..12c17ed1 100644 --- a/finality-provider/config/config.go +++ b/finality-provider/config/config.go @@ -69,6 +69,7 @@ type Config struct { FastSyncGap uint64 `long:"fastsyncgap" description:"The block gap that will trigger the fast sync"` EOTSManagerAddress string `long:"eotsmanageraddress" description:"The address of the remote EOTS manager; Empty if the EOTS manager is running locally"` MaxNumFinalityProviders uint32 `long:"maxnumfinalityproviders" description:"The maximum number of finality-provider instances running concurrently within the daemon"` + MinutesToWaitForConsumer uint32 `long:"minuteswaitforconsumer" description:"The number of minutes it should wait for the consumer chain to be available, before stop the app"` BitcoinNetwork string `long:"bitcoinnetwork" description:"Bitcoin network to run on" choise:"mainnet" choice:"regtest" choice:"testnet" choice:"simnet" choice:"signet"` @@ -112,6 +113,7 @@ func DefaultConfigWithHome(homePath string) Config { RpcListener: DefaultRpcListener, MaxNumFinalityProviders: defaultMaxNumFinalityProviders, Metrics: metrics.DefaultFpConfig(), + MinutesToWaitForConsumer: 60 * 24 * 7, // one week in minutes } if err := cfg.Validate(); err != nil { diff --git a/finality-provider/service/app.go b/finality-provider/service/app.go index 4b8e0b91..d86ccfea 100644 --- a/finality-provider/service/app.go +++ b/finality-provider/service/app.go @@ -7,6 +7,7 @@ import ( "time" sdkmath "cosmossdk.io/math" + "github.com/avast/retry-go/v4" bbntypes "github.com/babylonlabs-io/babylon/types" bstypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" "github.com/btcsuite/btcd/btcec/v2" @@ -238,7 +239,29 @@ func (app *FinalityProviderApp) getFpPrivKey(fpPk []byte) (*btcec.PrivateKey, er // SyncFinalityProviderStatus syncs the status of the finality-providers func (app *FinalityProviderApp) SyncFinalityProviderStatus() error { - latestBlock, err := app.cc.QueryBestBlock() + var ( + latestBlock *types.BlockInfo + err error + ) + + attempts := uint(app.config.MinutesToWaitForConsumer) + err = retry.Do(func() error { + latestBlock, err = app.cc.QueryBestBlock() + if err != nil { + return err + } + return nil + }, retry.OnRetry(func(n uint, err error) { + app.logger.Debug( + "failed to query the consumer chain for the latest block", + zap.Uint("attempt", n+1), + zap.Uint("max_attempts", attempts), + zap.Error(err), + ) + // waits for consumer chain to become online for one week + // usefull to turn fpd on before the consumer chain node is available + // and waiting for the consumer node to become available to start. + }), retry.Attempts(attempts), retry.Delay(time.Minute), RtyErr) if err != nil { return err } From d06cde51674f518d54aaabf92a17df54596dfadf Mon Sep 17 00:00:00 2001 From: Rafael Tenfen Date: Thu, 12 Sep 2024 08:39:54 -0300 Subject: [PATCH 05/10] chore: opt to set eots pk in `fpd create-finality-provider` (#51) New flag to command `create-finality-provider` to allow specify eots-pk as hex in the parameters to avoid creation of new EOTS key to store in database This will be useful for finality providers that are in Phase-1 and will need to transition to Phase-2. During Phase-1, only EOTS keys were generated for Phase-2 these finality providers will need to use the same EOTS pk to load and start their finality provider --- eotsmanager/proto/eotsmanager.pb.go | 2 +- .../cmd/fpd/daemon/daemon_commands.go | 22 +++++++++++++++++++ finality-provider/cmd/fpd/daemon/init.go | 2 +- .../proto/finality_providers.pb.go | 19 +++++++++++++--- .../proto/finality_providers.proto | 4 ++++ finality-provider/service/app.go | 20 +++++++++++------ finality-provider/service/app_test.go | 18 ++++++++++++++- finality-provider/service/client/rpcclient.go | 3 ++- finality-provider/service/fp_instance_test.go | 2 +- finality-provider/service/fp_store_adapter.go | 1 + finality-provider/service/rpcserver.go | 20 ++++++++++++++--- itest/test_manager.go | 2 +- testutil/datagen.go | 5 +++-- 13 files changed, 99 insertions(+), 21 deletions(-) diff --git a/eotsmanager/proto/eotsmanager.pb.go b/eotsmanager/proto/eotsmanager.pb.go index eaf5ac07..50c63822 100644 --- a/eotsmanager/proto/eotsmanager.pb.go +++ b/eotsmanager/proto/eotsmanager.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.31.0 +// protoc-gen-go v1.33.0 // protoc (unknown) // source: eotsmanager.proto diff --git a/finality-provider/cmd/fpd/daemon/daemon_commands.go b/finality-provider/cmd/fpd/daemon/daemon_commands.go index 4a982263..c8683e5c 100644 --- a/finality-provider/cmd/fpd/daemon/daemon_commands.go +++ b/finality-provider/cmd/fpd/daemon/daemon_commands.go @@ -8,6 +8,7 @@ import ( "strconv" "cosmossdk.io/math" + "github.com/babylonlabs-io/babylon/types" bbntypes "github.com/babylonlabs-io/babylon/types" "github.com/cosmos/cosmos-sdk/client" sdkflags "github.com/cosmos/cosmos-sdk/client/flags" @@ -66,6 +67,12 @@ func CommandCreateFP() *cobra.Command { Use: "create-finality-provider", Aliases: []string{"cfp"}, Short: "Create a finality provider object and save it in database.", + Long: fmt.Sprintf(` + Create a new finality provider object and store it in the finality provider database. + It needs to have an operating EOTS manager available and running. + + If the flag %s is set, it will ask for the key record from the EOTS manager for the + corresponding EOTS public key. If it is not set, it will create a new EOTS key`, fpEotsPkFlag), Example: fmt.Sprintf(`fpd create-finality-provider --daemon-address %s ...`, defaultFpdDaemonAddress), Args: cobra.NoArgs, RunE: fpcmd.RunEWithClientCtx(runCommandCreateFP), @@ -84,6 +91,7 @@ func CommandCreateFP() *cobra.Command { f.String(websiteFlag, "", "An optional website link") f.String(securityContactFlag, "", "An email for security contact") f.String(detailsFlag, "", "Other optional details") + f.String(fpEotsPkFlag, "", "Optional hex EOTS public key, if not provided a new one will be created") return cmd } @@ -135,10 +143,24 @@ func runCommandCreateFP(ctx client.Context, cmd *cobra.Command, _ []string) erro return fmt.Errorf("failed to read flag %s: %w", hdPathFlag, err) } + eotsPkHex, err := flags.GetString(fpEotsPkFlag) + if err != nil { + return fmt.Errorf("failed to read flag %s: %w", fpEotsPkFlag, err) + } + + if len(eotsPkHex) > 0 { + // if is set, validate before the creation request + _, err := types.NewBIP340PubKeyFromHex(eotsPkHex) + if err != nil { + return fmt.Errorf("invalid eots public key %s: %w", eotsPkHex, err) + } + } + info, err := client.CreateFinalityProvider( context.Background(), keyName, chainId, + eotsPkHex, passphrase, hdPath, description, diff --git a/finality-provider/cmd/fpd/daemon/init.go b/finality-provider/cmd/fpd/daemon/init.go index 3ccea218..99bb759a 100644 --- a/finality-provider/cmd/fpd/daemon/init.go +++ b/finality-provider/cmd/fpd/daemon/init.go @@ -36,7 +36,7 @@ func runInitCmd(ctx client.Context, cmd *cobra.Command, args []string) error { homePath = util.CleanAndExpandPath(homePath) force, err := cmd.Flags().GetBool(forceFlag) if err != nil { - return fmt.Errorf("failed to read flag %s: %w", fpEotsPkFlag, err) + return fmt.Errorf("failed to read flag %s: %w", forceFlag, err) } if util.FileExists(homePath) && !force { diff --git a/finality-provider/proto/finality_providers.pb.go b/finality-provider/proto/finality_providers.pb.go index 7aa726ad..760750c9 100644 --- a/finality-provider/proto/finality_providers.pb.go +++ b/finality-provider/proto/finality_providers.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.31.0 +// protoc-gen-go v1.33.0 // protoc (unknown) // source: finality_providers.proto @@ -201,6 +201,10 @@ type CreateFinalityProviderRequest struct { Description []byte `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty"` // commission defines the commission rate for the finality provider Commission string `protobuf:"bytes,6,opt,name=commission,proto3" json:"commission,omitempty"` + // eots_pk_hex it is the optional EOTS public key and used to ask for + // the key record from the EOTS manager for the corresponding EOTS public key. + // If this property is not set, it will create a new EOTS key. + EotsPkHex string `protobuf:"bytes,7,opt,name=eots_pk_hex,json=eotsPkHex,proto3" json:"eots_pk_hex,omitempty"` } func (x *CreateFinalityProviderRequest) Reset() { @@ -277,6 +281,13 @@ func (x *CreateFinalityProviderRequest) GetCommission() string { return "" } +func (x *CreateFinalityProviderRequest) GetEotsPkHex() string { + if x != nil { + return x.EotsPkHex + } + return "" +} + type CreateFinalityProviderResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1299,7 +1310,7 @@ var file_finality_providers_proto_rawDesc = []byte{ 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x2b, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xf5, 0x01, 0x0a, 0x1d, 0x43, 0x72, 0x65, 0x61, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x95, 0x02, 0x0a, 0x1d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6b, 0x65, 0x79, @@ -1314,7 +1325,9 @@ var file_finality_providers_proto_rawDesc = []byte{ 0x6d, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x42, 0x23, 0xc8, 0xde, 0x1f, 0x00, 0xda, 0xde, 0x1f, 0x1b, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x73, 0x64, 0x6b, 0x2e, 0x69, 0x6f, 0x2f, 0x6d, 0x61, 0x74, 0x68, 0x2e, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, - 0x44, 0x65, 0x63, 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x22, + 0x44, 0x65, 0x63, 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x1e, 0x0a, 0x0b, 0x65, 0x6f, 0x74, 0x73, 0x5f, 0x70, 0x6b, 0x5f, 0x68, 0x65, 0x78, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x6f, 0x74, 0x73, 0x50, 0x6b, 0x48, 0x65, 0x78, 0x22, 0x6a, 0x0a, 0x1e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x11, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x5f, 0x70, 0x72, diff --git a/finality-provider/proto/finality_providers.proto b/finality-provider/proto/finality_providers.proto index 73082b49..ac4a6a64 100644 --- a/finality-provider/proto/finality_providers.proto +++ b/finality-provider/proto/finality_providers.proto @@ -61,6 +61,10 @@ message CreateFinalityProviderRequest { (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", (gogoproto.nullable) = false ]; + // eots_pk_hex it is the optional EOTS public key and used to ask for + // the key record from the EOTS manager for the corresponding EOTS public key. + // If this property is not set, it will create a new EOTS key. + string eots_pk_hex = 7; } message CreateFinalityProviderResponse { diff --git a/finality-provider/service/app.go b/finality-provider/service/app.go index d86ccfea..8373b07c 100644 --- a/finality-provider/service/app.go +++ b/finality-provider/service/app.go @@ -351,6 +351,7 @@ func (app *FinalityProviderApp) Stop() error { func (app *FinalityProviderApp) CreateFinalityProvider( keyName, chainID, passPhrase, hdPath string, + eotsPk *bbntypes.BIP340PubKey, description *stakingtypes.Description, commission *sdkmath.LegacyDec, ) (*CreateFinalityProviderResult, error) { @@ -360,6 +361,7 @@ func (app *FinalityProviderApp) CreateFinalityProvider( chainID: chainID, passPhrase: passPhrase, hdPath: hdPath, + eotsPk: eotsPk, description: description, commission: commission, errResponse: make(chan error, 1), @@ -398,14 +400,18 @@ func (app *FinalityProviderApp) handleCreateFinalityProviderRequest(req *createF } // 2. create EOTS key - fpPkBytes, err := app.eotsManager.CreateKey(req.keyName, req.passPhrase, req.hdPath) - if err != nil { - return nil, err - } - fpPk, err := bbntypes.NewBIP340PubKey(fpPkBytes) - if err != nil { - return nil, err + fpPk := req.eotsPk + if req.eotsPk == nil { + fpPkBytes, err := app.eotsManager.CreateKey(req.keyName, req.passPhrase, req.hdPath) + if err != nil { + return nil, err + } + fpPk, err = bbntypes.NewBIP340PubKey(fpPkBytes) + if err != nil { + return nil, err + } } + fpRecord, err := app.eotsManager.KeyRecord(fpPk.MustMarshal(), req.passPhrase) if err != nil { return nil, fmt.Errorf("failed to get finality-provider record: %w", err) diff --git a/finality-provider/service/app_test.go b/finality-provider/service/app_test.go index 38ff9a5c..49bdeb46 100644 --- a/finality-provider/service/app_test.go +++ b/finality-provider/service/app_test.go @@ -76,8 +76,24 @@ func FuzzRegisterFinalityProvider(f *testing.F) { require.NoError(t, err) }() + var eotsPk *bbntypes.BIP340PubKey + eotsPk = nil + generateEotsKeyBefore := r.Int31n(10) > 5 + if generateEotsKeyBefore { + // sometimes uses the previously generated EOTS pk + eotsKeyName := testutil.GenRandomHexStr(r, 4) + eotsPkBz, err := em.CreateKey(eotsKeyName, passphrase, hdPath) + require.NoError(t, err) + eotsPk, err = bbntypes.NewBIP340PubKey(eotsPkBz) + require.NoError(t, err) + } + // create a finality-provider object and save it to db - fp := testutil.GenStoredFinalityProvider(r, t, app, passphrase, hdPath) + fp := testutil.GenStoredFinalityProvider(r, t, app, passphrase, hdPath, eotsPk) + if generateEotsKeyBefore { + require.Equal(t, eotsPk, bbntypes.NewBIP340PubKeyFromBTCPK(fp.BtcPk)) + } + btcSig := new(bbntypes.BIP340Signature) err = btcSig.Unmarshal(fp.Pop.BtcSig) require.NoError(t, err) diff --git a/finality-provider/service/client/rpcclient.go b/finality-provider/service/client/rpcclient.go index eda110c0..21bdc39f 100644 --- a/finality-provider/service/client/rpcclient.go +++ b/finality-provider/service/client/rpcclient.go @@ -60,7 +60,7 @@ func (c *FinalityProviderServiceGRpcClient) RegisterFinalityProvider( func (c *FinalityProviderServiceGRpcClient) CreateFinalityProvider( ctx context.Context, - keyName, chainID, passphrase, hdPath string, + keyName, chainID, eotsPkHex, passphrase, hdPath string, description types.Description, commission *sdkmath.LegacyDec, ) (*proto.CreateFinalityProviderResponse, error) { @@ -77,6 +77,7 @@ func (c *FinalityProviderServiceGRpcClient) CreateFinalityProvider( HdPath: hdPath, Description: descBytes, Commission: commission.String(), + EotsPkHex: eotsPkHex, } res, err := c.client.CreateFinalityProvider(ctx, req) diff --git a/finality-provider/service/fp_instance_test.go b/finality-provider/service/fp_instance_test.go index 84437e77..75cb57b7 100644 --- a/finality-provider/service/fp_instance_test.go +++ b/finality-provider/service/fp_instance_test.go @@ -124,7 +124,7 @@ func startFinalityProviderAppWithRegisteredFp(t *testing.T, r *rand.Rand, cc cli require.NoError(t, err) // create registered finality-provider - fp := testutil.GenStoredFinalityProvider(r, t, app, passphrase, hdPath) + fp := testutil.GenStoredFinalityProvider(r, t, app, passphrase, hdPath, nil) pubRandProofStore := app.GetPubRandProofStore() fpStore := app.GetFinalityProviderStore() err = fpStore.SetFpStatus(fp.BtcPk, proto.FinalityProviderStatus_REGISTERED) diff --git a/finality-provider/service/fp_store_adapter.go b/finality-provider/service/fp_store_adapter.go index e5dc7bd0..356475f5 100644 --- a/finality-provider/service/fp_store_adapter.go +++ b/finality-provider/service/fp_store_adapter.go @@ -24,6 +24,7 @@ type createFinalityProviderRequest struct { passPhrase string hdPath string chainID string + eotsPk *bbntypes.BIP340PubKey description *stakingtypes.Description commission *sdkmath.LegacyDec errResponse chan error diff --git a/finality-provider/service/rpcserver.go b/finality-provider/service/rpcserver.go index f1196f27..b792491d 100644 --- a/finality-provider/service/rpcserver.go +++ b/finality-provider/service/rpcserver.go @@ -81,9 +81,10 @@ func (r *rpcServer) GetInfo(context.Context, *proto.GetInfoRequest) (*proto.GetI } // CreateFinalityProvider generates a finality-provider object and saves it in the database -func (r *rpcServer) CreateFinalityProvider(ctx context.Context, req *proto.CreateFinalityProviderRequest) ( - *proto.CreateFinalityProviderResponse, error) { - +func (r *rpcServer) CreateFinalityProvider( + ctx context.Context, + req *proto.CreateFinalityProviderRequest, +) (*proto.CreateFinalityProviderResponse, error) { commissionRate, err := math.LegacyNewDecFromStr(req.Commission) if err != nil { return nil, err @@ -94,11 +95,17 @@ func (r *rpcServer) CreateFinalityProvider(ctx context.Context, req *proto.Creat return nil, err } + eotsPk, err := parseOptEotsPk(req.EotsPkHex) + if err != nil { + return nil, err + } + result, err := r.app.CreateFinalityProvider( req.KeyName, req.ChainId, req.Passphrase, req.HdPath, + eotsPk, &description, &commissionRate, ) @@ -218,3 +225,10 @@ func (r *rpcServer) SignMessageFromChainKey(ctx context.Context, req *proto.Sign return &proto.SignMessageFromChainKeyResponse{Signature: signature}, nil } + +func parseOptEotsPk(eotsPkHex string) (*bbntypes.BIP340PubKey, error) { + if len(eotsPkHex) > 0 { + return bbntypes.NewBIP340PubKeyFromHex(eotsPkHex) + } + return nil, nil +} diff --git a/itest/test_manager.go b/itest/test_manager.go index 9aff6817..4a55469b 100644 --- a/itest/test_manager.go +++ b/itest/test_manager.go @@ -187,7 +187,7 @@ func StartManagerWithFinalityProvider(t *testing.T, n int) (*TestManager, []*ser err = tm.BabylonHandler.BabylonNode.TxBankSend(fpBbnKeyInfo.AccAddress.String(), "1000000ubbn") require.NoError(t, err) - res, err := app.CreateFinalityProvider(fpName, chainID, passphrase, hdPath, desc, &commission) + res, err := app.CreateFinalityProvider(fpName, chainID, passphrase, hdPath, nil, desc, &commission) require.NoError(t, err) fpPk, err := bbntypes.NewBIP340PubKeyFromHex(res.FpInfo.BtcPkHex) require.NoError(t, err) diff --git a/testutil/datagen.go b/testutil/datagen.go index d9d5264f..4af9a291 100644 --- a/testutil/datagen.go +++ b/testutil/datagen.go @@ -16,6 +16,7 @@ import ( sdkmath "cosmossdk.io/math" "github.com/babylonlabs-io/babylon/testutil/datagen" bbn "github.com/babylonlabs-io/babylon/types" + bbntypes "github.com/babylonlabs-io/babylon/types" bstypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" "github.com/btcsuite/btcd/chaincfg" "github.com/cosmos/cosmos-sdk/client" @@ -104,7 +105,7 @@ func GenBlocks(r *rand.Rand, startHeight, endHeight uint64) []*types.BlockInfo { } // GenStoredFinalityProvider generates a random finality-provider from the keyring and store it in DB -func GenStoredFinalityProvider(r *rand.Rand, t *testing.T, app *service.FinalityProviderApp, passphrase, hdPath string) *store.StoredFinalityProvider { +func GenStoredFinalityProvider(r *rand.Rand, t *testing.T, app *service.FinalityProviderApp, passphrase, hdPath string, eotsPk *bbntypes.BIP340PubKey) *store.StoredFinalityProvider { // generate keyring keyName := GenRandomHexStr(r, 4) chainID := GenRandomHexStr(r, 4) @@ -113,7 +114,7 @@ func GenStoredFinalityProvider(r *rand.Rand, t *testing.T, app *service.Finality _, err := service.CreateChainKey(cfg.BabylonConfig.KeyDirectory, cfg.BabylonConfig.ChainID, keyName, keyring.BackendTest, passphrase, hdPath, "") require.NoError(t, err) - res, err := app.CreateFinalityProvider(keyName, chainID, passphrase, hdPath, RandomDescription(r), ZeroCommissionRate()) + res, err := app.CreateFinalityProvider(keyName, chainID, passphrase, hdPath, eotsPk, RandomDescription(r), ZeroCommissionRate()) require.NoError(t, err) btcPk, err := bbn.NewBIP340PubKeyFromHex(res.FpInfo.BtcPkHex) From 7a5db87e1ec2efa3691c3b409446bc233665590c Mon Sep 17 00:00:00 2001 From: Rafael Tenfen Date: Wed, 18 Sep 2024 09:16:43 -0300 Subject: [PATCH 06/10] chore: keep sync fp status (#52) - Removed previous `MinutesToWaitForConsumer` added in #50 since it was not good enough to wait for the chain to be up, without starting the fpd server to receive fpd creation - Add new loop that verifies the FP status on chain and update it accordingly (before it was only one time at startup) - Add check for keyring key is valid at start of `NewBabylonController` to avoid panic at `mustGetTxSigner` --- clientcontroller/babylon.go | 12 +- finality-provider/cmd/fpd/daemon/start.go | 5 - finality-provider/config/config.go | 5 +- finality-provider/service/app.go | 142 +++++++++++------- .../service/chain_poller_test.go | 1 + finality-provider/service/fp_manager.go | 15 +- finality-provider/store/fpstore.go | 23 +++ finality-provider/store/storedfp.go | 27 ++++ 8 files changed, 158 insertions(+), 72 deletions(-) diff --git a/clientcontroller/babylon.go b/clientcontroller/babylon.go index eb2fa382..5468994a 100644 --- a/clientcontroller/babylon.go +++ b/clientcontroller/babylon.go @@ -47,10 +47,6 @@ func NewBabylonController( bbnConfig := fpcfg.BBNConfigToBabylonConfig(cfg) - if err := bbnConfig.Validate(); err != nil { - return nil, fmt.Errorf("invalid config for Babylon client: %w", err) - } - bc, err := bbnclient.New( &bbnConfig, logger, @@ -59,6 +55,12 @@ func NewBabylonController( return nil, fmt.Errorf("failed to create Babylon client: %w", err) } + // makes sure that the key in config really exists and it is a valid bech 32 addr + // to allow using mustGetTxSigner + if _, err := bc.GetAddr(); err != nil { + return nil, err + } + return &BabylonController{ bc, cfg, @@ -274,7 +276,7 @@ func (bc *BabylonController) QueryFinalityProviderVotingPower(fpPk *btcec.Public blockHeight, ) if err != nil { - return 0, fmt.Errorf("failed to query BTC delegations: %w", err) + return 0, fmt.Errorf("failed to query Finality Voting Power at Height %d: %w", blockHeight, err) } return res.VotingPower, nil diff --git a/finality-provider/cmd/fpd/daemon/start.go b/finality-provider/cmd/fpd/daemon/start.go index 9a0593cd..14b4dc24 100644 --- a/finality-provider/cmd/fpd/daemon/start.go +++ b/finality-provider/cmd/fpd/daemon/start.go @@ -111,11 +111,6 @@ func loadApp( return nil, fmt.Errorf("failed to create finality-provider app: %v", err) } - // sync finality-provider status - if err := fpApp.SyncFinalityProviderStatus(); err != nil { - return nil, fmt.Errorf("failed to sync finality-provider status: %w", err) - } - return fpApp, nil } diff --git a/finality-provider/config/config.go b/finality-provider/config/config.go index 12c17ed1..d8a8b423 100644 --- a/finality-provider/config/config.go +++ b/finality-provider/config/config.go @@ -32,6 +32,7 @@ const ( defaultRandomInterval = 30 * time.Second defaultSubmitRetryInterval = 1 * time.Second defaultFastSyncInterval = 10 * time.Second + defaultSyncFpStatusInterval = 30 * time.Second defaultFastSyncLimit = 10 defaultFastSyncGap = 3 defaultMaxSubmissionRetries = 20 @@ -69,7 +70,7 @@ type Config struct { FastSyncGap uint64 `long:"fastsyncgap" description:"The block gap that will trigger the fast sync"` EOTSManagerAddress string `long:"eotsmanageraddress" description:"The address of the remote EOTS manager; Empty if the EOTS manager is running locally"` MaxNumFinalityProviders uint32 `long:"maxnumfinalityproviders" description:"The maximum number of finality-provider instances running concurrently within the daemon"` - MinutesToWaitForConsumer uint32 `long:"minuteswaitforconsumer" description:"The number of minutes it should wait for the consumer chain to be available, before stop the app"` + SyncFpStatusInterval time.Duration `long:"syncfpstatusinterval" description:"The duration of time that it should sync FP status with the client blockchain"` BitcoinNetwork string `long:"bitcoinnetwork" description:"Bitcoin network to run on" choise:"mainnet" choice:"regtest" choice:"testnet" choice:"simnet" choice:"signet"` @@ -113,7 +114,7 @@ func DefaultConfigWithHome(homePath string) Config { RpcListener: DefaultRpcListener, MaxNumFinalityProviders: defaultMaxNumFinalityProviders, Metrics: metrics.DefaultFpConfig(), - MinutesToWaitForConsumer: 60 * 24 * 7, // one week in minutes + SyncFpStatusInterval: defaultSyncFpStatusInterval, } if err := cfg.Validate(); err != nil { diff --git a/finality-provider/service/app.go b/finality-provider/service/app.go index 8373b07c..39ea9d96 100644 --- a/finality-provider/service/app.go +++ b/finality-provider/service/app.go @@ -7,7 +7,6 @@ import ( "time" sdkmath "cosmossdk.io/math" - "github.com/avast/retry-go/v4" bbntypes "github.com/babylonlabs-io/babylon/types" bstypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" "github.com/btcsuite/btcd/btcec/v2" @@ -72,7 +71,6 @@ func NewFinalityProviderAppFromConfig( } logger.Info("successfully connected to a remote EOTS manager", zap.String("address", cfg.EOTSManagerAddress)) - return NewFinalityProviderApp(cfg, cc, em, db, logger) } @@ -148,6 +146,11 @@ func (app *FinalityProviderApp) GetInput() *strings.Reader { return app.input } +// Logger returns the current logger of FP app. +func (app *FinalityProviderApp) Logger() *zap.Logger { + return app.logger +} + func (app *FinalityProviderApp) ListFinalityProviderInstances() []*FinalityProviderInstance { return app.fpManager.ListFinalityProviderInstances() } @@ -237,73 +240,68 @@ func (app *FinalityProviderApp) getFpPrivKey(fpPk []byte) (*btcec.PrivateKey, er return record.PrivKey, nil } -// SyncFinalityProviderStatus syncs the status of the finality-providers -func (app *FinalityProviderApp) SyncFinalityProviderStatus() error { - var ( - latestBlock *types.BlockInfo - err error - ) - - attempts := uint(app.config.MinutesToWaitForConsumer) - err = retry.Do(func() error { - latestBlock, err = app.cc.QueryBestBlock() - if err != nil { - return err - } - return nil - }, retry.OnRetry(func(n uint, err error) { - app.logger.Debug( - "failed to query the consumer chain for the latest block", - zap.Uint("attempt", n+1), - zap.Uint("max_attempts", attempts), - zap.Error(err), - ) - // waits for consumer chain to become online for one week - // usefull to turn fpd on before the consumer chain node is available - // and waiting for the consumer node to become available to start. - }), retry.Attempts(attempts), retry.Delay(time.Minute), RtyErr) +// SyncFinalityProviderStatus syncs the status of the finality-providers with the chain. +func (app *FinalityProviderApp) SyncFinalityProviderStatus() (fpInstanceRunning bool, err error) { + latestBlock, err := app.cc.QueryBestBlock() if err != nil { - return err + return false, err } fps, err := app.fps.GetAllStoredFinalityProviders() if err != nil { - return err + return false, err } for _, fp := range fps { vp, err := app.cc.QueryFinalityProviderVotingPower(fp.BtcPk, latestBlock.Height) if err != nil { - // if error occured then the finality-provider is not registered in the Babylon chain yet + // if ther error is that there is nothing in the voting power table + // it should continue and consider the voting power + // as zero to start the finality provider and send public randomness + allowedErr := fmt.Sprintf("failed to query Finality Voting Power at Height %d: rpc error: code = Unknown desc = %s: unknown request", latestBlock.Height, bstypes.ErrVotingPowerTableNotUpdated.Wrapf("height: %d", latestBlock.Height).Error()) + if !strings.EqualFold(err.Error(), allowedErr) { + // if some other error occured then the finality-provider is not registered in the Babylon chain yet + continue + } + } + + if !fp.ShouldSyncStatusFromVotingPower(vp) { continue } - if vp > 0 { - // voting power > 0 then set the status to ACTIVE - err = app.fps.SetFpStatus(fp.BtcPk, proto.FinalityProviderStatus_ACTIVE) - if err != nil { - return err - } - } else if vp == 0 { - // voting power == 0 then set status depending on previous status - switch fp.Status { - case proto.FinalityProviderStatus_CREATED: - // previous status is CREATED then set to REGISTERED - err = app.fps.SetFpStatus(fp.BtcPk, proto.FinalityProviderStatus_REGISTERED) - if err != nil { - return err - } - case proto.FinalityProviderStatus_ACTIVE: - // previous status is ACTIVE then set to INACTIVE - err = app.fps.SetFpStatus(fp.BtcPk, proto.FinalityProviderStatus_INACTIVE) - if err != nil { - return err - } - } + bip340PubKey := fp.GetBIP340BTCPK() + if app.fpManager.IsFinalityProviderRunning(bip340PubKey) { + // there is a instance running, no need to keep syncing + fpInstanceRunning = true + // if it is already running, no need to update status + continue + } + + oldStatus := fp.Status + newStatus, err := app.fps.UpdateFpStatusFromVotingPower(vp, fp) + if err != nil { + return false, err + } + + app.logger.Info( + "Update FP status", + zap.String("fp_addr", fp.FPAddr), + zap.String("old_status", oldStatus.String()), + zap.String("new_status", newStatus.String()), + ) + fp.Status = newStatus + + if !fp.ShouldStart() { + continue } + + if err := app.fpManager.StartFinalityProvider(bip340PubKey, ""); err != nil { + return false, err + } + fpInstanceRunning = true } - return nil + return fpInstanceRunning, nil } // Start starts only the finality-provider daemon without any finality-provider instances @@ -312,7 +310,8 @@ func (app *FinalityProviderApp) Start() error { app.startOnce.Do(func() { app.logger.Info("Starting FinalityProviderApp") - app.wg.Add(3) + app.wg.Add(4) + go app.syncChainFpStatusLoop() go app.eventLoop() go app.registrationLoop() go app.metricsUpdateLoop() @@ -682,3 +681,38 @@ func (app *FinalityProviderApp) metricsUpdateLoop() { } } } + +// syncChainFpStatusLoop keeps querying the chain for the finality +// provider voting power and update the FP status accordingly. +// If there is some voting power it sets to active, for zero voting power +// it goes from: CREATED -> REGISTERED or ACTIVE -> INACTIVE. +// if there is any node running or a new finality provider instance +// is started, the loop stops. +func (app *FinalityProviderApp) syncChainFpStatusLoop() { + defer app.wg.Done() + + interval := app.config.SyncFpStatusInterval + app.logger.Info( + "starting sync FP status loop", + zap.Float64("interval seconds", interval.Seconds()), + ) + syncFpStatusTicker := time.NewTicker(interval) + + for { + select { + case <-syncFpStatusTicker.C: + fpInstanceStarted, err := app.SyncFinalityProviderStatus() + if err != nil { + app.Logger().Error("failed to sync finality-provider status", zap.Error(err)) + } + if fpInstanceStarted { + return + } + + case <-app.quit: + syncFpStatusTicker.Stop() + app.logger.Info("exiting sync FP status loop") + return + } + } +} diff --git a/finality-provider/service/chain_poller_test.go b/finality-provider/service/chain_poller_test.go index 23d6158d..f3ddcdbf 100644 --- a/finality-provider/service/chain_poller_test.go +++ b/finality-provider/service/chain_poller_test.go @@ -72,6 +72,7 @@ func FuzzChainPoller_Start(f *testing.F) { // FuzzChainPoller_SkipHeight tests the functionality of SkipHeight func FuzzChainPoller_SkipHeight(f *testing.F) { testutil.AddRandomSeedsToFuzzer(f, 10) + f.Fuzz(func(t *testing.T, seed int64) { r := rand.New(rand.NewSource(seed)) diff --git a/finality-provider/service/fp_manager.go b/finality-provider/service/fp_manager.go index 499daaae..f5d5f18b 100644 --- a/finality-provider/service/fp_manager.go +++ b/finality-provider/service/fp_manager.go @@ -35,6 +35,7 @@ func (ce *CriticalError) Error() string { type FinalityProviderManager struct { isStarted *atomic.Bool + // mutex to acess map of fp instances (fpis) mu sync.Mutex wg sync.WaitGroup @@ -246,13 +247,16 @@ func (fpm *FinalityProviderManager) StartAll() error { } for _, fp := range storedFps { - if fp.Status == proto.FinalityProviderStatus_CREATED || fp.Status == proto.FinalityProviderStatus_SLASHED { - fpm.logger.Info("the finality provider cannot be started with status", - zap.String("eots-pk", fp.GetBIP340BTCPK().MarshalHex()), - zap.String("status", fp.Status.String())) + fpBtcPk := fp.GetBIP340BTCPK() + if !fp.ShouldStart() { + fpm.logger.Info( + "the finality provider cannot be started with status", + zap.String("eots-pk", fpBtcPk.MarshalHex()), + zap.String("status", fp.Status.String()), + ) continue } - if err := fpm.StartFinalityProvider(fp.GetBIP340BTCPK(), ""); err != nil { + if err := fpm.StartFinalityProvider(fpBtcPk, ""); err != nil { return err } } @@ -266,7 +270,6 @@ func (fpm *FinalityProviderManager) Stop() error { } var stopErr error - for _, fpi := range fpm.fpis { if !fpi.IsRunning() { continue diff --git a/finality-provider/store/fpstore.go b/finality-provider/store/fpstore.go index 3afb4252..58c9f0dc 100644 --- a/finality-provider/store/fpstore.go +++ b/finality-provider/store/fpstore.go @@ -112,6 +112,29 @@ func (s *FinalityProviderStore) SetFpStatus(btcPk *btcec.PublicKey, status proto return s.setFinalityProviderState(btcPk, setFpStatus) } +// UpdateFpStatusFromVotingPower based on the current voting power of the finality provider +// updates the status, if it has some voting power, sets to active +func (s *FinalityProviderStore) UpdateFpStatusFromVotingPower( + vp uint64, + fp *StoredFinalityProvider, +) (proto.FinalityProviderStatus, error) { + if vp > 0 { + // voting power > 0 then set the status to ACTIVE + return proto.FinalityProviderStatus_ACTIVE, s.SetFpStatus(fp.BtcPk, proto.FinalityProviderStatus_ACTIVE) + } + + // voting power == 0 then set status depending on previous status + switch fp.Status { + case proto.FinalityProviderStatus_CREATED: + // previous status is CREATED then set to REGISTERED + return proto.FinalityProviderStatus_REGISTERED, s.SetFpStatus(fp.BtcPk, proto.FinalityProviderStatus_REGISTERED) + case proto.FinalityProviderStatus_ACTIVE: + // previous status is ACTIVE then set to INACTIVE + return proto.FinalityProviderStatus_INACTIVE, s.SetFpStatus(fp.BtcPk, proto.FinalityProviderStatus_INACTIVE) + } + return fp.Status, nil +} + // SetFpLastVotedHeight sets the last voted height to the stored last voted height and last processed height // only if it is larger than the stored one. This is to ensure the stored state to increase monotonically func (s *FinalityProviderStore) SetFpLastVotedHeight(btcPk *btcec.PublicKey, lastVotedHeight uint64) error { diff --git a/finality-provider/store/storedfp.go b/finality-provider/store/storedfp.go index 25b74e10..08a86402 100644 --- a/finality-provider/store/storedfp.go +++ b/finality-provider/store/storedfp.go @@ -77,3 +77,30 @@ func (sfp *StoredFinalityProvider) ToFinalityProviderInfo() *proto.FinalityProvi Status: sfp.Status.String(), } } + +// ShouldSyncStatusFromVotingPower returns true if the status should be updated +// based on the provided voting power or the current status of the finality provider. +// +// It returns true if the voting power is greater than zero, or if the status +// is either 'CREATED' or 'ACTIVE'. +func (sfp *StoredFinalityProvider) ShouldSyncStatusFromVotingPower(vp uint64) bool { + if vp > 0 { + return true + } + + return sfp.Status == proto.FinalityProviderStatus_CREATED || + sfp.Status == proto.FinalityProviderStatus_ACTIVE +} + +// ShouldStart returns true if the finality provider should start his instance +// based on the current status of the finality provider. +// +// It returns false if the status is either 'CREATED' or 'SLASHED'. +// It returs true for all the other status. +func (sfp *StoredFinalityProvider) ShouldStart() bool { + if sfp.Status == proto.FinalityProviderStatus_CREATED || sfp.Status == proto.FinalityProviderStatus_SLASHED { + return false + } + + return true +} From 8c63dae68284cb5426cb9cc07967e367c7d627cd Mon Sep 17 00:00:00 2001 From: Cirrus Gai Date: Thu, 19 Sep 2024 19:29:12 +0800 Subject: [PATCH 07/10] feat: accomodate ADR-25 to add jailed status to fp instance (#56) Partially closes #5 by introducing `jailed` status to the fp instance. The jailing status can be found in two ways: 1. The status update loop periodically checks whether the fp is slashed or jailed 2. Upon `ErrFpAlreadyJailed` error when submitting finality signature Once jailing is detected, the status of the fp instance will be set to `jailed` and terminated. --- clientcontroller/babylon.go | 8 +- clientcontroller/interface.go | 4 +- clientcontroller/retry_utils.go | 1 + eotsmanager/proto/eotsmanager.pb.go | 2 +- .../proto/finality_providers.pb.go | 104 +++++++++--------- .../proto/finality_providers.proto | 2 + finality-provider/service/fp_instance.go | 9 +- finality-provider/service/fp_manager.go | 31 +++++- finality-provider/service/fp_manager_test.go | 20 +++- go.mod | 3 +- go.sum | 6 +- testutil/mocks/babylon.go | 17 +-- tools/go.mod | 4 +- tools/go.sum | 7 +- 14 files changed, 126 insertions(+), 92 deletions(-) diff --git a/clientcontroller/babylon.go b/clientcontroller/babylon.go index 5468994a..ff89ce8d 100644 --- a/clientcontroller/babylon.go +++ b/clientcontroller/babylon.go @@ -257,16 +257,14 @@ func (bc *BabylonController) SubmitBatchFinalitySigs( return &types.TxResponse{TxHash: res.TxHash, Events: res.Events}, nil } -func (bc *BabylonController) QueryFinalityProviderSlashed(fpPk *btcec.PublicKey) (bool, error) { +func (bc *BabylonController) QueryFinalityProviderSlashedOrJailed(fpPk *btcec.PublicKey) (slashed bool, jailed bool, err error) { fpPubKey := bbntypes.NewBIP340PubKeyFromBTCPK(fpPk) res, err := bc.bbnClient.QueryClient.FinalityProvider(fpPubKey.MarshalHex()) if err != nil { - return false, fmt.Errorf("failed to query the finality provider %s: %v", fpPubKey.MarshalHex(), err) + return false, false, fmt.Errorf("failed to query the finality provider %s: %v", fpPubKey.MarshalHex(), err) } - slashed := res.FinalityProvider.SlashedBtcHeight > 0 - - return slashed, nil + return res.FinalityProvider.SlashedBtcHeight > 0, res.FinalityProvider.Jailed, nil } // QueryFinalityProviderVotingPower queries the voting power of the finality provider at a given height diff --git a/clientcontroller/interface.go b/clientcontroller/interface.go index a7100068..c1e210a5 100644 --- a/clientcontroller/interface.go +++ b/clientcontroller/interface.go @@ -45,8 +45,8 @@ type ClientController interface { // QueryFinalityProviderVotingPower queries the voting power of the finality provider at a given height QueryFinalityProviderVotingPower(fpPk *btcec.PublicKey, blockHeight uint64) (uint64, error) - // QueryFinalityProviderSlashed queries if the finality provider is slashed - QueryFinalityProviderSlashed(fpPk *btcec.PublicKey) (bool, error) + // QueryFinalityProviderSlashedOrJailed queries if the finality provider is slashed or jailed + QueryFinalityProviderSlashedOrJailed(fpPk *btcec.PublicKey) (slashed bool, jailed bool, err error) // QueryLatestFinalizedBlocks returns the latest finalized blocks QueryLatestFinalizedBlocks(count uint64) ([]*types.BlockInfo, error) diff --git a/clientcontroller/retry_utils.go b/clientcontroller/retry_utils.go index a0eb1a4c..10813622 100644 --- a/clientcontroller/retry_utils.go +++ b/clientcontroller/retry_utils.go @@ -18,6 +18,7 @@ var unrecoverableErrors = []*sdkErr.Error{ finalitytypes.ErrPubRandNotFound, finalitytypes.ErrTooFewPubRand, btcstakingtypes.ErrFpAlreadySlashed, + btcstakingtypes.ErrFpAlreadyJailed, } // IsUnrecoverable returns true when the error is in the unrecoverableErrors list diff --git a/eotsmanager/proto/eotsmanager.pb.go b/eotsmanager/proto/eotsmanager.pb.go index 50c63822..eaf5ac07 100644 --- a/eotsmanager/proto/eotsmanager.pb.go +++ b/eotsmanager/proto/eotsmanager.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.33.0 +// protoc-gen-go v1.31.0 // protoc (unknown) // source: eotsmanager.proto diff --git a/finality-provider/proto/finality_providers.pb.go b/finality-provider/proto/finality_providers.pb.go index 760750c9..fe2b59f3 100644 --- a/finality-provider/proto/finality_providers.pb.go +++ b/finality-provider/proto/finality_providers.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.33.0 +// protoc-gen-go v1.31.0 // protoc (unknown) // source: finality_providers.proto @@ -52,6 +52,8 @@ const ( FinalityProviderStatus_INACTIVE FinalityProviderStatus = 3 // SLASHED defines a finality provider that has been slashed FinalityProviderStatus_SLASHED FinalityProviderStatus = 4 + // JAILED defines a finality provider that has been jailed + FinalityProviderStatus_JAILED FinalityProviderStatus = 5 ) // Enum value maps for FinalityProviderStatus. @@ -62,6 +64,7 @@ var ( 2: "ACTIVE", 3: "INACTIVE", 4: "SLASHED", + 5: "JAILED", } FinalityProviderStatus_value = map[string]int32{ "CREATED": 0, @@ -69,6 +72,7 @@ var ( "ACTIVE": 2, "INACTIVE": 3, "SLASHED": 4, + "JAILED": 5, } ) @@ -1458,7 +1462,7 @@ var file_finality_providers_proto_rawDesc = []byte{ 0x65, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, - 0x75, 0x72, 0x65, 0x2a, 0xa6, 0x01, 0x0a, 0x16, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, + 0x75, 0x72, 0x65, 0x2a, 0xbe, 0x01, 0x0a, 0x16, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x44, 0x10, 0x00, 0x1a, 0x0b, 0x8a, 0x9d, 0x20, 0x07, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x52, 0x45, 0x47, 0x49, @@ -1468,56 +1472,58 @@ var file_finality_providers_proto_rawDesc = []byte{ 0x12, 0x1a, 0x0a, 0x08, 0x49, 0x4e, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x03, 0x1a, 0x0c, 0x8a, 0x9d, 0x20, 0x08, 0x49, 0x4e, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x12, 0x18, 0x0a, 0x07, 0x53, 0x4c, 0x41, 0x53, 0x48, 0x45, 0x44, 0x10, 0x04, 0x1a, 0x0b, 0x8a, 0x9d, 0x20, 0x07, 0x53, - 0x4c, 0x41, 0x53, 0x48, 0x45, 0x44, 0x1a, 0x04, 0x88, 0xa3, 0x1e, 0x00, 0x32, 0xc0, 0x05, 0x0a, - 0x11, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, - 0x72, 0x73, 0x12, 0x38, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x15, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x47, 0x65, 0x74, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x65, 0x0a, 0x16, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x24, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x46, 0x69, 0x6e, 0x61, 0x6c, - 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x6b, 0x0a, 0x18, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x46, - 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, - 0x26, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, - 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x4c, 0x41, 0x53, 0x48, 0x45, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x4a, 0x41, 0x49, 0x4c, 0x45, 0x44, + 0x10, 0x05, 0x1a, 0x0a, 0x8a, 0x9d, 0x20, 0x06, 0x4a, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x1a, 0x04, + 0x88, 0xa3, 0x1e, 0x00, 0x32, 0xc0, 0x05, 0x0a, 0x11, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, + 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x38, 0x0a, 0x07, 0x47, 0x65, + 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x47, 0x65, + 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x65, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x46, 0x69, + 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x24, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x46, 0x69, 0x6e, + 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6b, 0x0a, 0x18, 0x52, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x26, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, - 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x5f, 0x0a, 0x14, 0x41, 0x64, 0x64, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x53, - 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x22, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x2e, 0x41, 0x64, 0x64, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x53, 0x69, 0x67, 0x6e, - 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x64, 0x64, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, - 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x62, 0x0a, 0x15, 0x51, 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, - 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x23, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x24, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, 0x6e, - 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6e, 0x0a, 0x19, 0x51, 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, - 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4c, 0x69, - 0x73, 0x74, 0x12, 0x27, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, + 0x27, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, - 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x68, 0x0a, 0x17, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x4b, 0x65, 0x79, - 0x12, 0x25, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x4b, 0x65, 0x79, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, - 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x46, 0x72, 0x6f, 0x6d, 0x43, - 0x68, 0x61, 0x69, 0x6e, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, - 0x45, 0x5a, 0x43, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x61, - 0x62, 0x79, 0x6c, 0x6f, 0x6e, 0x6c, 0x61, 0x62, 0x73, 0x2d, 0x69, 0x6f, 0x2f, 0x66, 0x69, 0x6e, - 0x61, 0x6c, 0x69, 0x74, 0x79, 0x2d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2f, 0x66, - 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x2d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x14, 0x41, 0x64, 0x64, 0x46, + 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x12, 0x22, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x64, 0x64, 0x46, 0x69, 0x6e, 0x61, + 0x6c, 0x69, 0x74, 0x79, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x64, 0x64, + 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x62, 0x0a, 0x15, 0x51, 0x75, 0x65, + 0x72, 0x79, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x12, 0x23, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, + 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x51, 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6e, 0x0a, + 0x19, 0x51, 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, + 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x27, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, + 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x51, 0x75, 0x65, 0x72, + 0x79, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x68, 0x0a, + 0x17, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x46, 0x72, 0x6f, 0x6d, + 0x43, 0x68, 0x61, 0x69, 0x6e, 0x4b, 0x65, 0x79, 0x12, 0x25, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x46, 0x72, 0x6f, 0x6d, + 0x43, 0x68, 0x61, 0x69, 0x6e, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x26, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x4b, 0x65, 0x79, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x45, 0x5a, 0x43, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x61, 0x62, 0x79, 0x6c, 0x6f, 0x6e, 0x6c, 0x61, 0x62, + 0x73, 0x2d, 0x69, 0x6f, 0x2f, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x2d, 0x70, 0x72, + 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2f, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x2d, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/finality-provider/proto/finality_providers.proto b/finality-provider/proto/finality_providers.proto index ac4a6a64..66083d1a 100644 --- a/finality-provider/proto/finality_providers.proto +++ b/finality-provider/proto/finality_providers.proto @@ -219,6 +219,8 @@ enum FinalityProviderStatus { INACTIVE = 3 [(gogoproto.enumvalue_customname) = "INACTIVE"]; // SLASHED defines a finality provider that has been slashed SLASHED = 4 [(gogoproto.enumvalue_customname) = "SLASHED"]; + // JAILED defines a finality provider that has been jailed + JAILED = 5 [(gogoproto.enumvalue_customname) = "JAILED"]; } message SignMessageFromChainKeyRequest { diff --git a/finality-provider/service/fp_instance.go b/finality-provider/service/fp_instance.go index e5b30ba7..4d0731a2 100644 --- a/finality-provider/service/fp_instance.go +++ b/finality-provider/service/fp_instance.go @@ -907,14 +907,15 @@ func (fp *FinalityProviderInstance) GetVotingPowerWithRetry(height uint64) (uint return power, nil } -func (fp *FinalityProviderInstance) GetFinalityProviderSlashedWithRetry() (bool, error) { +func (fp *FinalityProviderInstance) GetFinalityProviderSlashedOrJailedWithRetry() (bool, bool, error) { var ( slashed bool + jailed bool err error ) if err := retry.Do(func() error { - slashed, err = fp.cc.QueryFinalityProviderSlashed(fp.GetBtcPk()) + slashed, jailed, err = fp.cc.QueryFinalityProviderSlashedOrJailed(fp.GetBtcPk()) if err != nil { return err } @@ -927,8 +928,8 @@ func (fp *FinalityProviderInstance) GetFinalityProviderSlashedWithRetry() (bool, zap.Error(err), ) })); err != nil { - return false, err + return false, false, err } - return slashed, nil + return slashed, jailed, nil } diff --git a/finality-provider/service/fp_manager.go b/finality-provider/service/fp_manager.go index f5d5f18b..12a39d3f 100644 --- a/finality-provider/service/fp_manager.go +++ b/finality-provider/service/fp_manager.go @@ -106,6 +106,12 @@ func (fpm *FinalityProviderManager) monitorCriticalErr() { zap.String("pk", criticalErr.fpBtcPk.MarshalHex())) continue } + if strings.Contains(criticalErr.err.Error(), btcstakingtypes.ErrFpAlreadyJailed.Error()) { + fpm.setFinalityProviderJailed(fpi) + fpm.logger.Debug("the finality-provider has been jailed", + zap.String("pk", criticalErr.fpBtcPk.MarshalHex())) + continue + } fpm.logger.Fatal(instanceTerminatingMsg, zap.String("pk", criticalErr.fpBtcPk.MarshalHex()), zap.Error(criticalErr.err)) case <-fpm.quit: @@ -166,25 +172,35 @@ func (fpm *FinalityProviderManager) monitorStatusUpdate() { } continue } - slashed, err := fpi.GetFinalityProviderSlashedWithRetry() + slashed, jailed, err := fpi.GetFinalityProviderSlashedOrJailedWithRetry() if err != nil { fpm.logger.Debug( - "failed to get the slashed height", + "failed to get the slashed or jailed status", zap.String("fp_btc_pk", fpi.GetBtcPkHex()), zap.Error(err), ) continue } - // power == 0 and slashed == true, set status to SLASHED and stop and remove the finality-provider instance + // power == 0 and slashed == true, set status to SLASHED, stop, and remove the finality-provider instance if slashed { fpm.setFinalityProviderSlashed(fpi) - fpm.logger.Debug( + fpm.logger.Warn( "the finality-provider is slashed", zap.String("fp_btc_pk", fpi.GetBtcPkHex()), zap.String("old_status", oldStatus.String()), ) continue } + // power == 0 and jailed == true, set status to JAILED, stop, and remove the finality-provider instance + if jailed { + fpm.setFinalityProviderJailed(fpi) + fpm.logger.Warn( + "the finality-provider is jailed", + zap.String("fp_btc_pk", fpi.GetBtcPkHex()), + zap.String("old_status", oldStatus.String()), + ) + continue + } // power == 0 and slashed_height == 0, change to INACTIVE if the current status is ACTIVE if oldStatus == proto.FinalityProviderStatus_ACTIVE { fpi.MustSetStatus(proto.FinalityProviderStatus_INACTIVE) @@ -208,6 +224,13 @@ func (fpm *FinalityProviderManager) setFinalityProviderSlashed(fpi *FinalityProv } } +func (fpm *FinalityProviderManager) setFinalityProviderJailed(fpi *FinalityProviderInstance) { + fpi.MustSetStatus(proto.FinalityProviderStatus_JAILED) + if err := fpm.removeFinalityProviderInstance(fpi.GetBtcPkBIP340()); err != nil { + panic(fmt.Errorf("failed to terminate a jailed finality-provider %s: %w", fpi.GetBtcPkHex(), err)) + } +} + func (fpm *FinalityProviderManager) StartFinalityProvider(fpPk *bbntypes.BIP340PubKey, passphrase string) error { if !fpm.isStarted.Load() { fpm.isStarted.Store(true) diff --git a/finality-provider/service/fp_manager_test.go b/finality-provider/service/fp_manager_test.go index 5c77be61..71a02627 100644 --- a/finality-provider/service/fp_manager_test.go +++ b/finality-provider/service/fp_manager_test.go @@ -30,7 +30,7 @@ import ( ) var ( - eventuallyWaitTimeOut = 1 * time.Second + eventuallyWaitTimeOut = 5 * time.Second eventuallyPollTime = 10 * time.Millisecond ) @@ -61,9 +61,17 @@ func FuzzStatusUpdate(f *testing.F) { votingPower := uint64(r.Intn(2)) mockClientController.EXPECT().QueryFinalityProviderVotingPower(gomock.Any(), currentHeight).Return(votingPower, nil).AnyTimes() mockClientController.EXPECT().SubmitFinalitySig(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&types.TxResponse{TxHash: ""}, nil).AnyTimes() - var slashedHeight uint64 + var isSlashedOrJailed int if votingPower == 0 { - mockClientController.EXPECT().QueryFinalityProviderSlashed(gomock.Any()).Return(true, nil).AnyTimes() + // 0 means is slashed, 1 means is jailed, 2 means neither slashed nor jailed + isSlashedOrJailed = r.Intn(3) + if isSlashedOrJailed == 0 { + mockClientController.EXPECT().QueryFinalityProviderSlashedOrJailed(gomock.Any()).Return(true, false, nil).AnyTimes() + } else if isSlashedOrJailed == 1 { + mockClientController.EXPECT().QueryFinalityProviderSlashedOrJailed(gomock.Any()).Return(false, true, nil).AnyTimes() + } else { + mockClientController.EXPECT().QueryFinalityProviderSlashedOrJailed(gomock.Any()).Return(false, false, nil).AnyTimes() + } } err := vm.StartFinalityProvider(fpPk, passphrase) @@ -76,9 +84,11 @@ func FuzzStatusUpdate(f *testing.F) { if votingPower > 0 { waitForStatus(t, fpIns, proto.FinalityProviderStatus_ACTIVE) } else { - if slashedHeight == 0 && fpIns.GetStatus() == proto.FinalityProviderStatus_ACTIVE { + if isSlashedOrJailed == 2 && fpIns.GetStatus() == proto.FinalityProviderStatus_ACTIVE { waitForStatus(t, fpIns, proto.FinalityProviderStatus_INACTIVE) - } else if slashedHeight > 0 { + } else if isSlashedOrJailed == 1 { + waitForStatus(t, fpIns, proto.FinalityProviderStatus_JAILED) + } else if isSlashedOrJailed == 0 { waitForStatus(t, fpIns, proto.FinalityProviderStatus_SLASHED) } } diff --git a/go.mod b/go.mod index 349600b5..dfc9c8ad 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( cosmossdk.io/errors v1.0.1 cosmossdk.io/math v1.3.0 github.com/avast/retry-go/v4 v4.5.1 - github.com/babylonlabs-io/babylon v0.9.3-0.20240904154239-00330e49b177 + github.com/babylonlabs-io/babylon v0.9.3-0.20240919093955-dec7fa8fff9b github.com/btcsuite/btcd v0.24.2 github.com/btcsuite/btcd/btcec/v2 v2.3.2 github.com/btcsuite/btcd/btcutil v1.1.5 @@ -108,7 +108,6 @@ require ( github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect github.com/distribution/reference v0.5.0 // indirect - github.com/docker/docker v24.0.9+incompatible // indirect github.com/dsnet/compress v0.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/dvsekhvalnov/jose2go v1.6.0 // indirect diff --git a/go.sum b/go.sum index 83df9ab9..7deb5272 100644 --- a/go.sum +++ b/go.sum @@ -281,8 +281,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.9.3-0.20240904154239-00330e49b177 h1:rcbSkxPZVl5AdWt40prUUzj7Rpuv3jD6PvlLh1dQ0DM= -github.com/babylonlabs-io/babylon v0.9.3-0.20240904154239-00330e49b177/go.mod h1:9VUUAwVaalXiDdPZT65SPoawKWpp6ple6tBr8Vw0NI8= +github.com/babylonlabs-io/babylon v0.9.3-0.20240919093955-dec7fa8fff9b h1:2PU+equYzx7Hw1Tl2uO800R36suqXjnYl0XPLyNeSA0= +github.com/babylonlabs-io/babylon v0.9.3-0.20240919093955-dec7fa8fff9b/go.mod h1:ZOrTde9vs2xoqGTFw4xhupu2CMulnpywiuk0eh4kPOw= 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= @@ -475,8 +475,6 @@ github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WA github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v24.0.9+incompatible h1:HPGzNmwfLZWdxHqK9/II92pyi1EpYKsAqcl4G0Of9v0= -github.com/docker/docker v24.0.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= diff --git a/testutil/mocks/babylon.go b/testutil/mocks/babylon.go index 09210f01..31be816d 100644 --- a/testutil/mocks/babylon.go +++ b/testutil/mocks/babylon.go @@ -127,19 +127,20 @@ func (mr *MockClientControllerMockRecorder) QueryBlocks(startHeight, endHeight, return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryBlocks", reflect.TypeOf((*MockClientController)(nil).QueryBlocks), startHeight, endHeight, limit) } -// QueryFinalityProviderSlashed mocks base method. -func (m *MockClientController) QueryFinalityProviderSlashed(fpPk *btcec.PublicKey) (bool, error) { +// QueryFinalityProviderSlashedOrJailed mocks base method. +func (m *MockClientController) QueryFinalityProviderSlashedOrJailed(fpPk *btcec.PublicKey) (bool, bool, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "QueryFinalityProviderSlashed", fpPk) + ret := m.ctrl.Call(m, "QueryFinalityProviderSlashedOrJailed", fpPk) ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 + ret1, _ := ret[1].(bool) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 } -// QueryFinalityProviderSlashed indicates an expected call of QueryFinalityProviderSlashed. -func (mr *MockClientControllerMockRecorder) QueryFinalityProviderSlashed(fpPk interface{}) *gomock.Call { +// QueryFinalityProviderSlashedOrJailed indicates an expected call of QueryFinalityProviderSlashedOrJailed. +func (mr *MockClientControllerMockRecorder) QueryFinalityProviderSlashedOrJailed(fpPk interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryFinalityProviderSlashed", reflect.TypeOf((*MockClientController)(nil).QueryFinalityProviderSlashed), fpPk) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryFinalityProviderSlashedOrJailed", reflect.TypeOf((*MockClientController)(nil).QueryFinalityProviderSlashedOrJailed), fpPk) } // QueryFinalityProviderVotingPower mocks base method. diff --git a/tools/go.mod b/tools/go.mod index 2ce0cca6..18d48e7e 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -4,7 +4,7 @@ go 1.21 toolchain go1.21.4 -require github.com/babylonlabs-io/babylon v0.9.3-0.20240904154239-00330e49b177 +require github.com/babylonlabs-io/babylon v0.9.3-0.20240919093955-dec7fa8fff9b require ( cloud.google.com/go v0.112.0 // indirect @@ -83,7 +83,6 @@ require ( github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect github.com/distribution/reference v0.5.0 // indirect - github.com/docker/docker v24.0.9+incompatible // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/dvsekhvalnov/jose2go v1.6.0 // indirect github.com/emicklei/dot v1.6.1 // indirect @@ -171,7 +170,6 @@ require ( github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sasha-s/go-deadlock v0.3.1 // indirect - github.com/sirupsen/logrus v1.9.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.6.0 // indirect diff --git a/tools/go.sum b/tools/go.sum index a0af02a0..68f96a22 100644 --- a/tools/go.sum +++ b/tools/go.sum @@ -268,8 +268,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.9.3-0.20240904154239-00330e49b177 h1:rcbSkxPZVl5AdWt40prUUzj7Rpuv3jD6PvlLh1dQ0DM= -github.com/babylonlabs-io/babylon v0.9.3-0.20240904154239-00330e49b177/go.mod h1:9VUUAwVaalXiDdPZT65SPoawKWpp6ple6tBr8Vw0NI8= +github.com/babylonlabs-io/babylon v0.9.3-0.20240919093955-dec7fa8fff9b h1:2PU+equYzx7Hw1Tl2uO800R36suqXjnYl0XPLyNeSA0= +github.com/babylonlabs-io/babylon v0.9.3-0.20240919093955-dec7fa8fff9b/go.mod h1:ZOrTde9vs2xoqGTFw4xhupu2CMulnpywiuk0eh4kPOw= 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= @@ -443,8 +443,6 @@ github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WA github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v24.0.9+incompatible h1:HPGzNmwfLZWdxHqK9/II92pyi1EpYKsAqcl4G0Of9v0= -github.com/docker/docker v24.0.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= @@ -1389,7 +1387,6 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= From 7a0311b46e299aa32a02637769ea12859b9c8220 Mon Sep 17 00:00:00 2001 From: Cirrus Gai Date: Fri, 20 Sep 2024 18:01:51 +0800 Subject: [PATCH 08/10] chore: Add unjail-finality-provider cmd (#60) --- finality-provider/cmd/fpd/daemon/export.go | 10 +++++----- finality-provider/cmd/fpd/daemon/keys.go | 5 +++-- finality-provider/cmd/fpd/daemon/tx.go | 2 ++ 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/finality-provider/cmd/fpd/daemon/export.go b/finality-provider/cmd/fpd/daemon/export.go index c601aa6a..8686628b 100644 --- a/finality-provider/cmd/fpd/daemon/export.go +++ b/finality-provider/cmd/fpd/daemon/export.go @@ -6,16 +6,16 @@ import ( "fmt" "cosmossdk.io/math" - fpcmd "github.com/babylonlabs-io/finality-provider/finality-provider/cmd" - fpcfg "github.com/babylonlabs-io/finality-provider/finality-provider/config" - dc "github.com/babylonlabs-io/finality-provider/finality-provider/service/client" + bbn "github.com/babylonlabs-io/babylon/types" + btcstktypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" "github.com/cosmos/cosmos-sdk/client" sdkflags "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/spf13/cobra" - bbn "github.com/babylonlabs-io/babylon/types" - btcstktypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" + fpcmd "github.com/babylonlabs-io/finality-provider/finality-provider/cmd" + fpcfg "github.com/babylonlabs-io/finality-provider/finality-provider/config" + dc "github.com/babylonlabs-io/finality-provider/finality-provider/service/client" ) // FinalityProviderSigned wraps the finality provider by adding the diff --git a/finality-provider/cmd/fpd/daemon/keys.go b/finality-provider/cmd/fpd/daemon/keys.go index dbc9fbec..e4cf9e42 100644 --- a/finality-provider/cmd/fpd/daemon/keys.go +++ b/finality-provider/cmd/fpd/daemon/keys.go @@ -3,13 +3,14 @@ package daemon import ( "strings" - helper "github.com/babylonlabs-io/finality-provider/finality-provider/cmd" - fpcfg "github.com/babylonlabs-io/finality-provider/finality-provider/config" "github.com/cosmos/cosmos-sdk/client" sdkflags "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/keys" goflags "github.com/jessevdk/go-flags" "github.com/spf13/cobra" + + helper "github.com/babylonlabs-io/finality-provider/finality-provider/cmd" + fpcfg "github.com/babylonlabs-io/finality-provider/finality-provider/config" ) // CommandKeys returns the keys group command and updates the add command to do a diff --git a/finality-provider/cmd/fpd/daemon/tx.go b/finality-provider/cmd/fpd/daemon/tx.go index f13339d2..9a5897de 100644 --- a/finality-provider/cmd/fpd/daemon/tx.go +++ b/finality-provider/cmd/fpd/daemon/tx.go @@ -9,6 +9,7 @@ import ( btcstakingcli "github.com/babylonlabs-io/babylon/x/btcstaking/client/cli" btcstakingtypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" + finalitycli "github.com/babylonlabs-io/babylon/x/finality/client/cli" sdk "github.com/cosmos/cosmos-sdk/types" authclient "github.com/cosmos/cosmos-sdk/x/auth/client" authcli "github.com/cosmos/cosmos-sdk/x/auth/client/cli" @@ -27,6 +28,7 @@ func CommandTxs() *cobra.Command { cmd.AddCommand( authcli.GetSignCommand(), btcstakingcli.NewCreateFinalityProviderCmd(), + finalitycli.NewUnjailFinalityProviderCmd(), NewValidateSignedFinalityProviderCmd(), ) From 4f1469c48fecf5afc4affd96d57c22f128595e34 Mon Sep 17 00:00:00 2001 From: Rafael Tenfen Date: Fri, 20 Sep 2024 08:22:52 -0300 Subject: [PATCH 09/10] chore: keep sync fp test (#58) Add tests to created functions at #52 --- finality-provider/service/app.go | 22 ++-- finality-provider/service/app_test.go | 83 +++++++++++++ finality-provider/store/fpstore.go | 7 +- finality-provider/store/fpstore_test.go | 141 +++++++++++++++++++++++ finality-provider/store/storedfp.go | 14 --- finality-provider/store/storedfp_test.go | 55 +++++++++ 6 files changed, 295 insertions(+), 27 deletions(-) create mode 100644 finality-provider/store/storedfp_test.go diff --git a/finality-provider/service/app.go b/finality-provider/service/app.go index 39ea9d96..6c5c7c2a 100644 --- a/finality-provider/service/app.go +++ b/finality-provider/service/app.go @@ -265,10 +265,6 @@ func (app *FinalityProviderApp) SyncFinalityProviderStatus() (fpInstanceRunning } } - if !fp.ShouldSyncStatusFromVotingPower(vp) { - continue - } - bip340PubKey := fp.GetBIP340BTCPK() if app.fpManager.IsFinalityProviderRunning(bip340PubKey) { // there is a instance running, no need to keep syncing @@ -283,13 +279,15 @@ func (app *FinalityProviderApp) SyncFinalityProviderStatus() (fpInstanceRunning return false, err } - app.logger.Info( - "Update FP status", - zap.String("fp_addr", fp.FPAddr), - zap.String("old_status", oldStatus.String()), - zap.String("new_status", newStatus.String()), - ) - fp.Status = newStatus + if oldStatus != newStatus { + app.logger.Info( + "Update FP status", + zap.String("fp_addr", fp.FPAddr), + zap.String("old_status", oldStatus.String()), + zap.String("new_status", newStatus.String()), + ) + fp.Status = newStatus + } if !fp.ShouldStart() { continue @@ -697,6 +695,7 @@ func (app *FinalityProviderApp) syncChainFpStatusLoop() { zap.Float64("interval seconds", interval.Seconds()), ) syncFpStatusTicker := time.NewTicker(interval) + defer syncFpStatusTicker.Stop() for { select { @@ -710,7 +709,6 @@ func (app *FinalityProviderApp) syncChainFpStatusLoop() { } case <-app.quit: - syncFpStatusTicker.Stop() app.logger.Info("exiting sync FP status loop") return } diff --git a/finality-provider/service/app_test.go b/finality-provider/service/app_test.go index 49bdeb46..c9772897 100644 --- a/finality-provider/service/app_test.go +++ b/finality-provider/service/app_test.go @@ -1,11 +1,16 @@ package service_test import ( + "errors" + "fmt" "math/rand" "os" "path/filepath" + "strings" "testing" + "time" + "github.com/babylonlabs-io/babylon/testutil/datagen" bbntypes "github.com/babylonlabs-io/babylon/types" bstypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" "github.com/golang/mock/gomock" @@ -138,3 +143,81 @@ func FuzzRegisterFinalityProvider(f *testing.F) { require.Equal(t, true, fpInfo.IsRunning) }) } + +func FuzzSyncFinalityProviderStatus(f *testing.F) { + testutil.AddRandomSeedsToFuzzer(f, 14) + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + logger := zap.NewNop() + + pathSuffix := datagen.GenRandomHexStr(r, 10) + // create an EOTS manager + eotsHomeDir := filepath.Join(t.TempDir(), "eots-home", pathSuffix) + eotsCfg := eotscfg.DefaultConfigWithHomePath(eotsHomeDir) + dbBackend, err := eotsCfg.DatabaseConfig.GetDbBackend() + require.NoError(t, err) + em, err := eotsmanager.NewLocalEOTSManager(eotsHomeDir, eotsCfg.KeyringBackend, dbBackend, logger) + require.NoError(t, err) + + // Create randomized config + fpHomeDir := filepath.Join(t.TempDir(), "fp-home", pathSuffix) + fpCfg := config.DefaultConfigWithHome(fpHomeDir) + fpCfg.SyncFpStatusInterval = time.Millisecond * 100 + // no need for other intervals to run + fpCfg.StatusUpdateInterval = time.Minute * 10 + fpCfg.SubmissionRetryInterval = time.Minute * 10 + fpdb, err := fpCfg.DatabaseConfig.GetDbBackend() + require.NoError(t, err) + + randomStartingHeight := uint64(r.Int63n(100) + 1) + currentHeight := randomStartingHeight + uint64(r.Int63n(10)+2) + mockClientController := testutil.PrepareMockedClientController(t, r, randomStartingHeight, currentHeight) + + blkInfo := &types.BlockInfo{Height: currentHeight} + + mockClientController.EXPECT().QueryLatestFinalizedBlocks(gomock.Any()).Return(nil, nil).AnyTimes() + mockClientController.EXPECT().QueryBestBlock().Return(blkInfo, nil).Return(blkInfo, nil).AnyTimes() + mockClientController.EXPECT().QueryBlock(gomock.Any()).Return(nil, errors.New("chain not online")).AnyTimes() + + noVotingPowerTable := r.Int31n(10) > 5 + if noVotingPowerTable { + allowedErr := fmt.Sprintf("failed to query Finality Voting Power at Height %d: rpc error: code = Unknown desc = %s: unknown request", currentHeight, bstypes.ErrVotingPowerTableNotUpdated.Wrapf("height: %d", currentHeight).Error()) + mockClientController.EXPECT().QueryFinalityProviderVotingPower(gomock.Any(), gomock.Any()).Return(uint64(0), errors.New(allowedErr)).AnyTimes() + mockClientController.EXPECT().QueryActivatedHeight().Return(uint64(0), errors.New(allowedErr)).AnyTimes() + } else { + mockClientController.EXPECT().QueryActivatedHeight().Return(currentHeight, nil).AnyTimes() + mockClientController.EXPECT().QueryFinalityProviderVotingPower(gomock.Any(), gomock.Any()).Return(uint64(2), nil).AnyTimes() + } + + app, err := service.NewFinalityProviderApp(&fpCfg, mockClientController, em, fpdb, logger) + require.NoError(t, err) + + err = app.Start() + require.NoError(t, err) + + fp := testutil.GenStoredFinalityProvider(r, t, app, "", hdPath, nil) + + require.Eventually(t, func() bool { + fpPk := fp.GetBIP340BTCPK() + fpInfo, err := app.GetFinalityProviderInfo(fpPk) + if err != nil { + return false + } + + expectedStatus := proto.FinalityProviderStatus_ACTIVE + if noVotingPowerTable { + expectedStatus = proto.FinalityProviderStatus_REGISTERED + } + fpInstance, err := app.GetFinalityProviderInstance(fpPk) + if err != nil { + return false + } + + // TODO: verify why mocks are failing + btcPkEqual := fpInstance.GetBtcPk().IsEqual(fp.BtcPk) + statusEqual := strings.EqualFold(fpInfo.Status, expectedStatus.String()) + return statusEqual && btcPkEqual + }, time.Second*5, time.Millisecond*200, "should eventually be registered or active") + }) +} diff --git a/finality-provider/store/fpstore.go b/finality-provider/store/fpstore.go index 58c9f0dc..24715279 100644 --- a/finality-provider/store/fpstore.go +++ b/finality-provider/store/fpstore.go @@ -117,7 +117,12 @@ func (s *FinalityProviderStore) SetFpStatus(btcPk *btcec.PublicKey, status proto func (s *FinalityProviderStore) UpdateFpStatusFromVotingPower( vp uint64, fp *StoredFinalityProvider, -) (proto.FinalityProviderStatus, error) { +) (newStatus proto.FinalityProviderStatus, err error) { + if fp.Status == proto.FinalityProviderStatus_SLASHED { + // Slashed FP should not update status + return proto.FinalityProviderStatus_SLASHED, nil + } + if vp > 0 { // voting power > 0 then set the status to ACTIVE return proto.FinalityProviderStatus_ACTIVE, s.SetFpStatus(fp.BtcPk, proto.FinalityProviderStatus_ACTIVE) diff --git a/finality-provider/store/fpstore_test.go b/finality-provider/store/fpstore_test.go index 54feccf7..eb18a23c 100644 --- a/finality-provider/store/fpstore_test.go +++ b/finality-provider/store/fpstore_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/require" "github.com/babylonlabs-io/finality-provider/finality-provider/config" + "github.com/babylonlabs-io/finality-provider/finality-provider/proto" fpstore "github.com/babylonlabs-io/finality-provider/finality-provider/store" "github.com/babylonlabs-io/finality-provider/testutil" sdk "github.com/cosmos/cosmos-sdk/types" @@ -78,3 +79,143 @@ func FuzzFinalityProvidersStore(f *testing.F) { require.ErrorIs(t, err, fpstore.ErrFinalityProviderNotFound) }) } + +func TestUpdateFpStatusFromVotingPower(t *testing.T) { + r := rand.New(rand.NewSource(10)) + anyFpStatus := proto.FinalityProviderStatus(100) + + tcs := []struct { + name string + fpStoredStatus proto.FinalityProviderStatus + votingPowerOnChain uint64 + expStatus proto.FinalityProviderStatus + expErr error + }{ + { + "zero vp: Created to Registered", + proto.FinalityProviderStatus_CREATED, + 0, + proto.FinalityProviderStatus_REGISTERED, + nil, + }, + { + "zero vp: Active to Inactive", + proto.FinalityProviderStatus_ACTIVE, + 0, + proto.FinalityProviderStatus_INACTIVE, + nil, + }, + { + "zero vp: Registered should not update the status, but also not error out", + proto.FinalityProviderStatus_REGISTERED, + 0, + proto.FinalityProviderStatus_REGISTERED, + nil, + }, + { + "zero vp: Slashed to Slashed", + proto.FinalityProviderStatus_SLASHED, + 0, + proto.FinalityProviderStatus_SLASHED, + nil, + }, + { + "err: Slashed should not update status", + proto.FinalityProviderStatus_SLASHED, + 15, + proto.FinalityProviderStatus_SLASHED, + nil, + }, + { + "vp > 0: Created to Active", + proto.FinalityProviderStatus_CREATED, + 1, + proto.FinalityProviderStatus_ACTIVE, + nil, + }, + { + "vp > 0: Registered to Active", + proto.FinalityProviderStatus_REGISTERED, + 1, + proto.FinalityProviderStatus_ACTIVE, + nil, + }, + { + "vp > 0: Inactive to Active", + proto.FinalityProviderStatus_INACTIVE, + 1, + proto.FinalityProviderStatus_ACTIVE, + nil, + }, + { + "err: fp not found and vp > 0", + proto.FinalityProviderStatus_INACTIVE, + 1, + anyFpStatus, + fpstore.ErrFinalityProviderNotFound, + }, + { + "err: fp not found and vp == 0 && created", + proto.FinalityProviderStatus_CREATED, + 0, + anyFpStatus, + fpstore.ErrFinalityProviderNotFound, + }, + { + "err: fp not found and vp == 0 && active", + proto.FinalityProviderStatus_ACTIVE, + 0, + anyFpStatus, + fpstore.ErrFinalityProviderNotFound, + }, + } + + homePath := t.TempDir() + cfg := config.DefaultDBConfigWithHomePath(homePath) + + fpdb, err := cfg.GetDbBackend() + require.NoError(t, err) + fps, err := fpstore.NewFinalityProviderStore(fpdb) + require.NoError(t, err) + + defer func() { + err := fpdb.Close() + require.NoError(t, err) + err = os.RemoveAll(homePath) + require.NoError(t, err) + }() + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + fp := testutil.GenRandomFinalityProvider(r, t) + fp.Status = tc.fpStoredStatus + if tc.expErr == nil { + err = fps.CreateFinalityProvider( + sdk.MustAccAddressFromBech32(fp.FPAddr), + fp.BtcPk, + fp.Description, + fp.Commission, + fp.KeyName, + fp.ChainID, + fp.Pop.BtcSig, + ) + require.NoError(t, err) + + err = fps.SetFpStatus(fp.BtcPk, fp.Status) + require.NoError(t, err) + } + + actStatus, err := fps.UpdateFpStatusFromVotingPower(tc.votingPowerOnChain, fp) + if tc.expErr != nil { + require.EqualError(t, err, tc.expErr.Error()) + return + } + require.NoError(t, err) + require.Equal(t, tc.expStatus, actStatus) + + storedFp, err := fps.GetFinalityProvider(fp.BtcPk) + require.NoError(t, err) + require.Equal(t, tc.expStatus, storedFp.Status) + }) + } +} diff --git a/finality-provider/store/storedfp.go b/finality-provider/store/storedfp.go index 08a86402..84c2c4b0 100644 --- a/finality-provider/store/storedfp.go +++ b/finality-provider/store/storedfp.go @@ -78,20 +78,6 @@ func (sfp *StoredFinalityProvider) ToFinalityProviderInfo() *proto.FinalityProvi } } -// ShouldSyncStatusFromVotingPower returns true if the status should be updated -// based on the provided voting power or the current status of the finality provider. -// -// It returns true if the voting power is greater than zero, or if the status -// is either 'CREATED' or 'ACTIVE'. -func (sfp *StoredFinalityProvider) ShouldSyncStatusFromVotingPower(vp uint64) bool { - if vp > 0 { - return true - } - - return sfp.Status == proto.FinalityProviderStatus_CREATED || - sfp.Status == proto.FinalityProviderStatus_ACTIVE -} - // ShouldStart returns true if the finality provider should start his instance // based on the current status of the finality provider. // diff --git a/finality-provider/store/storedfp_test.go b/finality-provider/store/storedfp_test.go new file mode 100644 index 00000000..13d9f44d --- /dev/null +++ b/finality-provider/store/storedfp_test.go @@ -0,0 +1,55 @@ +package store_test + +import ( + "math/rand" + "testing" + + "github.com/babylonlabs-io/finality-provider/finality-provider/proto" + "github.com/babylonlabs-io/finality-provider/testutil" + "github.com/stretchr/testify/require" +) + +func TestShouldStart(t *testing.T) { + tcs := []struct { + name string + currFpStatus proto.FinalityProviderStatus + expShouldStart bool + }{ + { + "Created: Should NOT start", + proto.FinalityProviderStatus_CREATED, + false, + }, + { + "Slashed: Should NOT start", + proto.FinalityProviderStatus_SLASHED, + false, + }, + { + "Inactive: Should start", + proto.FinalityProviderStatus_INACTIVE, + true, + }, + { + "Registered: Should start", + proto.FinalityProviderStatus_REGISTERED, + true, + }, + { + "Active: Should start", + proto.FinalityProviderStatus_ACTIVE, + true, + }, + } + + r := rand.New(rand.NewSource(10)) + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + fp := testutil.GenRandomFinalityProvider(r, t) + fp.Status = tc.currFpStatus + + shouldStart := fp.ShouldStart() + require.Equal(t, tc.expShouldStart, shouldStart) + }) + } +} From 8bb68fff430728747d7ede6d237f4d6cf05fc07b Mon Sep 17 00:00:00 2001 From: Cirrus Gai Date: Mon, 23 Sep 2024 09:41:20 +0800 Subject: [PATCH 10/10] chore: Bump babylon to v0.10.0 (#61) --- go.mod | 2 +- go.sum | 4 ++-- tools/go.mod | 2 +- tools/go.sum | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index dfc9c8ad..b0949398 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( cosmossdk.io/errors v1.0.1 cosmossdk.io/math v1.3.0 github.com/avast/retry-go/v4 v4.5.1 - github.com/babylonlabs-io/babylon v0.9.3-0.20240919093955-dec7fa8fff9b + github.com/babylonlabs-io/babylon v0.10.0 github.com/btcsuite/btcd v0.24.2 github.com/btcsuite/btcd/btcec/v2 v2.3.2 github.com/btcsuite/btcd/btcutil v1.1.5 diff --git a/go.sum b/go.sum index 7deb5272..77e0f596 100644 --- a/go.sum +++ b/go.sum @@ -281,8 +281,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.9.3-0.20240919093955-dec7fa8fff9b h1:2PU+equYzx7Hw1Tl2uO800R36suqXjnYl0XPLyNeSA0= -github.com/babylonlabs-io/babylon v0.9.3-0.20240919093955-dec7fa8fff9b/go.mod h1:ZOrTde9vs2xoqGTFw4xhupu2CMulnpywiuk0eh4kPOw= +github.com/babylonlabs-io/babylon v0.10.0 h1:y6Mu4P8IolylybASRn33am/B7OZEwNXGhWHTC1oSnu8= +github.com/babylonlabs-io/babylon v0.10.0/go.mod h1:ZOrTde9vs2xoqGTFw4xhupu2CMulnpywiuk0eh4kPOw= 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/tools/go.mod b/tools/go.mod index 18d48e7e..d29c5941 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -4,7 +4,7 @@ go 1.21 toolchain go1.21.4 -require github.com/babylonlabs-io/babylon v0.9.3-0.20240919093955-dec7fa8fff9b +require github.com/babylonlabs-io/babylon v0.10.0 require ( cloud.google.com/go v0.112.0 // indirect diff --git a/tools/go.sum b/tools/go.sum index 68f96a22..f9524a99 100644 --- a/tools/go.sum +++ b/tools/go.sum @@ -268,8 +268,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.9.3-0.20240919093955-dec7fa8fff9b h1:2PU+equYzx7Hw1Tl2uO800R36suqXjnYl0XPLyNeSA0= -github.com/babylonlabs-io/babylon v0.9.3-0.20240919093955-dec7fa8fff9b/go.mod h1:ZOrTde9vs2xoqGTFw4xhupu2CMulnpywiuk0eh4kPOw= +github.com/babylonlabs-io/babylon v0.10.0 h1:y6Mu4P8IolylybASRn33am/B7OZEwNXGhWHTC1oSnu8= +github.com/babylonlabs-io/babylon v0.10.0/go.mod h1:ZOrTde9vs2xoqGTFw4xhupu2CMulnpywiuk0eh4kPOw= 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=