diff --git a/app/consumer-democracy/app.go b/app/consumer-democracy/app.go index dc147f2244..83722cb12f 100644 --- a/app/consumer-democracy/app.go +++ b/app/consumer-democracy/app.go @@ -8,6 +8,8 @@ import ( "os" "path/filepath" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/grpc/tmservice" @@ -114,7 +116,7 @@ import ( const ( AppName = "interchain-security-cd" - upgradeName = "v07-Theta" + upgradeName = "v07-Theta" // arbitrary name, define your own appropriately named upgrade AccountAddressPrefix = "cosmos" ) @@ -331,7 +333,7 @@ func New( app.AccountKeeper, ) - ccvstakingKeeper := stakingkeeper.NewKeeper( + stakingKeeper := stakingkeeper.NewKeeper( appCodec, keys[stakingtypes.StoreKey], app.AccountKeeper, @@ -340,7 +342,7 @@ func New( ) app.MintKeeper = mintkeeper.NewKeeper( - appCodec, keys[minttypes.StoreKey], app.GetSubspace(minttypes.ModuleName), &ccvstakingKeeper, + appCodec, keys[minttypes.StoreKey], app.GetSubspace(minttypes.ModuleName), &stakingKeeper, app.AccountKeeper, app.BankKeeper, authtypes.FeeCollectorName, ) @@ -356,7 +358,7 @@ func New( app.GetSubspace(distrtypes.ModuleName), app.AccountKeeper, app.BankKeeper, - &ccvstakingKeeper, + &stakingKeeper, consumertypes.ConsumerRedistributeName, app.ModuleAccountAddrs(), ) @@ -377,7 +379,7 @@ func New( // register the staking hooks // NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks // NOTE: slashing hook was removed since it's only relevant for consumerKeeper - app.StakingKeeper = *ccvstakingKeeper.SetHooks( + app.StakingKeeper = *stakingKeeper.SetHooks( stakingtypes.NewMultiStakingHooks(app.DistrKeeper.Hooks()), ) @@ -390,7 +392,7 @@ func New( AddRoute(upgradetypes.RouterKey, upgrade.NewSoftwareUpgradeProposalHandler(app.UpgradeKeeper)) govKeeper := govkeeper.NewKeeper( appCodec, keys[govtypes.StoreKey], app.GetSubspace(govtypes.ModuleName), app.AccountKeeper, app.BankKeeper, - &ccvstakingKeeper, ccvgovRouter, + &stakingKeeper, ccvgovRouter, ) app.GovKeeper = *govKeeper.SetHooks( @@ -426,6 +428,9 @@ func New( authtypes.FeeCollectorName, ) + // Setting the staking keeper is only needed for standalone to consumer changeover chains + app.ConsumerKeeper.SetStandaloneStakingKeeper(app.StakingKeeper) + // consumer keeper satisfies the staking keeper interface // of the slashing module app.SlashingKeeper = slashingkeeper.NewKeeper( @@ -626,6 +631,7 @@ func New( app.UpgradeKeeper.SetUpgradeHandler( upgradeName, func(ctx sdk.Context, _ upgradetypes.Plan, _ module.VersionMap) (module.VersionMap, error) { + app.IBCKeeper.ConnectionKeeper.SetParams(ctx, ibcconnectiontypes.DefaultParams()) fromVM := make(map[string]uint64) @@ -634,8 +640,33 @@ func New( fromVM[moduleName] = eachModule.ConsensusVersion() } + // For a new consumer chain, this code (together with the entire SetUpgradeHandler) is not needed at all, + // upgrade handler code is application specific. However, as an example, standalone to consumer + // changeover chains should utilize customized upgrade handler code similar to below. + + // 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) + ctx.Logger().Info("start to run module migrations...") + // Note: consumer ccv module is added to app.MM.Modules constructor above, + // meaning the consumer ccv module will have an entry in fromVM. + // Since a consumer ccv module entry exists in fromVM, the RunMigrations method + // will not call the consumer ccv module's InitGenesis method a second time. return app.MM.RunMigrations(ctx, app.configurator, fromVM) }, ) @@ -646,7 +677,12 @@ func New( } if upgradeInfo.Name == upgradeName && !app.UpgradeKeeper.IsSkipHeight(upgradeInfo.Height) { - storeUpgrades := store.StoreUpgrades{} + // Chains may need to add a KV store to their application. The following code + // is needed for standalone chains that're changing over to a consumer chain, with a consumer ccv module. + // When a chain starts from height 0 (like for testing purposes in this repo), the following code is not needed. + storeUpgrades := store.StoreUpgrades{ + Added: []string{consumertypes.ModuleName}, + } // configure store loader that checks if version == upgradeHeight and applies store upgrades app.SetStoreLoader(upgradetypes.UpgradeStoreLoader(upgradeInfo.Height, &storeUpgrades)) diff --git a/proto/interchain_security/ccv/consumer/v1/genesis.proto b/proto/interchain_security/ccv/consumer/v1/genesis.proto index 6adbc79f54..043556fee3 100644 --- a/proto/interchain_security/ccv/consumer/v1/genesis.proto +++ b/proto/interchain_security/ccv/consumer/v1/genesis.proto @@ -40,6 +40,7 @@ message GenesisState { // LastTransmissionBlockHeight nil on new chain, filled in on restart. interchain_security.ccv.consumer.v1.LastTransmissionBlockHeight last_transmission_block_height = 12 [ (gogoproto.nullable) = false ]; + bool preCCV = 13; // flag indicating whether the consumer CCV module starts in pre-CCV state } // HeightValsetUpdateID defines the genesis information for the mapping diff --git a/testutil/keeper/mocks.go b/testutil/keeper/mocks.go index 66fc77dbce..a3f1c48f6d 100644 --- a/testutil/keeper/mocks.go +++ b/testutil/keeper/mocks.go @@ -46,6 +46,20 @@ func (m *MockStakingKeeper) EXPECT() *MockStakingKeeperMockRecorder { return m.recorder } +// Delegation mocks base method. +func (m *MockStakingKeeper) Delegation(ctx types.Context, addr types.AccAddress, valAddr types.ValAddress) types4.DelegationI { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delegation", ctx, addr, valAddr) + ret0, _ := ret[0].(types4.DelegationI) + return ret0 +} + +// Delegation indicates an expected call of Delegation. +func (mr *MockStakingKeeperMockRecorder) Delegation(ctx, addr, valAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delegation", reflect.TypeOf((*MockStakingKeeper)(nil).Delegation), ctx, addr, valAddr) +} + // GetLastTotalPower mocks base method. func (m *MockStakingKeeper) GetLastTotalPower(ctx types.Context) types.Int { m.ctrl.T.Helper() @@ -74,6 +88,20 @@ func (mr *MockStakingKeeperMockRecorder) GetLastValidatorPower(ctx, operator int return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLastValidatorPower", reflect.TypeOf((*MockStakingKeeper)(nil).GetLastValidatorPower), ctx, operator) } +// GetLastValidators mocks base method. +func (m *MockStakingKeeper) GetLastValidators(ctx types.Context) []types4.Validator { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetLastValidators", ctx) + ret0, _ := ret[0].([]types4.Validator) + return ret0 +} + +// GetLastValidators indicates an expected call of GetLastValidators. +func (mr *MockStakingKeeperMockRecorder) GetLastValidators(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLastValidators", reflect.TypeOf((*MockStakingKeeper)(nil).GetLastValidators), ctx) +} + // GetValidator mocks base method. func (m *MockStakingKeeper) GetValidator(ctx types.Context, addr types.ValAddress) (types4.Validator, bool) { m.ctrl.T.Helper() @@ -118,6 +146,20 @@ func (mr *MockStakingKeeperMockRecorder) GetValidatorUpdates(ctx interface{}) *g return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValidatorUpdates", reflect.TypeOf((*MockStakingKeeper)(nil).GetValidatorUpdates), ctx) } +// IsValidatorJailed mocks base method. +func (m *MockStakingKeeper) IsValidatorJailed(ctx types.Context, addr types.ConsAddress) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsValidatorJailed", ctx, addr) + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsValidatorJailed indicates an expected call of IsValidatorJailed. +func (mr *MockStakingKeeperMockRecorder) IsValidatorJailed(ctx, addr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsValidatorJailed", reflect.TypeOf((*MockStakingKeeper)(nil).IsValidatorJailed), ctx, addr) +} + // IterateLastValidatorPowers mocks base method. func (m *MockStakingKeeper) IterateLastValidatorPowers(ctx types.Context, cb func(types.ValAddress, int64) bool) { m.ctrl.T.Helper() @@ -130,6 +172,18 @@ func (mr *MockStakingKeeperMockRecorder) IterateLastValidatorPowers(ctx, cb inte return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IterateLastValidatorPowers", reflect.TypeOf((*MockStakingKeeper)(nil).IterateLastValidatorPowers), ctx, cb) } +// IterateValidators mocks base method. +func (m *MockStakingKeeper) IterateValidators(ctx types.Context, f func(int64, types4.ValidatorI) bool) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "IterateValidators", ctx, f) +} + +// IterateValidators indicates an expected call of IterateValidators. +func (mr *MockStakingKeeperMockRecorder) IterateValidators(ctx, f interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IterateValidators", reflect.TypeOf((*MockStakingKeeper)(nil).IterateValidators), ctx, f) +} + // Jail mocks base method. func (m *MockStakingKeeper) Jail(arg0 types.Context, arg1 types.ConsAddress) { m.ctrl.T.Helper() @@ -142,6 +196,20 @@ func (mr *MockStakingKeeperMockRecorder) Jail(arg0, arg1 interface{}) *gomock.Ca return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Jail", reflect.TypeOf((*MockStakingKeeper)(nil).Jail), arg0, arg1) } +// MaxValidators mocks base method. +func (m *MockStakingKeeper) MaxValidators(ctx types.Context) uint32 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "MaxValidators", ctx) + ret0, _ := ret[0].(uint32) + return ret0 +} + +// MaxValidators indicates an expected call of MaxValidators. +func (mr *MockStakingKeeperMockRecorder) MaxValidators(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MaxValidators", reflect.TypeOf((*MockStakingKeeper)(nil).MaxValidators), ctx) +} + // PowerReduction mocks base method. func (m *MockStakingKeeper) PowerReduction(ctx types.Context) types.Int { m.ctrl.T.Helper() @@ -210,6 +278,46 @@ func (mr *MockStakingKeeperMockRecorder) UnbondingTime(ctx interface{}) *gomock. return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnbondingTime", reflect.TypeOf((*MockStakingKeeper)(nil).UnbondingTime), ctx) } +// Unjail mocks base method. +func (m *MockStakingKeeper) Unjail(ctx types.Context, addr types.ConsAddress) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Unjail", ctx, addr) +} + +// Unjail indicates an expected call of Unjail. +func (mr *MockStakingKeeperMockRecorder) Unjail(ctx, addr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Unjail", reflect.TypeOf((*MockStakingKeeper)(nil).Unjail), ctx, addr) +} + +// Validator mocks base method. +func (m *MockStakingKeeper) Validator(ctx types.Context, addr types.ValAddress) types4.ValidatorI { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Validator", ctx, addr) + ret0, _ := ret[0].(types4.ValidatorI) + return ret0 +} + +// Validator indicates an expected call of Validator. +func (mr *MockStakingKeeperMockRecorder) Validator(ctx, addr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Validator", reflect.TypeOf((*MockStakingKeeper)(nil).Validator), ctx, addr) +} + +// ValidatorByConsAddr mocks base method. +func (m *MockStakingKeeper) ValidatorByConsAddr(ctx types.Context, consAddr types.ConsAddress) types4.ValidatorI { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ValidatorByConsAddr", ctx, consAddr) + ret0, _ := ret[0].(types4.ValidatorI) + return ret0 +} + +// ValidatorByConsAddr indicates an expected call of ValidatorByConsAddr. +func (mr *MockStakingKeeperMockRecorder) ValidatorByConsAddr(ctx, consAddr interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidatorByConsAddr", reflect.TypeOf((*MockStakingKeeper)(nil).ValidatorByConsAddr), ctx, consAddr) +} + // MockEvidenceKeeper is a mock of EvidenceKeeper interface. type MockEvidenceKeeper struct { ctrl *gomock.Controller diff --git a/x/ccv/consumer/keeper/changeover.go b/x/ccv/consumer/keeper/changeover.go new file mode 100644 index 0000000000..607a2d47bc --- /dev/null +++ b/x/ccv/consumer/keeper/changeover.go @@ -0,0 +1,40 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + abci "github.com/tendermint/tendermint/abci/types" +) + +// ChangeoverToConsumer includes the logic that needs to execute during the process of a +// standalone to consumer changeover, where the previously standalone chain has +// just been upgraded to include the consumer ccv module, but the provider valset is not +// yet responsible for POS/block production. This method constructs validator updates +// that will be given to tendermint, which allows the consumer chain to +// start using the provider valset, while the standalone valset is given zero voting power where appropriate. +func (k Keeper) ChangeoverToConsumer(ctx sdk.Context) (initialValUpdates []abci.ValidatorUpdate) { + + initialValUpdates = k.GetInitialValSet(ctx) + // set last standalone height + k.SetLastStandaloneHeight(ctx, ctx.BlockHeight()) + // populate cross chain validators states with initial valset + k.ApplyCCValidatorChanges(ctx, initialValUpdates) + + // Add validator updates to initialValUpdates, such that the "old" validators returned from standalone staking module + // are given zero power, and the provider validators are given their full power. + initialUpdatesFlag := make(map[string]bool) + for _, val := range initialValUpdates { + initialUpdatesFlag[val.PubKey.String()] = true + } + for _, val := range k.GetLastStandaloneValidators(ctx) { + zeroPowerUpdate := val.ABCIValidatorUpdateZero() + if !initialUpdatesFlag[zeroPowerUpdate.PubKey.String()] { + initialValUpdates = append(initialValUpdates, zeroPowerUpdate) + } + } + + // Note: this method should only be executed once as a part of the changeover process. + // Therefore we set the PreCCV state to false so the endblocker caller doesn't call this method again. + k.DeletePreCCV(ctx) + + return initialValUpdates +} diff --git a/x/ccv/consumer/keeper/changeover_test.go b/x/ccv/consumer/keeper/changeover_test.go new file mode 100644 index 0000000000..15777f3fa4 --- /dev/null +++ b/x/ccv/consumer/keeper/changeover_test.go @@ -0,0 +1,174 @@ +package keeper_test + +import ( + "testing" + + sdkcryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/cosmos/interchain-security/testutil/crypto" + uthelpers "github.com/cosmos/interchain-security/testutil/keeper" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" +) + +func TestChangeoverToConsumer(t *testing.T) { + + cIds := []crypto.CryptoIdentity{} + for i := 0; i < 10; i++ { + cIds = append(cIds, *crypto.NewCryptoIdentityFromIntSeed(i + 42834729)) + } + + // Instantiate 5 sov validators for use in test + sovVals := []stakingtypes.Validator{ + cIds[0].SDKStakingValidator(), + cIds[1].SDKStakingValidator(), + cIds[2].SDKStakingValidator(), + cIds[3].SDKStakingValidator(), + cIds[4].SDKStakingValidator(), + } + + // Instantiate 5 ics val updates for use in test + initialValUpdates := []abci.ValidatorUpdate{ + {Power: 55, PubKey: cIds[5].TMProtoCryptoPublicKey()}, + {Power: 87324, PubKey: cIds[6].TMProtoCryptoPublicKey()}, + {Power: 2, PubKey: cIds[7].TMProtoCryptoPublicKey()}, + {Power: 42389479, PubKey: cIds[8].TMProtoCryptoPublicKey()}, + {Power: 9089080, PubKey: cIds[9].TMProtoCryptoPublicKey()}, + } + + testCases := []struct { + name string + // Last standalone validators that will be mock returned from stakingKeeper.GetLastValidators() + lastSovVals []stakingtypes.Validator + // Val updates corresponding to initial valset set for ccv set initGenesis + initialValUpdates []abci.ValidatorUpdate + // Expected length of val updates returned from ChangeoverToConsumer() + expectedReturnValUpdatesLen int + }{ + { + name: "no sov vals, no initial val updates", + lastSovVals: []stakingtypes.Validator{}, + initialValUpdates: []abci.ValidatorUpdate{}, + expectedReturnValUpdatesLen: 0, + }, + { + name: "one sov val, no initial val updates", + lastSovVals: []stakingtypes.Validator{sovVals[0]}, + initialValUpdates: []abci.ValidatorUpdate{}, + expectedReturnValUpdatesLen: 1, + }, + { + name: "no sov vals, one initial val update", + lastSovVals: []stakingtypes.Validator{}, + initialValUpdates: []abci.ValidatorUpdate{initialValUpdates[0]}, + expectedReturnValUpdatesLen: 1, + }, + { + name: "one sov val, one initial val update", + lastSovVals: []stakingtypes.Validator{sovVals[0]}, + initialValUpdates: []abci.ValidatorUpdate{initialValUpdates[0]}, + expectedReturnValUpdatesLen: 2, + }, + { + name: "five sov vals, five initial val updates", + lastSovVals: sovVals, + initialValUpdates: initialValUpdates, + expectedReturnValUpdatesLen: 10, + }, + { + name: "validator is contained in both sov val set and initial val updates, using cIds[7]", + lastSovVals: []stakingtypes.Validator{cIds[7].SDKStakingValidator()}, + initialValUpdates: []abci.ValidatorUpdate{ + {Power: 55, PubKey: cIds[7].TMProtoCryptoPublicKey()}, + }, + expectedReturnValUpdatesLen: 1, + }, + } + + for _, tc := range testCases { + + keeperParams := uthelpers.NewInMemKeeperParams(t) + keeperParams.RegisterSdkCryptoCodecInterfaces() + consumerKeeper, ctx, ctrl, mocks := uthelpers.GetConsumerKeeperAndCtx(t, keeperParams) + defer ctrl.Finish() + + // Set PRECCV to true, as would be done in InitGenesis + consumerKeeper.SetPreCCVTrue(ctx) + + // Set initial valset, as would be done in InitGenesis + consumerKeeper.SetInitialValSet(ctx, tc.initialValUpdates) + + // Setup mocked return value for stakingKeeper.GetLastValidators() + gomock.InOrder( + mocks.MockStakingKeeper.EXPECT().GetLastValidators(ctx).Return(tc.lastSovVals), + ) + + // Add ref to standalone staking keeper + consumerKeeper.SetStandaloneStakingKeeper(mocks.MockStakingKeeper) + + returnedInitialValUpdates := consumerKeeper.ChangeoverToConsumer(ctx) + + // PreCCV should now be toggled false + require.False(t, consumerKeeper.IsPreCCV(ctx)) + + // Last standalone height should be set to current block height + require.Equal(t, ctx.BlockHeight(), consumerKeeper.GetLastStandaloneHeight(ctx)) + + // Cross chain validator states should be populated with initial valset + ccVals := consumerKeeper.GetAllCCValidator(ctx) + require.Len(t, ccVals, len(tc.initialValUpdates)) + + // For each initial val update, assert that a corresponding ccVal entry exists + // with the same power and pubkey + for _, valUpdate := range tc.initialValUpdates { + found := false + for _, ccVal := range ccVals { + ccvValPubKey, err := ccVal.ConsPubKey() + require.NoError(t, err) + tmProtoPubKey, err := sdkcryptocodec.ToTmProtoPublicKey(ccvValPubKey) + require.NoError(t, err) + if tmProtoPubKey.Equal(valUpdate.PubKey) { + found = true + require.Equal(t, valUpdate.Power, ccVal.Power) + } + } + require.True(t, found) + } + + // Assert that initial val updates returned from ChangeoverToConsumer are formulated s.t. + // the "old" validators returned from standalone chain's staking module + // are given zero power, and the "new" validators are given their full power. + for _, returnedValUpdate := range returnedInitialValUpdates { + found := false + // Check all initial val updates for a pubkey match + for _, valUpdate := range tc.initialValUpdates { + if returnedValUpdate.PubKey.Equal(valUpdate.PubKey) { + require.Equal(t, valUpdate.Power, returnedValUpdate.Power) + found = true + } + } + // Check all standalone validators for a pubkey match + for _, val := range tc.lastSovVals { + ccvValPubKey, err := val.ConsPubKey() + require.NoError(t, err) + tmProtoPubKey, err := sdkcryptocodec.ToTmProtoPublicKey(ccvValPubKey) + require.NoError(t, err) + if returnedValUpdate.PubKey.Equal(tmProtoPubKey) { + // If val was already matched to a val update for new set, it's power wont be 0 + if found { + continue + } + // Assert power of the val update is zero + require.Equal(t, int64(0), returnedValUpdate.Power) + found = true + } + } + // Assert that a match was found + require.True(t, found) + } + + // Assert no extraneous entries + require.Len(t, returnedInitialValUpdates, tc.expectedReturnValUpdatesLen) + } +} diff --git a/x/ccv/consumer/keeper/genesis.go b/x/ccv/consumer/keeper/genesis.go index e849244999..00797e5810 100644 --- a/x/ccv/consumer/keeper/genesis.go +++ b/x/ccv/consumer/keeper/genesis.go @@ -17,6 +17,18 @@ import ( // 2. A consumer chain restarts after a client to the provider was created, but the CCV channel handshake is still in progress // 3. A consumer chain restarts after the CCV channel handshake was completed. func (k Keeper) InitGenesis(ctx sdk.Context, state *consumertypes.GenesisState) []abci.ValidatorUpdate { + + // PreCCV is true during the process of a standalone to consumer changeover. + // At the PreCCV point in the process, the standalone chain has just been upgraded to include + // the consumer ccv module, but the standalone staking keeper is still managing the validator set. + // Once the provider validator set starts validating blocks, the consumer CCV module + // will take over proof of stake capabilities, but the standalone staking keeper will + // stick around for slashing/jailing purposes. + if state.PreCCV { + k.SetPreCCVTrue(ctx) + k.SetInitialValSet(ctx, state.InitialValSet) + } + k.SetParams(ctx, state.Params) // TODO: Remove enabled flag and find a better way to setup integration tests // See: https://github.com/cosmos/interchain-security/issues/339 @@ -87,7 +99,10 @@ func (k Keeper) InitGenesis(ctx sdk.Context, state *consumertypes.GenesisState) // set provider client id k.SetProviderClientID(ctx, state.ProviderClientId) + } + if state.PreCCV { + return []abci.ValidatorUpdate{} } // populate cross chain validators states with initial valset diff --git a/x/ccv/consumer/keeper/keeper.go b/x/ccv/consumer/keeper/keeper.go index 99dea0aa7b..05c7845758 100644 --- a/x/ccv/consumer/keeper/keeper.go +++ b/x/ccv/consumer/keeper/keeper.go @@ -10,6 +10,7 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" conntypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" @@ -21,26 +22,31 @@ import ( consumertypes "github.com/cosmos/interchain-security/x/ccv/consumer/types" ccv "github.com/cosmos/interchain-security/x/ccv/types" "github.com/cosmos/interchain-security/x/ccv/utils" + tmtypes "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" ) // Keeper defines the Cross-Chain Validation Consumer Keeper type Keeper struct { - storeKey sdk.StoreKey - cdc codec.BinaryCodec - paramStore paramtypes.Subspace - scopedKeeper ccv.ScopedKeeper - channelKeeper ccv.ChannelKeeper - portKeeper ccv.PortKeeper - connectionKeeper ccv.ConnectionKeeper - clientKeeper ccv.ClientKeeper - slashingKeeper ccv.SlashingKeeper - hooks ccv.ConsumerHooks - bankKeeper ccv.BankKeeper - authKeeper ccv.AccountKeeper - ibcTransferKeeper ccv.IBCTransferKeeper - ibcCoreKeeper ccv.IBCCoreKeeper - feeCollectorName string + storeKey sdk.StoreKey + cdc codec.BinaryCodec + paramStore paramtypes.Subspace + scopedKeeper ccv.ScopedKeeper + channelKeeper ccv.ChannelKeeper + portKeeper ccv.PortKeeper + connectionKeeper ccv.ConnectionKeeper + clientKeeper ccv.ClientKeeper + // standaloneStakingKeeper is the staking keeper that managed proof of stake for a previously standalone chain, + // before the chain went through a standalone to consumer changeover. + // This keeper is not used for consumers that launched with ICS, and is therefore set after the constructor. + standaloneStakingKeeper ccv.StakingKeeper + slashingKeeper ccv.SlashingKeeper + hooks ccv.ConsumerHooks + bankKeeper ccv.BankKeeper + authKeeper ccv.AccountKeeper + ibcTransferKeeper ccv.IBCTransferKeeper + ibcCoreKeeper ccv.IBCCoreKeeper + feeCollectorName string } // NewKeeper creates a new Consumer Keeper instance @@ -61,36 +67,43 @@ func NewKeeper( } k := Keeper{ - storeKey: key, - cdc: cdc, - paramStore: paramSpace, - scopedKeeper: scopedKeeper, - channelKeeper: channelKeeper, - portKeeper: portKeeper, - connectionKeeper: connectionKeeper, - clientKeeper: clientKeeper, - slashingKeeper: slashingKeeper, - bankKeeper: bankKeeper, - authKeeper: accountKeeper, - ibcTransferKeeper: ibcTransferKeeper, - ibcCoreKeeper: ibcCoreKeeper, - feeCollectorName: feeCollectorName, + storeKey: key, + cdc: cdc, + paramStore: paramSpace, + scopedKeeper: scopedKeeper, + channelKeeper: channelKeeper, + portKeeper: portKeeper, + connectionKeeper: connectionKeeper, + clientKeeper: clientKeeper, + slashingKeeper: slashingKeeper, + bankKeeper: bankKeeper, + authKeeper: accountKeeper, + ibcTransferKeeper: ibcTransferKeeper, + ibcCoreKeeper: ibcCoreKeeper, + feeCollectorName: feeCollectorName, + standaloneStakingKeeper: nil, } k.mustValidateFields() return k } +func (k *Keeper) SetStandaloneStakingKeeper(sk ccv.StakingKeeper) { + k.standaloneStakingKeeper = sk +} + // Validates that the consumer keeper is initialized with non-zero and // non-nil values for all its fields. Otherwise this method will panic. func (k Keeper) mustValidateFields() { // Ensures no fields are missed in this validation - if reflect.ValueOf(k).NumField() != 15 { - panic("number of fields in provider keeper is not 15") + if reflect.ValueOf(k).NumField() != 16 { + panic("number of fields in consumer keeper is not 16") } - // Note 14 fields will be validated, hooks are explicitly set after the constructor + // Note 14 / 16 fields will be validated, + // hooks are explicitly set after the constructor, + // stakingKeeper is optionally set after the constructor, utils.PanicIfZeroOrNil(k.storeKey, "storeKey") // 1 utils.PanicIfZeroOrNil(k.cdc, "cdc") // 2 @@ -243,6 +256,66 @@ func (k Keeper) DeletePendingChanges(ctx sdk.Context) { store.Delete(types.PendingChangesKey()) } +func (k Keeper) GetLastStandaloneHeight(ctx sdk.Context) int64 { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.LastStandaloneHeightKey()) + if bz == nil { + return 0 + } + height := sdk.BigEndianToUint64(bz) + return int64(height) +} + +func (k Keeper) SetLastStandaloneHeight(ctx sdk.Context, height int64) { + bz := sdk.Uint64ToBigEndian(uint64(height)) + store := ctx.KVStore(k.storeKey) + store.Set(types.LastStandaloneHeightKey(), bz) +} + +func (k Keeper) IsPreCCV(ctx sdk.Context) bool { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.PreCCVKey()) + return bz != nil +} + +func (k Keeper) SetPreCCVTrue(ctx sdk.Context) { + store := ctx.KVStore(k.storeKey) + bz := sdk.Uint64ToBigEndian(uint64(1)) + store.Set(types.PreCCVKey(), bz) +} + +func (k Keeper) DeletePreCCV(ctx sdk.Context) { + store := ctx.KVStore(k.storeKey) + store.Delete(types.PreCCVKey()) +} + +func (k Keeper) SetInitialValSet(ctx sdk.Context, initialValSet []tmtypes.ValidatorUpdate) { + store := ctx.KVStore(k.storeKey) + initialValSetState := types.GenesisState{ + InitialValSet: initialValSet, + } + bz := k.cdc.MustMarshal(&initialValSetState) + store.Set(types.InitialValSetKey(), bz) +} + +func (k Keeper) GetInitialValSet(ctx sdk.Context) []tmtypes.ValidatorUpdate { + store := ctx.KVStore(k.storeKey) + initialValSet := types.GenesisState{} + bz := store.Get(types.InitialValSetKey()) + if bz != nil { + k.cdc.MustUnmarshal(bz, &initialValSet) + return initialValSet.InitialValSet + } + return []tmtypes.ValidatorUpdate{} +} + +func (k Keeper) GetLastStandaloneValidators(ctx sdk.Context) []stakingtypes.Validator { + if !k.IsPreCCV(ctx) || k.standaloneStakingKeeper == nil { + panic("cannot get last standalone validators if not in pre-ccv state, or if standalone staking keeper is nil") + } + return k.standaloneStakingKeeper.GetLastValidators(ctx) +} + // GetElapsedPacketMaturityTimes returns a slice of already elapsed PacketMaturityTimes, sorted by maturity times, // i.e., the slice contains the IDs of the matured VSCPackets. func (k Keeper) GetElapsedPacketMaturityTimes(ctx sdk.Context) (maturingVSCPackets []consumertypes.MaturingVSCPacket) { diff --git a/x/ccv/consumer/keeper/keeper_test.go b/x/ccv/consumer/keeper/keeper_test.go index b544b3b3ed..e510ca9960 100644 --- a/x/ccv/consumer/keeper/keeper_test.go +++ b/x/ccv/consumer/keeper/keeper_test.go @@ -10,6 +10,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" conntypes "github.com/cosmos/ibc-go/v4/modules/core/03-connection/types" + "github.com/cosmos/interchain-security/testutil/crypto" testkeeper "github.com/cosmos/interchain-security/testutil/keeper" "github.com/cosmos/interchain-security/x/ccv/consumer/types" ccv "github.com/cosmos/interchain-security/x/ccv/types" @@ -83,6 +84,118 @@ func TestPendingChanges(t *testing.T) { require.Nil(t, gotPd, "got non-nil pending changes after Delete") } +// TestLastSovereignHeight tests the getter and setter for the last standalone height +func TestLastSovereignHeight(t *testing.T) { + consumerKeeper, ctx, ctrl, _ := testkeeper.GetConsumerKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + // Default value should be 0 without any setter + require.Equal(t, int64(0), consumerKeeper.GetLastStandaloneHeight(ctx)) + + // Set/get the last standalone height being 10 + consumerKeeper.SetLastStandaloneHeight(ctx, 10) + require.Equal(t, int64(10), consumerKeeper.GetLastStandaloneHeight(ctx)) + + // Set/get the last standalone height being 43234426 + consumerKeeper.SetLastStandaloneHeight(ctx, 43234426) + require.Equal(t, int64(43234426), consumerKeeper.GetLastStandaloneHeight(ctx)) +} + +// TestPreCCV tests the getter, setter and deletion methods for the pre-CCV state flag +func TestPreCCV(t *testing.T) { + consumerKeeper, ctx, ctrl, _ := testkeeper.GetConsumerKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + // Default value is false without any setter + require.False(t, consumerKeeper.IsPreCCV(ctx)) + + // Set/get the pre-CCV state to true + consumerKeeper.SetPreCCVTrue(ctx) + require.True(t, consumerKeeper.IsPreCCV(ctx)) + + // Delete the pre-CCV state, setting it to false + consumerKeeper.DeletePreCCV(ctx) + require.False(t, consumerKeeper.IsPreCCV(ctx)) +} + +// TestInitialValSet tests the getter and setter methods for storing the initial validator set for a consumer +func TestInitialValSet(t *testing.T) { + consumerKeeper, ctx, ctrl, _ := testkeeper.GetConsumerKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + // Default value is empty val update list + require.Empty(t, consumerKeeper.GetInitialValSet(ctx)) + + // Set/get the initial validator set + cId1 := crypto.NewCryptoIdentityFromIntSeed(7896) + cId2 := crypto.NewCryptoIdentityFromIntSeed(7897) + cId3 := crypto.NewCryptoIdentityFromIntSeed(7898) + valUpdates := []abci.ValidatorUpdate{ + { + PubKey: cId1.TMProtoCryptoPublicKey(), + Power: 1097, + }, + { + PubKey: cId2.TMProtoCryptoPublicKey(), + Power: 19068, + }, + { + PubKey: cId3.TMProtoCryptoPublicKey(), + Power: 10978554, + }, + } + + consumerKeeper.SetInitialValSet(ctx, valUpdates) + require.Equal(t, []abci.ValidatorUpdate{ + { + PubKey: cId1.TMProtoCryptoPublicKey(), + Power: 1097, + }, + { + PubKey: cId2.TMProtoCryptoPublicKey(), + Power: 19068, + }, + { + PubKey: cId3.TMProtoCryptoPublicKey(), + Power: 10978554, + }, + }, consumerKeeper.GetInitialValSet(ctx)) + +} + +// TestGetLastSovereignValidators tests the getter method for getting the last valset +// from the standalone staking keeper +func TestGetLastSovereignValidators(t *testing.T) { + ck, ctx, ctrl, mocks := testkeeper.GetConsumerKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + // Should panic if pre-CCV is true but staking keeper is not set + ck.SetPreCCVTrue(ctx) + require.Panics(t, func() { ck.GetLastStandaloneValidators(ctx) }) + + // Should panic if staking keeper is set but pre-CCV is false + ck.SetStandaloneStakingKeeper(mocks.MockStakingKeeper) + ck.DeletePreCCV(ctx) + require.False(t, ck.IsPreCCV(ctx)) + require.Panics(t, func() { ck.GetLastStandaloneValidators(ctx) }) + + // Set the pre-CCV state to true and get the last standalone validators from mock + ck.SetPreCCVTrue(ctx) + require.True(t, ck.IsPreCCV(ctx)) + cId1 := crypto.NewCryptoIdentityFromIntSeed(11) + val := cId1.SDKStakingValidator() + val.Description.Moniker = "sanity check this is the correctly serialized val" + gomock.InOrder( + mocks.MockStakingKeeper.EXPECT().GetLastValidators(ctx).Return([]stakingtypes.Validator{ + val, + }), + ) + lastSovVals := ck.GetLastStandaloneValidators(ctx) + require.Equal(t, []stakingtypes.Validator{val}, lastSovVals) + require.Equal(t, "sanity check this is the correctly serialized val", + lastSovVals[0].Description.Moniker) +} + // TestPacketMaturityTime tests getter, setter, and iterator functionality for the packet maturity time of a received VSC packet func TestPacketMaturityTime(t *testing.T) { ck, ctx, ctrl, _ := testkeeper.GetConsumerKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) diff --git a/x/ccv/consumer/keeper/validators.go b/x/ccv/consumer/keeper/validators.go index debb4c66ce..ef0c6a0c77 100644 --- a/x/ccv/consumer/keeper/validators.go +++ b/x/ccv/consumer/keeper/validators.go @@ -11,6 +11,15 @@ import ( abci "github.com/tendermint/tendermint/abci/types" ) +// +// TODO: Address https://github.com/cosmos/interchain-security/issues/781 in this file. +// Particularly, we need to better define which keepers are responsible for slashing capabilities +// during/after a standalone to consumer changeover. +// +// TODO: make unit tests for all of: MVP consumer, democ consumer, and pre-ccv consumer +// for previously unimplemented methods, if they're implemented to solve the above issue. +// + // ApplyCCValidatorChanges applies the given changes to the cross-chain validators states // and returns updates to forward to tendermint. func (k Keeper) ApplyCCValidatorChanges(ctx sdk.Context, changes []abci.ValidatorUpdate) []abci.ValidatorUpdate { diff --git a/x/ccv/consumer/module.go b/x/ccv/consumer/module.go index 68a2d60c1c..56c84ae112 100644 --- a/x/ccv/consumer/module.go +++ b/x/ccv/consumer/module.go @@ -149,6 +149,7 @@ func (AppModule) ConsensusVersion() uint64 { return 1 } // Set the VSC ID for the subsequent block to the same value as the current block // Panic if the provider's channel was established and then closed func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { + channelID, found := am.keeper.GetProviderChannel(ctx) if found && am.keeper.IsChannelClosed(ctx, channelID) { // The CCV channel was established, but it was then closed; @@ -171,7 +172,17 @@ func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { // EndBlock implements the AppModule interface // Flush PendingChanges to ABCI, send pending packets, write acknowledgements for packets that have finished unbonding. +// +// TODO: e2e tests confirming behavior with and without standalone -> consumer changeover func (am AppModule) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.ValidatorUpdate { + + // If PreCCV state is active, consumer is a previously standalone chain + // that was just upgraded to include the consumer ccv module, execute changeover logic. + if am.keeper.IsPreCCV(ctx) { + initialValUpdates := am.keeper.ChangeoverToConsumer(ctx) + return initialValUpdates + } + // Execute EndBlock logic for the Reward Distribution sub-protocol am.keeper.EndBlockRD(ctx) diff --git a/x/ccv/consumer/types/genesis.pb.go b/x/ccv/consumer/types/genesis.pb.go index d55baa92ad..47253f25a1 100644 --- a/x/ccv/consumer/types/genesis.pb.go +++ b/x/ccv/consumer/types/genesis.pb.go @@ -50,6 +50,7 @@ type GenesisState struct { PendingConsumerPackets types2.ConsumerPacketDataList `protobuf:"bytes,11,opt,name=pending_consumer_packets,json=pendingConsumerPackets,proto3" json:"pending_consumer_packets"` // LastTransmissionBlockHeight nil on new chain, filled in on restart. LastTransmissionBlockHeight LastTransmissionBlockHeight `protobuf:"bytes,12,opt,name=last_transmission_block_height,json=lastTransmissionBlockHeight,proto3" json:"last_transmission_block_height"` + PreCCV bool `protobuf:"varint,13,opt,name=preCCV,proto3" json:"preCCV,omitempty"` } func (m *GenesisState) Reset() { *m = GenesisState{} } @@ -169,6 +170,13 @@ func (m *GenesisState) GetLastTransmissionBlockHeight() LastTransmissionBlockHei return LastTransmissionBlockHeight{} } +func (m *GenesisState) GetPreCCV() bool { + if m != nil { + return m.PreCCV + } + return false +} + // HeightValsetUpdateID defines the genesis information for the mapping // of each block height to a valset update id type HeightToValsetUpdateID struct { @@ -280,56 +288,56 @@ func init() { } var fileDescriptor_2db73a6057a27482 = []byte{ - // 773 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x55, 0xcf, 0x6f, 0xdb, 0x36, - 0x14, 0xb6, 0x92, 0xcc, 0xb3, 0x99, 0x6c, 0xc9, 0x98, 0xcd, 0xd0, 0x6c, 0x4c, 0xf3, 0xb2, 0x1d, - 0x0c, 0x6c, 0x93, 0x60, 0x0f, 0x18, 0x86, 0x15, 0x28, 0xda, 0x24, 0x40, 0x6b, 0x20, 0x6d, 0x03, - 0x3b, 0xf1, 0x21, 0x17, 0x81, 0xa6, 0x58, 0x89, 0x88, 0x44, 0x1a, 0x22, 0xa5, 0x34, 0x87, 0x5e, - 0x7a, 0xed, 0xa5, 0x7f, 0x56, 0x8e, 0x39, 0xf6, 0x14, 0x14, 0xc9, 0x3f, 0x52, 0x88, 0xa4, 0xfc, - 0xa3, 0x71, 0x50, 0x9f, 0x4c, 0xf2, 0x7d, 0xef, 0xfb, 0xde, 0x7b, 0x1f, 0x4d, 0x81, 0x2e, 0x65, - 0x92, 0xa4, 0x38, 0x42, 0x94, 0xf9, 0x82, 0xe0, 0x2c, 0xa5, 0xf2, 0xd2, 0xc3, 0x38, 0xf7, 0x30, - 0x67, 0x22, 0x4b, 0x48, 0xea, 0xe5, 0x5d, 0x2f, 0x24, 0x8c, 0x08, 0x2a, 0xdc, 0x49, 0xca, 0x25, - 0x87, 0xbf, 0x2f, 0x49, 0x71, 0x31, 0xce, 0xdd, 0x32, 0xc5, 0xcd, 0xbb, 0xcd, 0x3f, 0x1e, 0xe2, - 0xcd, 0xbb, 0xc5, 0x8f, 0xa6, 0x6a, 0xf6, 0x56, 0x51, 0x9f, 0xd2, 0xea, 0x9c, 0x96, 0x24, 0x2c, - 0x20, 0x69, 0x42, 0x99, 0xf4, 0xd0, 0x18, 0x53, 0x4f, 0x5e, 0x4e, 0x88, 0xa9, 0xad, 0xe9, 0xd1, - 0x31, 0xf6, 0x62, 0x1a, 0x46, 0x12, 0xc7, 0x94, 0x30, 0x29, 0xbc, 0x39, 0x74, 0xde, 0x9d, 0xdb, - 0x99, 0x84, 0xdf, 0x8a, 0x04, 0xcc, 0x53, 0xe2, 0xe1, 0x08, 0x31, 0x46, 0x62, 0xa5, 0xa8, 0x97, - 0x06, 0xe2, 0x84, 0x9c, 0x87, 0x31, 0xf1, 0xd4, 0x6e, 0x9c, 0xbd, 0xf6, 0x82, 0x2c, 0x45, 0x92, - 0x72, 0x66, 0xe2, 0x3f, 0x86, 0x3c, 0xe4, 0x6a, 0xe9, 0x15, 0x2b, 0x7d, 0xba, 0x77, 0x53, 0x03, - 0x5b, 0xcf, 0xf4, 0xdc, 0x86, 0x12, 0x49, 0x02, 0xfb, 0xa0, 0x3a, 0x41, 0x29, 0x4a, 0x84, 0x6d, - 0xb5, 0xad, 0xce, 0x66, 0xef, 0x4f, 0x77, 0x85, 0x39, 0xba, 0xc7, 0x2a, 0x65, 0x7f, 0xe3, 0xea, - 0xe6, 0xd7, 0xca, 0xc0, 0x10, 0xc0, 0xbf, 0x00, 0x9c, 0xa4, 0x3c, 0xa7, 0x01, 0x49, 0x7d, 0xdd, - 0xa7, 0x4f, 0x03, 0x7b, 0xad, 0x6d, 0x75, 0xea, 0x83, 0x9d, 0x32, 0x72, 0xa0, 0x02, 0xfd, 0x00, - 0xba, 0x60, 0x77, 0x86, 0xd6, 0x9d, 0x15, 0xf0, 0x75, 0x05, 0xff, 0x61, 0x0a, 0xd7, 0x91, 0x7e, - 0x00, 0x5b, 0xa0, 0xce, 0xc8, 0x85, 0xaf, 0x0a, 0xb3, 0x37, 0xda, 0x56, 0xa7, 0x36, 0xa8, 0x31, - 0x72, 0x71, 0x50, 0xec, 0xa1, 0x0f, 0x7e, 0xfa, 0x52, 0x5a, 0x14, 0xed, 0xd9, 0xdf, 0x94, 0x4d, - 0x8d, 0xb1, 0x3b, 0x6f, 0x80, 0x3b, 0x37, 0xf2, 0xbc, 0xeb, 0xea, 0xaa, 0xd4, 0x44, 0x06, 0xbb, - 0x8b, 0xa5, 0xea, 0x31, 0x45, 0xc0, 0x9e, 0x09, 0x70, 0x26, 0x08, 0x13, 0x99, 0x30, 0x1a, 0x55, - 0xa5, 0xe1, 0x7e, 0x55, 0xa3, 0x4c, 0xd3, 0x32, 0x8d, 0xa9, 0xcc, 0xc2, 0x39, 0x0c, 0xc1, 0x4e, - 0x82, 0x64, 0x96, 0x52, 0x16, 0xfa, 0x13, 0x84, 0xcf, 0x89, 0x14, 0xf6, 0xb7, 0xed, 0xf5, 0xce, - 0x66, 0xef, 0xdf, 0x95, 0xac, 0x79, 0x61, 0x92, 0x47, 0xc3, 0x83, 0x63, 0x95, 0x6e, 0x5c, 0xda, - 0x2e, 0x59, 0xf5, 0xa9, 0x80, 0x2f, 0xc1, 0x36, 0x65, 0x54, 0x52, 0x14, 0xfb, 0x39, 0x8a, 0x7d, - 0x41, 0xa4, 0x5d, 0x53, 0x3a, 0xed, 0xf9, 0xc2, 0x8b, 0xbb, 0xec, 0x8e, 0x50, 0x4c, 0x03, 0x24, - 0x79, 0x7a, 0x3a, 0x09, 0x90, 0x24, 0x86, 0xf1, 0x3b, 0x93, 0x3e, 0x42, 0xf1, 0x90, 0x48, 0xf8, - 0x16, 0x34, 0x23, 0x52, 0xb4, 0xef, 0x4b, 0x5e, 0x30, 0x0a, 0x22, 0xfd, 0x4c, 0xe1, 0x0b, 0x5f, - 0xeb, 0x8a, 0xfa, 0xd1, 0x4a, 0x2d, 0x3c, 0x57, 0x34, 0x27, 0x7c, 0xa4, 0x48, 0xb4, 0x66, 0xff, - 0xd0, 0xa8, 0x36, 0xa2, 0x65, 0xd1, 0x00, 0xbe, 0xb3, 0xc0, 0x2f, 0x3c, 0x93, 0x42, 0x22, 0x16, - 0x14, 0xb3, 0x0b, 0xf8, 0x05, 0x93, 0x34, 0x21, 0xbe, 0x88, 0x91, 0x88, 0x28, 0x0b, 0x6d, 0xa0, - 0x4a, 0xf8, 0x6f, 0xa5, 0x12, 0x5e, 0xcd, 0x98, 0x0e, 0x0d, 0x91, 0xd1, 0x6f, 0xf1, 0xfb, 0xa1, - 0xa1, 0x91, 0x80, 0x29, 0xb0, 0x27, 0x44, 0xeb, 0x97, 0x6c, 0x53, 0x13, 0x37, 0xd5, 0x35, 0xe9, - 0x3d, 0x28, 0x6f, 0xae, 0x48, 0x91, 0xa3, 0x2d, 0x3a, 0x44, 0x12, 0x1d, 0x51, 0x51, 0x1a, 0xd8, - 0x30, 0xcc, 0x8b, 0x20, 0x01, 0xdf, 0x5b, 0xc0, 0x89, 0x91, 0x90, 0xbe, 0x4c, 0x11, 0x13, 0x09, - 0x15, 0x82, 0x72, 0xe6, 0x8f, 0x63, 0x8e, 0xcf, 0x7d, 0x3d, 0x2b, 0x7b, 0x4b, 0x49, 0x3f, 0x59, - 0xa9, 0xf3, 0x23, 0x24, 0xe4, 0xc9, 0x1c, 0xd3, 0x7e, 0x41, 0xa4, 0x1d, 0x29, 0x27, 0x10, 0x3f, - 0x0c, 0xd9, 0x3b, 0x03, 0x8d, 0xe5, 0xf6, 0xc1, 0x06, 0xa8, 0x9a, 0x72, 0x8a, 0x97, 0x66, 0x63, - 0x60, 0x76, 0xb0, 0x03, 0x76, 0xee, 0xdd, 0x96, 0x35, 0x85, 0xf8, 0x3e, 0x5f, 0xb0, 0x78, 0xef, - 0x14, 0xec, 0x2e, 0xf1, 0x05, 0x3e, 0x06, 0xad, 0xbc, 0xbc, 0xa0, 0x73, 0x7f, 0x4e, 0x14, 0x04, - 0x29, 0x11, 0xfa, 0x5d, 0xab, 0x0f, 0x7e, 0x9e, 0x42, 0xa6, 0xff, 0xb7, 0xa7, 0x1a, 0xb0, 0x7f, - 0x72, 0x75, 0xeb, 0x58, 0xd7, 0xb7, 0x8e, 0xf5, 0xe9, 0xd6, 0xb1, 0x3e, 0xdc, 0x39, 0x95, 0xeb, - 0x3b, 0xa7, 0xf2, 0xf1, 0xce, 0xa9, 0x9c, 0xfd, 0x1f, 0x52, 0x19, 0x65, 0x63, 0x17, 0xf3, 0xc4, - 0xc3, 0x5c, 0x24, 0x5c, 0x78, 0xb3, 0x11, 0xfe, 0x3d, 0xfd, 0x34, 0xbc, 0x59, 0xfc, 0x38, 0xa8, - 0x97, 0x7f, 0x5c, 0x55, 0x0f, 0xee, 0x3f, 0x9f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xb4, 0x5a, 0x50, - 0x50, 0xcb, 0x06, 0x00, 0x00, + // 782 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x55, 0xcf, 0x6f, 0xeb, 0x34, + 0x1c, 0x6f, 0xde, 0x1b, 0xa5, 0xf5, 0xde, 0xe3, 0x0d, 0x0f, 0xaa, 0xd0, 0x8a, 0x50, 0x06, 0x87, + 0x4a, 0x40, 0xa2, 0x16, 0x09, 0x21, 0x90, 0x10, 0xac, 0x93, 0xa0, 0xd2, 0x80, 0xa9, 0xdd, 0x7a, + 0xd8, 0x25, 0x72, 0x1d, 0x93, 0x58, 0x4b, 0xec, 0xc8, 0x76, 0x32, 0x76, 0xe0, 0xc2, 0x95, 0x0b, + 0x7f, 0xd6, 0x8e, 0x3b, 0x72, 0x42, 0x68, 0xfb, 0x1f, 0x38, 0xa3, 0xd8, 0x4e, 0x7f, 0xb0, 0x4e, + 0xaf, 0xa7, 0xc4, 0xf9, 0x7e, 0x7e, 0x7c, 0x7f, 0x38, 0x36, 0x18, 0x52, 0xa6, 0x88, 0xc0, 0x09, + 0xa2, 0x2c, 0x94, 0x04, 0x17, 0x82, 0xaa, 0x9b, 0x00, 0xe3, 0x32, 0xc0, 0x9c, 0xc9, 0x22, 0x23, + 0x22, 0x28, 0x87, 0x41, 0x4c, 0x18, 0x91, 0x54, 0xfa, 0xb9, 0xe0, 0x8a, 0xc3, 0x8f, 0xb6, 0x50, + 0x7c, 0x8c, 0x4b, 0xbf, 0xa6, 0xf8, 0xe5, 0xb0, 0xfb, 0xf1, 0x53, 0xba, 0xe5, 0xb0, 0x7a, 0x18, + 0xa9, 0xee, 0x68, 0x17, 0xf7, 0xa5, 0xac, 0xe1, 0xf4, 0x14, 0x61, 0x11, 0x11, 0x19, 0x65, 0x2a, + 0x40, 0x0b, 0x4c, 0x03, 0x75, 0x93, 0x13, 0x9b, 0x5b, 0x37, 0xa0, 0x0b, 0x1c, 0xa4, 0x34, 0x4e, + 0x14, 0x4e, 0x29, 0x61, 0x4a, 0x06, 0x6b, 0xe8, 0x72, 0xb8, 0xb6, 0xb2, 0x84, 0x0f, 0x2b, 0x02, + 0xe6, 0x82, 0x04, 0x38, 0x41, 0x8c, 0x91, 0x54, 0x3b, 0x9a, 0x57, 0x0b, 0xf1, 0x62, 0xce, 0xe3, + 0x94, 0x04, 0x7a, 0xb5, 0x28, 0x7e, 0x09, 0xa2, 0x42, 0x20, 0x45, 0x39, 0xb3, 0xf1, 0x77, 0x62, + 0x1e, 0x73, 0xfd, 0x1a, 0x54, 0x6f, 0xe6, 0xeb, 0xd1, 0xbf, 0x2d, 0xf0, 0xe2, 0x7b, 0xd3, 0xb7, + 0x99, 0x42, 0x8a, 0xc0, 0x09, 0x68, 0xe6, 0x48, 0xa0, 0x4c, 0xba, 0x4e, 0xdf, 0x19, 0xec, 0x8f, + 0x3e, 0xf1, 0x77, 0xe8, 0xa3, 0x7f, 0xa6, 0x29, 0xc7, 0x7b, 0xb7, 0x7f, 0x7f, 0xd0, 0x98, 0x5a, + 0x01, 0xf8, 0x29, 0x80, 0xb9, 0xe0, 0x25, 0x8d, 0x88, 0x08, 0x4d, 0x9d, 0x21, 0x8d, 0xdc, 0x67, + 0x7d, 0x67, 0xd0, 0x9e, 0x1e, 0xd4, 0x91, 0xb1, 0x0e, 0x4c, 0x22, 0xe8, 0x83, 0xc3, 0x15, 0xda, + 0x54, 0x56, 0xc1, 0x9f, 0x6b, 0xf8, 0xdb, 0x4b, 0xb8, 0x89, 0x4c, 0x22, 0xd8, 0x03, 0x6d, 0x46, + 0xae, 0x43, 0x9d, 0x98, 0xbb, 0xd7, 0x77, 0x06, 0xad, 0x69, 0x8b, 0x91, 0xeb, 0x71, 0xb5, 0x86, + 0x21, 0x78, 0xf7, 0xff, 0xd6, 0xb2, 0x2a, 0xcf, 0x7d, 0xa3, 0x2e, 0x6a, 0x81, 0xfd, 0xf5, 0x01, + 0xf8, 0x6b, 0x2d, 0x2f, 0x87, 0xbe, 0xc9, 0x4a, 0x77, 0x64, 0x7a, 0xb8, 0x99, 0xaa, 0x69, 0x53, + 0x02, 0xdc, 0x95, 0x01, 0x67, 0x92, 0x30, 0x59, 0x48, 0xeb, 0xd1, 0xd4, 0x1e, 0xfe, 0x6b, 0x3d, + 0x6a, 0x9a, 0xb1, 0xe9, 0x2c, 0x6d, 0x36, 0xbe, 0xc3, 0x18, 0x1c, 0x64, 0x48, 0x15, 0x82, 0xb2, + 0x38, 0xcc, 0x11, 0xbe, 0x22, 0x4a, 0xba, 0x6f, 0xf6, 0x9f, 0x0f, 0xf6, 0x47, 0x5f, 0xec, 0x34, + 0x9a, 0x1f, 0x2d, 0x79, 0x3e, 0x1b, 0x9f, 0x69, 0xba, 0x9d, 0xd2, 0xab, 0x5a, 0xd5, 0x7c, 0x95, + 0xf0, 0x27, 0xf0, 0x8a, 0x32, 0xaa, 0x28, 0x4a, 0xc3, 0x12, 0xa5, 0xa1, 0x24, 0xca, 0x6d, 0x69, + 0x9f, 0xfe, 0x7a, 0xe2, 0xd5, 0x5e, 0xf6, 0xe7, 0x28, 0xa5, 0x11, 0x52, 0x5c, 0x5c, 0xe4, 0x11, + 0x52, 0xc4, 0x2a, 0xbe, 0xb4, 0xf4, 0x39, 0x4a, 0x67, 0x44, 0xc1, 0xdf, 0x40, 0x37, 0x21, 0x55, + 0xf9, 0xa1, 0xe2, 0x95, 0xa2, 0x24, 0x2a, 0x2c, 0x34, 0xbe, 0x9a, 0x6b, 0x5b, 0x4b, 0x7f, 0xbd, + 0x53, 0x09, 0x3f, 0x68, 0x99, 0x73, 0x3e, 0xd7, 0x22, 0xc6, 0x73, 0x72, 0x62, 0x5d, 0x3b, 0xc9, + 0xb6, 0x68, 0x04, 0x7f, 0x77, 0xc0, 0xfb, 0xbc, 0x50, 0x52, 0x21, 0x16, 0x55, 0xbd, 0x8b, 0xf8, + 0x35, 0x53, 0x34, 0x23, 0xa1, 0x4c, 0x91, 0x4c, 0x28, 0x8b, 0x5d, 0xa0, 0x53, 0xf8, 0x72, 0xa7, + 0x14, 0x7e, 0x5e, 0x29, 0x9d, 0x58, 0x21, 0xeb, 0xdf, 0xe3, 0x8f, 0x43, 0x33, 0x6b, 0x01, 0x05, + 0x70, 0x73, 0x62, 0xfc, 0x6b, 0xb5, 0xe5, 0x10, 0xf7, 0xf5, 0x36, 0x19, 0x3d, 0x69, 0x6f, 0xb7, + 0x48, 0xc5, 0x31, 0x23, 0x3a, 0x41, 0x0a, 0x9d, 0x52, 0x59, 0x0f, 0xb0, 0x63, 0x95, 0x37, 0x41, + 0x12, 0xfe, 0xe1, 0x00, 0x2f, 0x45, 0x52, 0x85, 0x4a, 0x20, 0x26, 0x33, 0x2a, 0x25, 0xe5, 0x2c, + 0x5c, 0xa4, 0x1c, 0x5f, 0x85, 0xa6, 0x57, 0xee, 0x0b, 0x6d, 0xfd, 0xed, 0x4e, 0x95, 0x9f, 0x22, + 0xa9, 0xce, 0xd7, 0x94, 0x8e, 0x2b, 0x21, 0x33, 0x91, 0xba, 0x03, 0xe9, 0xd3, 0x10, 0xd8, 0x01, + 0xcd, 0x5c, 0x90, 0xf1, 0x78, 0xee, 0xbe, 0xd4, 0xff, 0xa8, 0x5d, 0x1d, 0x5d, 0x82, 0xce, 0xf6, + 0xb1, 0x56, 0x0c, 0x9b, 0x66, 0x75, 0x02, 0xed, 0x4d, 0xed, 0x0a, 0x0e, 0xc0, 0xc1, 0xa3, 0x5d, + 0xf4, 0x4c, 0x23, 0xde, 0x2a, 0x37, 0x46, 0x7f, 0x74, 0x01, 0x0e, 0xb7, 0xcc, 0x0b, 0x7e, 0x03, + 0x7a, 0x65, 0xbd, 0x71, 0xd7, 0x7e, 0x5a, 0x14, 0x45, 0x82, 0x48, 0x73, 0xde, 0xb5, 0xa7, 0xef, + 0x2d, 0x21, 0xcb, 0xff, 0xf0, 0x3b, 0x03, 0x38, 0x3e, 0xbf, 0xbd, 0xf7, 0x9c, 0xbb, 0x7b, 0xcf, + 0xf9, 0xe7, 0xde, 0x73, 0xfe, 0x7c, 0xf0, 0x1a, 0x77, 0x0f, 0x5e, 0xe3, 0xaf, 0x07, 0xaf, 0x71, + 0xf9, 0x55, 0x4c, 0x55, 0x52, 0x2c, 0x7c, 0xcc, 0xb3, 0x00, 0x73, 0x99, 0x71, 0x19, 0xac, 0x5a, + 0xfb, 0xd9, 0xf2, 0xca, 0xf8, 0x75, 0xf3, 0xd2, 0xd0, 0x37, 0xc2, 0xa2, 0xa9, 0x0f, 0xe2, 0xcf, + 0xff, 0x0b, 0x00, 0x00, 0xff, 0xff, 0xab, 0x08, 0x12, 0x9c, 0xe3, 0x06, 0x00, 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { @@ -352,6 +360,16 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.PreCCV { + i-- + if m.PreCCV { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x68 + } { size, err := m.LastTransmissionBlockHeight.MarshalToSizedBuffer(dAtA[:i]) if err != nil { @@ -618,6 +636,9 @@ func (m *GenesisState) Size() (n int) { n += 1 + l + sovGenesis(uint64(l)) l = m.LastTransmissionBlockHeight.Size() n += 1 + l + sovGenesis(uint64(l)) + if m.PreCCV { + n += 2 + } return n } @@ -1075,6 +1096,26 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 13: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PreCCV", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.PreCCV = bool(v != 0) default: iNdEx = preIndex skippy, err := skipGenesis(dAtA[iNdEx:]) diff --git a/x/ccv/consumer/types/genesis_test.go b/x/ccv/consumer/types/genesis_test.go index a0046a5387..ebac47eafc 100644 --- a/x/ccv/consumer/types/genesis_test.go +++ b/x/ccv/consumer/types/genesis_test.go @@ -100,6 +100,7 @@ func TestValidateInitialGenesisState(t *testing.T) { nil, ccv.ConsumerPacketDataList{}, types.LastTransmissionBlockHeight{}, + false, }, true, }, @@ -118,6 +119,7 @@ func TestValidateInitialGenesisState(t *testing.T) { nil, ccv.ConsumerPacketDataList{}, types.LastTransmissionBlockHeight{}, + false, }, true, }, @@ -136,6 +138,7 @@ func TestValidateInitialGenesisState(t *testing.T) { nil, ccv.ConsumerPacketDataList{}, types.LastTransmissionBlockHeight{}, + false, }, true, }, @@ -154,6 +157,7 @@ func TestValidateInitialGenesisState(t *testing.T) { nil, ccv.ConsumerPacketDataList{}, types.LastTransmissionBlockHeight{Height: 1}, + false, }, true, }, @@ -172,6 +176,7 @@ func TestValidateInitialGenesisState(t *testing.T) { nil, ccv.ConsumerPacketDataList{List: []ccv.ConsumerPacketData{{}}}, types.LastTransmissionBlockHeight{}, + false, }, true, }, @@ -336,6 +341,7 @@ func TestValidateRestartGenesisState(t *testing.T) { nil, ccv.ConsumerPacketDataList{}, types.LastTransmissionBlockHeight{}, + false, }, true, }, @@ -354,6 +360,7 @@ func TestValidateRestartGenesisState(t *testing.T) { nil, ccv.ConsumerPacketDataList{}, types.LastTransmissionBlockHeight{}, + false, }, true, }, diff --git a/x/ccv/consumer/types/keys.go b/x/ccv/consumer/types/keys.go index ccebfba456..2ecb444d52 100644 --- a/x/ccv/consumer/types/keys.go +++ b/x/ccv/consumer/types/keys.go @@ -56,6 +56,15 @@ const ( // because the client is expired PendingDataPacketsByteKey + // PreCCVByteKey is the byte to store the consumer is running on democracy staking module without consumer + PreCCVByteKey + + // InitialValSetByteKey is the byte to store the initial validator set for a consumer + InitialValSetByteKey + + // LastStandaloneHeightByteKey is the byte that will store last standalone height + LastStandaloneHeightByteKey + // HistoricalInfoKey is the byte prefix that will store the historical info for a given height HistoricalInfoBytePrefix @@ -115,6 +124,18 @@ func PendingDataPacketsKey() []byte { return []byte{PendingDataPacketsByteKey} } +func PreCCVKey() []byte { + return []byte{PreCCVByteKey} +} + +func InitialValSetKey() []byte { + return []byte{InitialValSetByteKey} +} + +func LastStandaloneHeightKey() []byte { + return []byte{LastStandaloneHeightByteKey} +} + // HistoricalInfoKey returns the key to historical info to a given block height func HistoricalInfoKey(height int64) []byte { hBytes := make([]byte, 8) diff --git a/x/ccv/consumer/types/keys_test.go b/x/ccv/consumer/types/keys_test.go index 571c7ed3bf..05375f94ac 100644 --- a/x/ccv/consumer/types/keys_test.go +++ b/x/ccv/consumer/types/keys_test.go @@ -30,6 +30,9 @@ func getAllKeyPrefixes() []byte { ProviderChannelByteKey, PendingChangesByteKey, PendingDataPacketsByteKey, + PreCCVByteKey, + InitialValSetByteKey, + LastStandaloneHeightByteKey, HistoricalInfoBytePrefix, PacketMaturityTimeBytePrefix, HeightValsetUpdateIDBytePrefix, @@ -58,6 +61,9 @@ func getAllFullyDefinedKeys() [][]byte { ProviderChannelKey(), PendingChangesKey(), PendingDataPacketsKey(), + PreCCVKey(), + InitialValSetKey(), + LastStandaloneHeightKey(), HistoricalInfoKey(0), PacketMaturityTimeKey(0, time.Time{}), HeightValsetUpdateIDKey(0), diff --git a/x/ccv/democracy/staking/module.go b/x/ccv/democracy/staking/module.go index aa27177f0a..d2eaf26adb 100644 --- a/x/ccv/democracy/staking/module.go +++ b/x/ccv/democracy/staking/module.go @@ -12,6 +12,9 @@ import ( abci "github.com/tendermint/tendermint/abci/types" ) +// Note: for a democracy consumer, this "democracy staking" keeper is only for governance capabilities, +// where the ccv consumer module is responsible for proof of stake capabilities, validator set updates, etc. + var ( _ module.AppModule = AppModule{} _ module.AppModuleBasic = AppModuleBasic{} @@ -50,6 +53,10 @@ func NewAppModule(cdc codec.Codec, keeper keeper.Keeper, ak types.AccountKeeper, // however, it returns no validator updates as validators are tracked via the // consumer chain's x/cvv/consumer module and so this module is not responsible // for returning the initial validator set. +// +// Note: InitGenesis is not called during the soft upgrade of a module +// (as a part of a changeover from standalone -> consumer chain), +// so there is no special handling needed in this method for a consumer being in the pre-CCV state. func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate { var genesisState types.GenesisState @@ -63,6 +70,12 @@ func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json. // however, it returns no validator updates as validators are tracked via the // consumer chain's x/cvv/consumer module and so this module is not responsible // for returning the initial validator set. +// +// Note: This method does not require any special handling for PreCCV being true +// (as a part of the changeover from standalone -> consumer chain). +// The ccv consumer Endblocker is ordered to run before the staking Endblocker, +// so if PreCCV is true during one block, the ccv consumer Enblocker will return the proper validator updates, +// the PreCCV flag will be toggled to false, and no validator updates should be returned by this method. func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { _ = am.keeper.BlockValidatorUpdates(ctx) return []abci.ValidatorUpdate{} diff --git a/x/ccv/provider/keeper/genesis.go b/x/ccv/provider/keeper/genesis.go index 02ac8c81d3..79e6f2052d 100644 --- a/x/ccv/provider/keeper/genesis.go +++ b/x/ccv/provider/keeper/genesis.go @@ -43,7 +43,7 @@ func (k Keeper) InitGenesis(ctx sdk.Context, genState *types.GenesisState) { } // Note that MatureUnbondingOps aren't stored across blocks, but it - // might be used after implementing sovereign to consumer transition + // might be used after implementing standalone to consumer transition if genState.MatureUnbondingOps != nil { k.AppendMaturedUnbondingOps(ctx, genState.MatureUnbondingOps.Ids) } diff --git a/x/ccv/types/expected_keepers.go b/x/ccv/types/expected_keepers.go index 6ce079f454..56025820e6 100644 --- a/x/ccv/types/expected_keepers.go +++ b/x/ccv/types/expected_keepers.go @@ -30,11 +30,19 @@ type StakingKeeper interface { // slash the validator and delegators of the validator, specifying offence height, offence power, and slash fraction Jail(sdk.Context, sdk.ConsAddress) // jail a validator Slash(sdk.Context, sdk.ConsAddress, int64, int64, sdk.Dec, stakingtypes.InfractionType) + Unjail(ctx sdk.Context, addr sdk.ConsAddress) GetValidator(ctx sdk.Context, addr sdk.ValAddress) (validator stakingtypes.Validator, found bool) IterateLastValidatorPowers(ctx sdk.Context, cb func(addr sdk.ValAddress, power int64) (stop bool)) PowerReduction(ctx sdk.Context) sdk.Int PutUnbondingOnHold(ctx sdk.Context, id uint64) error + IterateValidators(ctx sdk.Context, f func(index int64, validator stakingtypes.ValidatorI) (stop bool)) + Validator(ctx sdk.Context, addr sdk.ValAddress) stakingtypes.ValidatorI + IsValidatorJailed(ctx sdk.Context, addr sdk.ConsAddress) bool + ValidatorByConsAddr(ctx sdk.Context, consAddr sdk.ConsAddress) stakingtypes.ValidatorI + Delegation(ctx sdk.Context, addr sdk.AccAddress, valAddr sdk.ValAddress) stakingtypes.DelegationI + MaxValidators(ctx sdk.Context) uint32 GetLastTotalPower(ctx sdk.Context) sdk.Int + GetLastValidators(ctx sdk.Context) (validators []stakingtypes.Validator) } type EvidenceKeeper interface {