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: pause unbondings during equivocation proposal voting period #791

Closed

Conversation

tbruyelle
Copy link
Contributor

Description

The change registers 2 gov module hooks in the provider module:

  • AfterProposalDeposit: if the proposal is an equivocation proposal in voting period, then call PutUnbondingOnHold for each unbondings of each validators found in the proposal.

  • AfterProposalVotingPeriodEnded: if the proposal is an equivocation proposal, then call UnbondingCanComplete for each unbondings of each validators found in the proposal.

A new key is also added, to store the equivocation proposal ID for which unbondings have been put on hold. This covers 2 specific cases:

  • The gov module allows additional deposits even if the proposal is already in voting period. So when AfterProposalDeposit is invoked, we have to make sure the proposal is in voting period for the first time before puting the unbondings on hold. This is simply handled by checking if the proposal ID exists in the store at the beginning of the hook, and then storing it if not.

  • If the provider chain is upgraded with this change and there's already an equivocation proposal in voting period, AfterProposalVotingPeriodEnded could be invoked without the initial AfterProposalDeposit, so some unbondings could be un-paused while they hadn't been paused (conflicts with AfterUnbondingInitiated hook). To prevent that, we check the proposal ID exists in the store, which means AfterProposalDeposit has been called prevouisly.

Linked issues

Closes: #747

Type of change

If you've checked more than one of the first three boxes, consider splitting this PR into multiple PRs!

  • Feature: Changes and/or adds code behavior, irrelevant to bug fixes
  • Fix: Changes and/or adds code behavior, specifically to fix a bug
  • Refactor: Changes existing code style, naming, structure, etc.
  • Testing: Adds testing
  • Docs: Adds documentation

Regression tests

If Refactor, describe the new or existing tests that verify no behavior was changed or added where refactors were introduced.

New behavior tests

If Feature or Fix, describe the new or existing tests that verify the new behavior is correct and expected.

A new e2e test have been added in tests/e2e/proposal.go.

Versioning Implications

If the above box is checked, which version should be bumped?

  • MAJOR: Consensus breaking changes to both the provider and consumers(s), including updates/breaking changes to IBC communication between provider and consumer(s)
  • MINOR: Consensus breaking changes which affect either only the provider or only the consumer(s)
  • PATCH: Non consensus breaking changes

Targeting

Please select one of the following:

  • This PR is only relevant to main
  • This PR is relevant to main, and should also be back-ported to ____ (ex: v1.0.0 and v1.1.0)
  • This PR is only relevant to ____ (ex: v1.0.0, v1.1.0, and v1.2.0)

Fix cosmos#747

The change registers 2 gov module hooks in the provider module:

- `AfterProposalDeposit`: if the proposal is an equivocation proposal in
  voting period, then call `PutUnbondingOnHold` for each unbondings of
  each validators found in the proposal.

- `AfterProposalVotingPeriodEnded`: if the proposal is an equivocation
  proposal, then call `UnbondingCanComplete` for each unbondings of each
  validators found in the proposal.

A new key is also added, to store the equivocation proposal ID for which
unbondings have been put on hold. This covers 2 specific cases:

- The gov module allows additional deposits even if the proposal is
  already in voting period. So when `AfterProposalDeposit` is invoked,
  we have to make sure the proposal is in voting period for the first time
  before puting the unbondings on hold. This is simply handled by
  checking if the proposal ID exists in the store at the beginning of
  the hook, and then storing it if not.

- If the provider chain is upgraded with this change and there's already
  an equivocation proposal in voting period, `AfterProposalVotingPeriodEnded`
  could be invoked without the initial `AfterProposalDeposit`, so some
  unbondings could be un-paused while they hadn't been paused (conflicts with
  `AfterUnbondingInitiated` hook). To prevent that, we check the
  proposal ID exists in the store, which means `AfterProposalDeposit`
  has been called prevouisly.

