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(ADR-25): Remove voting power from jailed finality providers #65

Merged
merged 13 commits into from
Sep 16, 2024

Conversation

gitferry
Copy link
Member

@gitferry gitferry commented Sep 10, 2024

This PR partially closes #64 by jailing sluggish finality providers by not assigning voting power to them in the voting power table. In particular, this PR made the following changes:

  1. Change the sluggish label of a finality provider to jailed.
  2. Implement AfterSluggishFinalityProviderDetected hook in the x/btcstaking module to set the label of a finality provider to jailed.
  3. Introduce EventJailedFinalityProvider event as EventPowerDistUpdate, which is emitted after a finality provider is jailed.
  4. Introduce a new parameter jail_duration to x/finality (needs to handle upgrade in a separate PR).
  5. Introduce a new field jailed_until to FinalityProviderSigningInfo in x/finality, which is calculated as the timestamp of the jailing block plus the jail_duration of the parameter.
  6. Clear the signing info record of the jailed finality provider and set jailed_until=jailingBlock.Time.Add(params.JailDuration).
  7. Within the BeginBlocker of the next block, upon EventJailedFinalityProvider, record the finality provider public key and do not assign voting power to it.
  8. Modify relevant tests

@gitferry gitferry marked this pull request as ready for review September 11, 2024 05:03
@gitferry gitferry requested a review from a team as a code owner September 11, 2024 05:03
Copy link
Contributor

@maurolacy maurolacy left a comment

Choose a reason for hiding this comment

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

LGTM. Just some questions, and suggestion for default jailing duration.

Agree with removing the distinction between sluggish and jailed, as they are basically the same state (i.e. sluggish => jailed).

I've seen that in some impls jailed and slashed are also, not exactly the same state, but implied (slashed => jailed). This can be done as well in a follow-up, as it might simplify the code paths, checks, etc. The only requirement would be, slashed => jailed with jailing duration = forever, i.e. you cannot unjail after slashing.

@@ -242,7 +243,7 @@ func (ms msgServer) WrappedCancelUnbondingDelegation(goCtx context.Context, msg
)
}

blockHeight := uint64(ctx.BlockHeader().Height)
blockHeight := uint64(ctx.HeaderInfo().Height)
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the difference here? Just curious.

Copy link
Member Author

Choose a reason for hiding this comment

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

ctx.BlockHeader() returns ctx.header which is deprecated. We should use HeaderInfo instead. See https://github.com/cosmos/cosmos-sdk/blob/698627016d64dbf33bea01837364c5a6e55a1ee3/types/context.go#L45

@@ -150,15 +150,14 @@ func (k Keeper) ProcessAllPowerDistUpdateEvents(
ctx context.Context,
dc *types.VotingPowerDistCache,
events []*types.EventPowerDistUpdate,
maxActiveFps uint32,
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this max being enforced somewhere else? Just curious.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep, here

k.recordVotingPowerAndCache(ctx, dc, newDc, maxActiveFps)
.

return fmt.Errorf("failed to emit sluggish finality provider detected event: %w", err)
}

finalitytypes.IncrementSluggishFinalityProviderCounter()
Copy link
Contributor

@maurolacy maurolacy Sep 11, 2024

Choose a reason for hiding this comment

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

Checked and couldn't find the call to the corresponding Decrement call. Perhaps just remove this counter?

Copy link
Member Author

Choose a reason for hiding this comment

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

Nice spot! Decrement part will be called in the implementation of unjailing, happening in the next PR. Nevertheless, we probably should not use sluggish here anymore. Fixed in 4452192

@@ -13,6 +14,7 @@ const (
DefaultSignedBlocksWindow = int64(100)
DefaultMinPubRand = 100
DefaultFinalitySigTimeout = 3
DefaultJailDuration = 60 * 10 * time.Second // 10 min
Copy link
Contributor

@maurolacy maurolacy Sep 11, 2024

Choose a reason for hiding this comment

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

This looks like too little to me. 24 hours or so sounds reasonable. So that jailing:

  • Is a punishment in itself for being offline.
  • Gives some time to the operator to properly restore service / recover liveness.

Copy link
Member Author

Choose a reason for hiding this comment

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

This was copied from Cosmos SDK but yeah I agree that 1 day seems more reasonable. Fixed in 2caa4af

@@ -63,10 +63,10 @@ func IncrementSluggishFinalityProviderCounter() {
)
}

// DecrementSluggishFinalityProviderCounter increments the counter for the sluggish
// DecrementJailedFinalityProviderCounter increments the counter for the jailed
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// DecrementJailedFinalityProviderCounter increments the counter for the jailed
// DecrementJailedFinalityProviderCounter decrements the counter for the jailed

Copy link
Contributor

@RafilxTenfen RafilxTenfen left a comment

Choose a reason for hiding this comment

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

LGTM, only some suggestions

Copy link
Member

@Lazar955 Lazar955 left a comment

Choose a reason for hiding this comment

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

Nice work 🚀

Copy link
Member

