diff --git a/config/config-local.yml b/config/config-local.yml index 8e95bd5..4aab66e 100644 --- a/config/config-local.yml +++ b/config/config-local.yml @@ -1,7 +1,7 @@ db: username: root password: example - address: "mongodb://localhost:27017" + address: "mongodb://localhost:27019/?replicaSet=RS&directConnection=true" db-name: babylon-staking-indexer btc: endpoint: localhost:18332 diff --git a/contrib/images/babylon-staking-indexer/Dockerfile b/contrib/images/babylon-staking-indexer/Dockerfile index 19a3309..991416d 100644 --- a/contrib/images/babylon-staking-indexer/Dockerfile +++ b/contrib/images/babylon-staking-indexer/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.21-alpine AS builder +FROM golang:1.22.3-alpine AS builder ARG VERSION="HEAD" diff --git a/docker-compose.yml b/docker-compose.yml index b300dfd..a41174e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,21 +1,21 @@ version: "3.9" services: babylon-staking-indexer: - image: babylonlabs/babylon-staking-indexer:latest + image: babylonlabs-io/babylon-staking-indexer:latest container_name: babylon-staking-indexer environment: - CONFIG=/home/babylon-staking-indexer/config.yml depends_on: - - mongodb + - indexer-mongodb - rabbitmq volumes: - ./config/config-docker.yml:/home/babylon-staking-indexer/config.yml:Z - mongodb: + indexer-mongodb: image: mongo:latest container_name: indexer-mongodb hostname: indexer-mongodb ports: - - "27017:27017" + - "27019:27017" environment: MONGO_INITDB_ROOT_USERNAME: root MONGO_INITDB_ROOT_PASSWORD: example diff --git a/go.mod b/go.mod index 5ac1aad..680d157 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,14 @@ module github.com/babylonlabs-io/babylon-staking-indexer -go 1.23.2 +go 1.22.3 require ( github.com/babylonlabs-io/babylon v0.12.1 github.com/babylonlabs-io/staking-queue-client v0.4.1 github.com/btcsuite/btcd v0.24.2 github.com/cometbft/cometbft v0.38.7 + github.com/cosmos/cosmos-sdk v0.50.6 + github.com/cosmos/gogoproto v1.7.0 github.com/go-chi/chi/v5 v5.1.0 github.com/spf13/viper v1.19.0 ) @@ -62,10 +64,8 @@ require ( github.com/cosmos/btcutil v1.0.5 // indirect github.com/cosmos/cosmos-db v1.0.2 // indirect github.com/cosmos/cosmos-proto v1.0.0-beta.5 // indirect - github.com/cosmos/cosmos-sdk v0.50.6 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect github.com/cosmos/gogogateway v1.2.0 // indirect - github.com/cosmos/gogoproto v1.7.0 // indirect github.com/cosmos/iavl v1.1.2 // indirect github.com/cosmos/ibc-go/modules/capability v1.0.0 // indirect github.com/cosmos/ibc-go/v8 v8.3.0 // indirect diff --git a/internal/db/delegation.go b/internal/db/delegation.go new file mode 100644 index 0000000..d5e5fbe --- /dev/null +++ b/internal/db/delegation.go @@ -0,0 +1,79 @@ +package db + +import ( + "context" + "errors" + + "github.com/babylonlabs-io/babylon-staking-indexer/internal/db/model" + "github.com/babylonlabs-io/babylon-staking-indexer/internal/types" + "go.mongodb.org/mongo-driver/mongo" +) + +func (db *Database) SaveNewBTCDelegation( + ctx context.Context, delegationDoc *model.BTCDelegationDetails, +) error { + _, err := db.client.Database(db.dbName). + Collection(model.BTCDelegationDetailsCollection). + InsertOne(ctx, delegationDoc) + if err != nil { + var writeErr mongo.WriteException + if errors.As(err, &writeErr) { + for _, e := range writeErr.WriteErrors { + if mongo.IsDuplicateKeyError(e) { + return &DuplicateKeyError{ + Key: delegationDoc.StakingTxHashHex, + Message: "delegation already exists", + } + } + } + } + return err + } + return nil +} + +func (db *Database) UpdateBTCDelegationState( + ctx context.Context, stakingTxHash string, newState types.DelegationState, +) error { + filter := map[string]interface{}{"_id": stakingTxHash} + update := map[string]interface{}{"$set": map[string]string{"state": newState.String()}} + + res := db.client.Database(db.dbName). + Collection(model.BTCDelegationDetailsCollection). + FindOneAndUpdate(ctx, filter, update) + + if res.Err() != nil { + if errors.Is(res.Err(), mongo.ErrNoDocuments) { + return &NotFoundError{ + Key: stakingTxHash, + Message: "BTC delegation not found when updating state", + } + } + return res.Err() + } + + return nil +} + +func (db *Database) GetBTCDelegationByStakingTxHash( + ctx context.Context, stakingTxHash string, +) (*model.BTCDelegationDetails, error) { + filter := map[string]interface{}{"_id": stakingTxHash} + res := db.client.Database(db.dbName). + Collection(model.BTCDelegationDetailsCollection). + FindOne(ctx, filter) + + var delegationDoc model.BTCDelegationDetails + err := res.Decode(&delegationDoc) + if err != nil { + if errors.Is(err, mongo.ErrNoDocuments) { + return nil, &NotFoundError{ + Key: stakingTxHash, + Message: "BTC delegation not found when getting by staking tx hash", + } + } + return nil, err + } + + return &delegationDoc, nil +} diff --git a/internal/db/interface.go b/internal/db/interface.go index d96cc62..4781ea7 100644 --- a/internal/db/interface.go +++ b/internal/db/interface.go @@ -5,6 +5,7 @@ import ( "github.com/babylonlabs-io/babylon-staking-indexer/internal/clients/bbnclient" "github.com/babylonlabs-io/babylon-staking-indexer/internal/db/model" + "github.com/babylonlabs-io/babylon-staking-indexer/internal/types" ) type DbInterface interface { @@ -73,4 +74,33 @@ type DbInterface interface { SaveCheckpointParams( ctx context.Context, params *bbnclient.CheckpointParams, ) error + /** + * SaveNewBTCDelegation saves a new BTC delegation to the database. + * If the BTC delegation already exists, DuplicateKeyError will be returned. + * @param ctx The context + * @param delegationDoc The BTC delegation details + * @return An error if the operation failed + */ + SaveNewBTCDelegation( + ctx context.Context, delegationDoc *model.BTCDelegationDetails, + ) error + /** + * SaveBTCDelegationStateUpdate saves a BTC delegation state update to the database. + * @param ctx The context + * @param delegationDoc The BTC delegation details + * @return An error if the operation failed + */ + UpdateBTCDelegationState( + ctx context.Context, stakingTxHash string, newState types.DelegationState, + ) error + /** + * GetBTCDelegationByStakingTxHash retrieves the BTC delegation details by the staking tx hash. + * If the BTC delegation does not exist, a NotFoundError will be returned. + * @param ctx The context + * @param stakingTxHash The staking tx hash + * @return The BTC delegation details or an error + */ + GetBTCDelegationByStakingTxHash( + ctx context.Context, stakingTxHash string, + ) (*model.BTCDelegationDetails, error) } diff --git a/internal/db/model/delegation.go b/internal/db/model/delegation.go index 901e93f..026e76a 100644 --- a/internal/db/model/delegation.go +++ b/internal/db/model/delegation.go @@ -2,10 +2,33 @@ package model import ( "github.com/babylonlabs-io/babylon-staking-indexer/internal/types" + bbntypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" ) -type DelegationDocument struct { - StakingTxHashHex string `bson:"_id"` // Primary key - State types.DelegationState `bson:"state"` - // TODO: Placeholder for more fields +type BTCDelegationDetails struct { + StakingTxHashHex string `bson:"_id"` // Primary key + ParamsVersion string `bson:"params_version"` + FinalityProviderBtcPksHex []string `bson:"finality_provider_btc_pks_hex"` + StakerBtcPkHex string `bson:"staker_btc_pk_hex"` + StakingTime string `bson:"staking_time"` + StakingAmount string `bson:"staking_amount"` + UnbondingTime string `bson:"unbonding_time"` + UnbondingTx string `bson:"unbonding_tx"` + State types.DelegationState `bson:"state"` +} + +func FromEventBTCDelegationCreated( + event *bbntypes.EventBTCDelegationCreated, +) *BTCDelegationDetails { + return &BTCDelegationDetails{ + StakingTxHashHex: event.StakingTxHash, // babylon returns a hex string + ParamsVersion: event.ParamsVersion, + FinalityProviderBtcPksHex: event.FinalityProviderBtcPksHex, + StakerBtcPkHex: event.StakerBtcPkHex, + StakingTime: event.StakingTime, + StakingAmount: event.StakingAmount, + UnbondingTime: event.UnbondingTime, + UnbondingTx: event.UnbondingTx, + State: types.DelegationState(event.NewState), + } } diff --git a/internal/db/model/setup.go b/internal/db/model/setup.go index a4edde5..0a96ecf 100644 --- a/internal/db/model/setup.go +++ b/internal/db/model/setup.go @@ -15,7 +15,7 @@ import ( const ( FinalityProviderDetailsCollection = "finality_provider_details" - DelegationCollection = "delegation" + BTCDelegationDetailsCollection = "btc_delegation_details" GlobalParamsCollection = "global_params" ) @@ -26,7 +26,7 @@ type index struct { var collections = map[string][]index{ FinalityProviderDetailsCollection: {{Indexes: map[string]int{}}}, - DelegationCollection: {{Indexes: map[string]int{}}}, + BTCDelegationDetailsCollection: {{Indexes: map[string]int{}}}, GlobalParamsCollection: {{Indexes: map[string]int{}}}, } diff --git a/internal/db/params.go b/internal/db/params.go index c089c5f..c66ac98 100644 --- a/internal/db/params.go +++ b/internal/db/params.go @@ -69,5 +69,6 @@ func (db *Database) SaveCheckpointParams( if err != nil { return fmt.Errorf("failed to save checkpoint params: %w", err) } + return nil } diff --git a/internal/services/delegation.go b/internal/services/delegation.go index d16a5c5..0234f23 100644 --- a/internal/services/delegation.go +++ b/internal/services/delegation.go @@ -1,13 +1,266 @@ package services -import "context" +import ( + "context" + "fmt" + "net/http" + + "github.com/babylonlabs-io/babylon-staking-indexer/internal/db" + "github.com/babylonlabs-io/babylon-staking-indexer/internal/db/model" + "github.com/babylonlabs-io/babylon-staking-indexer/internal/types" + bbntypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" + abcitypes "github.com/cometbft/cometbft/abci/types" +) const ( + EventBTCDelegationStateUpdate EventTypes = "babylon.btcstaking.v1.EventBTCDelegationStateUpdate" EventBTCDelegationCreated EventTypes = "babylon.btcstaking.v1.EventBTCDelegationCreated" + EventCovenantQuorumReached EventTypes = "babylon.btcstaking.v1.EventCovenantQuorumReached" EventBTCDelegationInclusionProofReceived EventTypes = "babylon.btcstaking.v1.EventBTCDelegationInclusionProofReceived" EventBTCDelgationUnbondedEarly EventTypes = "babylon.btcstaking.v1.EventBTCDelgationUnbondedEarly" EventBTCDelegationExpired EventTypes = "babylon.btcstaking.v1.EventBTCDelegationExpired" ) -func (s *Service) processBTCDelegationStateUpdateEvent(ctx context.Context) { +func (s *Service) processBTCDelegationStateUpdateEvent(ctx context.Context, event abcitypes.Event) *types.Error { + stateUpdate, err := parseEvent[*bbntypes.EventBTCDelegationStateUpdate]( + EventBTCDelegationStateUpdate, event, + ) + if err != nil { + return err + } + + if err := validateBTCDelegationStateUpdateEvent(stateUpdate); err != nil { + return err + } + + // Check if BTC delegation exists + _, dbErr := s.db.GetBTCDelegationByStakingTxHash(ctx, stateUpdate.StakingTxHash) + if dbErr != nil { + return types.NewError( + http.StatusInternalServerError, + types.InternalServiceError, + fmt.Errorf("failed to get BTC delegation by staking tx hash: %w", dbErr), + ) + } + + if err := s.db.UpdateBTCDelegationState( + ctx, stateUpdate.StakingTxHash, types.DelegationState(stateUpdate.NewState), + ); err != nil { + return types.NewError( + http.StatusInternalServerError, + types.InternalServiceError, + fmt.Errorf("failed to update BTC delegation state: %w", err), + ) + } + + return nil +} + +func (s *Service) processNewBTCDelegationEvent( + ctx context.Context, event abcitypes.Event, +) *types.Error { + newDelegation, err := parseEvent[*bbntypes.EventBTCDelegationCreated]( + EventBTCDelegationCreated, event, + ) + if err != nil { + return err + } + + if err := validateBTCDelegationCreatedEvent(newDelegation); err != nil { + return err + } + if err := s.db.SaveNewBTCDelegation( + ctx, model.FromEventBTCDelegationCreated(newDelegation), + ); err != nil { + if db.IsDuplicateKeyError(err) { + // BTC delegation already exists, ignore the event + return nil + } + return types.NewError( + http.StatusInternalServerError, + types.InternalServiceError, + fmt.Errorf("failed to save new BTC delegation: %w", err), + ) + } + + return nil +} + +func (s *Service) processCovenantQuorumReachedEvent( + ctx context.Context, event abcitypes.Event, +) *types.Error { + covenantQuorumReachedEvent, err := parseEvent[*bbntypes.EventCovenantQuorumReached]( + EventCovenantQuorumReached, event, + ) + if err != nil { + return err + } + + if err := validateCovenantQuorumReachedEvent(covenantQuorumReachedEvent); err != nil { + return err + } + + // Check if BTC delegation exists + _, dbErr := s.db.GetBTCDelegationByStakingTxHash(ctx, covenantQuorumReachedEvent.StakingTxHash) + if dbErr != nil { + return types.NewError( + http.StatusInternalServerError, + types.InternalServiceError, + fmt.Errorf("failed to get BTC delegation by staking tx hash: %w", dbErr), + ) + } + + if err := s.db.UpdateBTCDelegationState( + ctx, covenantQuorumReachedEvent.StakingTxHash, types.DelegationState(covenantQuorumReachedEvent.NewState), + ); err != nil { + return types.NewError( + http.StatusInternalServerError, + types.InternalServiceError, + fmt.Errorf("failed to update BTC delegation state: %w", err), + ) + } + + return nil +} + +func (s *Service) processBTCDelegationInclusionProofReceivedEvent( + ctx context.Context, event abcitypes.Event, +) *types.Error { + inclusionProofEvent, err := parseEvent[*bbntypes.EventBTCDelegationInclusionProofReceived]( + EventBTCDelegationInclusionProofReceived, event, + ) + if err != nil { + return err + } + + if err := validateBTCDelegationInclusionProofReceivedEvent(inclusionProofEvent); err != nil { + return err + } + + // Check if BTC delegation exists + _, dbErr := s.db.GetBTCDelegationByStakingTxHash(ctx, inclusionProofEvent.StakingTxHash) + if dbErr != nil { + return types.NewError( + http.StatusInternalServerError, + types.InternalServiceError, + fmt.Errorf("failed to get BTC delegation by staking tx hash: %w", dbErr), + ) + } + + if err := s.db.UpdateBTCDelegationState( + ctx, inclusionProofEvent.StakingTxHash, types.DelegationState(inclusionProofEvent.NewState), + ); err != nil { + return types.NewError( + http.StatusInternalServerError, + types.InternalServiceError, + fmt.Errorf("failed to update BTC delegation state: %w", err), + ) + } + + return nil +} + +func (s *Service) processBTCDelegationUnbondedEarlyEvent( + ctx context.Context, event abcitypes.Event, +) *types.Error { + unbondedEarlyEvent, err := parseEvent[*bbntypes.EventBTCDelgationUnbondedEarly]( + EventBTCDelgationUnbondedEarly, event, + ) + if err != nil { + return err + } + + if err := validateBTCDelegationUnbondedEarlyEvent(unbondedEarlyEvent); err != nil { + return err + } + + // Check if BTC delegation exists + _, dbErr := s.db.GetBTCDelegationByStakingTxHash(ctx, unbondedEarlyEvent.StakingTxHash) + if dbErr != nil { + return types.NewError( + http.StatusInternalServerError, + types.InternalServiceError, + fmt.Errorf("failed to get BTC delegation by staking tx hash: %w", dbErr), + ) + } + + if err := s.db.UpdateBTCDelegationState( + ctx, unbondedEarlyEvent.StakingTxHash, types.DelegationState(unbondedEarlyEvent.NewState), + ); err != nil { + return types.NewError( + http.StatusInternalServerError, + types.InternalServiceError, + fmt.Errorf("failed to update BTC delegation state: %w", err), + ) + } + + return nil +} + +func (s *Service) processBTCDelegationExpiredEvent( + ctx context.Context, event abcitypes.Event, +) *types.Error { + expiredEvent, err := parseEvent[*bbntypes.EventBTCDelegationExpired]( + EventBTCDelegationExpired, event, + ) + if err != nil { + return err + } + + if err := validateBTCDelegationExpiredEvent(expiredEvent); err != nil { + return err + } + + // Check if BTC delegation exists + _, dbErr := s.db.GetBTCDelegationByStakingTxHash(ctx, expiredEvent.StakingTxHash) + if dbErr != nil { + return types.NewError( + http.StatusInternalServerError, + types.InternalServiceError, + fmt.Errorf("failed to get BTC delegation by staking tx hash: %w", dbErr), + ) + } + + if err := s.db.UpdateBTCDelegationState( + ctx, expiredEvent.StakingTxHash, types.DelegationState(expiredEvent.NewState), + ); err != nil { + return types.NewError( + http.StatusInternalServerError, + types.InternalServiceError, + fmt.Errorf("failed to update BTC delegation state: %w", err), + ) + } + + return nil +} + +// You'll need to implement these functions: +func validateBTCDelegationCreatedEvent(event *bbntypes.EventBTCDelegationCreated) *types.Error { + // Implement validation logic here + return nil +} + +func validateBTCDelegationStateUpdateEvent(event *bbntypes.EventBTCDelegationStateUpdate) *types.Error { + // Implement validation logic here + return nil +} + +func validateCovenantQuorumReachedEvent(event *bbntypes.EventCovenantQuorumReached) *types.Error { + // Implement validation logic here + return nil +} + +func validateBTCDelegationInclusionProofReceivedEvent(event *bbntypes.EventBTCDelegationInclusionProofReceived) *types.Error { + // Implement validation logic here + return nil +} + +func validateBTCDelegationUnbondedEarlyEvent(event *bbntypes.EventBTCDelgationUnbondedEarly) *types.Error { + // Implement validation logic here + return nil +} + +func validateBTCDelegationExpiredEvent(event *bbntypes.EventBTCDelegationExpired) *types.Error { + // Implement validation logic here + return nil } diff --git a/internal/services/events.go b/internal/services/events.go index 0c1ad81..ff69b1a 100644 --- a/internal/services/events.go +++ b/internal/services/events.go @@ -2,14 +2,14 @@ package services import ( "context" - "encoding/json" "fmt" "net/http" "time" "github.com/babylonlabs-io/babylon-staking-indexer/internal/types" - "github.com/babylonlabs-io/babylon-staking-indexer/internal/utils" abcitypes "github.com/cometbft/cometbft/abci/types" + sdk "github.com/cosmos/cosmos-sdk/types" + proto "github.com/cosmos/gogoproto/proto" "github.com/rs/zerolog/log" ) @@ -55,6 +55,7 @@ func (s *Service) processEvent(ctx context.Context, event BbnEvent) { // Note: We no longer need to check for the event category here. We can directly // process the event based on its type. bbnEvent := event.Event + // log.Debug().Str("event_type", bbnEvent.Type).Msg("Processing event") switch EventTypes(bbnEvent.Type) { case EventFinalityProviderCreatedType: log.Debug().Msg("Processing new finality provider event") @@ -65,15 +66,36 @@ func (s *Service) processEvent(ctx context.Context, event BbnEvent) { case EventFinalityProviderStatusChange: log.Debug().Msg("Processing finality provider status change event") s.processFinalityProviderStateChangeEvent(ctx, bbnEvent) + case EventBTCDelegationCreated: + log.Debug().Msg("Processing new BTC delegation event") + s.processNewBTCDelegationEvent(ctx, bbnEvent) + case EventBTCDelegationStateUpdate: + log.Debug().Msg("Processing BTC delegation state update event") + s.processBTCDelegationStateUpdateEvent(ctx, bbnEvent) + case EventCovenantQuorumReached: + log.Debug().Msg("Processing covenant quorum reached event") + s.processCovenantQuorumReachedEvent(ctx, bbnEvent) + case EventBTCDelegationInclusionProofReceived: + log.Debug().Msg("Processing BTC delegation inclusion proof received event") + s.processBTCDelegationInclusionProofReceivedEvent(ctx, bbnEvent) + case EventBTCDelgationUnbondedEarly: + log.Debug().Msg("Processing BTC delegation unbonded early event") + s.processBTCDelegationUnbondedEarlyEvent(ctx, bbnEvent) + case EventBTCDelegationExpired: + log.Debug().Msg("Processing BTC delegation expired event") + s.processBTCDelegationExpiredEvent(ctx, bbnEvent) } } -func parseEvent[T any]( +func parseEvent[T proto.Message]( expectedType EventTypes, event abcitypes.Event, -) (*T, *types.Error) { +) (T, *types.Error) { + var result T + + // Check if the event type matches the expected type if EventTypes(event.Type) != expectedType { - return nil, types.NewErrorWithMsg( + return result, types.NewErrorWithMsg( http.StatusInternalServerError, types.InternalServiceError, fmt.Sprintf( @@ -83,8 +105,10 @@ func parseEvent[T any]( ), ) } + + // Check if the event has attributes if len(event.Attributes) == 0 { - return nil, types.NewErrorWithMsg( + return result, types.NewErrorWithMsg( http.StatusInternalServerError, types.InternalServiceError, fmt.Sprintf( @@ -94,35 +118,25 @@ func parseEvent[T any]( ) } - // Create a map to store the attributes - attributeMap := make(map[string]string) - - // Populate the attribute map from the event's attributes - for _, attr := range event.Attributes { - // Unescape the attribute value - attributeMap[attr.Key] = utils.SafeUnescape(attr.Value) - } - - // Marshal the attributeMap into JSON - attrJSON, err := json.Marshal(attributeMap) + // Use the SDK's ParseTypedEvent function + protoMsg, err := sdk.ParseTypedEvent(event) if err != nil { - return nil, types.NewError( + return result, types.NewError( http.StatusInternalServerError, types.InternalServiceError, - fmt.Errorf("failed to marshal attributes into JSON: %w", err), + fmt.Errorf("failed to parse typed event: %w", err), ) } - // Unmarshal the JSON into the T struct - var evt T - err = json.Unmarshal(attrJSON, &evt) - if err != nil { - return nil, types.NewError( + // Type assertion to ensure we have the correct concrete type + concreteMsg, ok := protoMsg.(T) + if !ok { + return result, types.NewError( http.StatusInternalServerError, types.InternalServiceError, - fmt.Errorf("failed to unmarshal attributes into %T: %w", evt, err), + fmt.Errorf("parsed event type %T does not match expected type %T", protoMsg, result), ) } - return &evt, nil + return concreteMsg, nil } diff --git a/internal/services/finality-provider.go b/internal/services/finality-provider.go index 979148a..474ef15 100644 --- a/internal/services/finality-provider.go +++ b/internal/services/finality-provider.go @@ -21,7 +21,7 @@ const ( func (s *Service) processNewFinalityProviderEvent( ctx context.Context, event abcitypes.Event, ) *types.Error { - newFinalityProvider, err := parseEvent[bbntypes.EventFinalityProviderCreated]( + newFinalityProvider, err := parseEvent[*bbntypes.EventFinalityProviderCreated]( EventFinalityProviderCreatedType, event, ) if err != nil { @@ -50,7 +50,7 @@ func (s *Service) processNewFinalityProviderEvent( func (s *Service) processFinalityProviderEditedEvent( ctx context.Context, event abcitypes.Event, ) *types.Error { - finalityProviderEdited, err := parseEvent[bbntypes.EventFinalityProviderEdited]( + finalityProviderEdited, err := parseEvent[*bbntypes.EventFinalityProviderEdited]( EventFinalityProviderEditedType, event, ) if err != nil { @@ -75,7 +75,7 @@ func (s *Service) processFinalityProviderEditedEvent( func (s *Service) processFinalityProviderStateChangeEvent( ctx context.Context, event abcitypes.Event, ) *types.Error { - finalityProviderStateChange, err := parseEvent[bbntypes.EventFinalityProviderStatusChange]( + finalityProviderStateChange, err := parseEvent[*bbntypes.EventFinalityProviderStatusChange]( EventFinalityProviderStatusChange, event, ) if err != nil { diff --git a/internal/services/global-params.go b/internal/services/global-params.go index 22bc0c3..717c7d6 100644 --- a/internal/services/global-params.go +++ b/internal/services/global-params.go @@ -37,6 +37,7 @@ func (s *Service) fetchAndSaveParams(ctx context.Context) *types.Error { fmt.Errorf("failed to get staking params: %w", err), ) } + for version, params := range allStakingParams { if params == nil { return types.NewInternalServiceError( @@ -49,5 +50,6 @@ func (s *Service) fetchAndSaveParams(ctx context.Context) *types.Error { ) } } + return nil } diff --git a/internal/types/state.go b/internal/types/state.go index f9c756d..cf5b645 100644 --- a/internal/types/state.go +++ b/internal/types/state.go @@ -5,9 +5,14 @@ type DelegationState string const ( StatePending DelegationState = "PENDING" + StateVerified DelegationState = "VERIFIED" StateActive DelegationState = "ACTIVE" StateUnbonding DelegationState = "UNBONDING" StateWithdrawn DelegationState = "WITHDRAWN" StateSlashed DelegationState = "SLASHED" StateUnbonded DelegationState = "UNBONDED" ) + +func (s DelegationState) String() string { + return string(s) +} diff --git a/tests/mocks/mock_bbn_client.go b/tests/mocks/mock_bbn_client.go index 92fdd8b..d2591c9 100644 --- a/tests/mocks/mock_bbn_client.go +++ b/tests/mocks/mock_bbn_client.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.41.0. DO NOT EDIT. +// Code generated by mockery v2.42.1. DO NOT EDIT. package mocks diff --git a/tests/mocks/mock_btc_client.go b/tests/mocks/mock_btc_client.go index 21a8239..83c0ef2 100644 --- a/tests/mocks/mock_btc_client.go +++ b/tests/mocks/mock_btc_client.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.41.0. DO NOT EDIT. +// Code generated by mockery v2.42.1. DO NOT EDIT. package mocks diff --git a/tests/mocks/mock_db_client.go b/tests/mocks/mock_db_client.go index 1efd07f..8b54bf7 100644 --- a/tests/mocks/mock_db_client.go +++ b/tests/mocks/mock_db_client.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.41.0. DO NOT EDIT. +// Code generated by mockery v2.42.1. DO NOT EDIT. package mocks @@ -7,6 +7,8 @@ import ( bbnclient "github.com/babylonlabs-io/babylon-staking-indexer/internal/clients/bbnclient" + internaltypes "github.com/babylonlabs-io/babylon-staking-indexer/internal/types" + mock "github.com/stretchr/testify/mock" model "github.com/babylonlabs-io/babylon-staking-indexer/internal/db/model" @@ -19,6 +21,36 @@ type DbInterface struct { mock.Mock } +// GetBTCDelegationByStakingTxHash provides a mock function with given fields: ctx, stakingTxHash +func (_m *DbInterface) GetBTCDelegationByStakingTxHash(ctx context.Context, stakingTxHash string) (*model.BTCDelegationDetails, error) { + ret := _m.Called(ctx, stakingTxHash) + + if len(ret) == 0 { + panic("no return value specified for GetBTCDelegationByStakingTxHash") + } + + var r0 *model.BTCDelegationDetails + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (*model.BTCDelegationDetails, error)); ok { + return rf(ctx, stakingTxHash) + } + if rf, ok := ret.Get(0).(func(context.Context, string) *model.BTCDelegationDetails); ok { + r0 = rf(ctx, stakingTxHash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.BTCDelegationDetails) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(ctx, stakingTxHash) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetFinalityProviderByBtcPk provides a mock function with given fields: ctx, btcPk func (_m *DbInterface) GetFinalityProviderByBtcPk(ctx context.Context, btcPk string) (*model.FinalityProviderDetails, error) { ret := _m.Called(ctx, btcPk) @@ -85,6 +117,24 @@ func (_m *DbInterface) SaveCheckpointParams(ctx context.Context, params *types.P return r0 } +// SaveNewBTCDelegation provides a mock function with given fields: ctx, delegationDoc +func (_m *DbInterface) SaveNewBTCDelegation(ctx context.Context, delegationDoc *model.BTCDelegationDetails) error { + ret := _m.Called(ctx, delegationDoc) + + if len(ret) == 0 { + panic("no return value specified for SaveNewBTCDelegation") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *model.BTCDelegationDetails) error); ok { + r0 = rf(ctx, delegationDoc) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // SaveNewFinalityProvider provides a mock function with given fields: ctx, fpDoc func (_m *DbInterface) SaveNewFinalityProvider(ctx context.Context, fpDoc *model.FinalityProviderDetails) error { ret := _m.Called(ctx, fpDoc) @@ -121,6 +171,24 @@ func (_m *DbInterface) SaveStakingParams(ctx context.Context, version uint32, pa return r0 } +// UpdateBTCDelegationState provides a mock function with given fields: ctx, stakingTxHash, newState +func (_m *DbInterface) UpdateBTCDelegationState(ctx context.Context, stakingTxHash string, newState internaltypes.DelegationState) error { + ret := _m.Called(ctx, stakingTxHash, newState) + + if len(ret) == 0 { + panic("no return value specified for UpdateBTCDelegationState") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, internaltypes.DelegationState) error); ok { + r0 = rf(ctx, stakingTxHash, newState) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // UpdateFinalityProviderDetailsFromEvent provides a mock function with given fields: ctx, detailsToUpdate func (_m *DbInterface) UpdateFinalityProviderDetailsFromEvent(ctx context.Context, detailsToUpdate *model.FinalityProviderDetails) error { ret := _m.Called(ctx, detailsToUpdate)