Co-authored-by: Albert Le Batteux <contact@albttx.tech>
@@ -376,7 +376,7 @@ func New(
stakingtypes.NewMultiStakingHooks(
app.DistrKeeper.Hooks(),
app.SlashingKeeper.Hooks(),
app.ProviderKeeper.Hooks(),
app.ProviderKeeper.Hooks(app.GovKeeper),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

As far as I can tell this is not very conventional, but on the other hand, adding the govKeeper to the providerKeeper is even more ugly, because the govKeeper needs the providerKeeper for the proposal routes. So we would have to write something like (pseudo code):

providerKeeper := providerKeeper.NewKeeper( ...  govKeeper ...) // govKeeper is nil for now
govKeeper := govKeeper.NewKeeper( ... providerKeeper ... )
providerKeeper.SetGovKeeper( govKeeper ) // finally set a non-nil govKeeper

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not against the current design per say, but imo adding a govKeeper pointer ref to providerKeeper, while using SetGovKeeper is the easiest to understand. No need to add a parameter for govKeeper in providerKeeper.NewKeeper(). You could just auto set govKeeper to nil, until it is explicitly set after the constructor

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I see one benefit of having an additional parameter in Hooks(): it breaks compilation after upgrade. Typically when gaia will upgrade its interchain-security dependency, the fix will be obvious, while on the other hand, having to call SetGovKeeper can be easily forgotten.
Is there any validation we could setup to ensure SetGovKeeper is actually called ? A little bit like keeper.mustValidateField but for setters.

@jtremback
Copy link
Contributor

Thanks! Will review this soon.

Copy link
Contributor

@shaspitz shaspitz left a comment

Choose a reason for hiding this comment

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

Awesome work

@@ -376,7 +376,7 @@ func New(
stakingtypes.NewMultiStakingHooks(
app.DistrKeeper.Hooks(),
app.SlashingKeeper.Hooks(),
app.ProviderKeeper.Hooks(),
app.ProviderKeeper.Hooks(app.GovKeeper),
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not against the current design per say, but imo adding a govKeeper pointer ref to providerKeeper, while using SetGovKeeper is the easiest to understand. No need to add a parameter for govKeeper in providerKeeper.NewKeeper(). You could just auto set govKeeper to nil, until it is explicitly set after the constructor

// activate prop
addDepositProp(s, proposalID, minDepositAmt)
checkStakingUnbondingOnHoldRefCount(s, valAddr, tt.expectedDuringProp)
// assert that an additionnal deposit done after the activation doesn't
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it worth a quick check here to validate the voting period has not been reset?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

How can that happen ? I see no message in the gov module that could reset the voting period.

tests/e2e/proposal.go Outdated Show resolved Hide resolved
@faddat
Copy link
Contributor

faddat commented Apr 30, 2023

Do you guys figure that this one is complete? I think it might solve one of the test cases in the 47 work that we have been doing

@mpoke mpoke marked this pull request as draft May 3, 2023 10:31
@mpoke
Copy link
Contributor

mpoke commented May 3, 2023

Converting to draft as this feature requires an analysis on the potential risks. @tbruyelle @jtremback it would be useful to create an ADR to be able to discuss on it.

@tbruyelle
Copy link
Contributor Author

tbruyelle commented May 10, 2023

Converting to draft as this feature requires an analysis on the potential risks. @tbruyelle @jtremback it would be useful to create an ADR to be able to discuss on it.

OK, we'll work on an ADR with @albttx and @giunatale.

@tbruyelle
Copy link
Contributor Author

Converting to draft as this feature requires an analysis on the potential risks. @tbruyelle @jtremback it would be useful to create an ADR to be able to discuss on it.

ADR ready for review #964

@mpoke
Copy link
Contributor

mpoke commented Nov 30, 2023

@tbruyelle Closing this as the equivocation proposal was removed by the cryptographic verification of equivocation feature #1340.

@mpoke mpoke closed this Nov 30, 2023
@@ -567,7 +567,7 @@ func TestHandleVSCMaturedPacket(t *testing.T) {

// Start first unbonding without any consumers registered
var unbondingOpId uint64 = 1
err := pk.Hooks().AfterUnbondingInitiated(ctx, unbondingOpId)
err := pk.Hooks(mocks.MockGovKeeper).AfterUnbondingInitiated(ctx, unbondingOpId)
require.NoError(t, err)
// Check that no unbonding op was stored
_, found := pk.GetUnbondingOp(ctx, unbondingOpId)

Choose a reason for hiding this comment

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

cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn

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.

Pause unbondings while equivocation slashing proposal is in voting period
6 participants