Skip to content

Commit

Permalink
add proto type change and jailing logic
Browse files Browse the repository at this point in the history
  • Loading branch information
gitferry committed Sep 9, 2024
1 parent 9aa86fa commit 2fc9d50
Show file tree
Hide file tree
Showing 21 changed files with 847 additions and 609 deletions.
10 changes: 9 additions & 1 deletion proto/babylon/btcstaking/v1/events.proto
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,19 @@ message EventPowerDistUpdate {
bytes pk = 1 [ (gogoproto.customtype) = "github.com/babylonlabs-io/babylon/types.BIP340PubKey" ];
}

// EventJailedFinalityProvider defines an event that a finality provider
// is jailed
message EventJailedFinalityProvider {
bytes pk = 1 [ (gogoproto.customtype) = "github.com/babylonlabs-io/babylon/types.BIP340PubKey" ];
}

// ev is the event that affects voting power distribution
oneof ev {
// slashed_fp means a finality provider is slashed
EventSlashedFinalityProvider slashed_fp = 1;
// jailed_fp means a finality provider is jailed
EventJailedFinalityProvider jailed_fp = 2;
// btc_del_state_update means a BTC delegation's state is updated
EventBTCDelegationStateUpdate btc_del_state_update = 2;
EventBTCDelegationStateUpdate btc_del_state_update = 3;
}
}
13 changes: 3 additions & 10 deletions proto/babylon/finality/v1/events.proto
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,9 @@ message EventSlashedFinalityProvider {
Evidence evidence = 1;
}

// EventSluggishFinalityProviderDetected is the event emitted when a finality provider is
// detected as sluggish
message EventSluggishFinalityProviderDetected {
// public_key is the BTC public key of the finality provider
string public_key = 1;
}