@SebastianElvis SebastianElvis left a comment

Choose a reason for hiding this comment

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

Great work! Mostly questions

proto/babylon/btcstaking/v1/events.proto Show resolved Hide resolved
x/btcstaking/keeper/hooks.go Show resolved Hide resolved
Comment on lines +187 to +190
err := k.hooks.AfterSluggishFinalityProviderDetected(ctx, fpBtcPk)
if err != nil {
return err
}
Copy link
Member

Choose a reason for hiding this comment

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

How about we call k.BTCStakingKeeper.JailFinalityProvider directly? From what I see the AfterSluggishFinalityProviderDetected hook is only used here and its only functionality is to call JailFinalityProvider. Removing this hook relationship makes the code easier to understand

Copy link
Member Author

@gitferry gitferry Sep 12, 2024

Choose a reason for hiding this comment

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

We would have slashing reward logic in the future. So better to have a hook where the incentive module can also implement it?

Copy link
Member

Choose a reason for hiding this comment

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

We can add the logic of slashing rewards into JailFinalityProvider, as removing FP voting power and slashing its reward always need to be done at the same time. No strong opinion on this though

Copy link
Member Author

Choose a reason for hiding this comment

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

Agreed that jailing and slashing rewards should happen at the same time, so we can achieve this by having both modules implement the same hook. Some discussion about why we decided to use a hook.

Copy link
Member

Choose a reason for hiding this comment

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

Sure. In fact x/finality module has access to x/incentive so it can invoke the incentive module to slash the reward. But no problem if you think this is better done in a hook

proto/babylon/finality/v1/finality.proto Show resolved Hide resolved
x/finality/keeper/liveness.go Show resolved Hide resolved
Comment on lines 100 to 104
fp, err := k.GetFinalityProvider(ctx, fpDistInfo.BtcPk.MustMarshal())
if err != nil {
panic(fmt.Errorf("the finality provider %s does not exist: %w", fp.BtcPk.MarshalHex(), err))
}
fpDistInfo.IsJailed = fp.IsJailed()
Copy link
Member

Choose a reason for hiding this comment

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

This will cause some overhead imo -- upon every new block computing the new voting power table will need to retrieve all FPs in the DB, leading to O(n) overhead in DB read where n is the number of FPs. This was not the case where the DB overhead is O(# of BTC delegations with state updates).

Since we will have MsgUnjail anyway, how about we do the state update using the event-based approach similar to the existing events? That is,

  • Add a field IsJailed to FinalityProviderDistInfo
  • Upon jailing a FP, push a jailed event to the event queue
  • Upon MsgUnjail is approved, push an unjailed event to the event queue
  • ProcessAllPowerDistUpdateEvents processes all the corresponding jailed/unjailed events and sets IsJailed for corresponding FinalityProviderDistInfo objects

Copy link
Member Author

Choose a reason for hiding this comment

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

Nice point! Chaned to setting IsJailed to FinalityProviderDistInfo upon event processing and will have UnjailEvent to revert the flag in the subsequent PR

Copy link
Member

@SebastianElvis SebastianElvis left a comment

Choose a reason for hiding this comment

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

Great work!

Comment on lines 51 to 62
if fps[i].IsTimestamped && !fps[j].IsTimestamped {
return true
}
if !fps[i].IsTimestamped && fps[j].IsTimestamped {
return false
}
if !fps[i].IsJailed && fps[j].IsJailed {
return true
}
if fps[i].IsJailed && !fps[j].IsJailed {
return false
}
Copy link
Member

Choose a reason for hiding this comment

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

How about computing a bool value zeroVotingPower := fps[i].IsJailed || !fps[i].IsTimestamped and then do the comparison?

Copy link
Member Author

Choose a reason for hiding this comment

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

That would be cleaner. Fixed in a871d44

Comment on lines 56 to +61
if !fp.IsTimestamped {
break
}
if fp.IsJailed {
break
}
Copy link
Member

Choose a reason for hiding this comment

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

Given all these new jailed/timestamped concepts, I started to feel TotalVotingPower is no longer an accurate name. How about we rename it to sth like TotalBTCStake? Not for this PR, just some thought

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point. Tracked in #72. Will fix it in a separate PR

@gitferry gitferry merged commit 2308deb into feat/adr-25-jailing Sep 16, 2024
17 checks passed
@gitferry gitferry deleted the gai/jail-sluggish-fps branch September 16, 2024 02:46
gitferry added a commit that referenced this pull request Sep 19, 2024
Closes #64.

TODOs:
- [x] #65 
- [x] #74
- [x] babylonlabs-io/finality-provider#56 
- [x] #81 

We still need to update the docs, add CLI cmd, and e2e tests but can be
done in separate PRs
SebastianElvis pushed a commit that referenced this pull request Sep 19, 2024
Closes #64.

TODOs:
- [x] #65
- [x] #74
- [x] babylonlabs-io/finality-provider#56
- [x] #81

We still need to update the docs, add CLI cmd, and e2e tests but can be
done in separate PRs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants