Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: granular delegation states #135

Merged
merged 7 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 22 additions & 15 deletions internal/indexer/db/model/delegation.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,29 @@ type CovenantSignature struct {
SignatureHex string `bson:"signature_hex"`
}

type BTCDelegationCreatedBbnBlock struct {
Height int64 `bson:"height"`
Timestamp int64 `bson:"timestamp"` // epoch time in seconds
}

type IndexerDelegationDetails struct {
StakingTxHashHex string `bson:"_id"` // Primary key
StakingTxHex string `bson:"staking_tx_hex"`
ParamsVersion uint32 `bson:"params_version"`
FinalityProviderBtcPksHex []string `bson:"finality_provider_btc_pks_hex"`
StakerBtcPkHex string `bson:"staker_btc_pk_hex"`
StakingTime uint32 `bson:"staking_time"`
StakingAmount uint64 `bson:"staking_amount"`
StakingOutputPkScript string `bson:"staking_output_pk_script"`
StakingOutputIdx uint32 `bson:"staking_output_idx"`
UnbondingTime uint32 `bson:"unbonding_time"`
UnbondingTx string `bson:"unbonding_tx"`
State indexertypes.DelegationState `bson:"state"`
StartHeight uint32 `bson:"start_height"`
EndHeight uint32 `bson:"end_height"`
CovenantUnbondingSignatures []CovenantSignature `bson:"covenant_unbonding_signatures"`
StakingTxHashHex string `bson:"_id"` // Primary key
StakingTxHex string `bson:"staking_tx_hex"`
ParamsVersion uint32 `bson:"params_version"`
FinalityProviderBtcPksHex []string `bson:"finality_provider_btc_pks_hex"`
StakerBtcPkHex string `bson:"staker_btc_pk_hex"`
StakingTime uint32 `bson:"staking_time"`
StakingAmount uint64 `bson:"staking_amount"`
StakingOutputPkScript string `bson:"staking_output_pk_script"`
StakingOutputIdx uint32 `bson:"staking_output_idx"`
UnbondingTime uint32 `bson:"unbonding_time"`
UnbondingTx string `bson:"unbonding_tx"`
State indexertypes.DelegationState `bson:"state"`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gusin13 I thought you were planning to store a list of the states this delegation has gone through? Or are you planning to address that in a separate PR later?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep that will be separate task
babylonlabs-io/babylon-staking-indexer#70

btw although after above ticket is done full list will be available in indexer db, but api won't access or return to fe? or do you think we should return this info to fe?

SubState indexertypes.DelegationSubState `bson:"sub_state,omitempty"`
StartHeight uint32 `bson:"start_height"`
EndHeight uint32 `bson:"end_height"`
CovenantUnbondingSignatures []CovenantSignature `bson:"covenant_unbonding_signatures"`
BTCDelegationCreatedBbnBlock BTCDelegationCreatedBbnBlock `bson:"btc_delegation_created_bbn_block"`
}

