-
Notifications
You must be signed in to change notification settings - Fork 12
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
Conversation
There was a problem hiding this 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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) |
x/finality/keeper/liveness.go
Outdated
return fmt.Errorf("failed to emit sluggish finality provider detected event: %w", err) | ||
} | ||
|
||
finalitytypes.IncrementSluggishFinalityProviderCounter() |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
x/finality/types/params.go
Outdated
@@ -13,6 +14,7 @@ const ( | |||
DefaultSignedBlocksWindow = int64(100) | |||
DefaultMinPubRand = 100 | |||
DefaultFinalitySigTimeout = 3 | |||
DefaultJailDuration = 60 * 10 * time.Second // 10 min |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
x/finality/types/metrics.go
Outdated
@@ -63,10 +63,10 @@ func IncrementSluggishFinalityProviderCounter() { | |||
) | |||
} | |||
|
|||
// DecrementSluggishFinalityProviderCounter increments the counter for the sluggish | |||
// DecrementJailedFinalityProviderCounter increments the counter for the jailed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// DecrementJailedFinalityProviderCounter increments the counter for the jailed | |
// DecrementJailedFinalityProviderCounter decrements the counter for the jailed |
4452192
to
5b46ebb
Compare
There was a problem hiding this 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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work 🚀
There was a problem hiding this 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
err := k.hooks.AfterSluggishFinalityProviderDetected(ctx, fpBtcPk) | ||
if err != nil { | ||
return err | ||
} |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
8f9cf72
to
a1b253d
Compare
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() |
There was a problem hiding this comment.
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
toFinalityProviderDistInfo
- 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 setsIsJailed
for correspondingFinalityProviderDistInfo
objects
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work!
x/btcstaking/types/btcstaking.go
Outdated
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 | ||
} |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
if !fp.IsTimestamped { | ||
break | ||
} | ||
if fp.IsJailed { | ||
break | ||
} |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
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
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
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:
sluggish
label of a finality provider tojailed
.AfterSluggishFinalityProviderDetected
hook in thex/btcstaking
module to set the label of a finality provider tojailed
.EventJailedFinalityProvider
event asEventPowerDistUpdate
, which is emitted after a finality provider is jailed.jail_duration
tox/finality
(needs to handle upgrade in a separate PR).jailed_until
toFinalityProviderSigningInfo
inx/finality
, which is calculated as the timestamp of the jailing block plus thejail_duration
of the parameter.jailed_until=jailingBlock.Time.Add(params.JailDuration)
.BeginBlocker
of the next block, uponEventJailedFinalityProvider
, record the finality provider public key and do not assign voting power to it.