Skip to content

Commit

Permalink
expire stake max(w, min_unbonding_time) before timelock ends
Browse files Browse the repository at this point in the history
  • Loading branch information
KonradStaniec committed Oct 24, 2024
1 parent 83312fe commit f071e13
Show file tree
Hide file tree
Showing 8 changed files with 51 additions and 36 deletions.
2 changes: 1 addition & 1 deletion proto/babylon/btcstaking/v1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ message MsgCreateBTCDelegation {
// - unbonding transaction, time lock spending path
// - staking slashing transaction, change output
// - unbonding slashing transaction, change output
// It must be smaller than (staking_time - CheckpointFinalizationTimeout) and larger that max(MinUnbondingTime, CheckpointFinalizationTimeout)
// It must be smaller than math.MaxUInt16 and larger that max(MinUnbondingTime, CheckpointFinalizationTimeout)
uint32 unbonding_time = 11;
// fields related to unbonding transaction
// unbonding_tx is a bitcoin unbonding transaction i.e transaction that spends
Expand Down
10 changes: 8 additions & 2 deletions x/btcstaking/keeper/btc_delegations.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,14 @@ func (k Keeper) AddBTCDelegation(ctx sdk.Context, btcDel *types.BTCDelegation) e
StakingTxHash: stakingTxHash.String(),
NewState: types.BTCDelegationStatus_UNBONDED,
})
// NOTE: we should have verified that EndHeight > btcTip.Height + btcDel.UnbondingTime
k.addPowerDistUpdateEvent(ctx, btcDel.EndHeight-btcDel.UnbondingTime, unbondedEvent)

btccheckpointParams := k.btccKeeper.GetParams(ctx)
stakingParams := k.GetParamsWithVersion(ctx).Params

minUnbondingTime := types.MinimumUnbondingTime(&stakingParams, &btccheckpointParams)

// NOTE: we should have verified that EndHeight > btcTip.Height + max(w, min_unbonding_time)
k.addPowerDistUpdateEvent(ctx, btcDel.EndHeight-minUnbondingTime, unbondedEvent)
}