// EventSluggishFinalityProviderReverted is the event emitted when a sluggish finality
// provider is no longer considered sluggish
message EventSluggishFinalityProviderReverted {
// EventJailedFinalityProvider is the event emitted when a finality provider is
// jailed due to inactivity
message EventJailedFinalityProvider {
// public_key is the BTC public key of the finality provider
string public_key = 1;
}
5 changes: 5 additions & 0 deletions proto/babylon/finality/v1/finality.proto
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ package babylon.finality.v1;
option go_package = "github.com/babylonlabs-io/babylon/x/finality/types";

import "gogoproto/gogo.proto";
import "amino/amino.proto";
import "google/protobuf/timestamp.proto";

// IndexedBlock is the necessary metadata and finalization status of a block
message IndexedBlock {
Expand Down Expand Up @@ -64,4 +66,7 @@ message FinalityProviderSigningInfo {
// missed_blocks_counter defines a counter to avoid unnecessary array reads.
// Note that `Sum(MissedBlocksBitArray)` always equals `MissedBlocksCounter`.
int64 missed_blocks_counter = 3;
// Timestamp until which the validator is jailed due to liveness downtime.
google.protobuf.Timestamp jailed_until = 4
[(gogoproto.stdtime) = true, (gogoproto.nullable) = false, (amino.dont_omitempty) = true];
}
4 changes: 4 additions & 0 deletions proto/babylon/finality/v1/params.proto
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package babylon.finality.v1;
import "gogoproto/gogo.proto";
import "amino/amino.proto";
import "cosmos_proto/cosmos.proto";
import "google/protobuf/duration.proto";

option go_package = "github.com/babylonlabs-io/babylon/x/finality/types";

Expand All @@ -26,4 +27,7 @@ message Params {
// min_pub_rand is the minimum number of public randomness each
// message should commit
uint64 min_pub_rand = 4;
// jail_duration is the minimum period of time that a finality provider remains jailed
google.protobuf.Duration jail_duration = 5
[(gogoproto.nullable) = false, (amino.dont_omitempty) = true, (gogoproto.stdduration) = true];
}
41 changes: 39 additions & 2 deletions x/btcstaking/keeper/finality_providers.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,43 @@ func (k Keeper) SlashFinalityProvider(ctx context.Context, fpBTCPK []byte) error
return nil
}

// JailFinalityProvider jails a finality provider with the given PK
// A jailed finality provider will not have voting power until it is
// unjailed (assuming it still ranks top N and has timestamped pub rand)
func (k Keeper) JailFinalityProvider(ctx context.Context, fpBTCPK []byte) error {
// ensure finality provider exists
fp, err := k.GetFinalityProvider(ctx, fpBTCPK)
if err != nil {
return err
}

// ensure finality provider is not slashed yet
if fp.IsSlashed() {
return types.ErrFpAlreadySlashed
}

// ensure finality provider is not jailed yet
if fp.IsJailed() {
return types.ErrFpAlreadySlashed
}

// set finality provider to be jailed
fp.Jailed = true
k.setFinalityProvider(ctx, fp)

btcTip := k.btclcKeeper.GetTipInfo(ctx)
if btcTip == nil {
return fmt.Errorf("failed to get current BTC tip")
}

// record jailed event. The next `BeginBlock` will consume this
// event for updating the finality provider set
powerUpdateEvent := types.NewEventPowerDistUpdateWithJailedFP(fp.BtcPk)
k.addPowerDistUpdateEvent(ctx, btcTip.Height, powerUpdateEvent)

return nil
}

// RevertSluggishFinalityProvider sets the Sluggish flag of the given finality provider
// to false
func (k Keeper) RevertSluggishFinalityProvider(ctx context.Context, fpBTCPK []byte) error {
Expand All @@ -113,11 +150,11 @@ func (k Keeper) RevertSluggishFinalityProvider(ctx context.Context, fpBTCPK []by

// ignore the finality provider is already slashed
// or detected as sluggish
if fp.IsSlashed() || fp.IsSluggish() {
if fp.IsSlashed() || fp.IsJailed() {
return nil
}

fp.Sluggish = false
fp.Jailed = false
k.setFinalityProvider(ctx, fp)

return nil
Expand Down
16 changes: 1 addition & 15 deletions x/btcstaking/keeper/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package keeper

import (
"context"
"fmt"

bbntypes "github.com/babylonlabs-io/babylon/types"
"github.com/babylonlabs-io/babylon/x/finality/types"
Expand All @@ -22,18 +21,5 @@ func (k Keeper) Hooks() Hooks {

// AfterSluggishFinalityProviderDetected updates the status of the given finality provider to `sluggish`
func (h Hooks) AfterSluggishFinalityProviderDetected(ctx context.Context, fpPk *bbntypes.BIP340PubKey) error {
fp, err := h.k.GetFinalityProvider(ctx, fpPk.MustMarshal())
if err != nil {
return err
}

if fp.IsSluggish() {
return fmt.Errorf("the finality provider %s is already detected as sluggish", fpPk.MarshalHex())
}

fp.Sluggish = true

h.k.setFinalityProvider(ctx, fp)

return nil
return h.k.JailFinalityProvider(ctx, fpPk.MustMarshal())
}
19 changes: 11 additions & 8 deletions x/btcstaking/keeper/power_dist_change.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func (k Keeper) UpdatePowerDist(ctx context.Context) {

// reconcile old voting power distribution cache and new events
// to construct the new distribution
newDc := k.ProcessAllPowerDistUpdateEvents(ctx, dc, events, maxActiveFps)
newDc := k.ProcessAllPowerDistUpdateEvents(ctx, dc, events)

// record voting power and cache for this height
k.recordVotingPowerAndCache(ctx, dc, newDc, maxActiveFps)
Expand Down Expand Up @@ -150,15 +150,14 @@ func (k Keeper) ProcessAllPowerDistUpdateEvents(
ctx context.Context,
dc *types.VotingPowerDistCache,
events []*types.EventPowerDistUpdate,
maxActiveFps uint32,
) *types.VotingPowerDistCache {
// a map where key is finality provider's BTC PK hex and value is a list
// of BTC delegations that newly become active under this provider
activeBTCDels := map[string][]*types.BTCDelegation{}
// a map where key is unbonded BTC delegation's staking tx hash
unbondedBTCDels := map[string]struct{}{}
// a map where key is slashed finality providers' BTC PK
slashedFPs := map[string]struct{}{}
// a map where key is slashed or jailed finality providers' BTC PK
slashedOrJailedFPs := map[string]struct{}{}

/*
filter and classify all events into new/expired BTC delegations and slashed FPs
Expand All @@ -183,8 +182,11 @@ func (k Keeper) ProcessAllPowerDistUpdateEvents(
unbondedBTCDels[delEvent.StakingTxHash] = struct{}{}
}
case *types.EventPowerDistUpdate_SlashedFp:
// slashed finality providers
slashedFPs[typedEvent.SlashedFp.Pk.MarshalHex()] = struct{}{}
// record slashed fps
slashedOrJailedFPs[typedEvent.SlashedFp.Pk.MarshalHex()] = struct{}{}
case *types.EventPowerDistUpdate_JailedFp:
// record jailed fps
slashedOrJailedFPs[typedEvent.JailedFp.Pk.MarshalHex()] = struct{}{}
}
}

Expand All @@ -208,8 +210,9 @@ func (k Keeper) ProcessAllPowerDistUpdateEvents(

fpBTCPKHex := fp.BtcPk.MarshalHex()

// if this finality provider is slashed, continue to avoid recording it
if _, ok := slashedFPs[fpBTCPKHex]; ok {
// if this finality provider is slashed or jailed, continue to avoid
// assigning voting power to it
if _, ok := slashedOrJailedFPs[fpBTCPKHex]; ok {
continue
}

Expand Down
4 changes: 2 additions & 2 deletions x/btcstaking/keeper/power_dist_change_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ func FuzzProcessAllPowerDistUpdateEvents_Determinism(f *testing.F) {
}
}

newDc := h.BTCStakingKeeper.ProcessAllPowerDistUpdateEvents(h.Ctx, dc, events, 100)
newDc := h.BTCStakingKeeper.ProcessAllPowerDistUpdateEvents(h.Ctx, dc, events)
for i := 0; i < 10; i++ {
newDc2 := h.BTCStakingKeeper.ProcessAllPowerDistUpdateEvents(h.Ctx, dc, events, 100)
newDc2 := h.BTCStakingKeeper.ProcessAllPowerDistUpdateEvents(h.Ctx, dc, events)
require.Equal(t, newDc, newDc2)
}
})
Expand Down
4 changes: 2 additions & 2 deletions x/btcstaking/types/btcstaking.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ func (fp *FinalityProvider) IsSlashed() bool {
return fp.SlashedBabylonHeight > 0
}

func (fp *FinalityProvider) IsSluggish() bool {
return fp.Sluggish
func (fp *FinalityProvider) IsJailed() bool {
return fp.Jailed
}

func (fp *FinalityProvider) ValidateBasic() error {
Expand Down
Loading

0 comments on commit 2fc9d50

Please sign in to comment.