From b5cd1ec5ca4ab83fff8ff7b4abbf374e318f4d7a Mon Sep 17 00:00:00 2001 From: Gurjot Date: Thu, 14 Nov 2024 17:03:43 +0530 Subject: [PATCH 1/7] move some funcs to different file --- internal/services/delegation.go | 221 +------------------------------- internal/services/events.go | 220 +++++++++++++++++++++++++++++++ 2 files changed, 221 insertions(+), 220 deletions(-) diff --git a/internal/services/delegation.go b/internal/services/delegation.go index 844b26c..4bbd5ce 100644 --- a/internal/services/delegation.go +++ b/internal/services/delegation.go @@ -8,10 +8,8 @@ import ( "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" - "github.com/babylonlabs-io/babylon-staking-indexer/internal/utils" bbntypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" abcitypes "github.com/cometbft/cometbft/abci/types" - "github.com/rs/zerolog/log" ) const ( @@ -20,6 +18,7 @@ const ( EventBTCDelegationInclusionProofReceived EventTypes = "babylon.btcstaking.v1.EventBTCDelegationInclusionProofReceived" EventBTCDelgationUnbondedEarly EventTypes = "babylon.btcstaking.v1.EventBTCDelgationUnbondedEarly" EventBTCDelegationExpired EventTypes = "babylon.btcstaking.v1.EventBTCDelegationExpired" + EventSlashedFinalityProvider EventTypes = "babylon.finality.v1.EventSlashedFinalityProvider" ) func (s *Service) processNewBTCDelegationEvent( @@ -244,221 +243,3 @@ func (s *Service) processBTCDelegationExpiredEvent( return nil } - -func (s *Service) validateBTCDelegationCreatedEvent(event *bbntypes.EventBTCDelegationCreated) *types.Error { - // Check if the staking tx hash is present - if event.StakingTxHash == "" { - return types.NewErrorWithMsg( - http.StatusInternalServerError, - types.InternalServiceError, - "new BTC delegation event missing staking tx hash", - ) - } - - // Validate the event state - if event.NewState != bbntypes.BTCDelegationStatus_PENDING.String() { - return types.NewValidationFailedError( - fmt.Errorf("invalid delegation state from Babylon: expected PENDING, got %s", event.NewState), - ) - } - - return nil -} - -func (s *Service) validateCovenantQuorumReachedEvent(ctx context.Context, event *bbntypes.EventCovenantQuorumReached) (bool, *types.Error) { - // Check if the staking tx hash is present - if event.StakingTxHash == "" { - return false, types.NewErrorWithMsg( - http.StatusInternalServerError, - types.InternalServiceError, - "covenant quorum reached event missing staking tx hash", - ) - } - - // Fetch the current delegation state from the database - delegation, dbErr := s.db.GetBTCDelegationByStakingTxHash(ctx, event.StakingTxHash) - if dbErr != nil { - return false, types.NewError( - http.StatusInternalServerError, - types.InternalServiceError, - fmt.Errorf("failed to get BTC delegation by staking tx hash: %w", dbErr), - ) - } - - // Retrieve the qualified states for the intended transition - qualifiedStates := types.QualifiedStatesForCovenantQuorumReached(event.NewState) - if qualifiedStates == nil { - return false, types.NewValidationFailedError( - fmt.Errorf("invalid delegation state from Babylon: %s", event.NewState), - ) - } - - // Check if the current state is qualified for the transition - if !utils.Contains(qualifiedStates, delegation.State) { - log.Debug(). - Str("stakingTxHashHex", event.StakingTxHash). - Str("currentState", delegation.State.String()). - Str("newState", event.NewState). - Msg("Ignoring EventCovenantQuorumReached because current state is not qualified for transition") - return false, nil // Ignore the event silently - } - - if event.NewState == bbntypes.BTCDelegationStatus_VERIFIED.String() { - // This will only happen if the staker is following the new pre-approval flow. - // For more info read https://github.com/babylonlabs-io/pm/blob/main/rfc/rfc-008-staking-transaction-pre-approval.md#handling-of-the-modified--msgcreatebtcdelegation-message - - // Delegation should not have the inclusion proof yet - if delegation.HasInclusionProof() { - log.Debug(). - Str("stakingTxHashHex", event.StakingTxHash). - Str("currentState", delegation.State.String()). - Str("newState", event.NewState). - Msg("Ignoring EventCovenantQuorumReached because inclusion proof already received") - return false, nil - } - } else if event.NewState == bbntypes.BTCDelegationStatus_ACTIVE.String() { - // This will happen if the inclusion proof is received in MsgCreateBTCDelegation, i.e the staker is following the old flow - - // Delegation should have the inclusion proof - if !delegation.HasInclusionProof() { - log.Debug(). - Str("stakingTxHashHex", event.StakingTxHash). - Str("currentState", delegation.State.String()). - Str("newState", event.NewState). - Msg("Ignoring EventCovenantQuorumReached because inclusion proof not received") - return false, nil - } - } - - return true, nil -} - -func (s *Service) validateBTCDelegationInclusionProofReceivedEvent(ctx context.Context, event *bbntypes.EventBTCDelegationInclusionProofReceived) (bool, *types.Error) { - // Check if the staking tx hash is present - if event.StakingTxHash == "" { - return false, types.NewErrorWithMsg( - http.StatusInternalServerError, - types.InternalServiceError, - "inclusion proof received event missing staking tx hash", - ) - } - - // Fetch the current delegation state from the database - delegation, dbErr := s.db.GetBTCDelegationByStakingTxHash(ctx, event.StakingTxHash) - if dbErr != nil { - return false, types.NewError( - http.StatusInternalServerError, - types.InternalServiceError, - fmt.Errorf("failed to get BTC delegation by staking tx hash: %w", dbErr), - ) - } - - // Retrieve the qualified states for the intended transition - qualifiedStates := types.QualifiedStatesForInclusionProofReceived(event.NewState) - if qualifiedStates == nil { - return false, types.NewValidationFailedError( - fmt.Errorf("no qualified states defined for new state: %s", event.NewState), - ) - } - - // Check if the current state is qualified for the transition - if !utils.Contains(qualifiedStates, delegation.State) { - log.Debug(). - Str("stakingTxHashHex", event.StakingTxHash). - Str("currentState", delegation.State.String()). - Str("newState", event.NewState). - Msg("Ignoring EventBTCDelegationInclusionProofReceived because current state is not qualified for transition") - return false, nil - } - - // Delegation should not have the inclusion proof yet - // After this event is processed, the inclusion proof will be set - if delegation.HasInclusionProof() { - log.Debug(). - Str("stakingTxHashHex", event.StakingTxHash). - Str("currentState", delegation.State.String()). - Str("newState", event.NewState). - Msg("Ignoring EventBTCDelegationInclusionProofReceived because inclusion proof already received") - return false, nil - } - - return true, nil -} - -func (s *Service) validateBTCDelegationUnbondedEarlyEvent(ctx context.Context, event *bbntypes.EventBTCDelgationUnbondedEarly) (bool, *types.Error) { - // Check if the staking tx hash is present - if event.StakingTxHash == "" { - return false, types.NewErrorWithMsg( - http.StatusInternalServerError, - types.InternalServiceError, - "unbonded early event missing staking tx hash", - ) - } - - // Validate the event state - if event.NewState != bbntypes.BTCDelegationStatus_UNBONDED.String() { - return false, types.NewValidationFailedError( - fmt.Errorf("invalid delegation state from Babylon: expected UNBONDED, got %s", event.NewState), - ) - } - - // Fetch the current delegation state from the database - delegation, dbErr := s.db.GetBTCDelegationByStakingTxHash(ctx, event.StakingTxHash) - if dbErr != nil { - return false, types.NewError( - http.StatusInternalServerError, - types.InternalServiceError, - fmt.Errorf("failed to get BTC delegation by staking tx hash: %w", dbErr), - ) - } - - // Check if the current state is qualified for the transition - if !utils.Contains(types.QualifiedStatesForUnbondedEarly(), delegation.State) { - log.Debug(). - Str("stakingTxHashHex", event.StakingTxHash). - Str("currentState", delegation.State.String()). - Msg("Ignoring EventBTCDelgationUnbondedEarly because current state is not qualified for transition") - return false, nil - } - - return true, nil -} - -func (s *Service) validateBTCDelegationExpiredEvent(ctx context.Context, event *bbntypes.EventBTCDelegationExpired) (bool, *types.Error) { - // Check if the staking tx hash is present - if event.StakingTxHash == "" { - return false, types.NewErrorWithMsg( - http.StatusInternalServerError, - types.InternalServiceError, - "expired event missing staking tx hash", - ) - } - - // Validate the event state - if event.NewState != bbntypes.BTCDelegationStatus_UNBONDED.String() { - return false, types.NewValidationFailedError( - fmt.Errorf("invalid delegation state from Babylon: expected UNBONDED, got %s", event.NewState), - ) - } - - // Fetch the current delegation state from the database - delegation, dbErr := s.db.GetBTCDelegationByStakingTxHash(ctx, event.StakingTxHash) - if dbErr != nil { - return false, types.NewError( - http.StatusInternalServerError, - types.InternalServiceError, - fmt.Errorf("failed to get BTC delegation by staking tx hash: %w", dbErr), - ) - } - - // Check if the current state is qualified for the transition - if !utils.Contains(types.QualifiedStatesForExpired(), delegation.State) { - log.Debug(). - Str("stakingTxHashHex", event.StakingTxHash). - Str("currentState", delegation.State.String()). - Msg("Ignoring EventBTCDelegationExpired because current state is not qualified for transition") - return false, nil - } - - return true, nil -} diff --git a/internal/services/events.go b/internal/services/events.go index dd5a4e2..4732744 100644 --- a/internal/services/events.go +++ b/internal/services/events.go @@ -7,6 +7,8 @@ import ( "time" "github.com/babylonlabs-io/babylon-staking-indexer/internal/types" + "github.com/babylonlabs-io/babylon-staking-indexer/internal/utils" + bbntypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" abcitypes "github.com/cometbft/cometbft/abci/types" sdk "github.com/cosmos/cosmos-sdk/types" proto "github.com/cosmos/gogoproto/proto" @@ -134,3 +136,221 @@ func parseEvent[T proto.Message]( return concreteMsg, nil } + +func (s *Service) validateBTCDelegationCreatedEvent(event *bbntypes.EventBTCDelegationCreated) *types.Error { + // Check if the staking tx hash is present + if event.StakingTxHash == "" { + return types.NewErrorWithMsg( + http.StatusInternalServerError, + types.InternalServiceError, + "new BTC delegation event missing staking tx hash", + ) + } + + // Validate the event state + if event.NewState != bbntypes.BTCDelegationStatus_PENDING.String() { + return types.NewValidationFailedError( + fmt.Errorf("invalid delegation state from Babylon: expected PENDING, got %s", event.NewState), + ) + } + + return nil +} + +func (s *Service) validateCovenantQuorumReachedEvent(ctx context.Context, event *bbntypes.EventCovenantQuorumReached) (bool, *types.Error) { + // Check if the staking tx hash is present + if event.StakingTxHash == "" { + return false, types.NewErrorWithMsg( + http.StatusInternalServerError, + types.InternalServiceError, + "covenant quorum reached event missing staking tx hash", + ) + } + + // Fetch the current delegation state from the database + delegation, dbErr := s.db.GetBTCDelegationByStakingTxHash(ctx, event.StakingTxHash) + if dbErr != nil { + return false, types.NewError( + http.StatusInternalServerError, + types.InternalServiceError, + fmt.Errorf("failed to get BTC delegation by staking tx hash: %w", dbErr), + ) + } + + // Retrieve the qualified states for the intended transition + qualifiedStates := types.QualifiedStatesForCovenantQuorumReached(event.NewState) + if qualifiedStates == nil { + return false, types.NewValidationFailedError( + fmt.Errorf("invalid delegation state from Babylon: %s", event.NewState), + ) + } + + // Check if the current state is qualified for the transition + if !utils.Contains(qualifiedStates, delegation.State) { + log.Debug(). + Str("stakingTxHashHex", event.StakingTxHash). + Str("currentState", delegation.State.String()). + Str("newState", event.NewState). + Msg("Ignoring EventCovenantQuorumReached because current state is not qualified for transition") + return false, nil // Ignore the event silently + } + + if event.NewState == bbntypes.BTCDelegationStatus_VERIFIED.String() { + // This will only happen if the staker is following the new pre-approval flow. + // For more info read https://github.com/babylonlabs-io/pm/blob/main/rfc/rfc-008-staking-transaction-pre-approval.md#handling-of-the-modified--msgcreatebtcdelegation-message + + // Delegation should not have the inclusion proof yet + if delegation.HasInclusionProof() { + log.Debug(). + Str("stakingTxHashHex", event.StakingTxHash). + Str("currentState", delegation.State.String()). + Str("newState", event.NewState). + Msg("Ignoring EventCovenantQuorumReached because inclusion proof already received") + return false, nil + } + } else if event.NewState == bbntypes.BTCDelegationStatus_ACTIVE.String() { + // This will happen if the inclusion proof is received in MsgCreateBTCDelegation, i.e the staker is following the old flow + + // Delegation should have the inclusion proof + if !delegation.HasInclusionProof() { + log.Debug(). + Str("stakingTxHashHex", event.StakingTxHash). + Str("currentState", delegation.State.String()). + Str("newState", event.NewState). + Msg("Ignoring EventCovenantQuorumReached because inclusion proof not received") + return false, nil + } + } + + return true, nil +} + +func (s *Service) validateBTCDelegationInclusionProofReceivedEvent(ctx context.Context, event *bbntypes.EventBTCDelegationInclusionProofReceived) (bool, *types.Error) { + // Check if the staking tx hash is present + if event.StakingTxHash == "" { + return false, types.NewErrorWithMsg( + http.StatusInternalServerError, + types.InternalServiceError, + "inclusion proof received event missing staking tx hash", + ) + } + + // Fetch the current delegation state from the database + delegation, dbErr := s.db.GetBTCDelegationByStakingTxHash(ctx, event.StakingTxHash) + if dbErr != nil { + return false, types.NewError( + http.StatusInternalServerError, + types.InternalServiceError, + fmt.Errorf("failed to get BTC delegation by staking tx hash: %w", dbErr), + ) + } + + // Retrieve the qualified states for the intended transition + qualifiedStates := types.QualifiedStatesForInclusionProofReceived(event.NewState) + if qualifiedStates == nil { + return false, types.NewValidationFailedError( + fmt.Errorf("no qualified states defined for new state: %s", event.NewState), + ) + } + + // Check if the current state is qualified for the transition + if !utils.Contains(qualifiedStates, delegation.State) { + log.Debug(). + Str("stakingTxHashHex", event.StakingTxHash). + Str("currentState", delegation.State.String()). + Str("newState", event.NewState). + Msg("Ignoring EventBTCDelegationInclusionProofReceived because current state is not qualified for transition") + return false, nil + } + + // Delegation should not have the inclusion proof yet + // After this event is processed, the inclusion proof will be set + if delegation.HasInclusionProof() { + log.Debug(). + Str("stakingTxHashHex", event.StakingTxHash). + Str("currentState", delegation.State.String()). + Str("newState", event.NewState). + Msg("Ignoring EventBTCDelegationInclusionProofReceived because inclusion proof already received") + return false, nil + } + + return true, nil +} + +func (s *Service) validateBTCDelegationUnbondedEarlyEvent(ctx context.Context, event *bbntypes.EventBTCDelgationUnbondedEarly) (bool, *types.Error) { + // Check if the staking tx hash is present + if event.StakingTxHash == "" { + return false, types.NewErrorWithMsg( + http.StatusInternalServerError, + types.InternalServiceError, + "unbonded early event missing staking tx hash", + ) + } + + // Validate the event state + if event.NewState != bbntypes.BTCDelegationStatus_UNBONDED.String() { + return false, types.NewValidationFailedError( + fmt.Errorf("invalid delegation state from Babylon: expected UNBONDED, got %s", event.NewState), + ) + } + + // Fetch the current delegation state from the database + delegation, dbErr := s.db.GetBTCDelegationByStakingTxHash(ctx, event.StakingTxHash) + if dbErr != nil { + return false, types.NewError( + http.StatusInternalServerError, + types.InternalServiceError, + fmt.Errorf("failed to get BTC delegation by staking tx hash: %w", dbErr), + ) + } + + // Check if the current state is qualified for the transition + if !utils.Contains(types.QualifiedStatesForUnbondedEarly(), delegation.State) { + log.Debug(). + Str("stakingTxHashHex", event.StakingTxHash). + Str("currentState", delegation.State.String()). + Msg("Ignoring EventBTCDelgationUnbondedEarly because current state is not qualified for transition") + return false, nil + } + + return true, nil +} + +func (s *Service) validateBTCDelegationExpiredEvent(ctx context.Context, event *bbntypes.EventBTCDelegationExpired) (bool, *types.Error) { + // Check if the staking tx hash is present + if event.StakingTxHash == "" { + return false, types.NewErrorWithMsg( + http.StatusInternalServerError, + types.InternalServiceError, + "expired event missing staking tx hash", + ) + } + + // Validate the event state + if event.NewState != bbntypes.BTCDelegationStatus_UNBONDED.String() { + return false, types.NewValidationFailedError( + fmt.Errorf("invalid delegation state from Babylon: expected UNBONDED, got %s", event.NewState), + ) + } + + // Fetch the current delegation state from the database + delegation, dbErr := s.db.GetBTCDelegationByStakingTxHash(ctx, event.StakingTxHash) + if dbErr != nil { + return false, types.NewError( + http.StatusInternalServerError, + types.InternalServiceError, + fmt.Errorf("failed to get BTC delegation by staking tx hash: %w", dbErr), + ) + } + + // Check if the current state is qualified for the transition + if !utils.Contains(types.QualifiedStatesForExpired(), delegation.State) { + log.Debug(). + Str("stakingTxHashHex", event.StakingTxHash). + Str("currentState", delegation.State.String()). + Msg("Ignoring EventBTCDelegationExpired because current state is not qualified for transition") + return false, nil + } + + return true, nil +} From f4d848616a07e3c3234e1f1bb531c0850a6cdc3c Mon Sep 17 00:00:00 2001 From: Gurjot Date: Sat, 16 Nov 2024 00:01:13 +0530 Subject: [PATCH 2/7] fix --- internal/services/delegation.go | 22 ++++++++++++++++++++++ internal/services/events.go | 30 +++++++++++++++++++----------- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/internal/services/delegation.go b/internal/services/delegation.go index 4bbd5ce..704742e 100644 --- a/internal/services/delegation.go +++ b/internal/services/delegation.go @@ -9,6 +9,7 @@ import ( "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" + ftypes "github.com/babylonlabs-io/babylon/x/finality/types" abcitypes "github.com/cometbft/cometbft/abci/types" ) @@ -243,3 +244,24 @@ func (s *Service) processBTCDelegationExpiredEvent( return nil } +func (s *Service) processSlashedFinalityProviderEvent( + ctx context.Context, event abcitypes.Event, +) *types.Error { + slashedFinalityProviderEvent, err := parseEvent[*ftypes.EventSlashedFinalityProvider]( + EventSlashedFinalityProvider, + event, + ) + if err != nil { + return err + } + + proceed, err := s.validateSlashedFinalityProviderEvent(ctx, slashedFinalityProviderEvent) + if err != nil { + return err + } + if !proceed { + return nil + } + + return nil +} diff --git a/internal/services/events.go b/internal/services/events.go index 4732744..5c77e72 100644 --- a/internal/services/events.go +++ b/internal/services/events.go @@ -8,7 +8,8 @@ import ( "github.com/babylonlabs-io/babylon-staking-indexer/internal/types" "github.com/babylonlabs-io/babylon-staking-indexer/internal/utils" - bbntypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" + bstypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" + ftypes "github.com/babylonlabs-io/babylon/x/finality/types" abcitypes "github.com/cometbft/cometbft/abci/types" sdk "github.com/cosmos/cosmos-sdk/types" proto "github.com/cosmos/gogoproto/proto" @@ -72,6 +73,9 @@ func (s *Service) processEvent(ctx context.Context, event BbnEvent) *types.Error case EventBTCDelegationExpired: log.Debug().Msg("Processing BTC delegation expired event") err = s.processBTCDelegationExpiredEvent(ctx, bbnEvent) + case EventSlashedFinalityProvider: + log.Debug().Msg("Processing slashed finality provider event") + err = s.processSlashedFinalityProviderEvent(ctx, bbnEvent) } if err != nil { @@ -137,7 +141,7 @@ func parseEvent[T proto.Message]( return concreteMsg, nil } -func (s *Service) validateBTCDelegationCreatedEvent(event *bbntypes.EventBTCDelegationCreated) *types.Error { +func (s *Service) validateBTCDelegationCreatedEvent(event *bstypes.EventBTCDelegationCreated) *types.Error { // Check if the staking tx hash is present if event.StakingTxHash == "" { return types.NewErrorWithMsg( @@ -148,7 +152,7 @@ func (s *Service) validateBTCDelegationCreatedEvent(event *bbntypes.EventBTCDele } // Validate the event state - if event.NewState != bbntypes.BTCDelegationStatus_PENDING.String() { + if event.NewState != bstypes.BTCDelegationStatus_PENDING.String() { return types.NewValidationFailedError( fmt.Errorf("invalid delegation state from Babylon: expected PENDING, got %s", event.NewState), ) @@ -157,7 +161,7 @@ func (s *Service) validateBTCDelegationCreatedEvent(event *bbntypes.EventBTCDele return nil } -func (s *Service) validateCovenantQuorumReachedEvent(ctx context.Context, event *bbntypes.EventCovenantQuorumReached) (bool, *types.Error) { +func (s *Service) validateCovenantQuorumReachedEvent(ctx context.Context, event *bstypes.EventCovenantQuorumReached) (bool, *types.Error) { // Check if the staking tx hash is present if event.StakingTxHash == "" { return false, types.NewErrorWithMsg( @@ -195,7 +199,7 @@ func (s *Service) validateCovenantQuorumReachedEvent(ctx context.Context, event return false, nil // Ignore the event silently } - if event.NewState == bbntypes.BTCDelegationStatus_VERIFIED.String() { + if event.NewState == bstypes.BTCDelegationStatus_VERIFIED.String() { // This will only happen if the staker is following the new pre-approval flow. // For more info read https://github.com/babylonlabs-io/pm/blob/main/rfc/rfc-008-staking-transaction-pre-approval.md#handling-of-the-modified--msgcreatebtcdelegation-message @@ -208,7 +212,7 @@ func (s *Service) validateCovenantQuorumReachedEvent(ctx context.Context, event Msg("Ignoring EventCovenantQuorumReached because inclusion proof already received") return false, nil } - } else if event.NewState == bbntypes.BTCDelegationStatus_ACTIVE.String() { + } else if event.NewState == bstypes.BTCDelegationStatus_ACTIVE.String() { // This will happen if the inclusion proof is received in MsgCreateBTCDelegation, i.e the staker is following the old flow // Delegation should have the inclusion proof @@ -225,7 +229,7 @@ func (s *Service) validateCovenantQuorumReachedEvent(ctx context.Context, event return true, nil } -func (s *Service) validateBTCDelegationInclusionProofReceivedEvent(ctx context.Context, event *bbntypes.EventBTCDelegationInclusionProofReceived) (bool, *types.Error) { +func (s *Service) validateBTCDelegationInclusionProofReceivedEvent(ctx context.Context, event *bstypes.EventBTCDelegationInclusionProofReceived) (bool, *types.Error) { // Check if the staking tx hash is present if event.StakingTxHash == "" { return false, types.NewErrorWithMsg( @@ -277,7 +281,7 @@ func (s *Service) validateBTCDelegationInclusionProofReceivedEvent(ctx context.C return true, nil } -func (s *Service) validateBTCDelegationUnbondedEarlyEvent(ctx context.Context, event *bbntypes.EventBTCDelgationUnbondedEarly) (bool, *types.Error) { +func (s *Service) validateBTCDelegationUnbondedEarlyEvent(ctx context.Context, event *bstypes.EventBTCDelgationUnbondedEarly) (bool, *types.Error) { // Check if the staking tx hash is present if event.StakingTxHash == "" { return false, types.NewErrorWithMsg( @@ -288,7 +292,7 @@ func (s *Service) validateBTCDelegationUnbondedEarlyEvent(ctx context.Context, e } // Validate the event state - if event.NewState != bbntypes.BTCDelegationStatus_UNBONDED.String() { + if event.NewState != bstypes.BTCDelegationStatus_UNBONDED.String() { return false, types.NewValidationFailedError( fmt.Errorf("invalid delegation state from Babylon: expected UNBONDED, got %s", event.NewState), ) @@ -316,7 +320,7 @@ func (s *Service) validateBTCDelegationUnbondedEarlyEvent(ctx context.Context, e return true, nil } -func (s *Service) validateBTCDelegationExpiredEvent(ctx context.Context, event *bbntypes.EventBTCDelegationExpired) (bool, *types.Error) { +func (s *Service) validateBTCDelegationExpiredEvent(ctx context.Context, event *bstypes.EventBTCDelegationExpired) (bool, *types.Error) { // Check if the staking tx hash is present if event.StakingTxHash == "" { return false, types.NewErrorWithMsg( @@ -327,7 +331,7 @@ func (s *Service) validateBTCDelegationExpiredEvent(ctx context.Context, event * } // Validate the event state - if event.NewState != bbntypes.BTCDelegationStatus_UNBONDED.String() { + if event.NewState != bstypes.BTCDelegationStatus_UNBONDED.String() { return false, types.NewValidationFailedError( fmt.Errorf("invalid delegation state from Babylon: expected UNBONDED, got %s", event.NewState), ) @@ -354,3 +358,7 @@ func (s *Service) validateBTCDelegationExpiredEvent(ctx context.Context, event * return true, nil } + +func (s *Service) validateSlashedFinalityProviderEvent(ctx context.Context, event *ftypes.EventSlashedFinalityProvider) (bool, *types.Error) { + return true, nil +} From bd81d5bd855fc0dd3b1ae44ec52890cb35b72121 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Sat, 16 Nov 2024 21:55:23 +0530 Subject: [PATCH 3/7] fix --- internal/db/delegation.go | 74 ++++++++++++++++++- ...ality-provider.go => finality_provider.go} | 0 internal/db/interface.go | 20 +++++ ...ality-provider.go => finality_provider.go} | 0 internal/services/delegation.go | 19 ++++- internal/services/events.go | 17 +++++ tests/mocks/mock_db_client.go | 48 ++++++++++++ 7 files changed, 173 insertions(+), 5 deletions(-) rename internal/db/{finality-provider.go => finality_provider.go} (100%) rename internal/db/model/{finality-provider.go => finality_provider.go} (100%) diff --git a/internal/db/delegation.go b/internal/db/delegation.go index ddf5e0b..15f316a 100644 --- a/internal/db/delegation.go +++ b/internal/db/delegation.go @@ -3,6 +3,8 @@ package db import ( "context" "errors" + "fmt" + "log" "github.com/babylonlabs-io/babylon-staking-indexer/internal/db/model" "github.com/babylonlabs-io/babylon-staking-indexer/internal/types" @@ -36,8 +38,12 @@ func (db *Database) SaveNewBTCDelegation( 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()}} + filter := bson.M{"_id": stakingTxHash} + update := bson.M{ + "$set": bson.M{ + "state": newState.String(), + }, + } res := db.client.Database(db.dbName). Collection(model.BTCDelegationDetailsCollection). @@ -110,7 +116,8 @@ func (db *Database) UpdateBTCDelegationDetails( func (db *Database) GetBTCDelegationByStakingTxHash( ctx context.Context, stakingTxHash string, ) (*model.BTCDelegationDetails, error) { - filter := map[string]interface{}{"_id": stakingTxHash} + filter := bson.M{"_id": stakingTxHash} + res := db.client.Database(db.dbName). Collection(model.BTCDelegationDetailsCollection). FindOne(ctx, filter) @@ -129,3 +136,64 @@ func (db *Database) GetBTCDelegationByStakingTxHash( return &delegationDoc, nil } + +func (db *Database) GetBTCDelegationsByFinalityProviderPk( + ctx context.Context, + fpBtcPkHex string, +) ([]*model.BTCDelegationDetails, error) { + filter := bson.M{ + "finality_provider_btc_pks_hex": fpBtcPkHex, + } + + cursor, err := db.client.Database(db.dbName). + Collection(model.BTCDelegationDetailsCollection). + Find(ctx, filter) + if err != nil { + return nil, fmt.Errorf("failed to find delegations: %w", err) + } + defer cursor.Close(ctx) + + var delegations []*model.BTCDelegationDetails + if err := cursor.All(ctx, &delegations); err != nil { + return nil, fmt.Errorf("failed to decode delegations: %w", err) + } + + if len(delegations) == 0 { + return nil, &NotFoundError{ + Key: fpBtcPkHex, + Message: "no BTC delegations found for finality provider public key", + } + } + + return delegations, nil +} + +func (db *Database) UpdateDelegationsStateByFinalityProvider( + ctx context.Context, + fpBTCPKHex string, + newState types.DelegationState, +) error { + filter := bson.M{ + "finality_provider_btc_pks_hex": fpBTCPKHex, + } + + update := bson.M{ + "$set": bson.M{ + "state": newState.String(), + }, + } + + result, err := db.client.Database(db.dbName). + Collection(model.BTCDelegationDetailsCollection). + UpdateMany(ctx, filter, update) + if err != nil { + return fmt.Errorf("failed to update delegations: %w", err) + } + + log.Printf("Updated %d delegations for finality provider %s from states %v to %s", + result.ModifiedCount, + fpBTCPKHex, + newState, + ) + return nil +} diff --git a/internal/db/finality-provider.go b/internal/db/finality_provider.go similarity index 100% rename from internal/db/finality-provider.go rename to internal/db/finality_provider.go diff --git a/internal/db/interface.go b/internal/db/interface.go index 8796635..5212fd5 100644 --- a/internal/db/interface.go +++ b/internal/db/interface.go @@ -127,6 +127,26 @@ type DbInterface interface { GetBTCDelegationByStakingTxHash( ctx context.Context, stakingTxHash string, ) (*model.BTCDelegationDetails, error) + /** + * GetBTCDelegationsByFinalityProviderPk retrieves the BTC delegations by the finality provider public key. + * @param ctx The context + * @param fpBtcPkHex The finality provider public key + * @return The BTC delegations or an error + */ + GetBTCDelegationsByFinalityProviderPk( + ctx context.Context, fpBtcPkHex string, + ) ([]*model.BTCDelegationDetails, error) + /** + * UpdateDelegationsStateByFinalityProvider updates the BTC delegation state by the finality provider public key. + * @param ctx The context + * @param fpBtcPkHex The finality provider public key + * @param newState The new state + * @param qualifiedStates The qualified states + * @return An error if the operation failed + */ + UpdateDelegationsStateByFinalityProvider( + ctx context.Context, fpBtcPkHex string, newState types.DelegationState, + ) error /** * SaveNewTimeLockExpire saves a new timelock expire to the database. * If the timelock expire already exists, DuplicateKeyError will be returned. diff --git a/internal/db/model/finality-provider.go b/internal/db/model/finality_provider.go similarity index 100% rename from internal/db/model/finality-provider.go rename to internal/db/model/finality_provider.go diff --git a/internal/services/delegation.go b/internal/services/delegation.go index 704742e..f800fb3 100644 --- a/internal/services/delegation.go +++ b/internal/services/delegation.go @@ -244,6 +244,7 @@ func (s *Service) processBTCDelegationExpiredEvent( return nil } + func (s *Service) processSlashedFinalityProviderEvent( ctx context.Context, event abcitypes.Event, ) *types.Error { @@ -255,13 +256,27 @@ func (s *Service) processSlashedFinalityProviderEvent( return err } - proceed, err := s.validateSlashedFinalityProviderEvent(ctx, slashedFinalityProviderEvent) + shouldProcess, err := s.validateSlashedFinalityProviderEvent(ctx, slashedFinalityProviderEvent) if err != nil { return err } - if !proceed { + if !shouldProcess { + // Event is valid but should be skipped return nil } + evidence := slashedFinalityProviderEvent.Evidence + fpBTCPKHex := evidence.FpBtcPk.MarshalHex() + + if dbErr := s.db.UpdateDelegationsStateByFinalityProvider( + ctx, fpBTCPKHex, types.StateSlashed, + ); dbErr != nil { + return types.NewError( + http.StatusInternalServerError, + types.InternalServiceError, + fmt.Errorf("failed to update BTC delegation state: %w", dbErr), + ) + } + return nil } diff --git a/internal/services/events.go b/internal/services/events.go index 5c77e72..048d494 100644 --- a/internal/services/events.go +++ b/internal/services/events.go @@ -360,5 +360,22 @@ func (s *Service) validateBTCDelegationExpiredEvent(ctx context.Context, event * } func (s *Service) validateSlashedFinalityProviderEvent(ctx context.Context, event *ftypes.EventSlashedFinalityProvider) (bool, *types.Error) { + if event.Evidence == nil { + return false, types.NewErrorWithMsg( + http.StatusInternalServerError, + types.InternalServiceError, + "slashed finality provider event missing evidence", + ) + } + + _, err := event.Evidence.ExtractBTCSK() + if err != nil { + return false, types.NewError( + http.StatusInternalServerError, + types.InternalServiceError, + fmt.Errorf("failed to extract BTC SK of the slashed finality provider: %w", err), + ) + } + return true, nil } diff --git a/tests/mocks/mock_db_client.go b/tests/mocks/mock_db_client.go index ddd0a4e..ebd4e60 100644 --- a/tests/mocks/mock_db_client.go +++ b/tests/mocks/mock_db_client.go @@ -127,6 +127,36 @@ func (_m *DbInterface) GetBTCDelegationState(ctx context.Context, stakingTxHash return r0, r1 } +// GetBTCDelegationsByFinalityProviderPk provides a mock function with given fields: ctx, fpBtcPkHex +func (_m *DbInterface) GetBTCDelegationsByFinalityProviderPk(ctx context.Context, fpBtcPkHex string) ([]*model.BTCDelegationDetails, error) { + ret := _m.Called(ctx, fpBtcPkHex) + + if len(ret) == 0 { + panic("no return value specified for GetBTCDelegationsByFinalityProviderPk") + } + + var r0 []*model.BTCDelegationDetails + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) ([]*model.BTCDelegationDetails, error)); ok { + return rf(ctx, fpBtcPkHex) + } + if rf, ok := ret.Get(0).(func(context.Context, string) []*model.BTCDelegationDetails); ok { + r0 = rf(ctx, fpBtcPkHex) + } 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, fpBtcPkHex) + } 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) @@ -359,6 +389,24 @@ func (_m *DbInterface) UpdateBTCDelegationState(ctx context.Context, stakingTxHa return r0 } +// UpdateDelegationsStateByFinalityProvider provides a mock function with given fields: ctx, fpBtcPkHex, newState +func (_m *DbInterface) UpdateDelegationsStateByFinalityProvider(ctx context.Context, fpBtcPkHex string, newState types.DelegationState) error { + ret := _m.Called(ctx, fpBtcPkHex, newState) + + if len(ret) == 0 { + panic("no return value specified for UpdateDelegationsStateByFinalityProvider") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, types.DelegationState) error); ok { + r0 = rf(ctx, fpBtcPkHex, 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) From 4c7a78c6226611ae83865cea0909da868bd390e2 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Sat, 16 Nov 2024 21:56:16 +0530 Subject: [PATCH 4/7] rm method --- internal/db/delegation.go | 31 ------------------------------- internal/db/interface.go | 9 --------- tests/mocks/mock_db_client.go | 30 ------------------------------ 3 files changed, 70 deletions(-) diff --git a/internal/db/delegation.go b/internal/db/delegation.go index 15f316a..48a3779 100644 --- a/internal/db/delegation.go +++ b/internal/db/delegation.go @@ -137,37 +137,6 @@ func (db *Database) GetBTCDelegationByStakingTxHash( return &delegationDoc, nil } -func (db *Database) GetBTCDelegationsByFinalityProviderPk( - ctx context.Context, - fpBtcPkHex string, -) ([]*model.BTCDelegationDetails, error) { - filter := bson.M{ - "finality_provider_btc_pks_hex": fpBtcPkHex, - } - - cursor, err := db.client.Database(db.dbName). - Collection(model.BTCDelegationDetailsCollection). - Find(ctx, filter) - if err != nil { - return nil, fmt.Errorf("failed to find delegations: %w", err) - } - defer cursor.Close(ctx) - - var delegations []*model.BTCDelegationDetails - if err := cursor.All(ctx, &delegations); err != nil { - return nil, fmt.Errorf("failed to decode delegations: %w", err) - } - - if len(delegations) == 0 { - return nil, &NotFoundError{ - Key: fpBtcPkHex, - Message: "no BTC delegations found for finality provider public key", - } - } - - return delegations, nil -} - func (db *Database) UpdateDelegationsStateByFinalityProvider( ctx context.Context, fpBTCPKHex string, diff --git a/internal/db/interface.go b/internal/db/interface.go index 5212fd5..22fdd1b 100644 --- a/internal/db/interface.go +++ b/internal/db/interface.go @@ -127,15 +127,6 @@ type DbInterface interface { GetBTCDelegationByStakingTxHash( ctx context.Context, stakingTxHash string, ) (*model.BTCDelegationDetails, error) - /** - * GetBTCDelegationsByFinalityProviderPk retrieves the BTC delegations by the finality provider public key. - * @param ctx The context - * @param fpBtcPkHex The finality provider public key - * @return The BTC delegations or an error - */ - GetBTCDelegationsByFinalityProviderPk( - ctx context.Context, fpBtcPkHex string, - ) ([]*model.BTCDelegationDetails, error) /** * UpdateDelegationsStateByFinalityProvider updates the BTC delegation state by the finality provider public key. * @param ctx The context diff --git a/tests/mocks/mock_db_client.go b/tests/mocks/mock_db_client.go index ebd4e60..53a0d40 100644 --- a/tests/mocks/mock_db_client.go +++ b/tests/mocks/mock_db_client.go @@ -127,36 +127,6 @@ func (_m *DbInterface) GetBTCDelegationState(ctx context.Context, stakingTxHash return r0, r1 } -// GetBTCDelegationsByFinalityProviderPk provides a mock function with given fields: ctx, fpBtcPkHex -func (_m *DbInterface) GetBTCDelegationsByFinalityProviderPk(ctx context.Context, fpBtcPkHex string) ([]*model.BTCDelegationDetails, error) { - ret := _m.Called(ctx, fpBtcPkHex) - - if len(ret) == 0 { - panic("no return value specified for GetBTCDelegationsByFinalityProviderPk") - } - - var r0 []*model.BTCDelegationDetails - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) ([]*model.BTCDelegationDetails, error)); ok { - return rf(ctx, fpBtcPkHex) - } - if rf, ok := ret.Get(0).(func(context.Context, string) []*model.BTCDelegationDetails); ok { - r0 = rf(ctx, fpBtcPkHex) - } 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, fpBtcPkHex) - } 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) From df6e47c8f094b8ef1630a141501611579839634e Mon Sep 17 00:00:00 2001 From: Gurjot Date: Sun, 17 Nov 2024 12:27:59 +0530 Subject: [PATCH 5/7] fix log --- internal/db/delegation.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/db/delegation.go b/internal/db/delegation.go index 48a3779..8d1cb87 100644 --- a/internal/db/delegation.go +++ b/internal/db/delegation.go @@ -159,10 +159,10 @@ func (db *Database) UpdateDelegationsStateByFinalityProvider( return fmt.Errorf("failed to update delegations: %w", err) } - log.Printf("Updated %d delegations for finality provider %s from states %v to %s", + log.Printf("Updated %d delegations for finality provider %s to state %s", result.ModifiedCount, fpBTCPKHex, - newState, + newState.String(), ) return nil } From 3c3f0c6b0d312a92545313b7d51350ac3c2ee1ac Mon Sep 17 00:00:00 2001 From: Gurjot Date: Sun, 17 Nov 2024 13:37:56 +0530 Subject: [PATCH 6/7] add todo --- internal/services/delegation.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/services/delegation.go b/internal/services/delegation.go index f800fb3..c008052 100644 --- a/internal/services/delegation.go +++ b/internal/services/delegation.go @@ -278,5 +278,8 @@ func (s *Service) processSlashedFinalityProviderEvent( ) } + // TODO: start watching for slashing spend to + // identify if staker has withdrawn after slashing + return nil } From 347d0206d9be762fbf6a5ff34b0ca82374586d9d Mon Sep 17 00:00:00 2001 From: Gurjot Date: Sun, 17 Nov 2024 17:30:57 +0530 Subject: [PATCH 7/7] fix todo --- internal/services/delegation.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/services/delegation.go b/internal/services/delegation.go index c008052..a71768f 100644 --- a/internal/services/delegation.go +++ b/internal/services/delegation.go @@ -278,8 +278,9 @@ func (s *Service) processSlashedFinalityProviderEvent( ) } - // TODO: start watching for slashing spend to - // identify if staker has withdrawn after slashing + // TODO: babylon needs to emit slashing tx + // so indexer can start watching for slashing spend + // to identify if staker has withdrawn after slashing return nil }