return nil
Expand Down
7 changes: 5 additions & 2 deletions x/btcstaking/keeper/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@ import (

func TestExportGenesis(t *testing.T) {
r, h := rand.New(rand.NewSource(11)), helper.NewHelper(t)
k, btclcK, _, ctx := h.App.BTCStakingKeeper, h.App.BTCLightClientKeeper, h.App.BtcCheckpointKeeper, h.Ctx
k, btclcK, btcCheckK, ctx := h.App.BTCStakingKeeper, h.App.BTCLightClientKeeper, h.App.BtcCheckpointKeeper, h.Ctx
numFps := 3

fps := datagen.CreateNFinalityProviders(r, t, numFps)
params := k.GetParams(ctx)
btcckptParams := btcCheckK.GetParams(ctx)

minUnbondingTime := types.MinimumUnbondingTime(&params, &btcckptParams)

chainsHeight := make([]*types.BlockHeightBbnToBtc, 0)
// creates the first as it starts already with an chain height from the helper.
Expand Down Expand Up @@ -98,7 +101,7 @@ func TestExportGenesis(t *testing.T) {
idxEvent := uint64(totalDelegations - 1)
eventsIdx[idxEvent] = &types.EventIndex{
Idx: idxEvent,
BlockHeightBtc: del.EndHeight - del.UnbondingTime,
BlockHeightBtc: del.EndHeight - minUnbondingTime,
Event: unbondedEvent,
}
}
Expand Down
8 changes: 4 additions & 4 deletions x/btcstaking/keeper/inclusion_proof.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ type delegationTimeRangeInfo struct {
}

// VerifyInclusionProofAndGetHeight verifies the inclusion proof of the given staking tx
// and returns the inclusion height as well as endheigh
// and returns the start height and end height
func (k Keeper) VerifyInclusionProofAndGetHeight(
ctx sdk.Context,
stakingTx *btcutil.Tx,
confirmationDepth uint32,
stakingTime uint32,
unbondingTime uint32,
minUnbondingTime uint32,
inclusionProof *types.ParsedProofOfInclusion,
) (*delegationTimeRangeInfo, error) {
// Check:
Expand Down Expand Up @@ -59,8 +59,8 @@ func (k Keeper) VerifyInclusionProofAndGetHeight(
return nil, types.ErrInvalidStakingTx.Wrapf("not k-deep: k=%d; depth=%d", confirmationDepth, stakingTxDepth)
}
// ensure staking tx's timelock has more than unbonding BTC blocks left
if btcTip.Height+unbondingTime >= endHeight {
return nil, types.ErrInvalidStakingTx.Wrapf("staking tx's timelock has no more than unbonding(=%d) blocks left", unbondingTime)
if btcTip.Height+minUnbondingTime >= endHeight {
return nil, types.ErrInvalidStakingTx.Wrapf("staking tx's timelock has no more than unbonding(=%d) blocks left", minUnbondingTime)
}

return &delegationTimeRangeInfo{
Expand Down
12 changes: 8 additions & 4 deletions x/btcstaking/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ func (ms msgServer) CreateBTCDelegation(goCtx context.Context, req *types.MsgCre
return nil, err
}

minUnbondingTime := types.MinimumUnbondingTime(&vp.Params, &btccParams)

// 6. If the delegation contains the inclusion proof, we need to verify the proof
// and set start height and end height
var startHeight, endHeight uint32
Expand All @@ -192,7 +194,7 @@ func (ms msgServer) CreateBTCDelegation(goCtx context.Context, req *types.MsgCre
btcutil.NewTx(parsedMsg.StakingTx.Transaction),
btccParams.BtcConfirmationDepth,
uint32(parsedMsg.StakingTime),
uint32(parsedMsg.UnbondingTime),
minUnbondingTime,
parsedMsg.StakingTxProofOfInclusion)
if err != nil {
return nil, fmt.Errorf("invalid inclusion proof: %w", err)
Expand Down Expand Up @@ -285,12 +287,14 @@ func (ms msgServer) AddBTCDelegationInclusionProof(

btccParams := ms.btccKeeper.GetParams(ctx)

minUnbondingTime := types.MinimumUnbondingTime(params, &btccParams)

timeInfo, err := ms.VerifyInclusionProofAndGetHeight(
ctx,
btcutil.NewTx(stakingTx),
btccParams.BtcConfirmationDepth,
btcDel.StakingTime,
btcDel.UnbondingTime,
minUnbondingTime,
parsedInclusionProof,
)

Expand Down Expand Up @@ -332,8 +336,8 @@ func (ms msgServer) AddBTCDelegationInclusionProof(
NewState: types.BTCDelegationStatus_UNBONDED,
})

// NOTE: we should have verified that EndHeight > btcTip.Height + btcDel.UnbondingTime
ms.addPowerDistUpdateEvent(ctx, btcDel.EndHeight-btcDel.UnbondingTime, unbondedEvent)
// NOTE: we should have verified that EndHeight > btcTip.Height + max(w, min_unbonding_time)
ms.addPowerDistUpdateEvent(ctx, btcDel.EndHeight-minUnbondingTime, unbondedEvent)

// at this point, the BTC delegation inclusion proof is verified and is not duplicated
// thus, we can safely consider this message as refundable
Expand Down
30 changes: 24 additions & 6 deletions x/btcstaking/keeper/power_dist_change_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -459,8 +459,14 @@ func FuzzBTCDelegationEvents_NoPreApproval(f *testing.F) {
btcTip := btclcKeeper.GetTipInfo(h.Ctx)
events := h.BTCStakingKeeper.GetAllPowerDistUpdateEvents(h.Ctx, btcTip.Height, btcTip.Height)
require.Len(t, events, 0)
// the BTC delegation will be unbonded at end height - unbonding time
unbondedHeight := actualDel.EndHeight - actualDel.UnbondingTime

btckptParams := btccKeeper.GetParams(h.Ctx)
stakingParams := h.BTCStakingKeeper.GetParamsWithVersion(h.Ctx).Params

minUnbondingTime := types.MinimumUnbondingTime(&stakingParams, &btckptParams)

// the BTC delegation will be unbonded at end height - max(w, min_unbonding_time)
unbondedHeight := actualDel.EndHeight - minUnbondingTime
events = h.BTCStakingKeeper.GetAllPowerDistUpdateEvents(h.Ctx, unbondedHeight, unbondedHeight)
require.Len(t, events, 1)
btcDelStateUpdate := events[0].GetBtcDelStateUpdate()
Expand Down Expand Up @@ -612,8 +618,14 @@ func FuzzBTCDelegationEvents_WithPreApproval(f *testing.F) {
require.NotNil(t, btcDelStateUpdate)
require.Equal(t, stakingTxHash, btcDelStateUpdate.StakingTxHash)
require.Equal(t, types.BTCDelegationStatus_ACTIVE, btcDelStateUpdate.NewState)
// there exists 1 event that the BTC delegation becomes unbonded at end height - w
unbondedHeight := activatedDel.EndHeight - activatedDel.UnbondingTime

btckptParams := btccKeeper.GetParams(h.Ctx)
stakingParams := h.BTCStakingKeeper.GetParamsWithVersion(h.Ctx).Params

minUnbondingTime := types.MinimumUnbondingTime(&stakingParams, &btckptParams)

// the BTC delegation will be unbonded at end height - max(w, min_unbonding_time)
unbondedHeight := activatedDel.EndHeight - minUnbondingTime
events = h.BTCStakingKeeper.GetAllPowerDistUpdateEvents(h.Ctx, unbondedHeight, unbondedHeight)
require.Len(t, events, 1)
btcDelStateUpdate = events[0].GetBtcDelStateUpdate()
Expand Down Expand Up @@ -704,8 +716,14 @@ func TestDoNotGenerateDuplicateEventsAfterHavingCovenantQuorum(t *testing.T) {
btcTip := btclcKeeper.GetTipInfo(h.Ctx)
events := h.BTCStakingKeeper.GetAllPowerDistUpdateEvents(h.Ctx, btcTip.Height, btcTip.Height)
require.Len(t, events, 0)
// the BTC delegation will be unbonded at end height - w
unbondedHeight := actualDel.EndHeight - actualDel.UnbondingTime

btckptParams := btccKeeper.GetParams(h.Ctx)
stakingParams := h.BTCStakingKeeper.GetParamsWithVersion(h.Ctx).Params

minUnbondingTime := types.MinimumUnbondingTime(&stakingParams, &btckptParams)

// the BTC delegation will be unbonded at end height - max(w, min_unbonding_time)
unbondedHeight := actualDel.EndHeight - minUnbondingTime
events = h.BTCStakingKeeper.GetAllPowerDistUpdateEvents(h.Ctx, unbondedHeight, unbondedHeight)
require.Len(t, events, 1)
btcDelStateUpdate := events[0].GetBtcDelStateUpdate()
Expand Down
2 changes: 1 addition & 1 deletion x/btcstaking/types/tx.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 0 additions & 16 deletions x/btcstaking/types/validate_parsed_message.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,22 +77,6 @@ func ValidateParsedMessageAgainstTheParams(
parameters.MaxStakingValueSat,
)
}
// at this point we validated that pm.StakingTime >= parameters.MinStakingTimeBlocks
// and parameters.MinStakingTimeBlocks should always be > btcheckpointParamseters.CheckpointFinalizationTimeout
// and parameters.MinStakingTimeBlocks > MinUnbondingTimeBlocks
maxUnbondingTime := pm.StakingTime - uint16(btcheckpointParamseters.CheckpointFinalizationTimeout)

// this is necessary to avoid creating delegationd which later cannot be
// acitvated, as voting power of delegation is removed pm.UnbondingTime before
// timelock finishes
if pm.UnbondingTime > uint16(maxUnbondingTime) {
return nil, ErrInvalidUnbondingTx.Wrapf(
"unbonding time value %d is out of bounds. Min: %d, Max: %d",
pm.UnbondingTime,
minUnbondingTime,
maxUnbondingTime,
)
}

if err := btcstaking.CheckSlashingTxMatchFundingTx(
pm.StakingSlashingTx.Transaction,
Expand Down

0 comments on commit f071e13

Please sign in to comment.