From 1eb464c81c877f8b88ff0755219fc59337949171 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Thu, 2 Jan 2025 16:07:09 +0530 Subject: [PATCH 01/13] emit withdrawable event --- consumer/event_consumer.go | 1 + go.mod | 2 +- go.sum | 4 ++-- internal/services/consumer_events.go | 13 +++++++++++++ internal/services/expiry_checker.go | 7 +++++++ 5 files changed, 24 insertions(+), 3 deletions(-) diff --git a/consumer/event_consumer.go b/consumer/event_consumer.go index 9f0c3f7..3f896ae 100644 --- a/consumer/event_consumer.go +++ b/consumer/event_consumer.go @@ -8,5 +8,6 @@ type EventConsumer interface { Start() error PushActiveStakingEvent(ev *client.StakingEvent) error PushUnbondingStakingEvent(ev *client.StakingEvent) error + PushWithdrawableStakingEvent(ev *client.StakingEvent) error Stop() error } diff --git a/go.mod b/go.mod index 0270c06..f010cf7 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( cosmossdk.io/math v1.4.0 github.com/avast/retry-go/v4 v4.5.1 github.com/babylonlabs-io/babylon v1.0.0-rc.2 - github.com/babylonlabs-io/staking-queue-client v0.4.7-0.20241212112557-9ac7de686075 + github.com/babylonlabs-io/staking-queue-client v0.4.7-0.20250102103040-970b1f069cf1 github.com/btcsuite/btcd v0.24.3-0.20241011125836-24eb815168f4 github.com/btcsuite/btcd/btcec/v2 v2.3.4 github.com/btcsuite/btcd/btcutil v1.1.6 diff --git a/go.sum b/go.sum index 93aaac3..d1f9df8 100644 --- a/go.sum +++ b/go.sum @@ -1429,8 +1429,8 @@ github.com/aws/aws-sdk-go v1.44.312/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8 github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/babylonlabs-io/babylon v1.0.0-rc.2 h1:H7OpEDNNOXyC+9TUo4vVYLlHNhOQ8m9KqWP1qzjEt0c= github.com/babylonlabs-io/babylon v1.0.0-rc.2/go.mod h1:B8ma8IjGUEKhmoRfwv60Qa7DtUXssCgtmD89huQ4+5I= -github.com/babylonlabs-io/staking-queue-client v0.4.7-0.20241212112557-9ac7de686075 h1:gB+jslBkK5/ror4sn9NHldKjLu4nE88jgD43d2L3osc= -github.com/babylonlabs-io/staking-queue-client v0.4.7-0.20241212112557-9ac7de686075/go.mod h1:n3fr3c+9LNiJlyETmcrVk94Zn76rAADhGZKxX+rVf+Q= +github.com/babylonlabs-io/staking-queue-client v0.4.7-0.20250102103040-970b1f069cf1 h1:Sz8eiPtnYgcgX50gtXK6a/gH1an/LE3s6ug4PSe2LU4= +github.com/babylonlabs-io/staking-queue-client v0.4.7-0.20250102103040-970b1f069cf1/go.mod h1:n3fr3c+9LNiJlyETmcrVk94Zn76rAADhGZKxX+rVf+Q= 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/internal/services/consumer_events.go b/internal/services/consumer_events.go index 84790b1..7de51f8 100644 --- a/internal/services/consumer_events.go +++ b/internal/services/consumer_events.go @@ -41,3 +41,16 @@ func (s *Service) emitUnbondingDelegationEvent(ctx context.Context, delegation * } return nil } + +func (s *Service) emitWithdrawableDelegationEvent(ctx context.Context, delegation *model.BTCDelegationDetails) *types.Error { + ev := queuecli.NewWithdrawableStakingEvent( + delegation.StakingTxHashHex, + delegation.StakerBtcPkHex, + delegation.FinalityProviderBtcPksHex, + delegation.StakingAmount, + ) + if err := s.queueManager.PushWithdrawableStakingEvent(&ev); err != nil { + return types.NewInternalServiceError(fmt.Errorf("failed to push the withdrawable event to the queue: %w", err)) + } + return nil +} diff --git a/internal/services/expiry_checker.go b/internal/services/expiry_checker.go index 954fac8..913a93d 100644 --- a/internal/services/expiry_checker.go +++ b/internal/services/expiry_checker.go @@ -76,6 +76,13 @@ func (s *Service) checkExpiry(ctx context.Context) *types.Error { ) } + if err := s.emitWithdrawableDelegationEvent(ctx, delegation); err != nil { + log.Error(). + Str("staking_tx", delegation.StakingTxHashHex). + Msg("failed to emit withdrawable delegation event") + return err + } + if err := s.db.DeleteExpiredDelegation(ctx, delegation.StakingTxHashHex); err != nil { log.Error(). Str("staking_tx", delegation.StakingTxHashHex). From 30c60d6da4e684a75828a10afd1e3896d29f46f4 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Thu, 2 Jan 2025 17:25:53 +0530 Subject: [PATCH 02/13] emit withdrawable and withdrawn events --- consumer/event_consumer.go | 1 + go.mod | 2 +- go.sum | 2 ++ internal/services/consumer_events.go | 13 +++++++++++++ internal/services/watch_btc_events.go | 24 ++++++++++++++++++++++-- 5 files changed, 39 insertions(+), 3 deletions(-) diff --git a/consumer/event_consumer.go b/consumer/event_consumer.go index 3f896ae..4e88b0d 100644 --- a/consumer/event_consumer.go +++ b/consumer/event_consumer.go @@ -9,5 +9,6 @@ type EventConsumer interface { PushActiveStakingEvent(ev *client.StakingEvent) error PushUnbondingStakingEvent(ev *client.StakingEvent) error PushWithdrawableStakingEvent(ev *client.StakingEvent) error + PushWithdrawnStakingEvent(ev *client.StakingEvent) error Stop() error } diff --git a/go.mod b/go.mod index f010cf7..ef70a10 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( cosmossdk.io/math v1.4.0 github.com/avast/retry-go/v4 v4.5.1 github.com/babylonlabs-io/babylon v1.0.0-rc.2 - github.com/babylonlabs-io/staking-queue-client v0.4.7-0.20250102103040-970b1f069cf1 + github.com/babylonlabs-io/staking-queue-client v0.4.7-0.20250102114800-e2899342683b github.com/btcsuite/btcd v0.24.3-0.20241011125836-24eb815168f4 github.com/btcsuite/btcd/btcec/v2 v2.3.4 github.com/btcsuite/btcd/btcutil v1.1.6 diff --git a/go.sum b/go.sum index d1f9df8..611bc6d 100644 --- a/go.sum +++ b/go.sum @@ -1431,6 +1431,8 @@ github.com/babylonlabs-io/babylon v1.0.0-rc.2 h1:H7OpEDNNOXyC+9TUo4vVYLlHNhOQ8m9 github.com/babylonlabs-io/babylon v1.0.0-rc.2/go.mod h1:B8ma8IjGUEKhmoRfwv60Qa7DtUXssCgtmD89huQ4+5I= github.com/babylonlabs-io/staking-queue-client v0.4.7-0.20250102103040-970b1f069cf1 h1:Sz8eiPtnYgcgX50gtXK6a/gH1an/LE3s6ug4PSe2LU4= github.com/babylonlabs-io/staking-queue-client v0.4.7-0.20250102103040-970b1f069cf1/go.mod h1:n3fr3c+9LNiJlyETmcrVk94Zn76rAADhGZKxX+rVf+Q= +github.com/babylonlabs-io/staking-queue-client v0.4.7-0.20250102114800-e2899342683b h1:U88aKs6LYDsS1+Co8MvMvxtogO7HYjUoeyeO9PDFiaY= +github.com/babylonlabs-io/staking-queue-client v0.4.7-0.20250102114800-e2899342683b/go.mod h1:n3fr3c+9LNiJlyETmcrVk94Zn76rAADhGZKxX+rVf+Q= 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/internal/services/consumer_events.go b/internal/services/consumer_events.go index 7de51f8..286471a 100644 --- a/internal/services/consumer_events.go +++ b/internal/services/consumer_events.go @@ -54,3 +54,16 @@ func (s *Service) emitWithdrawableDelegationEvent(ctx context.Context, delegatio } return nil } + +func (s *Service) emitWithdrawnDelegationEvent(ctx context.Context, delegation *model.BTCDelegationDetails) *types.Error { + ev := queuecli.NewWithdrawnStakingEvent( + delegation.StakingTxHashHex, + delegation.StakerBtcPkHex, + delegation.FinalityProviderBtcPksHex, + delegation.StakingAmount, + ) + if err := s.queueManager.PushWithdrawnStakingEvent(&ev); err != nil { + return types.NewInternalServiceError(fmt.Errorf("failed to push the withdrawn event to the queue: %w", err)) + } + return nil +} diff --git a/internal/services/watch_btc_events.go b/internal/services/watch_btc_events.go index 7760b5b..4abc203 100644 --- a/internal/services/watch_btc_events.go +++ b/internal/services/watch_btc_events.go @@ -146,6 +146,14 @@ func (s *Service) watchForSpendSlashingChange( return } + if err := s.emitWithdrawnDelegationEvent(quitCtx, delegation); err != nil { + log.Error(). + Err(err). + Str("staking_tx", delegation.StakingTxHashHex). + Msg("failed to emit withdrawn delegation event") + return + } + case <-s.quit: return case <-quitCtx.Done(): @@ -321,13 +329,25 @@ func (s *Service) handleWithdrawal( Str("state", types.StateWithdrawn.String()). Str("sub_state", subState.String()). Msg("updating delegation state to withdrawn") - return s.db.UpdateBTCDelegationState( + if err := s.db.UpdateBTCDelegationState( ctx, delegation.StakingTxHashHex, types.QualifiedStatesForWithdrawn(), types.StateWithdrawn, &subState, - ) + ); err != nil { + return fmt.Errorf("failed to update delegation state to withdrawn: %w", err) + } + + if err := s.emitWithdrawnDelegationEvent(ctx, delegation); err != nil { + log.Error(). + Err(err). + Str("staking_tx", delegation.StakingTxHashHex). + Msg("failed to emit withdrawn delegation event") + return err + } + + return nil } func (s *Service) startWatchingSlashingChange( From 1571ac86981e45ac7ab838af744d141580fc4a62 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Thu, 2 Jan 2025 18:12:14 +0530 Subject: [PATCH 03/13] bump queue client --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index ef70a10..2d663c2 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( cosmossdk.io/math v1.4.0 github.com/avast/retry-go/v4 v4.5.1 github.com/babylonlabs-io/babylon v1.0.0-rc.2 - github.com/babylonlabs-io/staking-queue-client v0.4.7-0.20250102114800-e2899342683b + github.com/babylonlabs-io/staking-queue-client v0.4.7-0.20250102124054-14d01f4accfb github.com/btcsuite/btcd v0.24.3-0.20241011125836-24eb815168f4 github.com/btcsuite/btcd/btcec/v2 v2.3.4 github.com/btcsuite/btcd/btcutil v1.1.6 diff --git a/go.sum b/go.sum index 611bc6d..c92d17b 100644 --- a/go.sum +++ b/go.sum @@ -1433,6 +1433,8 @@ github.com/babylonlabs-io/staking-queue-client v0.4.7-0.20250102103040-970b1f069 github.com/babylonlabs-io/staking-queue-client v0.4.7-0.20250102103040-970b1f069cf1/go.mod h1:n3fr3c+9LNiJlyETmcrVk94Zn76rAADhGZKxX+rVf+Q= github.com/babylonlabs-io/staking-queue-client v0.4.7-0.20250102114800-e2899342683b h1:U88aKs6LYDsS1+Co8MvMvxtogO7HYjUoeyeO9PDFiaY= github.com/babylonlabs-io/staking-queue-client v0.4.7-0.20250102114800-e2899342683b/go.mod h1:n3fr3c+9LNiJlyETmcrVk94Zn76rAADhGZKxX+rVf+Q= +github.com/babylonlabs-io/staking-queue-client v0.4.7-0.20250102124054-14d01f4accfb h1:+lWIl/RENFXQRnc8bVmU/RwUfgbeqd+UAmFXLDeAsCk= +github.com/babylonlabs-io/staking-queue-client v0.4.7-0.20250102124054-14d01f4accfb/go.mod h1:n3fr3c+9LNiJlyETmcrVk94Zn76rAADhGZKxX+rVf+Q= 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 a79bc48bf17ad9f8282f73243820ccb9db083bbd Mon Sep 17 00:00:00 2001 From: Gurjot Date: Thu, 2 Jan 2025 18:20:20 +0530 Subject: [PATCH 04/13] pub image --- .github/workflows/publish.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 49b0290..59189d7 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -4,6 +4,7 @@ on: push: branches: - 'main' + - '188-add-staked-withdrawable-tvl' tags: - '*' From d4c7e0e722d1e1f7337895769bda41c1f8d846eb Mon Sep 17 00:00:00 2001 From: Gurjot Date: Fri, 3 Jan 2025 14:43:56 +0530 Subject: [PATCH 05/13] emit events --- internal/services/delegation.go | 34 ++++++++++++++- internal/services/events.go | 68 +++++++++++++++++++++-------- internal/services/expiry_checker.go | 32 +++++++++++++- internal/types/state.go | 15 +++++++ 4 files changed, 128 insertions(+), 21 deletions(-) diff --git a/internal/services/delegation.go b/internal/services/delegation.go index 05f52f5..264f901 100644 --- a/internal/services/delegation.go +++ b/internal/services/delegation.go @@ -284,12 +284,27 @@ func (s *Service) processBTCDelegationUnbondedEarlyEvent( return err } - shouldProcess, err := s.validateBTCDelegationUnbondedEarlyEvent(ctx, unbondedEarlyEvent) + shouldProcess, shouldEmitEvent, err := s.validateBTCDelegationUnbondedEarlyEvent(ctx, unbondedEarlyEvent) if err != nil { return err } if !shouldProcess { // Event is valid but should be skipped + if shouldEmitEvent { + delegation, 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), + ) + } + + // Emit consumer event + if err := s.emitUnbondingDelegationEvent(ctx, delegation); err != nil { + return err + } + } return nil } @@ -372,12 +387,27 @@ func (s *Service) processBTCDelegationExpiredEvent( return err } - shouldProcess, err := s.validateBTCDelegationExpiredEvent(ctx, expiredEvent) + shouldProcess, shouldEmitEvent, err := s.validateBTCDelegationExpiredEvent(ctx, expiredEvent) if err != nil { return err } if !shouldProcess { // Event is valid but should be skipped + if shouldEmitEvent { + delegation, 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), + ) + } + + // Emit consumer event + if err := s.emitUnbondingDelegationEvent(ctx, delegation); err != nil { + return err + } + } return nil } diff --git a/internal/services/events.go b/internal/services/events.go index 031834f..fe725ec 100644 --- a/internal/services/events.go +++ b/internal/services/events.go @@ -298,10 +298,10 @@ func (s *Service) validateBTCDelegationInclusionProofReceivedEvent(ctx context.C return true, nil } -func (s *Service) validateBTCDelegationUnbondedEarlyEvent(ctx context.Context, event *bstypes.EventBTCDelgationUnbondedEarly) (bool, *types.Error) { +func (s *Service) validateBTCDelegationUnbondedEarlyEvent(ctx context.Context, event *bstypes.EventBTCDelgationUnbondedEarly) (bool, bool, *types.Error) { // Check if the staking tx hash is present if event.StakingTxHash == "" { - return false, types.NewErrorWithMsg( + return false, false, types.NewErrorWithMsg( http.StatusInternalServerError, types.InternalServiceError, "unbonded early event missing staking tx hash", @@ -310,7 +310,7 @@ func (s *Service) validateBTCDelegationUnbondedEarlyEvent(ctx context.Context, e // Validate the event state if event.NewState != bstypes.BTCDelegationStatus_UNBONDED.String() { - return false, types.NewValidationFailedError( + return false, false, types.NewValidationFailedError( fmt.Errorf("invalid delegation state from Babylon when processing EventBTCDelgationUnbondedEarly: expected UNBONDED, got %s", event.NewState), ) } @@ -318,29 +318,45 @@ func (s *Service) validateBTCDelegationUnbondedEarlyEvent(ctx context.Context, e // Fetch the current delegation state from the database delegation, dbErr := s.db.GetBTCDelegationByStakingTxHash(ctx, event.StakingTxHash) if dbErr != nil { - return false, types.NewError( + return false, 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 outdated for the transition + // We should emit the event if the current state is outdated + if utils.Contains(types.OutdatedStatesForUnbondedEarly(), delegation.State) { + log.Debug(). + Str("stakingTxHashHex", event.StakingTxHash). + Str("currentState", event.NewState). + Str("event_type", "EventBTCDelgationUnbondedEarly"). + Msg("Current state is outdated for transition") + return false, true, nil + } + // Check if the current state is qualified for the transition if !utils.Contains(types.QualifiedStatesForUnbondedEarly(), delegation.State) { - log.Debug(). + log.Error(). Str("stakingTxHashHex", event.StakingTxHash). Str("currentState", delegation.State.String()). - Msg("Ignoring EventBTCDelgationUnbondedEarly because current state is not qualified for transition") - return false, nil + Str("event_type", "EventBTCDelgationUnbondedEarly"). + Msg("Current state is not qualified for transition") + return false, false, types.NewErrorWithMsg( + http.StatusForbidden, + types.Forbidden, + "current state is not qualified for transition", + ) } - return true, nil + return true, true, nil } -func (s *Service) validateBTCDelegationExpiredEvent(ctx context.Context, event *bstypes.EventBTCDelegationExpired) (bool, *types.Error) { +func (s *Service) validateBTCDelegationExpiredEvent(ctx context.Context, event *bstypes.EventBTCDelegationExpired) (bool, bool, *types.Error) { // Check if the staking tx hash is present if event.StakingTxHash == "" { - return false, types.NewErrorWithMsg( + return false, false, types.NewErrorWithMsg( http.StatusInternalServerError, types.InternalServiceError, "expired event missing staking tx hash", @@ -349,31 +365,49 @@ func (s *Service) validateBTCDelegationExpiredEvent(ctx context.Context, event * // Validate the event state if event.NewState != bstypes.BTCDelegationStatus_EXPIRED.String() { - return false, types.NewValidationFailedError( - fmt.Errorf("invalid delegation state from Babylon when processing EventBTCDelegationExpired: expected EXPIRED, got %s", event.NewState), + return false, false, types.NewErrorWithMsg( + http.StatusInternalServerError, + types.InternalServiceError, + fmt.Sprintf("invalid delegation state from Babylon when processing EventBTCDelegationExpired: expected EXPIRED, 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( + return false, 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 outdated for the transition + // We should emit the event if the current state is outdated + if utils.Contains(types.OutdatedStatesForExpired(), delegation.State) { + log.Debug(). + Str("stakingTxHashHex", event.StakingTxHash). + Str("currentState", event.NewState). + Str("event_type", "EventBTCDelegationExpired"). + Msg("Current state is outdated for transition") + return false, true, nil + } + // Check if the current state is qualified for the transition if !utils.Contains(types.QualifiedStatesForExpired(), delegation.State) { - log.Debug(). + log.Error(). Str("stakingTxHashHex", event.StakingTxHash). Str("currentState", delegation.State.String()). - Msg("Ignoring EventBTCDelegationExpired because current state is not qualified for transition") - return false, nil + Str("event_type", "EventBTCDelegationExpired"). + Msg("Current state is not qualified for transition") + return false, false, types.NewErrorWithMsg( + http.StatusForbidden, + types.Forbidden, + "Current state is not qualified for transition", + ) } - return true, nil + return true, true, nil } func (s *Service) validateSlashedFinalityProviderEvent(ctx context.Context, event *ftypes.EventSlashedFinalityProvider) (bool, *types.Error) { diff --git a/internal/services/expiry_checker.go b/internal/services/expiry_checker.go index 913a93d..e4dacc8 100644 --- a/internal/services/expiry_checker.go +++ b/internal/services/expiry_checker.go @@ -52,13 +52,41 @@ func (s *Service) checkExpiry(ctx context.Context) *types.Error { Str("expire_height", strconv.FormatUint(uint64(tlDoc.ExpireHeight), 10)). Msg("checking if delegation is expired") + if utils.Contains(types.OutdatedStatesForExpired(), delegation.State) { + log.Debug(). + Str("staking_tx", delegation.StakingTxHashHex). + Str("current_state", delegation.State.String()). + Msg("current state is outdated for expired") + + if err := s.emitWithdrawableDelegationEvent(ctx, delegation); err != nil { + log.Error(). + Str("staking_tx", delegation.StakingTxHashHex). + Msg("failed to emit withdrawable delegation event") + return err + } + + if err := s.db.DeleteExpiredDelegation(ctx, delegation.StakingTxHashHex); err != nil { + log.Error(). + Str("staking_tx", delegation.StakingTxHashHex). + Msg("failed to delete expired delegation") + return types.NewInternalServiceError( + fmt.Errorf("failed to delete expired delegation: %w", err), + ) + } + + continue + } + // Check if the delegation is in a qualified state to transition to Withdrawable if !utils.Contains(types.QualifiedStatesForWithdrawable(), delegation.State) { - log.Debug(). + log.Error(). Str("staking_tx", delegation.StakingTxHashHex). Str("current_state", delegation.State.String()). Msg("current state is not qualified for withdrawable") - continue + + return types.NewInternalServiceError( + fmt.Errorf("current state is not qualified for withdrawable"), + ) } if err := s.db.UpdateBTCDelegationState( diff --git a/internal/types/state.go b/internal/types/state.go index 91b1631..4afc008 100644 --- a/internal/types/state.go +++ b/internal/types/state.go @@ -46,11 +46,21 @@ func QualifiedStatesForUnbondedEarly() []DelegationState { return []DelegationState{StateActive} } +// OutdatedStatesForUnbondedEarly returns the outdated current states for UnbondedEarly event +func OutdatedStatesForUnbondedEarly() []DelegationState { + return []DelegationState{StateUnbonding, StateWithdrawable, StateWithdrawn} +} + // QualifiedStatesForExpired returns the qualified current states for Expired event func QualifiedStatesForExpired() []DelegationState { return []DelegationState{StateActive} } +// OutdatedStatesForExpired returns the outdated current states for Expired event +func OutdatedStatesForExpired() []DelegationState { + return []DelegationState{StateUnbonding, StateWithdrawable, StateWithdrawn} +} + // QualifiedStatesForWithdrawn returns the qualified current states for Withdrawn event func QualifiedStatesForWithdrawn() []DelegationState { // StateActive/StateUnbonding/StateSlashed is included b/c its possible that expiry checker @@ -64,6 +74,11 @@ func QualifiedStatesForWithdrawable() []DelegationState { return []DelegationState{StateUnbonding, StateSlashed} } +// OutdatedStatesForWithdrawable returns the outdated current states for Withdrawable event +func OutdatedStatesForWithdrawable() []DelegationState { + return []DelegationState{StateWithdrawable, StateWithdrawn} +} + type DelegationSubState string const ( From cd43bc9fa8e62f1d48d7611b9f6de2b0f70fd92e Mon Sep 17 00:00:00 2001 From: Gurjot Date: Fri, 3 Jan 2025 15:43:25 +0530 Subject: [PATCH 06/13] fix --- internal/db/timelock.go | 2 +- internal/services/events.go | 4 ++-- internal/services/expiry_checker.go | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/db/timelock.go b/internal/db/timelock.go index 5d7c2ac..1420b8b 100644 --- a/internal/db/timelock.go +++ b/internal/db/timelock.go @@ -44,7 +44,7 @@ func (db *Database) FindExpiredDelegations(ctx context.Context, btcTipHeight, li func (db *Database) DeleteExpiredDelegation(ctx context.Context, stakingTxHashHex string) error { client := db.client.Database(db.dbName).Collection(model.TimeLockCollection) - filter := bson.M{"_id": stakingTxHashHex} + filter := bson.M{"staking_tx_hash_hex": stakingTxHashHex} result, err := client.DeleteOne(ctx, filter) if err != nil { diff --git a/internal/services/events.go b/internal/services/events.go index fe725ec..b7df90b 100644 --- a/internal/services/events.go +++ b/internal/services/events.go @@ -399,11 +399,11 @@ func (s *Service) validateBTCDelegationExpiredEvent(ctx context.Context, event * Str("stakingTxHashHex", event.StakingTxHash). Str("currentState", delegation.State.String()). Str("event_type", "EventBTCDelegationExpired"). - Msg("Current state is not qualified for transition") + Msg("current state is not qualified for transition") return false, false, types.NewErrorWithMsg( http.StatusForbidden, types.Forbidden, - "Current state is not qualified for transition", + "current state is not qualified for transition", ) } diff --git a/internal/services/expiry_checker.go b/internal/services/expiry_checker.go index e4dacc8..7bc4255 100644 --- a/internal/services/expiry_checker.go +++ b/internal/services/expiry_checker.go @@ -50,13 +50,13 @@ func (s *Service) checkExpiry(ctx context.Context) *types.Error { Str("current_state", delegation.State.String()). Str("new_sub_state", tlDoc.DelegationSubState.String()). Str("expire_height", strconv.FormatUint(uint64(tlDoc.ExpireHeight), 10)). - Msg("checking if delegation is expired") + Msg("checking if delegation is withdrawable") - if utils.Contains(types.OutdatedStatesForExpired(), delegation.State) { + if utils.Contains(types.OutdatedStatesForWithdrawable(), delegation.State) { log.Debug(). Str("staking_tx", delegation.StakingTxHashHex). Str("current_state", delegation.State.String()). - Msg("current state is outdated for expired") + Msg("current state is outdated for withdrawable") if err := s.emitWithdrawableDelegationEvent(ctx, delegation); err != nil { log.Error(). From 2f6429e5fad0736058bb8aba78eb49fdf2568d5c Mon Sep 17 00:00:00 2001 From: Gurjot Date: Fri, 3 Jan 2025 18:21:15 +0530 Subject: [PATCH 07/13] fix --- internal/services/delegation.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/internal/services/delegation.go b/internal/services/delegation.go index 264f901..fdf4d5d 100644 --- a/internal/services/delegation.go +++ b/internal/services/delegation.go @@ -291,6 +291,11 @@ func (s *Service) processBTCDelegationUnbondedEarlyEvent( if !shouldProcess { // Event is valid but should be skipped if shouldEmitEvent { + log.Debug(). + Str("staking_tx", unbondedEarlyEvent.StakingTxHash). + Str("event_type", EventBTCDelgationUnbondedEarly.String()). + Msg("skip processing but emit unbonding event") + delegation, dbErr := s.db.GetBTCDelegationByStakingTxHash(ctx, unbondedEarlyEvent.StakingTxHash) if dbErr != nil { return types.NewError( @@ -394,6 +399,11 @@ func (s *Service) processBTCDelegationExpiredEvent( if !shouldProcess { // Event is valid but should be skipped if shouldEmitEvent { + log.Debug(). + Str("staking_tx", expiredEvent.StakingTxHash). + Str("event_type", EventBTCDelegationExpired.String()). + Msg("skip processing but emit unbonding event") + delegation, dbErr := s.db.GetBTCDelegationByStakingTxHash(ctx, expiredEvent.StakingTxHash) if dbErr != nil { return types.NewError( From 064ba151f400ee719e6e815575d6ae35c7ad4800 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Sun, 5 Jan 2025 18:23:04 +0530 Subject: [PATCH 08/13] fix slashed event --- internal/db/delegation.go | 28 ++++++++++++++++++++++------ internal/db/interface.go | 7 +++++-- internal/services/delegation.go | 18 +++++++++++++++++- internal/services/events.go | 4 ++-- internal/types/state.go | 5 +++++ tests/mocks/mock_db_client.go | 10 +++++----- 6 files changed, 56 insertions(+), 16 deletions(-) diff --git a/internal/db/delegation.go b/internal/db/delegation.go index d80164b..d30ca96 100644 --- a/internal/db/delegation.go +++ b/internal/db/delegation.go @@ -4,7 +4,8 @@ import ( "context" "errors" "fmt" - "log" + + "github.com/rs/zerolog/log" "github.com/babylonlabs-io/babylon-staking-indexer/internal/db/model" "github.com/babylonlabs-io/babylon-staking-indexer/internal/types" @@ -182,10 +183,23 @@ func (db *Database) GetBTCDelegationByStakingTxHash( func (db *Database) UpdateDelegationsStateByFinalityProvider( ctx context.Context, fpBTCPKHex string, + qualifiedPreviousStates []types.DelegationState, newState types.DelegationState, ) error { + if len(qualifiedPreviousStates) == 0 { + return fmt.Errorf("qualified previous states array cannot be empty") + } + + // Convert states to strings + qualifiedStateStrs := make([]string, len(qualifiedPreviousStates)) + for i, state := range qualifiedPreviousStates { + qualifiedStateStrs[i] = state.String() + } + + // Build filter with both FP and qualified states filter := bson.M{ "finality_provider_btc_pks_hex": fpBTCPKHex, + "state": bson.M{"$in": qualifiedStateStrs}, } update := bson.M{ @@ -201,11 +215,13 @@ func (db *Database) UpdateDelegationsStateByFinalityProvider( return fmt.Errorf("failed to update delegations: %w", err) } - log.Printf("Updated %d delegations for finality provider %s to state %s", - result.ModifiedCount, - fpBTCPKHex, - newState.String(), - ) + log.Debug(). + Str("finality_provider", fpBTCPKHex). + Strs("qualified_states", qualifiedStateStrs). + Str("new_state", newState.String()). + Int64("modified_count", result.ModifiedCount). + Msg("Updated delegations for finality provider") + return nil } diff --git a/internal/db/interface.go b/internal/db/interface.go index 3d24efe..a0ec944 100644 --- a/internal/db/interface.go +++ b/internal/db/interface.go @@ -147,12 +147,15 @@ type DbInterface interface { * UpdateDelegationsStateByFinalityProvider updates the BTC delegation state by the finality provider public key. * @param ctx The context * @param fpBtcPkHex The finality provider public key + * @param qualifiedPreviousStates The qualified previous states * @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, + ctx context.Context, + fpBtcPkHex string, + qualifiedPreviousStates []types.DelegationState, + newState types.DelegationState, ) error /** * GetDelegationsByFinalityProvider retrieves the BTC delegations by the finality provider public key. diff --git a/internal/services/delegation.go b/internal/services/delegation.go index fdf4d5d..784a7a4 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" "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" ftypes "github.com/babylonlabs-io/babylon/x/finality/types" abcitypes "github.com/cometbft/cometbft/abci/types" @@ -493,7 +494,7 @@ func (s *Service) processSlashedFinalityProviderEvent( fpBTCPKHex := evidence.FpBtcPk.MarshalHex() if dbErr := s.db.UpdateDelegationsStateByFinalityProvider( - ctx, fpBTCPKHex, types.StateSlashed, + ctx, fpBTCPKHex, types.QualifiedStatesForSlashedDelegation(), types.StateSlashed, ); dbErr != nil { return types.NewError( http.StatusInternalServerError, @@ -513,13 +514,28 @@ func (s *Service) processSlashedFinalityProviderEvent( for _, delegation := range delegations { if !delegation.HasInclusionProof() { + // If the delegation was never active/has no inclusion proof + // no need to emit the event, as it doesn't contribute to stats log.Debug(). Str("staking_tx", delegation.StakingTxHashHex). + Str("event_type", EventSlashedFinalityProvider.String()). + Str("current_state", delegation.State.String()). Str("reason", "missing_inclusion_proof"). Msg("skipping slashed delegation event") continue } + if !utils.Contains(types.QualifiedStatesForSlashedDelegation(), delegation.State) { + // If the current state is not qualified, no need to emit the event + log.Debug(). + Str("staking_tx", delegation.StakingTxHashHex). + Str("event_type", EventSlashedFinalityProvider.String()). + Str("current_state", delegation.State.String()). + Str("reason", "not_qualified"). + Msg("skipped slashed delegation event") + continue + } + if err := s.emitUnbondingDelegationEvent(ctx, delegation); err != nil { return err } diff --git a/internal/services/events.go b/internal/services/events.go index b7df90b..adeffb3 100644 --- a/internal/services/events.go +++ b/internal/services/events.go @@ -330,7 +330,7 @@ func (s *Service) validateBTCDelegationUnbondedEarlyEvent(ctx context.Context, e if utils.Contains(types.OutdatedStatesForUnbondedEarly(), delegation.State) { log.Debug(). Str("stakingTxHashHex", event.StakingTxHash). - Str("currentState", event.NewState). + Str("currentState", delegation.State.String()). Str("event_type", "EventBTCDelgationUnbondedEarly"). Msg("Current state is outdated for transition") return false, true, nil @@ -387,7 +387,7 @@ func (s *Service) validateBTCDelegationExpiredEvent(ctx context.Context, event * if utils.Contains(types.OutdatedStatesForExpired(), delegation.State) { log.Debug(). Str("stakingTxHashHex", event.StakingTxHash). - Str("currentState", event.NewState). + Str("currentState", delegation.State.String()). Str("event_type", "EventBTCDelegationExpired"). Msg("Current state is outdated for transition") return false, true, nil diff --git a/internal/types/state.go b/internal/types/state.go index 4afc008..debaf98 100644 --- a/internal/types/state.go +++ b/internal/types/state.go @@ -61,6 +61,11 @@ func OutdatedStatesForExpired() []DelegationState { return []DelegationState{StateUnbonding, StateWithdrawable, StateWithdrawn} } +// QualifiedStatesForSlashedDelegation returns the qualified current states when a delegation is slashed +func QualifiedStatesForSlashedDelegation() []DelegationState { + return []DelegationState{StatePending, StateVerified, StateActive, StateUnbonding, StateWithdrawable} +} + // QualifiedStatesForWithdrawn returns the qualified current states for Withdrawn event func QualifiedStatesForWithdrawn() []DelegationState { // StateActive/StateUnbonding/StateSlashed is included b/c its possible that expiry checker diff --git a/tests/mocks/mock_db_client.go b/tests/mocks/mock_db_client.go index 40a3532..3c5cb96 100644 --- a/tests/mocks/mock_db_client.go +++ b/tests/mocks/mock_db_client.go @@ -473,17 +473,17 @@ 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) +// UpdateDelegationsStateByFinalityProvider provides a mock function with given fields: ctx, fpBtcPkHex, qualifiedPreviousStates, newState +func (_m *DbInterface) UpdateDelegationsStateByFinalityProvider(ctx context.Context, fpBtcPkHex string, qualifiedPreviousStates []types.DelegationState, newState types.DelegationState) error { + ret := _m.Called(ctx, fpBtcPkHex, qualifiedPreviousStates, 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) + if rf, ok := ret.Get(0).(func(context.Context, string, []types.DelegationState, types.DelegationState) error); ok { + r0 = rf(ctx, fpBtcPkHex, qualifiedPreviousStates, newState) } else { r0 = ret.Error(0) } From 76184829082189d2017e0a70f593bf103cc65b8b Mon Sep 17 00:00:00 2001 From: Gurjot Date: Sun, 5 Jan 2025 18:25:28 +0530 Subject: [PATCH 09/13] TODO --- internal/services/delegation.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/services/delegation.go b/internal/services/delegation.go index 784a7a4..465788d 100644 --- a/internal/services/delegation.go +++ b/internal/services/delegation.go @@ -512,6 +512,8 @@ func (s *Service) processSlashedFinalityProviderEvent( ) } + // TODO: ideally indexer should simply emit the slashed FP + // queue handlers should handle the rest for _, delegation := range delegations { if !delegation.HasInclusionProof() { // If the delegation was never active/has no inclusion proof From 7774cc55a595bc1efa5d5708d0894fb814c19311 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Mon, 6 Jan 2025 13:33:12 +0530 Subject: [PATCH 10/13] fix --- internal/services/delegation.go | 70 +++-------------------------- internal/services/events.go | 54 +++++++--------------- internal/services/expiry_checker.go | 29 +----------- internal/types/state.go | 10 ----- 4 files changed, 24 insertions(+), 139 deletions(-) diff --git a/internal/services/delegation.go b/internal/services/delegation.go index 465788d..7d1e1c4 100644 --- a/internal/services/delegation.go +++ b/internal/services/delegation.go @@ -9,7 +9,6 @@ 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" ftypes "github.com/babylonlabs-io/babylon/x/finality/types" abcitypes "github.com/cometbft/cometbft/abci/types" @@ -70,8 +69,6 @@ func (s *Service) processNewBTCDelegationEvent( ) } - // TODO: start watching for BTC confirmation if we need PendingBTCConfirmation state - return nil } @@ -285,32 +282,12 @@ func (s *Service) processBTCDelegationUnbondedEarlyEvent( return err } - shouldProcess, shouldEmitEvent, err := s.validateBTCDelegationUnbondedEarlyEvent(ctx, unbondedEarlyEvent) + shouldProcess, err := s.validateBTCDelegationUnbondedEarlyEvent(ctx, unbondedEarlyEvent) if err != nil { return err } if !shouldProcess { - // Event is valid but should be skipped - if shouldEmitEvent { - log.Debug(). - Str("staking_tx", unbondedEarlyEvent.StakingTxHash). - Str("event_type", EventBTCDelgationUnbondedEarly.String()). - Msg("skip processing but emit unbonding event") - - delegation, 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), - ) - } - - // Emit consumer event - if err := s.emitUnbondingDelegationEvent(ctx, delegation); err != nil { - return err - } - } + // Ignore the event silently return nil } @@ -393,32 +370,13 @@ func (s *Service) processBTCDelegationExpiredEvent( return err } - shouldProcess, shouldEmitEvent, err := s.validateBTCDelegationExpiredEvent(ctx, expiredEvent) + shouldProcess, err := s.validateBTCDelegationExpiredEvent(ctx, expiredEvent) if err != nil { return err } + if !shouldProcess { - // Event is valid but should be skipped - if shouldEmitEvent { - log.Debug(). - Str("staking_tx", expiredEvent.StakingTxHash). - Str("event_type", EventBTCDelegationExpired.String()). - Msg("skip processing but emit unbonding event") - - delegation, 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), - ) - } - - // Emit consumer event - if err := s.emitUnbondingDelegationEvent(ctx, delegation); err != nil { - return err - } - } + // Ignore the event silently return nil } @@ -481,14 +439,10 @@ func (s *Service) processSlashedFinalityProviderEvent( return err } - shouldProcess, err := s.validateSlashedFinalityProviderEvent(ctx, slashedFinalityProviderEvent) + err = s.validateSlashedFinalityProviderEvent(ctx, slashedFinalityProviderEvent) if err != nil { return err } - if !shouldProcess { - // Event is valid but should be skipped - return nil - } evidence := slashedFinalityProviderEvent.Evidence fpBTCPKHex := evidence.FpBtcPk.MarshalHex() @@ -514,6 +468,7 @@ func (s *Service) processSlashedFinalityProviderEvent( // TODO: ideally indexer should simply emit the slashed FP // queue handlers should handle the rest + for _, delegation := range delegations { if !delegation.HasInclusionProof() { // If the delegation was never active/has no inclusion proof @@ -527,17 +482,6 @@ func (s *Service) processSlashedFinalityProviderEvent( continue } - if !utils.Contains(types.QualifiedStatesForSlashedDelegation(), delegation.State) { - // If the current state is not qualified, no need to emit the event - log.Debug(). - Str("staking_tx", delegation.StakingTxHashHex). - Str("event_type", EventSlashedFinalityProvider.String()). - Str("current_state", delegation.State.String()). - Str("reason", "not_qualified"). - Msg("skipped slashed delegation event") - continue - } - if err := s.emitUnbondingDelegationEvent(ctx, delegation); err != nil { return err } diff --git a/internal/services/events.go b/internal/services/events.go index adeffb3..c9dd5a1 100644 --- a/internal/services/events.go +++ b/internal/services/events.go @@ -298,10 +298,10 @@ func (s *Service) validateBTCDelegationInclusionProofReceivedEvent(ctx context.C return true, nil } -func (s *Service) validateBTCDelegationUnbondedEarlyEvent(ctx context.Context, event *bstypes.EventBTCDelgationUnbondedEarly) (bool, 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, false, types.NewErrorWithMsg( + return false, types.NewErrorWithMsg( http.StatusInternalServerError, types.InternalServiceError, "unbonded early event missing staking tx hash", @@ -310,7 +310,7 @@ func (s *Service) validateBTCDelegationUnbondedEarlyEvent(ctx context.Context, e // Validate the event state if event.NewState != bstypes.BTCDelegationStatus_UNBONDED.String() { - return false, false, types.NewValidationFailedError( + return false, types.NewValidationFailedError( fmt.Errorf("invalid delegation state from Babylon when processing EventBTCDelgationUnbondedEarly: expected UNBONDED, got %s", event.NewState), ) } @@ -318,24 +318,13 @@ func (s *Service) validateBTCDelegationUnbondedEarlyEvent(ctx context.Context, e // Fetch the current delegation state from the database delegation, dbErr := s.db.GetBTCDelegationByStakingTxHash(ctx, event.StakingTxHash) if dbErr != nil { - return false, false, types.NewError( + 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 outdated for the transition - // We should emit the event if the current state is outdated - if utils.Contains(types.OutdatedStatesForUnbondedEarly(), delegation.State) { - log.Debug(). - Str("stakingTxHashHex", event.StakingTxHash). - Str("currentState", delegation.State.String()). - Str("event_type", "EventBTCDelgationUnbondedEarly"). - Msg("Current state is outdated for transition") - return false, true, nil - } - // Check if the current state is qualified for the transition if !utils.Contains(types.QualifiedStatesForUnbondedEarly(), delegation.State) { log.Error(). @@ -343,20 +332,20 @@ func (s *Service) validateBTCDelegationUnbondedEarlyEvent(ctx context.Context, e Str("currentState", delegation.State.String()). Str("event_type", "EventBTCDelgationUnbondedEarly"). Msg("Current state is not qualified for transition") - return false, false, types.NewErrorWithMsg( + return false, types.NewErrorWithMsg( http.StatusForbidden, types.Forbidden, "current state is not qualified for transition", ) } - return true, true, nil + return true, nil } -func (s *Service) validateBTCDelegationExpiredEvent(ctx context.Context, event *bstypes.EventBTCDelegationExpired) (bool, 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, false, types.NewErrorWithMsg( + return false, types.NewErrorWithMsg( http.StatusInternalServerError, types.InternalServiceError, "expired event missing staking tx hash", @@ -365,7 +354,7 @@ func (s *Service) validateBTCDelegationExpiredEvent(ctx context.Context, event * // Validate the event state if event.NewState != bstypes.BTCDelegationStatus_EXPIRED.String() { - return false, false, types.NewErrorWithMsg( + return false, types.NewErrorWithMsg( http.StatusInternalServerError, types.InternalServiceError, fmt.Sprintf("invalid delegation state from Babylon when processing EventBTCDelegationExpired: expected EXPIRED, got %s", event.NewState), @@ -375,24 +364,13 @@ func (s *Service) validateBTCDelegationExpiredEvent(ctx context.Context, event * // Fetch the current delegation state from the database delegation, dbErr := s.db.GetBTCDelegationByStakingTxHash(ctx, event.StakingTxHash) if dbErr != nil { - return false, false, types.NewError( + 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 outdated for the transition - // We should emit the event if the current state is outdated - if utils.Contains(types.OutdatedStatesForExpired(), delegation.State) { - log.Debug(). - Str("stakingTxHashHex", event.StakingTxHash). - Str("currentState", delegation.State.String()). - Str("event_type", "EventBTCDelegationExpired"). - Msg("Current state is outdated for transition") - return false, true, nil - } - // Check if the current state is qualified for the transition if !utils.Contains(types.QualifiedStatesForExpired(), delegation.State) { log.Error(). @@ -400,19 +378,19 @@ func (s *Service) validateBTCDelegationExpiredEvent(ctx context.Context, event * Str("currentState", delegation.State.String()). Str("event_type", "EventBTCDelegationExpired"). Msg("current state is not qualified for transition") - return false, false, types.NewErrorWithMsg( + return false, types.NewErrorWithMsg( http.StatusForbidden, types.Forbidden, "current state is not qualified for transition", ) } - return true, true, nil + return true, nil } -func (s *Service) validateSlashedFinalityProviderEvent(ctx context.Context, event *ftypes.EventSlashedFinalityProvider) (bool, *types.Error) { +func (s *Service) validateSlashedFinalityProviderEvent(ctx context.Context, event *ftypes.EventSlashedFinalityProvider) *types.Error { if event.Evidence == nil { - return false, types.NewErrorWithMsg( + return types.NewErrorWithMsg( http.StatusInternalServerError, types.InternalServiceError, "slashed finality provider event missing evidence", @@ -421,14 +399,14 @@ func (s *Service) validateSlashedFinalityProviderEvent(ctx context.Context, even _, err := event.Evidence.ExtractBTCSK() if err != nil { - return false, types.NewError( + return types.NewError( http.StatusInternalServerError, types.InternalServiceError, fmt.Errorf("failed to extract BTC SK of the slashed finality provider: %w", err), ) } - return true, nil + return nil } func sanitizeEvent(event abcitypes.Event) abcitypes.Event { diff --git a/internal/services/expiry_checker.go b/internal/services/expiry_checker.go index 7bc4255..64bde37 100644 --- a/internal/services/expiry_checker.go +++ b/internal/services/expiry_checker.go @@ -52,31 +52,6 @@ func (s *Service) checkExpiry(ctx context.Context) *types.Error { Str("expire_height", strconv.FormatUint(uint64(tlDoc.ExpireHeight), 10)). Msg("checking if delegation is withdrawable") - if utils.Contains(types.OutdatedStatesForWithdrawable(), delegation.State) { - log.Debug(). - Str("staking_tx", delegation.StakingTxHashHex). - Str("current_state", delegation.State.String()). - Msg("current state is outdated for withdrawable") - - if err := s.emitWithdrawableDelegationEvent(ctx, delegation); err != nil { - log.Error(). - Str("staking_tx", delegation.StakingTxHashHex). - Msg("failed to emit withdrawable delegation event") - return err - } - - if err := s.db.DeleteExpiredDelegation(ctx, delegation.StakingTxHashHex); err != nil { - log.Error(). - Str("staking_tx", delegation.StakingTxHashHex). - Msg("failed to delete expired delegation") - return types.NewInternalServiceError( - fmt.Errorf("failed to delete expired delegation: %w", err), - ) - } - - continue - } - // Check if the delegation is in a qualified state to transition to Withdrawable if !utils.Contains(types.QualifiedStatesForWithdrawable(), delegation.State) { log.Error(). @@ -84,9 +59,7 @@ func (s *Service) checkExpiry(ctx context.Context) *types.Error { Str("current_state", delegation.State.String()). Msg("current state is not qualified for withdrawable") - return types.NewInternalServiceError( - fmt.Errorf("current state is not qualified for withdrawable"), - ) + continue } if err := s.db.UpdateBTCDelegationState( diff --git a/internal/types/state.go b/internal/types/state.go index debaf98..4afc97e 100644 --- a/internal/types/state.go +++ b/internal/types/state.go @@ -46,21 +46,11 @@ func QualifiedStatesForUnbondedEarly() []DelegationState { return []DelegationState{StateActive} } -// OutdatedStatesForUnbondedEarly returns the outdated current states for UnbondedEarly event -func OutdatedStatesForUnbondedEarly() []DelegationState { - return []DelegationState{StateUnbonding, StateWithdrawable, StateWithdrawn} -} - // QualifiedStatesForExpired returns the qualified current states for Expired event func QualifiedStatesForExpired() []DelegationState { return []DelegationState{StateActive} } -// OutdatedStatesForExpired returns the outdated current states for Expired event -func OutdatedStatesForExpired() []DelegationState { - return []DelegationState{StateUnbonding, StateWithdrawable, StateWithdrawn} -} - // QualifiedStatesForSlashedDelegation returns the qualified current states when a delegation is slashed func QualifiedStatesForSlashedDelegation() []DelegationState { return []DelegationState{StatePending, StateVerified, StateActive, StateUnbonding, StateWithdrawable} From 71a0d3ef3cdb80b85e8ba1efbfeba77346b5af76 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Mon, 6 Jan 2025 13:38:45 +0530 Subject: [PATCH 11/13] fix --- internal/services/delegation.go | 1 - internal/services/events.go | 18 +++++------------- internal/types/state.go | 5 ----- 3 files changed, 5 insertions(+), 19 deletions(-) diff --git a/internal/services/delegation.go b/internal/services/delegation.go index 7d1e1c4..6e90135 100644 --- a/internal/services/delegation.go +++ b/internal/services/delegation.go @@ -374,7 +374,6 @@ func (s *Service) processBTCDelegationExpiredEvent( if err != nil { return err } - if !shouldProcess { // Ignore the event silently return nil diff --git a/internal/services/events.go b/internal/services/events.go index c9dd5a1..5275a06 100644 --- a/internal/services/events.go +++ b/internal/services/events.go @@ -327,16 +327,12 @@ func (s *Service) validateBTCDelegationUnbondedEarlyEvent(ctx context.Context, e // Check if the current state is qualified for the transition if !utils.Contains(types.QualifiedStatesForUnbondedEarly(), delegation.State) { - log.Error(). + log.Debug(). Str("stakingTxHashHex", event.StakingTxHash). Str("currentState", delegation.State.String()). Str("event_type", "EventBTCDelgationUnbondedEarly"). - Msg("Current state is not qualified for transition") - return false, types.NewErrorWithMsg( - http.StatusForbidden, - types.Forbidden, - "current state is not qualified for transition", - ) + Msg("current state is not qualified for transition") + return false, nil } return true, nil @@ -373,16 +369,12 @@ func (s *Service) validateBTCDelegationExpiredEvent(ctx context.Context, event * // Check if the current state is qualified for the transition if !utils.Contains(types.QualifiedStatesForExpired(), delegation.State) { - log.Error(). + log.Debug(). Str("stakingTxHashHex", event.StakingTxHash). Str("currentState", delegation.State.String()). Str("event_type", "EventBTCDelegationExpired"). Msg("current state is not qualified for transition") - return false, types.NewErrorWithMsg( - http.StatusForbidden, - types.Forbidden, - "current state is not qualified for transition", - ) + return false, nil } return true, nil diff --git a/internal/types/state.go b/internal/types/state.go index 4afc97e..a487094 100644 --- a/internal/types/state.go +++ b/internal/types/state.go @@ -69,11 +69,6 @@ func QualifiedStatesForWithdrawable() []DelegationState { return []DelegationState{StateUnbonding, StateSlashed} } -// OutdatedStatesForWithdrawable returns the outdated current states for Withdrawable event -func OutdatedStatesForWithdrawable() []DelegationState { - return []DelegationState{StateWithdrawable, StateWithdrawn} -} - type DelegationSubState string const ( From e814b62fd3de76ab226cec5acd6f91d44e79e0f3 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Mon, 6 Jan 2025 13:42:44 +0530 Subject: [PATCH 12/13] fix log --- internal/services/expiry_checker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/services/expiry_checker.go b/internal/services/expiry_checker.go index 64bde37..6f29685 100644 --- a/internal/services/expiry_checker.go +++ b/internal/services/expiry_checker.go @@ -54,7 +54,7 @@ func (s *Service) checkExpiry(ctx context.Context) *types.Error { // Check if the delegation is in a qualified state to transition to Withdrawable if !utils.Contains(types.QualifiedStatesForWithdrawable(), delegation.State) { - log.Error(). + log.Debug(). Str("staking_tx", delegation.StakingTxHashHex). Str("current_state", delegation.State.String()). Msg("current state is not qualified for withdrawable") From 79fa53142ff26ee618cfd12b2fafbde6d7786150 Mon Sep 17 00:00:00 2001 From: Gurjot Date: Tue, 14 Jan 2025 13:13:53 +0530 Subject: [PATCH 13/13] bump q client --- go.mod | 2 +- go.sum | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 2d663c2..9bb28bf 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( cosmossdk.io/math v1.4.0 github.com/avast/retry-go/v4 v4.5.1 github.com/babylonlabs-io/babylon v1.0.0-rc.2 - github.com/babylonlabs-io/staking-queue-client v0.4.7-0.20250102124054-14d01f4accfb + github.com/babylonlabs-io/staking-queue-client v0.4.7-0.20250114073544-2875aea182ab github.com/btcsuite/btcd v0.24.3-0.20241011125836-24eb815168f4 github.com/btcsuite/btcd/btcec/v2 v2.3.4 github.com/btcsuite/btcd/btcutil v1.1.6 diff --git a/go.sum b/go.sum index c92d17b..86e658f 100644 --- a/go.sum +++ b/go.sum @@ -1429,12 +1429,8 @@ github.com/aws/aws-sdk-go v1.44.312/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8 github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/babylonlabs-io/babylon v1.0.0-rc.2 h1:H7OpEDNNOXyC+9TUo4vVYLlHNhOQ8m9KqWP1qzjEt0c= github.com/babylonlabs-io/babylon v1.0.0-rc.2/go.mod h1:B8ma8IjGUEKhmoRfwv60Qa7DtUXssCgtmD89huQ4+5I= -github.com/babylonlabs-io/staking-queue-client v0.4.7-0.20250102103040-970b1f069cf1 h1:Sz8eiPtnYgcgX50gtXK6a/gH1an/LE3s6ug4PSe2LU4= -github.com/babylonlabs-io/staking-queue-client v0.4.7-0.20250102103040-970b1f069cf1/go.mod h1:n3fr3c+9LNiJlyETmcrVk94Zn76rAADhGZKxX+rVf+Q= -github.com/babylonlabs-io/staking-queue-client v0.4.7-0.20250102114800-e2899342683b h1:U88aKs6LYDsS1+Co8MvMvxtogO7HYjUoeyeO9PDFiaY= -github.com/babylonlabs-io/staking-queue-client v0.4.7-0.20250102114800-e2899342683b/go.mod h1:n3fr3c+9LNiJlyETmcrVk94Zn76rAADhGZKxX+rVf+Q= -github.com/babylonlabs-io/staking-queue-client v0.4.7-0.20250102124054-14d01f4accfb h1:+lWIl/RENFXQRnc8bVmU/RwUfgbeqd+UAmFXLDeAsCk= -github.com/babylonlabs-io/staking-queue-client v0.4.7-0.20250102124054-14d01f4accfb/go.mod h1:n3fr3c+9LNiJlyETmcrVk94Zn76rAADhGZKxX+rVf+Q= +github.com/babylonlabs-io/staking-queue-client v0.4.7-0.20250114073544-2875aea182ab h1:APSYaAU89zceHIzE+QK3vfg+qQSS8fwMPGn0rMLvQN4= +github.com/babylonlabs-io/staking-queue-client v0.4.7-0.20250114073544-2875aea182ab/go.mod h1:n3fr3c+9LNiJlyETmcrVk94Zn76rAADhGZKxX+rVf+Q= 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=