func BuildDelegationPaginationToken(d IndexerDelegationDetails) (string, error) {
Expand Down
9 changes: 9 additions & 0 deletions internal/indexer/types/delegation_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,12 @@ const (
StateWithdrawn DelegationState = "WITHDRAWN"
StateSlashed DelegationState = "SLASHED"
)

type DelegationSubState string

const (
SubStateTimelock DelegationSubState = "TIMELOCK"
SubStateEarlyUnbonding DelegationSubState = "EARLY_UNBONDING"
SubStateTimelockSlashing DelegationSubState = "TIMELOCK_SLASHING"
SubStateEarlyUnbondingSlashing DelegationSubState = "EARLY_UNBONDING_SLASHING"
)
70 changes: 43 additions & 27 deletions internal/v2/service/delegation.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,21 @@ import (
"net/http"

indexerdbmodel "github.com/babylonlabs-io/staking-api-service/internal/indexer/db/model"
indexertypes "github.com/babylonlabs-io/staking-api-service/internal/indexer/types"
"github.com/babylonlabs-io/staking-api-service/internal/shared/db"
"github.com/babylonlabs-io/staking-api-service/internal/shared/types"
v2types "github.com/babylonlabs-io/staking-api-service/internal/v2/types"
"github.com/rs/zerolog/log"
)

type DelegationStaking struct {
StakingTxHashHex string `json:"staking_tx_hash_hex"`
StakingTxHex string `json:"staking_tx_hex"`
StakingTime uint32 `json:"staking_time"`
StakingAmount uint64 `json:"staking_amount"`
StartHeight uint32 `json:"start_height,omitempty"`
EndHeight uint32 `json:"end_height,omitempty"`
StakingTxHashHex string `json:"staking_tx_hash_hex"`
StakingTxHex string `json:"staking_tx_hex"`
StakingTime uint32 `json:"staking_time"`
StakingAmount uint64 `json:"staking_amount"`
StartHeight uint32 `json:"start_height,omitempty"`
EndHeight uint32 `json:"end_height,omitempty"`
BbnInceptionHeight int64 `json:"bbn_inception_height"`
BbnInceptionTime int64 `json:"bbn_inception_time"`
}

type CovenantSignature struct {
Expand All @@ -32,12 +34,12 @@ type DelegationUnbonding struct {
}

type StakerDelegationPublic struct {
ParamsVersion uint32 `json:"params_version"`
StakerBtcPkHex string `json:"staker_btc_pk_hex"`
FinalityProviderBtcPksHex []string `json:"finality_provider_btc_pks_hex"`
DelegationStaking DelegationStaking `json:"delegation_staking"`
DelegationUnbonding DelegationUnbonding `json:"delegation_unbonding"`
State indexertypes.DelegationState `json:"state"`
ParamsVersion uint32 `json:"params_version"`
StakerBtcPkHex string `json:"staker_btc_pk_hex"`
FinalityProviderBtcPksHex []string `json:"finality_provider_btc_pks_hex"`
DelegationStaking DelegationStaking `json:"delegation_staking"`
DelegationUnbonding DelegationUnbonding `json:"delegation_unbonding"`
State v2types.DelegationState `json:"state"`
}

func (s *V2Service) GetDelegation(ctx context.Context, stakingTxHashHex string) (*StakerDelegationPublic, *types.Error) {
Expand All @@ -50,17 +52,24 @@ func (s *V2Service) GetDelegation(ctx context.Context, stakingTxHashHex string)
return nil, types.NewErrorWithMsg(http.StatusInternalServerError, types.InternalServiceError, "failed to get staker delegation")
}

state, err := v2types.MapDelegationState(delegation.State, delegation.SubState)
if err != nil {
return nil, types.NewErrorWithMsg(http.StatusInternalServerError, types.InternalServiceError, "failed to get delegation state")
}

delegationPublic := &StakerDelegationPublic{
ParamsVersion: delegation.ParamsVersion,
FinalityProviderBtcPksHex: delegation.FinalityProviderBtcPksHex,
StakerBtcPkHex: delegation.StakerBtcPkHex,
DelegationStaking: DelegationStaking{
StakingTxHashHex: delegation.StakingTxHashHex,
StakingTxHex: delegation.StakingTxHex,
StakingTime: delegation.StakingTime,
StakingAmount: delegation.StakingAmount,
StartHeight: delegation.StartHeight,
EndHeight: delegation.EndHeight,
StakingTxHashHex: delegation.StakingTxHashHex,
StakingTxHex: delegation.StakingTxHex,
StakingTime: delegation.StakingTime,
StakingAmount: delegation.StakingAmount,
StartHeight: delegation.StartHeight,
EndHeight: delegation.EndHeight,
BbnInceptionHeight: delegation.BTCDelegationCreatedBbnBlock.Height,
BbnInceptionTime: delegation.BTCDelegationCreatedBbnBlock.Timestamp,
},
DelegationUnbonding: DelegationUnbonding{
UnbondingTime: delegation.UnbondingTime,
Expand All @@ -69,7 +78,7 @@ func (s *V2Service) GetDelegation(ctx context.Context, stakingTxHashHex string)
delegation.CovenantUnbondingSignatures,
),
},
State: delegation.State,
State: state,
}
return delegationPublic, nil
}
Expand All @@ -89,17 +98,24 @@ func (s *V2Service) GetDelegations(ctx context.Context, stakerPkHex string, pagi

// Group delegations by state
for _, delegation := range resultMap.Data {
state, err := v2types.MapDelegationState(delegation.State, delegation.SubState)
if err != nil {
return nil, "", types.NewErrorWithMsg(http.StatusInternalServerError, types.InternalServiceError, "failed to get delegation state")
}

delegationPublic := &StakerDelegationPublic{
ParamsVersion: delegation.ParamsVersion,
FinalityProviderBtcPksHex: delegation.FinalityProviderBtcPksHex,
StakerBtcPkHex: delegation.StakerBtcPkHex,
DelegationStaking: DelegationStaking{
StakingTxHashHex: delegation.StakingTxHashHex,
StakingTxHex: delegation.StakingTxHex,
StakingTime: delegation.StakingTime,
StakingAmount: delegation.StakingAmount,
StartHeight: delegation.StartHeight,
EndHeight: delegation.EndHeight,
StakingTxHashHex: delegation.StakingTxHashHex,
StakingTxHex: delegation.StakingTxHex,
StakingTime: delegation.StakingTime,
StakingAmount: delegation.StakingAmount,
StartHeight: delegation.StartHeight,
EndHeight: delegation.EndHeight,
BbnInceptionHeight: delegation.BTCDelegationCreatedBbnBlock.Height,
BbnInceptionTime: delegation.BTCDelegationCreatedBbnBlock.Timestamp,
},
DelegationUnbonding: DelegationUnbonding{
UnbondingTime: delegation.UnbondingTime,
Expand All @@ -108,7 +124,7 @@ func (s *V2Service) GetDelegations(ctx context.Context, stakerPkHex string, pagi
delegation.CovenantUnbondingSignatures,
),
},
State: delegation.State,
State: state,
}
delegationsPublic = append(delegationsPublic, delegationPublic)
}
Expand Down
91 changes: 91 additions & 0 deletions internal/v2/types/delegation_states.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package v2types

import (
"fmt"

indexertypes "github.com/babylonlabs-io/staking-api-service/internal/indexer/types"
)

// DelegationState represents the flattened state for frontend consumption
type DelegationState string

const (
// Basic states
StatePending DelegationState = "PENDING"
StateVerified DelegationState = "VERIFIED"
StateActive DelegationState = "ACTIVE"

// Unbonding states
StateTimelockUnbonding DelegationState = "TIMELOCK_UNBONDING"
StateEarlyUnbonding DelegationState = "EARLY_UNBONDING"

// Withdrawable states
StateTimelockWithdrawable DelegationState = "TIMELOCK_WITHDRAWABLE"
StateEarlyUnbondingWithdrawable DelegationState = "EARLY_UNBONDING_WITHDRAWABLE"
StateTimelockSlashingWithdrawable DelegationState = "TIMELOCK_SLASHING_WITHDRAWABLE"
StateEarlyUnbondingSlashingWithdrawable DelegationState = "EARLY_UNBONDING_SLASHING_WITHDRAWABLE"

// Withdrawn states
StateTimelockWithdrawn DelegationState = "TIMELOCK_WITHDRAWN"
StateEarlyUnbondingWithdrawn DelegationState = "EARLY_UNBONDING_WITHDRAWN"
StateTimelockSlashingWithdrawn DelegationState = "TIMELOCK_SLASHING_WITHDRAWN"
StateEarlyUnbondingSlashingWithdrawn DelegationState = "EARLY_UNBONDING_SLASHING_WITHDRAWN"

// Slashed states
StateTimelockSlashed DelegationState = "TIMELOCK_SLASHED"
StateEarlyUnbondingSlashed DelegationState = "EARLY_UNBONDING_SLASHED"
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fyi @totraev the states that API will return


// MapDelegationState consumes internal indexer states and maps them to the frontend-facing states
func MapDelegationState(state indexertypes.DelegationState, subState indexertypes.DelegationSubState) (DelegationState, error) {
switch state {
case indexertypes.StatePending:
return StatePending, nil
case indexertypes.StateVerified:
return StateVerified, nil
case indexertypes.StateActive:
return StateActive, nil

case indexertypes.StateUnbonding:
switch subState {
case indexertypes.SubStateTimelock:
return StateTimelockUnbonding, nil
case indexertypes.SubStateEarlyUnbonding:
return StateEarlyUnbonding, nil
}

case indexertypes.StateWithdrawable:
switch subState {
case indexertypes.SubStateTimelock:
return StateTimelockWithdrawable, nil
case indexertypes.SubStateEarlyUnbonding:
return StateEarlyUnbondingWithdrawable, nil
case indexertypes.SubStateTimelockSlashing:
return StateTimelockSlashingWithdrawable, nil
case indexertypes.SubStateEarlyUnbondingSlashing:
return StateEarlyUnbondingSlashingWithdrawable, nil
}

case indexertypes.StateWithdrawn:
switch subState {
case indexertypes.SubStateTimelock:
return StateTimelockWithdrawn, nil
case indexertypes.SubStateEarlyUnbonding:
return StateEarlyUnbondingWithdrawn, nil
case indexertypes.SubStateTimelockSlashing:
return StateTimelockSlashingWithdrawn, nil
case indexertypes.SubStateEarlyUnbondingSlashing:
return StateEarlyUnbondingSlashingWithdrawn, nil
}

case indexertypes.StateSlashed:
switch subState {
case indexertypes.SubStateTimelock:
return StateTimelockSlashed, nil
case indexertypes.SubStateEarlyUnbonding:
return StateEarlyUnbondingSlashed, nil
}
}

return "", fmt.Errorf("invalid state/subState combination: state=%s, subState=%s", state, subState)
}
Loading