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

fix getStatus call on BTCDelegation #318

Merged
merged 2 commits into from
Dec 4, 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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
- [#310](https://github.com/babylonlabs-io/babylon/pull/310) implement adr-37 -
making params valid for btc light client ranges

### Bug fixes

- [#318](https://github.com/babylonlabs-io/babylon/pull/318) Fix BTC delegation status check
to relay on UnbondingTime in delegation

## v0.17.2

### Improvements
Expand Down
11 changes: 4 additions & 7 deletions testutil/btcstaking-helper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,6 @@ func (h *Helper) CreateDelegationWithBtcBlockHeight(
) (string, *types.MsgCreateBTCDelegation, *types.BTCDelegation, *btclctypes.BTCHeaderInfo, *types.InclusionProof, *UnbondingTxInfo, error) {
stakingTimeBlocks := stakingTime
bsParams := h.BTCStakingKeeper.GetParams(h.Ctx)
bcParams := h.BTCCheckpointKeeper.GetParams(h.Ctx)
covPKs, err := bbn.NewBTCPKsFromBIP340PKs(bsParams.CovenantPks)
h.NoError(err)

Expand Down Expand Up @@ -384,7 +383,7 @@ func (h *Helper) CreateDelegationWithBtcBlockHeight(
h.NoError(err)

// ensure the delegation is still pending
require.Equal(h.t, btcDel.GetStatus(btcTipHeight, bcParams.CheckpointFinalizationTimeout, bsParams.CovenantQuorum), types.BTCDelegationStatus_PENDING)
require.Equal(h.t, btcDel.GetStatus(btcTipHeight, bsParams.CovenantQuorum), types.BTCDelegationStatus_PENDING)

if usePreApproval {
// the BTC delegation does not have inclusion proof
Expand Down Expand Up @@ -481,7 +480,6 @@ func (h *Helper) CreateCovenantSigs(
msgCreateBTCDel *types.MsgCreateBTCDelegation,
del *types.BTCDelegation,
) {
bcParams := h.BTCCheckpointKeeper.GetParams(h.Ctx)
bsParams := h.BTCStakingKeeper.GetParams(h.Ctx)

stakingTx, err := bbn.NewBTCTxFromBytes(del.StakingTx)
Expand Down Expand Up @@ -510,7 +508,7 @@ func (h *Helper) CreateCovenantSigs(
require.Len(h.t, actualDelWithCovenantSigs.BtcUndelegation.CovenantSlashingSigs[0].AdaptorSigs, 1)

// ensure the BTC delegation is verified (if using pre-approval flow) or active
status := actualDelWithCovenantSigs.GetStatus(btcTipHeight, bcParams.CheckpointFinalizationTimeout, bsParams.CovenantQuorum)
status := actualDelWithCovenantSigs.GetStatus(btcTipHeight, bsParams.CovenantQuorum)
if msgCreateBTCDel.StakingTxInclusionProof != nil {
// not pre-approval flow, the BTC delegation should be active
require.Equal(h.t, status, types.BTCDelegationStatus_ACTIVE)
Expand All @@ -525,13 +523,12 @@ func (h *Helper) AddInclusionProof(
btcHeader *btclctypes.BTCHeaderInfo,
proof *types.InclusionProof,
) {
bcParams := h.BTCCheckpointKeeper.GetParams(h.Ctx)
bsParams := h.BTCStakingKeeper.GetParams(h.Ctx)

// Get the BTC delegation and ensure it's verified
del, err := h.BTCStakingKeeper.GetBTCDelegation(h.Ctx, stakingTxHash)
h.NoError(err)
status := del.GetStatus(btcTipHeight, bcParams.CheckpointFinalizationTimeout, bsParams.CovenantQuorum)
status := del.GetStatus(btcTipHeight, bsParams.CovenantQuorum)
require.Equal(h.t, status, types.BTCDelegationStatus_VERIFIED, "the BTC delegation shall be verified")

// Create the MsgAddBTCDelegationInclusionProof message
Expand All @@ -551,7 +548,7 @@ func (h *Helper) AddInclusionProof(
// has been activated
updatedDel, err := h.BTCStakingKeeper.GetBTCDelegation(h.Ctx, stakingTxHash)
h.NoError(err)
status = updatedDel.GetStatus(btcTipHeight, bcParams.CheckpointFinalizationTimeout, bsParams.CovenantQuorum)
status = updatedDel.GetStatus(btcTipHeight, bsParams.CovenantQuorum)
require.Equal(h.t, status, types.BTCDelegationStatus_ACTIVE, "the BTC delegation shall be active")
}

Expand Down
8 changes: 1 addition & 7 deletions x/btcstaking/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,6 @@ func (k Keeper) BTCDelegations(ctx context.Context, req *types.QueryBTCDelegatio

// get current BTC height
btcTipHeight := k.btclcKeeper.GetTipInfo(ctx).Height
// get value of w
wValue := k.btccKeeper.GetParams(ctx).CheckpointFinalizationTimeout

store := k.btcDelegationStore(ctx)
var btcDels []*types.BTCDelegationResponse
Expand All @@ -97,7 +95,7 @@ func (k Keeper) BTCDelegations(ctx context.Context, req *types.QueryBTCDelegatio
k.cdc.MustUnmarshal(value, &btcDel)

// hit if the queried status is ANY or matches the BTC delegation status
status := btcDel.GetStatus(btcTipHeight, wValue, covenantQuorum)
status := btcDel.GetStatus(btcTipHeight, covenantQuorum)
if req.Status == types.BTCDelegationStatus_ANY || status == req.Status {
if accumulate {
resp := types.NewBTCDelegationResponse(&btcDel, status)
Expand Down Expand Up @@ -137,7 +135,6 @@ func (k Keeper) FinalityProviderDelegations(ctx context.Context, req *types.Quer
sdkCtx := sdk.UnwrapSDKContext(ctx)
btcDelStore := k.btcDelegatorFpStore(sdkCtx, fpPK)

currentWValue := k.btccKeeper.GetParams(ctx).CheckpointFinalizationTimeout
btcHeight := k.btclcKeeper.GetTipInfo(ctx).Height
covenantQuorum := k.GetParams(ctx).CovenantQuorum

Expand All @@ -154,7 +151,6 @@ func (k Keeper) FinalityProviderDelegations(ctx context.Context, req *types.Quer
for i, btcDel := range curBTCDels.Dels {
status := btcDel.GetStatus(
btcHeight,
currentWValue,
covenantQuorum,
)
btcDelsResp[i] = types.NewBTCDelegationResponse(btcDel, status)
Expand Down Expand Up @@ -190,10 +186,8 @@ func (k Keeper) BTCDelegation(ctx context.Context, req *types.QueryBTCDelegation
return nil, types.ErrBTCDelegationNotFound
}

currentWValue := k.btccKeeper.GetParams(ctx).CheckpointFinalizationTimeout
status := btcDel.GetStatus(
k.btclcKeeper.GetTipInfo(ctx).Height,
currentWValue,
k.GetParams(ctx).CovenantQuorum,
)

Expand Down
12 changes: 6 additions & 6 deletions x/btcstaking/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -436,8 +436,7 @@ func (ms msgServer) AddCovenantSigs(goCtx context.Context, req *types.MsgAddCove

// ensure BTC delegation is still pending, i.e., not unbonded
btcTipHeight := ms.btclcKeeper.GetTipInfo(ctx).Height
wValue := ms.btccKeeper.GetParams(ctx).CheckpointFinalizationTimeout
status := btcDel.GetStatus(btcTipHeight, wValue, params.CovenantQuorum)
status := btcDel.GetStatus(btcTipHeight, params.CovenantQuorum)
if status == types.BTCDelegationStatus_UNBONDED {
ms.Logger(ctx).Debug("Received covenant signature after the BTC delegation is already unbonded", "covenant pk", req.Pk.MarshalHex())
return nil, types.ErrInvalidCovenantSig.Wrap("the BTC delegation is already unbonded")
Expand Down Expand Up @@ -583,9 +582,11 @@ func (ms msgServer) BTCUndelegate(goCtx context.Context, req *types.MsgBTCUndele

// ensure the BTC delegation with the given staking tx hash is active
btcTip := ms.btclcKeeper.GetTipInfo(ctx)
wValue := ms.btccKeeper.GetParams(ctx).CheckpointFinalizationTimeout

btcDelStatus := btcDel.GetStatus(btcTip.Height, wValue, bsParams.CovenantQuorum)
btcDelStatus := btcDel.GetStatus(
btcTip.Height,
bsParams.CovenantQuorum,
)

if btcDelStatus == types.BTCDelegationStatus_UNBONDED {
return nil, types.ErrInvalidBTCUndelegateReq.Wrap("cannot unbond an unbonded BTC delegation")
Expand Down Expand Up @@ -690,9 +691,8 @@ func (ms msgServer) SelectiveSlashingEvidence(goCtx context.Context, req *types.
// ensure the BTC delegation is active, or its BTC undelegation receives an
// unbonding signature from the staker
btcTip := ms.btclcKeeper.GetTipInfo(ctx)
wValue := ms.btccKeeper.GetParams(ctx).CheckpointFinalizationTimeout
covQuorum := bsParams.CovenantQuorum
if btcDel.GetStatus(btcTip.Height, wValue, covQuorum) != types.BTCDelegationStatus_ACTIVE && !btcDel.IsUnbondedEarly() {
if btcDel.GetStatus(btcTip.Height, covQuorum) != types.BTCDelegationStatus_ACTIVE && !btcDel.IsUnbondedEarly() {
return nil, types.ErrBTCDelegationNotFound.Wrap("a BTC delegation that is not active or unbonding early cannot be slashed")
}

Expand Down
19 changes: 8 additions & 11 deletions x/btcstaking/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -458,10 +458,9 @@ func FuzzAddCovenantSigs(f *testing.F) {
require.True(h.T(), actualDel.BtcUndelegation.HasCovenantQuorums(h.BTCStakingKeeper.GetParams(h.Ctx).CovenantQuorum))

tipHeight := h.BTCLightClientKeeper.GetTipInfo(h.Ctx).Height
checkpointTimeout := h.BTCCheckpointKeeper.GetParams(h.Ctx).CheckpointFinalizationTimeout
covenantQuorum := h.BTCStakingKeeper.GetParams(h.Ctx).CovenantQuorum
status := actualDel.GetStatus(tipHeight, checkpointTimeout, covenantQuorum)
votingPower := actualDel.VotingPower(tipHeight, checkpointTimeout, covenantQuorum)
status := actualDel.GetStatus(tipHeight, covenantQuorum)
votingPower := actualDel.VotingPower(tipHeight, covenantQuorum)

if usePreApproval {
require.Equal(t, status, types.BTCDelegationStatus_VERIFIED)
Expand Down Expand Up @@ -520,10 +519,9 @@ func FuzzAddBTCDelegationInclusionProof(f *testing.F) {

// ensure the BTC delegation is now verified and does not have voting power
tipHeight := h.BTCLightClientKeeper.GetTipInfo(h.Ctx).Height
checkpointTimeout := h.BTCCheckpointKeeper.GetParams(h.Ctx).CheckpointFinalizationTimeout
covenantQuorum := h.BTCStakingKeeper.GetParams(h.Ctx).CovenantQuorum
status := actualDel.GetStatus(tipHeight, checkpointTimeout, covenantQuorum)
votingPower := actualDel.VotingPower(tipHeight, checkpointTimeout, covenantQuorum)
status := actualDel.GetStatus(tipHeight, covenantQuorum)
votingPower := actualDel.VotingPower(tipHeight, covenantQuorum)

require.Equal(t, status, types.BTCDelegationStatus_VERIFIED)
require.Zero(t, votingPower)
Expand All @@ -534,8 +532,8 @@ func FuzzAddBTCDelegationInclusionProof(f *testing.F) {

actualDel, err = h.BTCStakingKeeper.GetBTCDelegation(h.Ctx, stakingTxHash)
h.NoError(err)
status = actualDel.GetStatus(tipHeight, checkpointTimeout, covenantQuorum)
votingPower = actualDel.VotingPower(tipHeight, checkpointTimeout, covenantQuorum)
status = actualDel.GetStatus(tipHeight, covenantQuorum)
votingPower = actualDel.VotingPower(tipHeight, covenantQuorum)

require.Equal(t, status, types.BTCDelegationStatus_ACTIVE)
require.Equal(t, uint64(stakingValue), votingPower)
Expand All @@ -559,7 +557,6 @@ func FuzzBTCUndelegate(f *testing.F) {
covenantSKs, _ := h.GenAndApplyParams(r)

bsParams := h.BTCStakingKeeper.GetParams(h.Ctx)
wValue := h.BTCCheckpointKeeper.GetParams(h.Ctx).CheckpointFinalizationTimeout

changeAddress, err := datagen.GenRandomBTCAddress(r, h.Net)
require.NoError(t, err)
Expand Down Expand Up @@ -594,7 +591,7 @@ func FuzzBTCUndelegate(f *testing.F) {
actualDel, err = h.BTCStakingKeeper.GetBTCDelegation(h.Ctx, stakingTxHash)
h.NoError(err)
btcTip := h.BTCLightClientKeeper.GetTipInfo(h.Ctx).Height
status := actualDel.GetStatus(btcTip, wValue, bsParams.CovenantQuorum)
status := actualDel.GetStatus(btcTip, bsParams.CovenantQuorum)
require.Equal(t, types.BTCDelegationStatus_ACTIVE, status)

msg := &types.MsgBTCUndelegate{
Expand All @@ -617,7 +614,7 @@ func FuzzBTCUndelegate(f *testing.F) {
// ensure the BTC delegation is unbonded
actualDel, err = h.BTCStakingKeeper.GetBTCDelegation(h.Ctx, stakingTxHash)
h.NoError(err)
status = actualDel.GetStatus(btcTip, wValue, bsParams.CovenantQuorum)
status = actualDel.GetStatus(btcTip, bsParams.CovenantQuorum)
require.Equal(t, types.BTCDelegationStatus_UNBONDED, status)
})
}
Expand Down
36 changes: 17 additions & 19 deletions x/btcstaking/types/btc_delegation.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,19 @@ func (d *BTCDelegation) FinalityProviderKeys() []string {
return fpPks
}

// GetStatus returns the status of the BTC Delegation based on BTC height, w value, and covenant quorum
// Pending: the BTC height is in the range of d's [startHeight, endHeight-w] and the delegation does not have covenant signatures
// Active: the BTC height is in the range of d's [startHeight, endHeight-w] and the delegation has quorum number of signatures over slashing tx, unbonding tx, and slashing unbonding tx from covenant committee
// Unbonded: the BTC height is larger than `endHeight-w` or the BTC delegation has received a signature on unbonding tx from the delegator
func (d *BTCDelegation) GetStatus(btcHeight uint32, w uint32, covenantQuorum uint32) BTCDelegationStatus {
// GetStatus returns the status of the BTC Delegation based on BTC height,
// unbonding time, and covenant quorum
// Pending: the BTC height is in the range of d's [startHeight, endHeight-unbondingTime]
// and the delegation does not have covenant signatures
// Active: the BTC height is in the range of d's [startHeight, endHeight-unbondingTime]
// and the delegation has quorum number of signatures over slashing tx,
// unbonding tx, and slashing unbonding tx from covenant committee
// Unbonded: the BTC height is larger than `endHeight-unbondingTime` or the
// BTC delegation has received a signature on unbonding tx from the delegator
func (d *BTCDelegation) GetStatus(
btcHeight uint32,
covenantQuorum uint32,
) BTCDelegationStatus {
if d.IsUnbondedEarly() {
return BTCDelegationStatus_UNBONDED
}
Expand All @@ -119,8 +127,8 @@ func (d *BTCDelegation) GetStatus(btcHeight uint32, w uint32, covenantQuorum uin

// At this point we already have covenant quorum and inclusion proof,
// we can check the status based on the BTC height
if btcHeight < d.StartHeight || btcHeight+w > d.EndHeight {
// staking tx's timelock has not begun, or is less than w BTC
if btcHeight < d.StartHeight || btcHeight+d.UnbondingTime > d.EndHeight {
// staking tx's timelock has not begun, or is less than unbonding time BTC
// blocks left, or is expired
return BTCDelegationStatus_UNBONDED
}
Expand All @@ -133,10 +141,9 @@ func (d *BTCDelegation) GetStatus(btcHeight uint32, w uint32, covenantQuorum uin
}

// VotingPower returns the voting power of the BTC delegation at a given BTC height
// and a given w value.
// The BTC delegation d has voting power iff it is active.
func (d *BTCDelegation) VotingPower(btcHeight uint32, w uint32, covenantQuorum uint32) uint64 {
if d.GetStatus(btcHeight, w, covenantQuorum) != BTCDelegationStatus_ACTIVE {
func (d *BTCDelegation) VotingPower(btcHeight uint32, covenantQuorum uint32) uint64 {
if d.GetStatus(btcHeight, covenantQuorum) != BTCDelegationStatus_ACTIVE {
return 0
}
return d.GetTotalSat()
Expand Down Expand Up @@ -486,12 +493,3 @@ func (i *BTCDelegatorDelegationIndex) Add(stakingTxHash chainhash.Hash) error {

return nil
}

// VotingPower calculates the total voting power of all BTC delegations
func (dels *BTCDelegatorDelegations) VotingPower(btcHeight uint32, w uint32, covenantQuorum uint32) uint64 {
power := uint64(0)
for _, del := range dels.Dels {
power += del.VotingPower(btcHeight, w, covenantQuorum)
}
return power
}
9 changes: 4 additions & 5 deletions x/btcstaking/types/btc_delegation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ func FuzzBTCDelegation(f *testing.F) {

f.Fuzz(func(t *testing.T, seed int64) {
r := rand.New(rand.NewSource(seed))

unbondingTime := uint32(datagen.RandomInt(r, 50))
btcDel := &types.BTCDelegation{}
// randomise voting power
btcDel.TotalSat = datagen.RandomInt(r, 100000)
btcDel.BtcUndelegation = &types.BTCUndelegation{}

btcDel.UnbondingTime = unbondingTime
// randomise covenant sig
hasCovenantSig := datagen.RandomInt(r, 2) == 0
if hasCovenantSig {
Expand Down Expand Up @@ -56,11 +56,10 @@ func FuzzBTCDelegation(f *testing.F) {

// randomise BTC tip and w
btcHeight := btcDel.StartHeight + uint32(datagen.RandomInt(r, 50))
w := uint32(datagen.RandomInt(r, 50))

// test expected voting power
hasVotingPower := hasCovenantSig && btcDel.StartHeight <= btcHeight && btcHeight+w <= btcDel.EndHeight
actualVotingPower := btcDel.VotingPower(btcHeight, w, 1)
hasVotingPower := hasCovenantSig && btcDel.StartHeight <= btcHeight && btcHeight+unbondingTime <= btcDel.EndHeight
actualVotingPower := btcDel.VotingPower(btcHeight, 1)
if hasVotingPower {
require.Equal(t, btcDel.TotalSat, actualVotingPower)
} else {
Expand Down
Loading