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

Onchain upgrade to consumer chain from sovereign chain #697

Conversation

jstr1121
Copy link
Contributor

@jstr1121 jstr1121 commented Feb 2, 2023

Description

This PR is supposed to upgrade intechain-security module for on-chain upgrade from sovereign to consumer chain.
Applying the changes mentioned in cosmos/ibc#840

Changes

  • Protobuf modification
  • democracy app to use consumer keeper's params to see if consumer is enabled.
  • PreCCV check on consumer InitGenesis to consider using staking module
  • Unit test for changes on Endblocker
  • Slashing sovereign validators for the fault made before consumer chain transition

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.

storeUpgrades := store.StoreUpgrades{}
storeUpgrades := store.StoreUpgrades{
Added: []string{ibcconsumertypes.ModuleName},
}

Copy link
Contributor

Choose a reason for hiding this comment

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

if IsSkipHeight(upgradeInfo.Height) {
		return
	}

if err != nil {
panic(err)
}
_, _ = app.DistrKeeper.WithdrawDelegationRewards(ctx, delAddr, valAddr)
Copy link
Contributor

Choose a reason for hiding this comment

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

why not check err and panic here ?

_, err := app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx)
if err != nil {
log.Fatal(err)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Hi, about store iterator, please check this issue in gaia, cosmos/gaia#1924
please let me know your opinion.

@shaspitz
Copy link
Contributor

Hi @jstr1121, thanks again for your code! We've merged a lot of PRs over the past few weeks, so it looks like this branch needs to catch up to main. Let's eventually work together on making an ADR for the implementation of this feature. It would also be useful to setup a 30-60 minute code walkthrough of how the implementation works, and why certain design decisions were made. Would sometime next week work for you? Let's chat on slack

@jstr1121
Copy link
Contributor Author

Hi @jstr1121, thanks again for your code! We've merged a lot of PRs over the past few weeks, so it looks like this branch needs to catch up to main. Let's eventually work together on making an ADR for the implementation of this feature. It would also be useful to setup a 30-60 minute code walkthrough of how the implementation works, and why certain design decisions were made. Would sometime next week work for you? Let's chat on slack

Will get the conflict fixed soon - I am fine for 30-60 mins call this week, we can schedule a time on slack.

jstr1121 and others added 3 commits February 21, 2023 23:05
Co-authored-by: yaruwangway <69694322+yaruwangway@users.noreply.github.com>
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.

Great stuff!

@@ -506,6 +509,8 @@ func New(
distrtypes.ModuleName,
slashingtypes.ModuleName,
evidencetypes.ModuleName,
consumertypes.ModuleName, // Note: consumer beginblocker before staking module
Copy link
Contributor

Choose a reason for hiding this comment

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

I think changes like this would need to be upstreamed to all consumer chains that pattern match this file. No action item for the Stride team, just commenting for awareness on our team

@@ -334,6 +334,7 @@ func New(
&app.IBCKeeper.PortKeeper,
app.IBCKeeper.ConnectionKeeper,
app.IBCKeeper.ClientKeeper,
nil,
Copy link
Contributor

Choose a reason for hiding this comment

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

See

func (k Keeper) mustValidateFields() {
on this keeper being nil. Maybe we can add a flag likeisDemocConsumer to make things very clear

return false
}

func (k Keeper) SetPreCCV(ctx sdk.Context, state *types.GenesisState) {
Copy link
Contributor

Choose a reason for hiding this comment

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

We usually make quick unit tests for CRUD operations like these, especially because this method is not just setting a flag

panic("unimplemented on CCV keeper")
if k.IsPreCCV(ctx) || ctx.BlockHeight() <= k.LastSovereignHeight(ctx) {
if k.stakingKeeper == nil {
panic("empty staking keeper")
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we panic or return in cases like this?

}

return k.stakingKeeper.ValidatorByConsAddr(ctx, consAddr)
}
return stakingtypes.Validator{}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: it'd be cleaner to pass a stakingtypes.InfractionEmpty infraction type here.

if k.stakingKeeper == nil {
return
}

Copy link
Contributor

Choose a reason for hiding this comment

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

This error message is hard to understand without context imo.

@@ -344,7 +345,7 @@ func New(

// register slashing module Slashing hooks to the consumer keeper
app.ConsumerKeeper = *app.ConsumerKeeper.SetHooks(app.SlashingKeeper.Hooks())
consumerModule := ibcconsumer.NewAppModule(app.ConsumerKeeper)
consumerModule := ibcconsumer.NewAppModule(app.ConsumerKeeper, nil)
Copy link
Contributor

Choose a reason for hiding this comment

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

Could use a comment here


if state.PreCCV {
return []abci.ValidatorUpdate{}
Copy link
Contributor

Choose a reason for hiding this comment

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

Could use comment that ccv module shouldn't be doing anything yet because staking keeper is still active

func (k Keeper) IterateValidators(sdk.Context, func(index int64, validator stakingtypes.ValidatorI) (stop bool)) {
// IterateValidators iterate democracy staking validators when the block height is before the last sovereign height
func (k Keeper) IterateValidators(ctx sdk.Context, f func(index int64, validator stakingtypes.ValidatorI) (stop bool)) {
if k.IsPreCCV(ctx) || ctx.BlockHeight() <= k.LastSovereignHeight(ctx) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Could use comment on what edge case this covers

Copy link
Contributor

Choose a reason for hiding this comment

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

The idea is that all the calls to the staking module are directed to the consumer CCV module as it plays the role of the staking module, even though until the chain fully transitions to be a consumer chain the staking module should be the original staking module.

Copy link
Contributor

Choose a reason for hiding this comment

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

So basically, once the consumer CCV module is added, the democracy-staking module is also added. In the first block after the upgrade, the democracy-staking acts exactly as the original staking module as the preCCV flag is set (see democracy/staking/module.go). In the EndBlock of the first block, the preCCV flag is unset, which means that the democracy-staking module stops sending updates to TM and the consumer CCV module starts sending these updates.

However, the provider validators are producing blocks two heights later due to the +1 delay of TM (see ABCI spec). Thus, all the conditions for this edge case are wrong.

}

// IsJailed returns the outstanding slashing flag for the given validator adddress
func (k Keeper) IsValidatorJailed(ctx sdk.Context, addr sdk.ConsAddress) bool {
if k.IsPreCCV(ctx) || ctx.BlockHeight() <= k.LastSovereignHeight(ctx) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here on edge case comment

@@ -89,16 +108,32 @@ func (k Keeper) ValidatorByConsAddr(sdk.Context, sdk.ConsAddress) stakingtypes.V
Also, the slashing module will cal lthis function when it observes downtime. In that case
the only requirement on the returned value is that it isn't null.
*/
if k.IsPreCCV(ctx) || ctx.BlockHeight() <= k.LastSovereignHeight(ctx) {
Copy link
Contributor

Choose a reason for hiding this comment

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

same

if infraction == stakingtypes.InfractionEmpty {
return
}

if k.IsPreCCV(ctx) || ctx.BlockHeight() <= k.LastSovereignHeight(ctx) {
Copy link
Contributor

Choose a reason for hiding this comment

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

same, maybe we just make this a func to be more readable

@shaspitz
Copy link
Contributor

shaspitz commented Mar 1, 2023

Slashing sovereign validators for the fault made before consumer chain transition

I'm having trouble finding the section of code this is located at

// populate cross chain validators states with initial valset
am.keeper.ApplyCCValidatorChanges(ctx, initialValSet)

initialSetFlag := make(map[string]bool)
Copy link
Contributor

Choose a reason for hiding this comment

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

@smarshall-spitzbart please add a comment here explaining the logic.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'll add "Add validator updates to initialValSet, such that the "old" validators returned from GetLastValidators are given zero power, and the "new" validators are given their full power."

if infraction == stakingtypes.InfractionEmpty {
return
}

if k.IsPreCCV(ctx) || ctx.BlockHeight() <= k.LastSovereignHeight(ctx) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can never be the case that ctx.BlockHeight() < k.LastSovereignHeight(ctx) as the CCV module was not on the chain.

Copy link
Contributor

@mpoke mpoke Mar 2, 2023

Choose a reason for hiding this comment

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

This is not the correct way to deal with slashing. This slash request could be for one of the following cases:

  • An infraction committed by a sovereign chain validator. This can be detected via the infraction height. The sovereign chain validator set is active until the height h (inclusive). Note that h != LastSovereignHeight. LastSovereignHeight is the height the provider validator set was sent to TM. According to the ABCI spec, h = LastSovereignHeight + 1. In this case, the validator should be slashed if the unbonding period didn't pass yet. Still need to implement a way in democracy-staking that not slash validators after unbonding period elapsed after the switch.
  • An infraction committed by a provider validator. Again, the infraction height can be used for detection.

return AppModule{
keeper: k,
sk: sk,
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need this? The DemocracyStakingKeeper can be access via am.keeper.stakingKeeper

Copy link
Contributor

Choose a reason for hiding this comment

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

This has to do with the capabilities model, am.keeper.stakingKeeper is non-exported and not accessible.

@@ -506,6 +509,8 @@ func New(
distrtypes.ModuleName,
slashingtypes.ModuleName,
evidencetypes.ModuleName,
consumertypes.ModuleName, // Note: consumer beginblocker before staking module
Copy link
Contributor

Choose a reason for hiding this comment

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

@jstr1121 Why is this order necessary?

Copy link
Contributor

Choose a reason for hiding this comment

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

Beginblocker order shouldn't matter AFAIK

)
app.MM.SetOrderEndBlockers(
crisistypes.ModuleName,
govtypes.ModuleName,
consumertypes.ModuleName, // Note: consumer endblocker before staking module
Copy link
Contributor

Choose a reason for hiding this comment

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

@jstr1121 Why is this order necessary?

Copy link
Contributor

Choose a reason for hiding this comment

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

With 1269163, the order doesn't matter AFAIK

@@ -550,6 +554,7 @@ func New(
capabilitytypes.ModuleName,
authtypes.ModuleName,
banktypes.ModuleName,
consumertypes.ModuleName, // Note: consumer initiation before staking module
Copy link
Contributor

Choose a reason for hiding this comment

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

@jstr1121 Why is this order necessary?

Copy link
Contributor

Choose a reason for hiding this comment

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

Same here, InitGenesis shouldn't matter unless I'm missing something

Comment on lines +641 to +657
// TODO: should have a way to read from current node home
userHomeDir, err := os.UserHomeDir()
if err != nil {
stdlog.Println("Failed to get home dir %2", err)
}
nodeHome := userHomeDir + "/.sovereign/config/genesis.json"
appState, _, err := genutiltypes.GenesisStateFromGenFile(nodeHome)
if err != nil {
return fromVM, fmt.Errorf("failed to unmarshal genesis state: %w", err)
}

var consumerGenesis = consumertypes.GenesisState{}
appCodec.MustUnmarshalJSON(appState[consumertypes.ModuleName], &consumerGenesis)

consumerGenesis.PreCCV = true
app.ConsumerKeeper.InitGenesis(ctx, &consumerGenesis)

Copy link
Contributor

Choose a reason for hiding this comment

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

This is needed only because the provider doesn't create a correct init genesis for the consumer CCV module with preCCV set to true. See the spec for more details. @smarshall-spitzbart please open an issue to address this.

Comment on lines +670 to 673
storeUpgrades := store.StoreUpgrades{
Added: []string{consumertypes.ModuleName},
}

Copy link
Contributor

Choose a reason for hiding this comment

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

@jstr1121 What are these changes for?

@@ -0,0 +1,808 @@
package app
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need to have the app.go for the sovereign chain in this repo?

// PreCCV is true when consumer chain used to be running on non-consumer chain, and when it is in the progress of upgrading
// to consumer chain, where consumer chain upgrade is done.
if state.PreCCV {
k.SetPreCCV(ctx, state)
Copy link
Contributor

Choose a reason for hiding this comment

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

@smarshall-spitzbart please store the init val set explicitly.

func (k Keeper) Jail(ctx sdk.Context, addr sdk.ConsAddress) {}
// Jail performs jail operation on democracy staking validator if block height after last sovereign height
func (k Keeper) Jail(ctx sdk.Context, addr sdk.ConsAddress) {
if k.IsPreCCV(ctx) || ctx.BlockHeight() <= k.LastSovereignHeight(ctx) {
Copy link
Contributor

Choose a reason for hiding this comment

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

ditto: see comments from Slash


// Unjail performs unjail operation on democracy staking validator if block height after last sovereign height
func (k Keeper) Unjail(ctx sdk.Context, addr sdk.ConsAddress) {
if k.IsPreCCV(ctx) || ctx.BlockHeight() <= k.LastSovereignHeight(ctx) {
Copy link
Contributor

Choose a reason for hiding this comment

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

ditto: see comments from Slash

panic("unimplemented on CCV keeper")
// MaxValidators returns max number of validators on democracy staking if block height after last sovereign height
func (k Keeper) MaxValidators(ctx sdk.Context) uint32 {
if k.IsPreCCV(ctx) || ctx.BlockHeight() <= k.LastSovereignHeight(ctx) {
Copy link
Contributor

Choose a reason for hiding this comment

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

ditto

func (k Keeper) Unjail(sdk.Context, sdk.ConsAddress) {}
// Delegation returns delegation from an address to a democracy staking validator if block height after last sovereign height
func (k Keeper) Delegation(ctx sdk.Context, addr sdk.AccAddress, valAddr sdk.ValAddress) stakingtypes.DelegationI {
if k.IsPreCCV(ctx) || ctx.BlockHeight() <= k.LastSovereignHeight(ctx) {
Copy link
Contributor

Choose a reason for hiding this comment

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

ditto

@@ -89,16 +108,32 @@ func (k Keeper) ValidatorByConsAddr(sdk.Context, sdk.ConsAddress) stakingtypes.V
Also, the slashing module will cal lthis function when it observes downtime. In that case
the only requirement on the returned value is that it isn't null.
*/
if k.IsPreCCV(ctx) || ctx.BlockHeight() <= k.LastSovereignHeight(ctx) {
Copy link
Contributor

Choose a reason for hiding this comment

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

ditto

@shaspitz
Copy link
Contributor

shaspitz commented Mar 6, 2023

@jstr1121 your contributions are much appreciated! We're going to work off your commits in a branch native to the parent repository, tracked as #757

Consequently I'll close this PR in favor of the new one. Thanks again!

@shaspitz shaspitz closed this Mar 6, 2023
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