diff --git a/proto/interchain_security/ccv/consumer/v1/genesis.proto b/proto/interchain_security/ccv/consumer/v1/genesis.proto index d0798b33dd..eb83598e6f 100644 --- a/proto/interchain_security/ccv/consumer/v1/genesis.proto +++ b/proto/interchain_security/ccv/consumer/v1/genesis.proto @@ -13,25 +13,31 @@ import "tendermint/abci/types.proto"; // GenesisState defines the CCV consumer chain genesis state message GenesisState { Params params = 1 [ (gogoproto.nullable) = false ]; - string provider_client_id = 2; // empty for a completely new chain - string provider_channel_id = 3; // empty for a completely new chain + string provider_client_id = 2; // empty for a new chain, filled in on restart. + string provider_channel_id = 3; // empty for a new chain, filled in on restart. bool new_chain = 4; // true for new chain GenesisState, false for chain restart. // ProviderClientState filled in on new chain, nil on restart. ibc.lightclients.tendermint.v1.ClientState provider_client_state = 5; // ProviderConsensusState filled in on new chain, nil on restart. ibc.lightclients.tendermint.v1.ConsensusState provider_consensus_state = 6; - // MaturingPackets nil on new chain, filled on restart. + // MaturingPackets nil on new chain, filled in on restart. repeated MaturingVSCPacket maturing_packets = 7 [ (gogoproto.nullable) = false ]; // InitialValset filled in on new chain and on restart. repeated .tendermint.abci.ValidatorUpdate initial_val_set = 8 [ (gogoproto.nullable) = false ]; - // HeightToValsetUpdateId nil on new chain, filled on restart. + // HeightToValsetUpdateId nil on new chain, filled in on restart. repeated HeightToValsetUpdateID height_to_valset_update_id = 9 [ (gogoproto.nullable) = false ]; - // OutstandingDowntimes nil on new chain, filled on restart. + // OutstandingDowntimes nil on new chain, filled in on restart. repeated OutstandingDowntime outstanding_downtime_slashing = 10 [ (gogoproto.nullable) = false ]; + // PendingConsumerPackets nil on new chain, filled in on restart. + ConsumerPackets pending_consumer_packets = 11 + [ (gogoproto.nullable) = false ]; + // LastTransmissionBlockHeight nil on new chain, filled in on restart. + interchain_security.ccv.consumer.v1.LastTransmissionBlockHeight last_transmission_block_height = 12 + [ (gogoproto.nullable) = false ]; } // MaturingVSCPacket defines the genesis information for the diff --git a/proto/interchain_security/ccv/provider/v1/genesis.proto b/proto/interchain_security/ccv/provider/v1/genesis.proto index dedfc72120..dd7c862d5b 100644 --- a/proto/interchain_security/ccv/provider/v1/genesis.proto +++ b/proto/interchain_security/ccv/provider/v1/genesis.proto @@ -40,7 +40,7 @@ message GenesisState { // consumer chain message ConsumerState { - // ChannelID defines the chain ID for the consumer chain + // ChainID defines the chain ID for the consumer chain string chain_id = 1; // ChannelID defines the IBC channel ID for the consumer chain string channel_id = 2; diff --git a/tests/e2e/channel_init.go b/tests/e2e/channel_init.go index f202aaeba6..f1c82f2d0e 100644 --- a/tests/e2e/channel_init.go +++ b/tests/e2e/channel_init.go @@ -21,11 +21,6 @@ func (suite *CCVTestSuite) TestConsumerGenesis() { genesis := consumerKeeper.ExportGenesis(suite.consumerChain.GetContext()) - // Confirm that client and cons state are exported from consumer keeper properly - consumerEndpointClientState, consumerEndpointConsState := suite.GetConsumerEndpointClientAndConsState() - suite.Require().Equal(consumerEndpointClientState, genesis.ProviderClientState) - suite.Require().Equal(consumerEndpointConsState, genesis.ProviderConsensusState) - suite.Require().NotPanics(func() { consumerKeeper.InitGenesis(suite.consumerChain.GetContext(), genesis) // reset suite to reset provider client @@ -39,9 +34,8 @@ func (suite *CCVTestSuite) TestConsumerGenesis() { clientId, ok := consumerKeeper.GetProviderClientID(ctx) suite.Require().True(ok) - clientState, ok := suite.consumerApp.GetIBCKeeper().ClientKeeper.GetClientState(ctx, clientId) + _, ok = suite.consumerApp.GetIBCKeeper().ClientKeeper.GetClientState(ctx, clientId) suite.Require().True(ok) - suite.Require().Equal(genesis.ProviderClientState, clientState, "client state not set correctly after InitGenesis") suite.SetupCCVChannel() diff --git a/testutil/keeper/expectations.go b/testutil/keeper/expectations.go index 6d437b1f07..9654ba7e61 100644 --- a/testutil/keeper/expectations.go +++ b/testutil/keeper/expectations.go @@ -11,6 +11,7 @@ import ( ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" "github.com/golang/mock/gomock" + host "github.com/cosmos/ibc-go/v3/modules/core/24-host" ccv "github.com/cosmos/interchain-security/x/ccv/types" extra "github.com/oxyno-zeta/gomock-extra-matcher" @@ -88,3 +89,18 @@ func GetMocksForStopConsumerChain(ctx sdk.Context, mocks *MockedKeepers) []*gomo mocks.MockChannelKeeper.EXPECT().ChanCloseInit(ctx, ccv.ProviderPortID, "channelID", dummyCap).Times(1), } } + +func ExpectLatestConsensusStateMock(ctx sdk.Context, mocks MockedKeepers, clientID string, consState *ibctmtypes.ConsensusState) *gomock.Call { + return mocks.MockClientKeeper.EXPECT(). + GetLatestClientConsensusState(ctx, clientID).Return(consState, true).Times(1) +} + +func ExpectCreateClientMock(ctx sdk.Context, mocks MockedKeepers, clientID string, clientState *ibctmtypes.ClientState, consState *ibctmtypes.ConsensusState) *gomock.Call { + return mocks.MockClientKeeper.EXPECT().CreateClient(ctx, clientState, consState).Return(clientID, nil).Times(1) +} + +func ExpectGetCapabilityMock(ctx sdk.Context, mocks MockedKeepers) *gomock.Call { + return mocks.MockScopedKeeper.EXPECT().GetCapability( + ctx, host.PortPath(ccv.ConsumerPortID), + ).Return(nil, true).Times(1) +} diff --git a/testutil/keeper/unit_test_helpers.go b/testutil/keeper/unit_test_helpers.go index 203f1750fe..3365683d0b 100644 --- a/testutil/keeper/unit_test_helpers.go +++ b/testutil/keeper/unit_test_helpers.go @@ -2,9 +2,6 @@ package keeper import ( "testing" - time "time" - - tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" @@ -27,8 +24,6 @@ import ( cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" - ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" ) // Parameters needed to instantiate an in-memory keeper @@ -187,17 +182,6 @@ func GenPubKey() (crypto.PubKey, error) { return cryptocodec.ToTmPubKeyInterface(privKey.PrivKey.PubKey()) } -func GetClientState(chainID string) *ibctmtypes.ClientState { - return ibctmtypes.NewClientState(chainID, ibctmtypes.DefaultTrustLevel, 0, 0, - time.Second*10, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), - []string{"upgrade", "upgradedIBCState"}, true, true) -} - -func GetConsensusState(clientID string, timestamp time.Time, vals ...*tmtypes.Validator) *ibctmtypes.ConsensusState { - return ibctmtypes.NewConsensusState(timestamp, commitmenttypes.NewMerkleRoot([]byte("apphash")), - tmtypes.NewValidatorSet(vals).Hash()[:]) -} - // SetupForStoppingConsumerChain registers expected mock calls and corresponding state setup // which asserts that a consumer chain was properly stopped from StopConsumerChain(). func SetupForStoppingConsumerChain(t *testing.T, ctx sdk.Context, diff --git a/x/ccv/consumer/keeper/genesis.go b/x/ccv/consumer/keeper/genesis.go index 0320c719e7..49a4e792b9 100644 --- a/x/ccv/consumer/keeper/genesis.go +++ b/x/ccv/consumer/keeper/genesis.go @@ -6,7 +6,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" - "github.com/cosmos/interchain-security/x/ccv/consumer/types" consumertypes "github.com/cosmos/interchain-security/x/ccv/consumer/types" ccv "github.com/cosmos/interchain-security/x/ccv/types" @@ -15,6 +14,11 @@ import ( ) // InitGenesis initializes the CCV consumer state and binds to PortID. +// The three states in which a consumer chain can start/restart: +// +// 1. A client to the provider was never created, i.e. a new consumer chain is started for the first time. +// 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 { k.SetParams(ctx, state.Params) // TODO: Remove enabled flag and find a better way to setup e2e tests @@ -37,41 +41,56 @@ func (k Keeper) InitGenesis(ctx sdk.Context, state *consumertypes.GenesisState) } // initialValSet is checked in NewChain case by ValidateGenesis + // start a new chain if state.NewChain { - // Create the provider client in InitGenesis for new consumer chain. CCV Handshake must be established with this client id. + // create the provider client in InitGenesis for new consumer chain. CCV Handshake must be established with this client id. clientID, err := k.clientKeeper.CreateClient(ctx, state.ProviderClientState, state.ProviderConsensusState) if err != nil { panic(err) } - // Set default value for valset update ID - k.SetHeightValsetUpdateID(ctx, uint64(ctx.BlockHeight()), uint64(0)) // set provider client id. k.SetProviderClientID(ctx, clientID) - } else { - // verify that latest consensus state on provider client matches the initial validator set of restarted chain - // thus, IBC genesis MUST run before CCV consumer genesis - consState, ok := k.clientKeeper.GetLatestClientConsensusState(ctx, state.ProviderClientId) - if !ok { - panic("consensus state for provider client not found. MUST run IBC genesis before CCV consumer genesis") - } - tmConsState, ok := consState.(*ibctmtypes.ConsensusState) - if !ok { - panic(fmt.Sprintf("consensus state has wrong type. expected: %T, got: %T", &ibctmtypes.ConsensusState{}, consState)) - } - // ensure that initial validator set is same as initial consensus state on provider client. - // this will be verified by provider module on channel handshake. - vals, err := tmtypes.PB2TM.ValidatorUpdates(state.InitialValSet) - if err != nil { + // set default value for valset update ID + k.SetHeightValsetUpdateID(ctx, uint64(ctx.BlockHeight()), uint64(0)) + + } else { + // verify genesis initial valset against the latest consensus state + // IBC genesis MUST run before CCV consumer genesis + if err := k.verifyGenesisInitValset(ctx, state); err != nil { panic(err) } - valSet := tmtypes.NewValidatorSet(vals) - if !bytes.Equal(tmConsState.NextValidatorsHash, valSet.Hash()) { - panic("initial validator set does not match last consensus state of the provider client") + // chain restarts with the CCV channel established + if state.ProviderChannelId != "" { + // set provider channel ID + k.SetProviderChannel(ctx, state.ProviderChannelId) + // set all unbonding sequences + for _, mp := range state.MaturingPackets { + k.SetPacketMaturityTime(ctx, mp.VscId, mp.MaturityTime) + } + // set outstanding downtime slashing requests + for _, od := range state.OutstandingDowntimeSlashing { + consAddr, err := sdk.ConsAddressFromBech32(od.ValidatorConsensusAddress) + if err != nil { + panic(err) + } + k.SetOutstandingDowntime(ctx, consAddr) + } + + // set last transmission block height + err := k.SetLastTransmissionBlockHeight(ctx, state.LastTransmissionBlockHeight) + if err != nil { + panic(fmt.Sprintf("could not set last transmission block height: %v", err)) + } + } + // set pending consumer pending packets + // note that the list includes pending mature VSC packet only if the handshake is completed + k.AppendPendingPacket(ctx, state.PendingConsumerPackets.List...) + // set height to valset update id mapping for _, h2v := range state.HeightToValsetUpdateId { k.SetHeightValsetUpdateID(ctx, h2v.Height, h2v.ValsetUpdateId) @@ -79,12 +98,7 @@ func (k Keeper) InitGenesis(ctx sdk.Context, state *consumertypes.GenesisState) // set provider client id k.SetProviderClientID(ctx, state.ProviderClientId) - // set provider channel id. - k.SetProviderChannel(ctx, state.ProviderChannelId) - // set all unbonding sequences - for _, mp := range state.MaturingPackets { - k.SetPacketMaturityTime(ctx, mp.VscId, mp.MaturityTime) - } + } // populate cross chain validators states with initial valset @@ -93,8 +107,7 @@ func (k Keeper) InitGenesis(ctx sdk.Context, state *consumertypes.GenesisState) return state.InitialValSet } -// ExportGenesis exports the CCV consumer state. If the channel has already been established, then we export -// provider chain. Otherwise, this is still considered a new chain and we export latest client state. +// ExportGenesis returns the CCV consumer module's exported genesis func (k Keeper) ExportGenesis(ctx sdk.Context) (genesis *consumertypes.GenesisState) { params := k.GetParams(ctx) if !params.Enabled { @@ -102,7 +115,7 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) (genesis *consumertypes.GenesisSt } // export the current validator set - valset, err := k.GetValidatorUpdates(ctx) + valset, err := k.GetCurrentValidatorsAsABCIUpdates(ctx) if err != nil { panic(fmt.Sprintf("fail to retrieve the validator set: %s", err)) } @@ -114,9 +127,9 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) (genesis *consumertypes.GenesisSt panic("provider client does not exist") } - maturingPackets := []types.MaturingVSCPacket{} + maturingPackets := []consumertypes.MaturingVSCPacket{} k.IteratePacketMaturityTime(ctx, func(vscId, timeNs uint64) bool { - mat := types.MaturingVSCPacket{ + mat := consumertypes.MaturingVSCPacket{ VscId: vscId, MaturityTime: timeNs, } @@ -124,60 +137,80 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) (genesis *consumertypes.GenesisSt return false }) - heightToVCIDs := []types.HeightToValsetUpdateID{} - k.IterateHeightToValsetUpdateID(ctx, func(height, vscID uint64) bool { - hv := types.HeightToValsetUpdateID{ - Height: height, - ValsetUpdateId: vscID, - } - heightToVCIDs = append(heightToVCIDs, hv) - return true - }) - - outstandingDowntimes := []types.OutstandingDowntime{} + outstandingDowntimes := []consumertypes.OutstandingDowntime{} k.IterateOutstandingDowntime(ctx, func(addr string) bool { - od := types.OutstandingDowntime{ + od := consumertypes.OutstandingDowntime{ ValidatorConsensusAddress: addr, } outstandingDowntimes = append(outstandingDowntimes, od) return true }) - genesis = types.NewRestartGenesisState( + // TODO: update GetLastTransmissionBlockHeight to not return an error + ltbh, err := k.GetLastTransmissionBlockHeight(ctx) + if err != nil { + panic(err) + } + + genesis = consumertypes.NewRestartGenesisState( clientID, channelID, maturingPackets, valset, - heightToVCIDs, + k.GetHeightToValsetUpdateIDs(ctx), + k.GetPendingPackets(ctx), outstandingDowntimes, + *ltbh, params, ) } else { clientID, ok := k.GetProviderClientID(ctx) - // if provider clientID and channelID don't exist on the consumer chain, then CCV protocol is disabled for this chain - // return a disabled genesis state + // if provider clientID and channelID don't exist on the consumer chain, + // then CCV protocol is disabled for this chain return a default genesis state if !ok { return consumertypes.DefaultGenesisState() } - cs, ok := k.clientKeeper.GetClientState(ctx, clientID) - if !ok { - panic("provider client not set on already running consumer chain") - } - tmCs, ok := cs.(*ibctmtypes.ClientState) - if !ok { - panic("provider client consensus state is not tendermint client state") - } - consState, ok := k.clientKeeper.GetLatestClientConsensusState(ctx, clientID) - if !ok { - panic("provider consensus state not set on already running consumer chain") - } - tmConsState, ok := consState.(*ibctmtypes.ConsensusState) - if !ok { - panic("provider consensus state is not tendermint consensus state") - } + // export client states and pending slashing requests into a new chain genesis - genesis = consumertypes.NewInitialGenesisState(tmCs, tmConsState, valset, params) + genesis = consumertypes.NewRestartGenesisState( + clientID, + "", + nil, + valset, + k.GetHeightToValsetUpdateIDs(ctx), + k.GetPendingPackets(ctx), + nil, + consumertypes.LastTransmissionBlockHeight{}, + params, + ) } return } + +// verifyGenesisInitValset verifies the latest consensus state on provider client matches +// the initial validator set of restarted chain thus +func (k Keeper) verifyGenesisInitValset(ctx sdk.Context, genState *consumertypes.GenesisState) error { + + consState, ok := k.clientKeeper.GetLatestClientConsensusState(ctx, genState.ProviderClientId) + if !ok { + return fmt.Errorf("consensus state for provider client not found. MUST run IBC genesis before CCV consumer genesis") + } + tmConsState, ok := consState.(*ibctmtypes.ConsensusState) + if !ok { + return fmt.Errorf(fmt.Sprintf("consensus state has wrong type. expected: %T, got: %T", &ibctmtypes.ConsensusState{}, consState)) + } + + // ensure that initial validator set is same as initial consensus state on provider client. + // this will be verified by provider module on channel handshake. + vals, err := tmtypes.PB2TM.ValidatorUpdates(genState.InitialValSet) + if err != nil { + return err + } + valSet := tmtypes.NewValidatorSet(vals) + + if !bytes.Equal(tmConsState.NextValidatorsHash, valSet.Hash()) { + return fmt.Errorf("initial validator set does not match last consensus state of the provider client") + } + return nil +} diff --git a/x/ccv/consumer/keeper/genesis_test.go b/x/ccv/consumer/keeper/genesis_test.go index 139446e7ea..57c1aac49f 100644 --- a/x/ccv/consumer/keeper/genesis_test.go +++ b/x/ccv/consumer/keeper/genesis_test.go @@ -5,7 +5,10 @@ import ( "time" sdk "github.com/cosmos/cosmos-sdk/types" - host "github.com/cosmos/ibc-go/v3/modules/core/24-host" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" testkeeper "github.com/cosmos/interchain-security/testutil/keeper" consumerkeeper "github.com/cosmos/interchain-security/x/ccv/consumer/keeper" "github.com/cosmos/interchain-security/x/ccv/consumer/types" @@ -22,41 +25,77 @@ import ( testutil "github.com/cosmos/interchain-security/testutil/keeper" ) +// TestInitGenesis tests that a consumer chain is correctly initialised from genesis. +// It covers the start of a new chain, the restart of a chain during the CCV channel handshake +// and finally the restart of chain when the CCV channel is already established. func TestInitGenesis(t *testing.T) { - // store consumer chain states in variables + // mock the consumer genesis state values + provClientID := "tendermint-07" + provChannelID := "ChannelID" - // create channel and client IDs for the consumer - channelID := "channelID" - clientID := "tendermint-07" + vscID := uint64(0) + blockHeight := uint64(0) - // generate validator public key + // create validator set pubKey, err := testutil.GenPubKey() require.NoError(t, err) - - // create validator set with single validator validator := tmtypes.NewValidator(pubKey, 1) + abciValidator := abci.Validator{Address: pubKey.Address(), Power: int64(1)} + valset := []abci.ValidatorUpdate{tmtypes.TM2PB.ValidatorUpdate(validator)} + + // create ibc client and last consensus states + provConsState := ibctmtypes.NewConsensusState( + time.Time{}, + commitmenttypes.NewMerkleRoot([]byte("apphash")), + tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}).Hash()[:], + ) - // create consensus state using a single validator - consensusState := testutil.GetConsensusState(clientID, time.Time{}, validator) + provClientState := ibctmtypes.NewClientState( + "provider", + ibctmtypes.DefaultTrustLevel, + 0, + stakingtypes.DefaultUnbondingTime, + time.Second*10, + clienttypes.Height{}, + commitmenttypes.GetSDKSpecs(), + []string{"upgrade", "upgradedIBCState"}, + true, + true, + ) - matPacket := consumertypes.MaturingVSCPacket{ - VscId: uint64(1), - MaturityTime: uint64(time.Now().UnixNano()), + matPackets := []consumertypes.MaturingVSCPacket{ + { + VscId: 1, + MaturityTime: uint64(time.Now().UnixNano()), + }, } - - // create parameters for a new chain - params := types.NewParams(true, - types.DefaultBlocksPerDistributionTransmission, - "", - "", - ccv.DefaultCCVTimeoutPeriod, - consumertypes.DefaultTransferTimeoutPeriod, - consumertypes.DefaultConsumerRedistributeFrac, - consumertypes.DefaultHistoricalEntries, - consumertypes.DefaultConsumerUnbondingPeriod, + pendingDataPackets := consumertypes.ConsumerPackets{ + List: []consumertypes.ConsumerPacket{ + { + Type: consumertypes.SlashPacket, + Data: ccv.NewSlashPacketData(abciValidator, vscID, stakingtypes.Downtime).GetBytes(), + }, + { + Type: consumertypes.VscMaturedPacket, + Data: ccv.NewVSCMaturedPacketData(1).GetBytes(), + }, + }, + } + // mock height to valset update ID values + defaultHeightValsetUpdateIDs := []consumertypes.HeightToValsetUpdateID{ + {ValsetUpdateId: vscID, Height: blockHeight}, + } + updatedHeightValsetUpdateIDs := append(defaultHeightValsetUpdateIDs, + consumertypes.HeightToValsetUpdateID{ValsetUpdateId: vscID + 1, Height: blockHeight + 1}, ) + // create default parameters for a new chain + params := types.DefaultParams() + params.Enabled = true + + // define three test cases which respectively create a genesis struct, use it to call InitGenesis + // and finally check that the genesis states are successfully imported in the consumer keeper stores testCases := []struct { name string malleate func(sdk.Context, testutil.MockedKeepers) @@ -64,55 +103,103 @@ func TestInitGenesis(t *testing.T) { assertStates func(sdk.Context, consumerkeeper.Keeper, *consumertypes.GenesisState) }{ { - name: "restart a new chain", - malleate: func(ctx sdk.Context, mocks testutil.MockedKeepers) { + "start a new chain", + func(ctx sdk.Context, mocks testutil.MockedKeepers) { gomock.InOrder( - expectGetCapabilityMock(ctx, mocks), - expectCreateClientMock(ctx, mocks, "", clientID, validator), + testkeeper.ExpectGetCapabilityMock(ctx, mocks), + testkeeper.ExpectCreateClientMock(ctx, mocks, provClientID, provClientState, provConsState), + testkeeper.ExpectGetCapabilityMock(ctx, mocks), ) }, - genesis: consumertypes.NewInitialGenesisState(testutil.GetClientState(""), consensusState, - []abci.ValidatorUpdate{tmtypes.TM2PB.ValidatorUpdate(validator)}, params), - - assertStates: func(ctx sdk.Context, ck consumerkeeper.Keeper, gs *consumertypes.GenesisState) { - require.Equal(t, gs.Params, ck.GetParams(ctx)) - require.Equal(t, ccv.ConsumerPortID, ck.GetPort(ctx)) - - ubdPeriod := ck.GetUnbondingPeriod(ctx) - require.Equal(t, consumertypes.DefaultConsumerUnbondingPeriod, ubdPeriod) + consumertypes.NewInitialGenesisState( + provClientState, + provConsState, + valset, + params, + ), + func(ctx sdk.Context, ck consumerkeeper.Keeper, gs *consumertypes.GenesisState) { + assertConsumerPortIsBound(t, ctx, &ck) - require.Zero(t, ck.GetHeightValsetUpdateID(ctx, uint64(ctx.BlockHeight()))) + assertProviderClientID(t, ctx, &ck, provClientID) + assertHeightValsetUpdateIDs(t, ctx, &ck, defaultHeightValsetUpdateIDs) - cid, ok := ck.GetProviderClientID(ctx) - require.True(t, ok) - require.Equal(t, clientID, cid) + require.Equal(t, validator.Address.Bytes(), ck.GetAllCCValidator(ctx)[0].Address) + require.Equal(t, gs.Params, ck.GetParams(ctx)) }, }, { - name: "restart a chain with an already established channel", - malleate: func(ctx sdk.Context, mocks testutil.MockedKeepers) { + "restart a chain without an established CCV channel", + func(ctx sdk.Context, mocks testutil.MockedKeepers) { gomock.InOrder( - expectGetCapabilityMock(ctx, mocks), - expectLatestConsensusStateMock(ctx, mocks, clientID, validator), + testkeeper.ExpectGetCapabilityMock(ctx, mocks), + testkeeper.ExpectLatestConsensusStateMock(ctx, mocks, provClientID, provConsState), + testkeeper.ExpectGetCapabilityMock(ctx, mocks), ) }, - genesis: consumertypes.NewRestartGenesisState(clientID, channelID, - []consumertypes.MaturingVSCPacket{matPacket}, - []abci.ValidatorUpdate{tmtypes.TM2PB.ValidatorUpdate(validator)}, - []consumertypes.HeightToValsetUpdateID{{ValsetUpdateId: matPacket.VscId, Height: uint64(0)}}, - []consumertypes.OutstandingDowntime{{ValidatorConsensusAddress: sdk.ConsAddress(validator.Bytes()).String()}}, + consumertypes.NewRestartGenesisState( + provClientID, + "", + matPackets, + valset, + defaultHeightValsetUpdateIDs, + pendingDataPackets, + nil, + consumertypes.LastTransmissionBlockHeight{}, params, ), - assertStates: func(ctx sdk.Context, ck consumerkeeper.Keeper, gs *consumertypes.GenesisState) { + func(ctx sdk.Context, ck consumerkeeper.Keeper, gs *consumertypes.GenesisState) { + assertConsumerPortIsBound(t, ctx, &ck) + + require.Equal(t, pendingDataPackets, ck.GetPendingPackets(ctx)) + assertHeightValsetUpdateIDs(t, ctx, &ck, defaultHeightValsetUpdateIDs) + assertProviderClientID(t, ctx, &ck, provClientID) + require.Equal(t, validator.Address.Bytes(), ck.GetAllCCValidator(ctx)[0].Address) require.Equal(t, gs.Params, ck.GetParams(ctx)) - require.Equal(t, ccv.ConsumerPortID, ck.GetPort(ctx)) + }, + }, { + "restart a chain with an established CCV channel", + func(ctx sdk.Context, mocks testutil.MockedKeepers) { + // simulate a CCV channel handshake completition + params.DistributionTransmissionChannel = "distribution-channel" + params.ProviderFeePoolAddrStr = "provider-fee-pool-address" + gomock.InOrder( + testkeeper.ExpectGetCapabilityMock(ctx, mocks), + testkeeper.ExpectLatestConsensusStateMock(ctx, mocks, provClientID, provConsState), + testkeeper.ExpectGetCapabilityMock(ctx, mocks), + ) + }, + // create a genesis for a restarted chain + consumertypes.NewRestartGenesisState( + provClientID, + provChannelID, + matPackets, + valset, + updatedHeightValsetUpdateIDs, + pendingDataPackets, + []consumertypes.OutstandingDowntime{ + {ValidatorConsensusAddress: sdk.ConsAddress(validator.Bytes()).String()}, + }, + consumertypes.LastTransmissionBlockHeight{Height: int64(100)}, + params, + ), + func(ctx sdk.Context, ck consumerkeeper.Keeper, gs *consumertypes.GenesisState) { + assertConsumerPortIsBound(t, ctx, &ck) - ubdPeriod := ck.GetUnbondingPeriod(ctx) - require.Equal(t, consumertypes.DefaultConsumerUnbondingPeriod, ubdPeriod) + gotChannelID, ok := ck.GetProviderChannel(ctx) + require.True(t, ok) + require.Equal(t, provChannelID, gotChannelID) + + require.Equal(t, vscID, ck.GetPacketMaturityTime(ctx, vscID)) + require.Equal(t, pendingDataPackets, ck.GetPendingPackets(ctx)) - // export states to genesis - require.Equal(t, matPacket.VscId, ck.GetHeightValsetUpdateID(ctx, uint64(0))) + require.Equal(t, gs.OutstandingDowntimeSlashing, ck.GetOutstandingDowntimes(ctx)) + + ltbh, err := ck.GetLastTransmissionBlockHeight(ctx) + require.NoError(t, err) + require.Equal(t, gs.LastTransmissionBlockHeight, *ltbh) + + assertHeightValsetUpdateIDs(t, ctx, &ck, updatedHeightValsetUpdateIDs) + assertProviderClientID(t, ctx, &ck, provClientID) - require.Equal(t, matPacket.MaturityTime, ck.GetPacketMaturityTime(ctx, matPacket.VscId)) require.Equal(t, gs.Params, ck.GetParams(ctx)) }, }, @@ -120,9 +207,8 @@ func TestInitGenesis(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - keeperParams := testkeeper.NewInMemKeeperParams(t) - // Explicitly register codec with public key interface + // explicitly register codec with public key interface keeperParams.RegisterSdkCryptoCodecInterfaces() consumerKeeper, ctx, ctrl, mocks := testkeeper.GetConsumerKeeperAndCtx(t, keeperParams) defer ctrl.Finish() @@ -130,94 +216,133 @@ func TestInitGenesis(t *testing.T) { // test setup tc.malleate(ctx, mocks) - // init the chain states using a genesis + // init chain states consumerKeeper.InitGenesis(ctx, tc.genesis) - // assert states + // assert consumer keeper states tc.assertStates(ctx, consumerKeeper, tc.genesis) }) } } +// TestExportGenesis tests that a consumer chain genesis is correctly exported to genesis +// It covers the restart of chain when a CCV channel is or isn't established yet. func TestExportGenesis(t *testing.T) { - clientID := "tendermint-07" - channelID := "channelID" + // create provider channel and client ids + provClientID := "tendermint-07" + provChannelID := "provChannelID" - // define the states exported into genesis - restartHeight := uint64(0) - matPacket := consumertypes.MaturingVSCPacket{ - VscId: uint64(1), - MaturityTime: uint64(time.Now().UnixNano()), - } + vscID := uint64(0) + blockHeight := uint64(0) - params := types.NewParams( - true, - types.DefaultBlocksPerDistributionTransmission, - "", - "", - ccv.DefaultCCVTimeoutPeriod, - consumertypes.DefaultTransferTimeoutPeriod, - consumertypes.DefaultConsumerRedistributeFrac, - consumertypes.DefaultHistoricalEntries, - consumertypes.DefaultConsumerUnbondingPeriod, - ) + matPackets := []consumertypes.MaturingVSCPacket{ + { + VscId: 1, + MaturityTime: uint64(time.Now().UnixNano()), + }, + } - // create a single validator + // mock a validator set pubKey := ed25519.GenPrivKey().PubKey() tmPK, err := cryptocodec.ToTmPubKeyInterface(pubKey) require.NoError(t, err) validator := tmtypes.NewValidator(tmPK, 1) - - // create consensus state using a single validator - consensusState := testutil.GetConsensusState(clientID, time.Time{}, validator) - + abciValidator := abci.Validator{Address: pubKey.Address(), Power: int64(1)} + valset := []abci.ValidatorUpdate{tmtypes.TM2PB.ValidatorUpdate(validator)} + + // create pending consumer packets + consPackets := consumertypes.ConsumerPackets{ + List: []consumertypes.ConsumerPacket{ + { + Type: consumertypes.SlashPacket, + Data: ccv.NewSlashPacketData(abciValidator, vscID, stakingtypes.Downtime).GetBytes()}, + { + Type: consumertypes.VscMaturedPacket, + Data: ccv.NewVSCMaturedPacketData(vscID).GetBytes(), + }, + }, + } + // mock height to valset update ID values + defaultHeightValsetUpdateIDs := []consumertypes.HeightToValsetUpdateID{ + {ValsetUpdateId: vscID, Height: blockHeight}, + } + updatedHeightValsetUpdateIDs := append(defaultHeightValsetUpdateIDs, + consumertypes.HeightToValsetUpdateID{ValsetUpdateId: vscID + 1, Height: blockHeight + 1}, + ) + ltbh := consumertypes.LastTransmissionBlockHeight{Height: int64(1000)} + // create default parameters for a new chain + params := types.DefaultParams() + params.Enabled = true + + // define two test cases which respectively populate the consumer chain store + // using the states declared above then call ExportGenesis to finally check + // that the resulting genesis struct contains the same states testCases := []struct { name string malleate func(sdk.Context, consumerkeeper.Keeper, testutil.MockedKeepers) expGenesis *consumertypes.GenesisState }{ { - name: "export a new chain", - malleate: func(ctx sdk.Context, ck consumerkeeper.Keeper, mocks testutil.MockedKeepers) { - // populate the states used by a new consumer chain + "export a chain without an established CCV channel", + func(ctx sdk.Context, ck consumerkeeper.Keeper, mocks testutil.MockedKeepers) { + // populate the states allowed before a CCV channel is established + ck.SetProviderClientID(ctx, provClientID) cVal, err := consumertypes.NewCCValidator(validator.Address.Bytes(), 1, pubKey) require.NoError(t, err) ck.SetCCValidator(ctx, cVal) - ck.SetProviderClientID(ctx, clientID) + ck.SetParams(ctx, params) - // set the mock calls executed during the export - gomock.InOrder( - expectGetClientStateMock(ctx, mocks, "", clientID), - expectLatestConsensusStateMock(ctx, mocks, clientID, validator), - ) - }, + ck.AppendPendingPacket(ctx, consPackets.List...) + ck.SetHeightValsetUpdateID(ctx, defaultHeightValsetUpdateIDs[0].Height, defaultHeightValsetUpdateIDs[0].ValsetUpdateId) - expGenesis: consumertypes.NewInitialGenesisState(testutil.GetClientState(""), consensusState, - []abci.ValidatorUpdate{tmtypes.TM2PB.ValidatorUpdate(validator)}, params), + }, + consumertypes.NewRestartGenesisState( + provClientID, + "", + nil, + valset, + defaultHeightValsetUpdateIDs, + consPackets, + nil, + consumertypes.LastTransmissionBlockHeight{}, + params, + ), }, { - name: "export a chain that has an established CCV channel", - malleate: func(ctx sdk.Context, ck consumerkeeper.Keeper, mocks testutil.MockedKeepers) { - // populate the states used by a running chain + "export a chain with an established CCV channel", + func(ctx sdk.Context, ck consumerkeeper.Keeper, mocks testutil.MockedKeepers) { + ck.SetProviderClientID(ctx, provClientID) + ck.SetProviderChannel(ctx, provChannelID) + cVal, err := consumertypes.NewCCValidator(validator.Address.Bytes(), 1, pubKey) require.NoError(t, err) ck.SetCCValidator(ctx, cVal) - ck.SetOutstandingDowntime(ctx, sdk.ConsAddress(validator.Address.Bytes())) - // populate the required states to simulate a completed handshake - ck.SetProviderClientID(ctx, clientID) - ck.SetProviderChannel(ctx, channelID) - ck.SetHeightValsetUpdateID(ctx, restartHeight, matPacket.VscId) - ck.SetPacketMaturityTime(ctx, matPacket.VscId, matPacket.MaturityTime) + ck.SetParams(ctx, params) + + ck.SetHeightValsetUpdateID(ctx, updatedHeightValsetUpdateIDs[0].Height, updatedHeightValsetUpdateIDs[0].ValsetUpdateId) + ck.SetHeightValsetUpdateID(ctx, updatedHeightValsetUpdateIDs[1].Height, updatedHeightValsetUpdateIDs[1].ValsetUpdateId) + + ck.AppendPendingPacket(ctx, consPackets.List...) + + // populate the required states for an established CCV channel + ck.SetPacketMaturityTime(ctx, matPackets[0].VscId, matPackets[0].MaturityTime) + ck.SetOutstandingDowntime(ctx, sdk.ConsAddress(validator.Address.Bytes())) + err = ck.SetLastTransmissionBlockHeight(ctx, ltbh) + require.NoError(t, err) }, - expGenesis: consumertypes.NewRestartGenesisState( - clientID, - channelID, - []consumertypes.MaturingVSCPacket{matPacket}, - []abci.ValidatorUpdate{tmtypes.TM2PB.ValidatorUpdate(validator)}, - []types.HeightToValsetUpdateID{{Height: restartHeight, ValsetUpdateId: matPacket.VscId}}, - []consumertypes.OutstandingDowntime{{ValidatorConsensusAddress: sdk.ConsAddress(validator.Address.Bytes()).String()}}, + consumertypes.NewRestartGenesisState( + provClientID, + provChannelID, + matPackets, + valset, + updatedHeightValsetUpdateIDs, + consPackets, + []consumertypes.OutstandingDowntime{ + {ValidatorConsensusAddress: sdk.ConsAddress(validator.Address.Bytes()).String()}, + }, + ltbh, params, ), }, @@ -236,35 +361,35 @@ func TestExportGenesis(t *testing.T) { // test setup tc.malleate(ctx, consumerKeeper, mocks) - // export staet to genesis + // export states to genesis gotGen := consumerKeeper.ExportGenesis(ctx) - // check the obtained genesis + // check obtained genesis require.EqualValues(t, tc.expGenesis, gotGen) }) } } -func expectLatestConsensusStateMock(ctx sdk.Context, mocks testutil.MockedKeepers, clientID string, vals ...*tmtypes.Validator) *gomock.Call { - consState := testutil.GetConsensusState(clientID, time.Time{}, vals...) - return mocks.MockClientKeeper.EXPECT(). - GetLatestClientConsensusState(ctx, clientID).Return(consState, true).Times(1) -} - -func expectGetClientStateMock(ctx sdk.Context, mocks testutil.MockedKeepers, chainID, clientID string) *gomock.Call { - cs := testutil.GetClientState(chainID) - return mocks.MockClientKeeper.EXPECT().GetClientState(ctx, clientID).Return(cs, true).Times(1) +// assert that the default CCV consumer port ID is stored and bounded +func assertConsumerPortIsBound(t *testing.T, ctx sdk.Context, ck *consumerkeeper.Keeper) { + require.Equal(t, ck.GetPort(ctx), string(ccv.ConsumerPortID)) + require.True(t, ck.IsBound(ctx, ccv.ConsumerPortID)) } -func expectCreateClientMock(ctx sdk.Context, mocks testutil.MockedKeepers, chainID, clientID string, vals ...*tmtypes.Validator) *gomock.Call { - cs := testutil.GetClientState(chainID) - consState := testutil.GetConsensusState(clientID, time.Time{}, vals...) - - return mocks.MockClientKeeper.EXPECT().CreateClient(ctx, cs, consState).Return(clientID, nil).Times(1) +// assert that the given client ID matches the provider client ID in the store +func assertProviderClientID(t *testing.T, ctx sdk.Context, ck *consumerkeeper.Keeper, clientID string) { + cid, ok := ck.GetProviderClientID(ctx) + require.True(t, ok) + require.Equal(t, clientID, cid) } -func expectGetCapabilityMock(ctx sdk.Context, mocks testutil.MockedKeepers) *gomock.Call { - return mocks.MockScopedKeeper.EXPECT().GetCapability( - ctx, host.PortPath(ccv.ConsumerPortID), - ).Return(nil, true).Times(1) +// assert that the given input match the height to valset update ID mappings in the store +func assertHeightValsetUpdateIDs(t *testing.T, ctx sdk.Context, ck *consumerkeeper.Keeper, heighValsetUpdateIDs []consumertypes.HeightToValsetUpdateID) { + ctr := 0 + ck.IterateHeightToValsetUpdateID(ctx, func(height uint64, vscID uint64) bool { + require.Equal(t, heighValsetUpdateIDs[ctr].Height, height) + require.Equal(t, heighValsetUpdateIDs[ctr].ValsetUpdateId, vscID) + ctr++ + return true + }) } diff --git a/x/ccv/consumer/keeper/keeper.go b/x/ccv/consumer/keeper/keeper.go index 105853ff92..0578b2726c 100644 --- a/x/ccv/consumer/keeper/keeper.go +++ b/x/ccv/consumer/keeper/keeper.go @@ -16,6 +16,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/interchain-security/x/ccv/consumer/types" + consumertypes "github.com/cosmos/interchain-security/x/ccv/consumer/types" ccv "github.com/cosmos/interchain-security/x/ccv/types" "github.com/tendermint/tendermint/libs/log" ) @@ -428,8 +429,36 @@ func (k Keeper) DeletePendingDataPackets(ctx sdk.Context) { } // AppendPendingDataPacket appends the given data packet to the pending data packets in store -func (k Keeper) AppendPendingPacket(ctx sdk.Context, packet types.ConsumerPacket) { +func (k Keeper) AppendPendingPacket(ctx sdk.Context, packet ...types.ConsumerPacket) { pending := k.GetPendingPackets(ctx) - list := append(pending.GetList(), packet) + list := append(pending.GetList(), packet...) k.SetPendingPackets(ctx, types.ConsumerPackets{List: list}) } + +// GetHeightToValsetUpdateIDs returns all height to valset update id mappings in store +func (k Keeper) GetHeightToValsetUpdateIDs(ctx sdk.Context) []types.HeightToValsetUpdateID { + heightToVCIDs := []types.HeightToValsetUpdateID{} + k.IterateHeightToValsetUpdateID(ctx, func(height, vscID uint64) bool { + hv := types.HeightToValsetUpdateID{ + Height: height, + ValsetUpdateId: vscID, + } + heightToVCIDs = append(heightToVCIDs, hv) + return true + }) + + return heightToVCIDs +} + +// GetOutstandingDowntimes returns all outstanding downtimes in store +func (k Keeper) GetOutstandingDowntimes(ctx sdk.Context) []consumertypes.OutstandingDowntime { + outstandingDowntimes := []types.OutstandingDowntime{} + k.IterateOutstandingDowntime(ctx, func(addr string) bool { + od := types.OutstandingDowntime{ + ValidatorConsensusAddress: addr, + } + outstandingDowntimes = append(outstandingDowntimes, od) + return false + }) + return outstandingDowntimes +} diff --git a/x/ccv/consumer/keeper/validators.go b/x/ccv/consumer/keeper/validators.go index 2d2cfbdc93..73f015ca96 100644 --- a/x/ccv/consumer/keeper/validators.go +++ b/x/ccv/consumer/keeper/validators.go @@ -200,8 +200,8 @@ func (k Keeper) TrackHistoricalInfo(ctx sdk.Context) { k.SetHistoricalInfo(ctx, ctx.BlockHeight(), &historicalEntry) } -// ValidatorUpdates gets all cross-chain validators converted to the ABCI validator update type -func (k Keeper) GetValidatorUpdates(ctx sdk.Context) ([]abci.ValidatorUpdate, error) { +// GetCurrentValidatorsAsABCIUpdates gets all cross-chain validators converted to the ABCI validator update type +func (k Keeper) GetCurrentValidatorsAsABCIUpdates(ctx sdk.Context) ([]abci.ValidatorUpdate, error) { vals := k.GetAllCCValidator(ctx) valUpdates := make([]abci.ValidatorUpdate, 0, len(vals)) for _, v := range vals { diff --git a/x/ccv/consumer/types/codec.go b/x/ccv/consumer/types/codec.go deleted file mode 100644 index e90cf8a7f9..0000000000 --- a/x/ccv/consumer/types/codec.go +++ /dev/null @@ -1,10 +0,0 @@ -package types - -// // RegisterInterfaces register the ibc transfer module interfaces to protobuf -// // Any. -// func RegisterInterfaces(registry codectypes.InterfaceRegistry) { -// registry.RegisterImplementations( -// (*govtypes.Content)(nil), -// &ConsumerAdditionProposal{}, -// ) -// } diff --git a/x/ccv/consumer/types/genesis.go b/x/ccv/consumer/types/genesis.go index 1576b57ad0..00d2ae60cd 100644 --- a/x/ccv/consumer/types/genesis.go +++ b/x/ccv/consumer/types/genesis.go @@ -30,7 +30,9 @@ func NewRestartGenesisState( maturingPackets []MaturingVSCPacket, initValSet []abci.ValidatorUpdate, heightToValsetUpdateIDs []HeightToValsetUpdateID, + pendingConsumerPackets ConsumerPackets, outstandingDowntimes []OutstandingDowntime, + lastTransBlockHeight LastTransmissionBlockHeight, params Params, ) *GenesisState { @@ -42,7 +44,9 @@ func NewRestartGenesisState( NewChain: false, InitialValSet: initValSet, HeightToValsetUpdateId: heightToValsetUpdateIDs, + PendingConsumerPackets: pendingConsumerPackets, OutstandingDowntimeSlashing: outstandingDowntimes, + LastTransmissionBlockHeight: lastTransBlockHeight, } } @@ -55,6 +59,22 @@ func DefaultGenesisState() *GenesisState { } // Validate performs basic genesis state validation returning an error upon any failure. +// +// The three cases where a consumer chain starts/restarts +// expect the following optional and mandatory genesis states: +// +// 1. New chain starts: +// - Params, InitialValset, provider client state, provider consensus state // mandatory +// +// 2. Chain restarts with CCV handshake still in progress: +// - Params, InitialValset, ProviderID, HeightToValidatorSetUpdateID // mandatory +// - PendingConsumerPacket // optional +// +// 3. Chain restarts with CCV handshake completed: +// - Params, InitialValset, ProviderID, channelID, HeightToValidatorSetUpdateID // mandatory +// - MaturingVSCPackets, OutstandingDowntime, PendingConsumerPacket, LastTransmissionBlockHeight // optional +// + func (gs GenesisState) Validate() error { if !gs.Params.Enabled { return nil @@ -88,6 +108,12 @@ func (gs GenesisState) Validate() error { if len(gs.MaturingPackets) != 0 { return sdkerrors.Wrap(ccv.ErrInvalidGenesis, "maturing packets must be empty for new chain") } + if len(gs.PendingConsumerPackets.List) != 0 { + return sdkerrors.Wrap(ccv.ErrInvalidGenesis, "pending consumer packets must be empty for new chain") + } + if gs.LastTransmissionBlockHeight.Height != 0 { + return sdkerrors.Wrap(ccv.ErrInvalidGenesis, "last transmission block height must be empty for new chain") + } // ensure that initial validator set is same as initial consensus state on provider client. // this will be verified by provider module on channel handshake. @@ -104,11 +130,37 @@ func (gs GenesisState) Validate() error { if gs.ProviderClientId == "" { return sdkerrors.Wrap(ccv.ErrInvalidGenesis, "provider client id must be set for a restarting consumer genesis state") } - if gs.ProviderChannelId == "" { - return sdkerrors.Wrap(ccv.ErrInvalidGenesis, "provider channel id must be set for a restarting consumer genesis state") + // handshake is still in progress + handshakeInProgress := gs.ProviderChannelId == "" + if handshakeInProgress { + if len(gs.MaturingPackets) != 0 { + return sdkerrors.Wrap( + ccv.ErrInvalidGenesis, "maturing packets must be empty when handshake isn't completed") + } + if len(gs.OutstandingDowntimeSlashing) != 0 { + return sdkerrors.Wrap( + ccv.ErrInvalidGenesis, "outstanding downtime must be empty when handshake isn't completed") + } + if gs.LastTransmissionBlockHeight.Height != 0 { + return sdkerrors.Wrap( + ccv.ErrInvalidGenesis, "last transmission block height must be zero when handshake isn't completed") + } + if len(gs.PendingConsumerPackets.List) != 0 { + for _, packet := range gs.PendingConsumerPackets.List { + if packet.Type == VscMaturedPacket { + return sdkerrors.Wrap(ccv.ErrInvalidGenesis, "pending maturing packets must be empty when handshake isn't completed") + } + } + } + } + if gs.HeightToValsetUpdateId == nil { + return sdkerrors.Wrap( + ccv.ErrInvalidGenesis, + "empty height to validator set update id mapping", + ) } if gs.ProviderClientState != nil || gs.ProviderConsensusState != nil { - return sdkerrors.Wrap(ccv.ErrInvalidGenesis, "provider client state and consensus states must be nil for a restarting genesis state") + return sdkerrors.Wrap(ccv.ErrInvalidGenesis, "provider client state and consensus state must be nil for a restarting genesis state") } for _, mat := range gs.MaturingPackets { if err := mat.Validate(); err != nil { diff --git a/x/ccv/consumer/types/genesis.pb.go b/x/ccv/consumer/types/genesis.pb.go index 74fef7f071..ebe4bea185 100644 --- a/x/ccv/consumer/types/genesis.pb.go +++ b/x/ccv/consumer/types/genesis.pb.go @@ -36,14 +36,18 @@ type GenesisState struct { ProviderClientState *types.ClientState `protobuf:"bytes,5,opt,name=provider_client_state,json=providerClientState,proto3" json:"provider_client_state,omitempty"` // ProviderConsensusState filled in on new chain, nil on restart. ProviderConsensusState *types.ConsensusState `protobuf:"bytes,6,opt,name=provider_consensus_state,json=providerConsensusState,proto3" json:"provider_consensus_state,omitempty"` - // MaturingPackets nil on new chain, filled on restart. + // MaturingPackets nil on new chain, filled in on restart. MaturingPackets []MaturingVSCPacket `protobuf:"bytes,7,rep,name=maturing_packets,json=maturingPackets,proto3" json:"maturing_packets"` // InitialValset filled in on new chain and on restart. InitialValSet []types1.ValidatorUpdate `protobuf:"bytes,8,rep,name=initial_val_set,json=initialValSet,proto3" json:"initial_val_set"` - // HeightToValsetUpdateId nil on new chain, filled on restart. + // HeightToValsetUpdateId nil on new chain, filled in on restart. HeightToValsetUpdateId []HeightToValsetUpdateID `protobuf:"bytes,9,rep,name=height_to_valset_update_id,json=heightToValsetUpdateId,proto3" json:"height_to_valset_update_id"` - // OutstandingDowntimes nil on new chain, filled on restart. + // OutstandingDowntimes nil on new chain, filled in on restart. OutstandingDowntimeSlashing []OutstandingDowntime `protobuf:"bytes,10,rep,name=outstanding_downtime_slashing,json=outstandingDowntimeSlashing,proto3" json:"outstanding_downtime_slashing"` + // PendingConsumerPackets nil on new chain, filled in on restart. + PendingConsumerPackets ConsumerPackets `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"` } func (m *GenesisState) Reset() { *m = GenesisState{} } @@ -149,6 +153,20 @@ func (m *GenesisState) GetOutstandingDowntimeSlashing() []OutstandingDowntime { return nil } +func (m *GenesisState) GetPendingConsumerPackets() ConsumerPackets { + if m != nil { + return m.PendingConsumerPackets + } + return ConsumerPackets{} +} + +func (m *GenesisState) GetLastTransmissionBlockHeight() LastTransmissionBlockHeight { + if m != nil { + return m.LastTransmissionBlockHeight + } + return LastTransmissionBlockHeight{} +} + // MaturingVSCPacket defines the genesis information for the // unbonding VSC packet type MaturingVSCPacket struct { @@ -315,51 +333,56 @@ func init() { } var fileDescriptor_2db73a6057a27482 = []byte{ - // 693 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x54, 0xcf, 0x6f, 0xd3, 0x30, - 0x14, 0x6e, 0x58, 0x57, 0x56, 0x6f, 0x63, 0x9b, 0x37, 0xaa, 0xb0, 0x8a, 0x52, 0xba, 0x4b, 0x25, - 0x20, 0x51, 0x87, 0x84, 0x10, 0x48, 0x48, 0x6c, 0x93, 0xa0, 0x07, 0xc6, 0xd4, 0x6e, 0x3d, 0xec, - 0x12, 0xb9, 0x8e, 0x95, 0x58, 0x24, 0x76, 0x15, 0x3b, 0x19, 0x3b, 0x70, 0xe1, 0x2f, 0xe0, 0xcf, - 0xda, 0x71, 0x47, 0x0e, 0x08, 0xa1, 0xed, 0x1f, 0x41, 0xb1, 0x9d, 0xfe, 0x60, 0x95, 0xe8, 0x2d, - 0xf6, 0x7b, 0xdf, 0xf7, 0xbd, 0xf7, 0x3d, 0xe7, 0x81, 0x0e, 0x65, 0x92, 0x24, 0x38, 0x44, 0x94, - 0x79, 0x82, 0xe0, 0x34, 0xa1, 0xf2, 0xd2, 0xc5, 0x38, 0x73, 0x31, 0x67, 0x22, 0x8d, 0x49, 0xe2, - 0x66, 0x1d, 0x37, 0x20, 0x8c, 0x08, 0x2a, 0x9c, 0x51, 0xc2, 0x25, 0x87, 0x7b, 0x73, 0x20, 0x0e, - 0xc6, 0x99, 0x53, 0x40, 0x9c, 0xac, 0xb3, 0xeb, 0xd2, 0x21, 0x76, 0x23, 0x1a, 0x84, 0x12, 0x47, - 0x94, 0x30, 0x29, 0x5c, 0x49, 0x98, 0x4f, 0x92, 0x98, 0x32, 0x99, 0x53, 0x4e, 0x4e, 0x9a, 0x75, - 0xf7, 0x69, 0x0e, 0xc0, 0x3c, 0x21, 0x2e, 0x0e, 0x11, 0x63, 0x24, 0xca, 0xb3, 0xcc, 0xa7, 0x49, - 0xd9, 0x09, 0x78, 0xc0, 0xd5, 0xa7, 0x9b, 0x7f, 0x99, 0xdb, 0xfd, 0x45, 0x3a, 0x18, 0x97, 0xa6, - 0x31, 0xf5, 0xa9, 0x62, 0xd0, 0x10, 0x53, 0x57, 0x5e, 0x8e, 0x88, 0xe9, 0xaf, 0xf5, 0xab, 0x02, - 0xd6, 0x3e, 0xe8, 0x8e, 0xfb, 0x12, 0x49, 0x02, 0xbb, 0xa0, 0x32, 0x42, 0x09, 0x8a, 0x85, 0x6d, - 0x35, 0xad, 0xf6, 0xea, 0xfe, 0x33, 0x67, 0x01, 0x07, 0x9c, 0x13, 0x05, 0x39, 0x28, 0x5f, 0xfd, - 0x7e, 0x52, 0xea, 0x19, 0x02, 0xf8, 0x1c, 0xc0, 0x51, 0xc2, 0x33, 0xea, 0x93, 0xc4, 0xd3, 0xc6, - 0x78, 0xd4, 0xb7, 0xef, 0x35, 0xad, 0x76, 0xb5, 0xb7, 0x59, 0x44, 0x0e, 0x55, 0xa0, 0xeb, 0x43, - 0x07, 0x6c, 0x4f, 0xb2, 0xb5, 0x15, 0x79, 0xfa, 0x92, 0x4a, 0xdf, 0x1a, 0xa7, 0xeb, 0x48, 0xd7, - 0x87, 0x75, 0x50, 0x65, 0xe4, 0xc2, 0x53, 0x85, 0xd9, 0xe5, 0xa6, 0xd5, 0x5e, 0xe9, 0xad, 0x30, - 0x72, 0x71, 0x98, 0x9f, 0xa1, 0x07, 0x1e, 0xfe, 0x2b, 0x2d, 0xf2, 0xf6, 0xec, 0xe5, 0xa2, 0xa9, - 0x21, 0x76, 0xa6, 0x27, 0xe6, 0x4c, 0xcd, 0x28, 0xeb, 0x38, 0xba, 0x2a, 0xe5, 0x48, 0x6f, 0x7b, - 0xb6, 0x54, 0x6d, 0x53, 0x08, 0xec, 0x89, 0x00, 0x67, 0x82, 0x30, 0x91, 0x0a, 0xa3, 0x51, 0x51, - 0x1a, 0xce, 0x7f, 0x35, 0x0a, 0x98, 0x96, 0xa9, 0x8d, 0x65, 0x66, 0xee, 0x61, 0x00, 0x36, 0x63, - 0x24, 0xd3, 0x84, 0xb2, 0xc0, 0x1b, 0x21, 0xfc, 0x85, 0x48, 0x61, 0xdf, 0x6f, 0x2e, 0xb5, 0x57, - 0xf7, 0x5f, 0x2d, 0x34, 0x9a, 0x4f, 0x06, 0x3c, 0xe8, 0x1f, 0x9e, 0x28, 0xb8, 0x99, 0xd2, 0x46, - 0xc1, 0xaa, 0x6f, 0x05, 0x3c, 0x06, 0x1b, 0x94, 0x51, 0x49, 0x51, 0xe4, 0x65, 0x28, 0xf2, 0x04, - 0x91, 0xf6, 0x8a, 0xd2, 0x69, 0x4e, 0x17, 0x9e, 0xbf, 0x20, 0x67, 0x80, 0x22, 0xea, 0x23, 0xc9, - 0x93, 0xb3, 0x91, 0x8f, 0x24, 0x31, 0x8c, 0xeb, 0x06, 0x3e, 0x40, 0x51, 0x9f, 0x48, 0xf8, 0x0d, - 0xec, 0x86, 0x24, 0x6f, 0xdf, 0x93, 0x3c, 0x67, 0x14, 0x44, 0x7a, 0xa9, 0xca, 0xcf, 0xe7, 0x5a, - 0x55, 0xd4, 0x6f, 0x17, 0x6a, 0xe1, 0xa3, 0xa2, 0x39, 0xe5, 0x03, 0x45, 0xa2, 0x35, 0xbb, 0x47, - 0x46, 0xb5, 0x16, 0xce, 0x8b, 0xfa, 0xf0, 0xbb, 0x05, 0x1e, 0xf3, 0x54, 0x0a, 0x89, 0x98, 0x9f, - 0x7b, 0xe7, 0xf3, 0x0b, 0x26, 0x69, 0x4c, 0x3c, 0x11, 0x21, 0x11, 0x52, 0x16, 0xd8, 0x40, 0x95, - 0xf0, 0x7a, 0xa1, 0x12, 0x3e, 0x4f, 0x98, 0x8e, 0x0c, 0x91, 0xd1, 0xaf, 0xf3, 0xbb, 0xa1, 0xbe, - 0x91, 0x68, 0x1d, 0x83, 0xad, 0x3b, 0xfe, 0xc3, 0x1d, 0xb0, 0x9c, 0x09, 0xdc, 0xf5, 0xd5, 0x1f, - 0x56, 0xee, 0xe9, 0x03, 0xdc, 0x03, 0xeb, 0x7a, 0x22, 0xf2, 0xd2, 0xcb, 0x39, 0xd4, 0x8f, 0x52, - 0xee, 0xad, 0x15, 0x97, 0xa7, 0x34, 0x26, 0xad, 0x73, 0x50, 0x9b, 0x6f, 0x06, 0xac, 0x81, 0x8a, - 0x36, 0xc2, 0xb0, 0x9a, 0x13, 0x6c, 0x83, 0xcd, 0x3b, 0xde, 0x6b, 0xe6, 0x07, 0xd9, 0x8c, 0x61, - 0xad, 0x33, 0xb0, 0x3d, 0xa7, 0x4b, 0xf8, 0x0e, 0xd4, 0xb3, 0x62, 0xdc, 0x53, 0x4f, 0x1d, 0xf9, - 0x7e, 0x42, 0x84, 0xde, 0x12, 0xd5, 0xde, 0xa3, 0x71, 0xca, 0xf8, 0xf5, 0xbe, 0xd7, 0x09, 0x07, - 0xa7, 0x57, 0x37, 0x0d, 0xeb, 0xfa, 0xa6, 0x61, 0xfd, 0xb9, 0x69, 0x58, 0x3f, 0x6e, 0x1b, 0xa5, - 0xeb, 0xdb, 0x46, 0xe9, 0xe7, 0x6d, 0xa3, 0x74, 0xfe, 0x26, 0xa0, 0x32, 0x4c, 0x87, 0x0e, 0xe6, - 0xb1, 0x8b, 0xb9, 0x88, 0xb9, 0x70, 0x27, 0xa3, 0x78, 0x31, 0x5e, 0x6f, 0x5f, 0x67, 0x17, 0x9c, - 0xda, 0x5e, 0xc3, 0x8a, 0x5a, 0x5f, 0x2f, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0xd5, 0xa8, 0x41, - 0x3e, 0xd3, 0x05, 0x00, 0x00, + // 772 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x54, 0xcf, 0x6f, 0xdb, 0x36, + 0x14, 0xb6, 0x16, 0xc7, 0xb3, 0x99, 0x64, 0x49, 0x98, 0xcc, 0xd0, 0x62, 0xcc, 0xf3, 0x9c, 0x8b, + 0x81, 0x6d, 0x12, 0x9c, 0x0d, 0xc3, 0xb0, 0x01, 0x43, 0x1b, 0x07, 0x68, 0x0d, 0xb4, 0x69, 0x60, + 0x3b, 0x3e, 0xe4, 0x22, 0xd0, 0x14, 0x21, 0x11, 0x91, 0x48, 0x43, 0xa4, 0x95, 0xe6, 0xd0, 0x4b, + 0xaf, 0xbd, 0xf4, 0xcf, 0xca, 0x31, 0xc7, 0x9e, 0x8a, 0x20, 0xf9, 0x47, 0x0a, 0x91, 0x94, 0x7f, + 0x24, 0x2e, 0xea, 0x1b, 0xc9, 0xf7, 0xde, 0xf7, 0xbd, 0xf7, 0xbe, 0xc7, 0x07, 0xda, 0x94, 0x49, + 0x92, 0xe0, 0x10, 0x51, 0xe6, 0x09, 0x82, 0x27, 0x09, 0x95, 0xd7, 0x2e, 0xc6, 0xa9, 0x8b, 0x39, + 0x13, 0x93, 0x98, 0x24, 0x6e, 0xda, 0x76, 0x03, 0xc2, 0x88, 0xa0, 0xc2, 0x19, 0x27, 0x5c, 0x72, + 0x78, 0xb8, 0x24, 0xc4, 0xc1, 0x38, 0x75, 0xf2, 0x10, 0x27, 0x6d, 0x1f, 0xb8, 0x74, 0x84, 0xdd, + 0x88, 0x06, 0xa1, 0xc4, 0x11, 0x25, 0x4c, 0x0a, 0x57, 0x12, 0xe6, 0x93, 0x24, 0xa6, 0x4c, 0x66, + 0x90, 0xb3, 0x9b, 0x46, 0x3d, 0xf8, 0x35, 0x0b, 0xc0, 0x3c, 0x21, 0x2e, 0x0e, 0x11, 0x63, 0x24, + 0xca, 0xbc, 0xcc, 0xd1, 0xb8, 0xec, 0x07, 0x3c, 0xe0, 0xea, 0xe8, 0x66, 0x27, 0xf3, 0x7a, 0xb4, + 0x4a, 0x05, 0xd3, 0xd4, 0x74, 0x4c, 0x6d, 0x2e, 0x19, 0x34, 0xc2, 0xd4, 0x95, 0xd7, 0x63, 0x62, + 0xea, 0x6b, 0xde, 0x95, 0xc1, 0xe6, 0x0b, 0x5d, 0x71, 0x5f, 0x22, 0x49, 0x60, 0x17, 0x94, 0xc6, + 0x28, 0x41, 0xb1, 0xb0, 0xad, 0x86, 0xd5, 0xda, 0x38, 0xfa, 0xcd, 0x59, 0xa1, 0x03, 0xce, 0x99, + 0x0a, 0x39, 0x2e, 0xde, 0x7c, 0xfe, 0xa5, 0xd0, 0x33, 0x00, 0xf0, 0x77, 0x00, 0xc7, 0x09, 0x4f, + 0xa9, 0x4f, 0x12, 0x4f, 0x37, 0xc6, 0xa3, 0xbe, 0xfd, 0x5d, 0xc3, 0x6a, 0x55, 0x7a, 0x3b, 0xb9, + 0xa5, 0xa3, 0x0c, 0x5d, 0x1f, 0x3a, 0x60, 0x6f, 0xe6, 0xad, 0x5b, 0x91, 0xb9, 0xaf, 0x29, 0xf7, + 0xdd, 0xa9, 0xbb, 0xb6, 0x74, 0x7d, 0x58, 0x03, 0x15, 0x46, 0xae, 0x3c, 0x95, 0x98, 0x5d, 0x6c, + 0x58, 0xad, 0x72, 0xaf, 0xcc, 0xc8, 0x55, 0x27, 0xbb, 0x43, 0x0f, 0xfc, 0xf8, 0x98, 0x5a, 0x64, + 0xe5, 0xd9, 0xeb, 0x79, 0x51, 0x23, 0xec, 0xcc, 0x2b, 0xe6, 0xcc, 0x69, 0x94, 0xb6, 0x1d, 0x9d, + 0x95, 0xea, 0x48, 0x6f, 0x6f, 0x31, 0x55, 0xdd, 0xa6, 0x10, 0xd8, 0x33, 0x02, 0xce, 0x04, 0x61, + 0x62, 0x22, 0x0c, 0x47, 0x49, 0x71, 0x38, 0xdf, 0xe4, 0xc8, 0xc3, 0x34, 0x4d, 0x75, 0x4a, 0xb3, + 0xf0, 0x0e, 0x03, 0xb0, 0x13, 0x23, 0x39, 0x49, 0x28, 0x0b, 0xbc, 0x31, 0xc2, 0x97, 0x44, 0x0a, + 0xfb, 0xfb, 0xc6, 0x5a, 0x6b, 0xe3, 0xe8, 0xef, 0x95, 0xa4, 0x79, 0x6d, 0x82, 0x87, 0xfd, 0xce, + 0x99, 0x0a, 0x37, 0x2a, 0x6d, 0xe7, 0xa8, 0xfa, 0x55, 0xc0, 0x53, 0xb0, 0x4d, 0x19, 0x95, 0x14, + 0x45, 0x5e, 0x8a, 0x22, 0x4f, 0x10, 0x69, 0x97, 0x15, 0x4f, 0x63, 0x3e, 0xf1, 0x6c, 0x82, 0x9c, + 0x21, 0x8a, 0xa8, 0x8f, 0x24, 0x4f, 0xce, 0xc7, 0x3e, 0x92, 0xc4, 0x20, 0x6e, 0x99, 0xf0, 0x21, + 0x8a, 0xfa, 0x44, 0xc2, 0x77, 0xe0, 0x20, 0x24, 0x59, 0xf9, 0x9e, 0xe4, 0x19, 0xa2, 0x20, 0xd2, + 0x9b, 0x28, 0xff, 0x4c, 0xd7, 0x8a, 0x82, 0xfe, 0x6f, 0xa5, 0x12, 0x5e, 0x2a, 0x98, 0x01, 0x1f, + 0x2a, 0x10, 0xcd, 0xd9, 0x3d, 0x31, 0xac, 0xd5, 0x70, 0x99, 0xd5, 0x87, 0xef, 0x2d, 0xf0, 0x33, + 0x9f, 0x48, 0x21, 0x11, 0xf3, 0xb3, 0xde, 0xf9, 0xfc, 0x8a, 0x49, 0x1a, 0x13, 0x4f, 0x44, 0x48, + 0x84, 0x94, 0x05, 0x36, 0x50, 0x29, 0xfc, 0xb3, 0x52, 0x0a, 0x6f, 0x66, 0x48, 0x27, 0x06, 0xc8, + 0xf0, 0xd7, 0xf8, 0x53, 0x53, 0xdf, 0x50, 0x40, 0x09, 0xec, 0x31, 0xd1, 0xfc, 0x39, 0xda, 0x54, + 0xc4, 0x0d, 0x35, 0x26, 0x7f, 0xad, 0x44, 0xdf, 0x31, 0x67, 0xa3, 0x55, 0x5e, 0xba, 0xc1, 0x7e, + 0x64, 0x85, 0x1f, 0x2c, 0x50, 0x8f, 0x90, 0x90, 0x9e, 0x4c, 0x10, 0x13, 0x31, 0x15, 0x82, 0x72, + 0xe6, 0x8d, 0x22, 0x8e, 0x2f, 0x3d, 0xdd, 0x2d, 0x7b, 0x53, 0x91, 0x3f, 0x5b, 0x89, 0xfc, 0x15, + 0x12, 0x72, 0x30, 0x87, 0x74, 0x9c, 0x01, 0x69, 0x4d, 0xf2, 0x1e, 0x44, 0x5f, 0x77, 0x69, 0x9e, + 0x82, 0xdd, 0x27, 0x33, 0x08, 0xf7, 0xc1, 0x7a, 0x2a, 0x70, 0xd7, 0x57, 0x5b, 0xa6, 0xd8, 0xd3, + 0x17, 0x78, 0x08, 0xb6, 0xf4, 0x54, 0xca, 0x6b, 0x2f, 0xeb, 0xa3, 0x5a, 0x16, 0xc5, 0xde, 0x66, + 0xfe, 0x38, 0xa0, 0x31, 0x69, 0x5e, 0x80, 0xea, 0xf2, 0x81, 0x80, 0x55, 0x50, 0x32, 0xe5, 0x69, + 0x54, 0x73, 0x83, 0x2d, 0xb0, 0xf3, 0x64, 0xfe, 0x34, 0xf2, 0x0f, 0xe9, 0xc2, 0xd0, 0x34, 0xcf, + 0xc1, 0xde, 0x12, 0xa5, 0xe1, 0xff, 0xa0, 0x96, 0xe6, 0x23, 0x3f, 0xf7, 0xdd, 0x91, 0xef, 0x27, + 0x44, 0xe8, 0x4d, 0x59, 0xe9, 0xfd, 0x34, 0x75, 0x99, 0xfe, 0xe0, 0xe7, 0xda, 0xe1, 0x78, 0x70, + 0x73, 0x5f, 0xb7, 0x6e, 0xef, 0xeb, 0xd6, 0xdd, 0x7d, 0xdd, 0xfa, 0xf8, 0x50, 0x2f, 0xdc, 0x3e, + 0xd4, 0x0b, 0x9f, 0x1e, 0xea, 0x85, 0x8b, 0x7f, 0x03, 0x2a, 0xc3, 0xc9, 0xc8, 0xc1, 0x3c, 0x76, + 0x31, 0x17, 0x31, 0x17, 0xee, 0x4c, 0x92, 0x3f, 0xa6, 0x2b, 0xfe, 0xed, 0xe2, 0x92, 0x57, 0x1b, + 0x7c, 0x54, 0x52, 0x2b, 0xfc, 0xcf, 0x2f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x49, 0x3d, 0x28, 0xe0, + 0xd7, 0x06, 0x00, 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { @@ -382,6 +405,26 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + { + size, err := m.LastTransmissionBlockHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x62 + { + size, err := m.PendingConsumerPackets.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x5a if len(m.OutstandingDowntimeSlashing) > 0 { for iNdEx := len(m.OutstandingDowntimeSlashing) - 1; iNdEx >= 0; iNdEx-- { { @@ -657,6 +700,10 @@ func (m *GenesisState) Size() (n int) { n += 1 + l + sovGenesis(uint64(l)) } } + l = m.PendingConsumerPackets.Size() + n += 1 + l + sovGenesis(uint64(l)) + l = m.LastTransmissionBlockHeight.Size() + n += 1 + l + sovGenesis(uint64(l)) return n } @@ -1063,6 +1110,72 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PendingConsumerPackets", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.PendingConsumerPackets.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LastTransmissionBlockHeight", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.LastTransmissionBlockHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex 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 410ba28a7e..c074cba70e 100644 --- a/x/ccv/consumer/types/genesis_test.go +++ b/x/ccv/consumer/types/genesis_test.go @@ -4,9 +4,13 @@ import ( "testing" "time" + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + abci "github.com/tendermint/tendermint/abci/types" "github.com/cosmos/interchain-security/x/ccv/consumer/types" @@ -14,6 +18,7 @@ import ( testutil "github.com/cosmos/interchain-security/testutil/keeper" + ccv "github.com/cosmos/interchain-security/x/ccv/types" "github.com/stretchr/testify/require" ) @@ -93,6 +98,8 @@ func TestValidateInitialGenesisState(t *testing.T) { valUpdates, nil, nil, + types.ConsumerPackets{}, + types.LastTransmissionBlockHeight{}, }, true, }, @@ -109,6 +116,8 @@ func TestValidateInitialGenesisState(t *testing.T) { valUpdates, nil, nil, + types.ConsumerPackets{}, + types.LastTransmissionBlockHeight{}, }, true, }, @@ -125,6 +134,44 @@ func TestValidateInitialGenesisState(t *testing.T) { valUpdates, nil, nil, + types.ConsumerPackets{}, + types.LastTransmissionBlockHeight{}, + }, + true, + }, + { + "invalid new consumer genesis state: non-empty last transmission packet", + &types.GenesisState{ + params, + "", + "", + true, + cs, + consensusState, + nil, + valUpdates, + nil, + nil, + types.ConsumerPackets{}, + types.LastTransmissionBlockHeight{Height: 1}, + }, + true, + }, + { + "invalid new consumer genesis state: non-empty pending consumer packets", + &types.GenesisState{ + params, + "", + "", + true, + cs, + consensusState, + nil, + valUpdates, + nil, + nil, + types.ConsumerPackets{List: []types.ConsumerPacket{{}}}, + types.LastTransmissionBlockHeight{}, }, true, }, @@ -133,11 +180,27 @@ func TestValidateInitialGenesisState(t *testing.T) { types.NewInitialGenesisState(cs, consensusState, nil, params), true, }, + { + "invalid new consumer genesis state: invalid consensus state validator set hash", + types.NewInitialGenesisState( + cs, ibctmtypes.NewConsensusState( + time.Now(), commitmenttypes.NewMerkleRoot([]byte("apphash")), []byte("wrong_length_hash")), + valUpdates, params), + true, + }, + { + "invalid new consumer genesis state: initial validator set does not match validator set hash", + types.NewInitialGenesisState( + cs, ibctmtypes.NewConsensusState( + time.Now(), commitmenttypes.NewMerkleRoot([]byte("apphash")), []byte("9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08")), + valUpdates, params), + true, + }, { "invalid new consumer genesis state: initial validator set does not match validator set hash", types.NewInitialGenesisState( cs, ibctmtypes.NewConsensusState( - time.Now(), commitmenttypes.NewMerkleRoot([]byte("apphash")), []byte("wrong_hash")), + time.Now(), commitmenttypes.NewMerkleRoot([]byte("apphash")), []byte("9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08")), valUpdates, params), true, }, @@ -182,6 +245,24 @@ func TestValidateRestartGenesisState(t *testing.T) { valHash := valSet.Hash() valUpdates := tmtypes.TM2PB.ValidatorUpdates(valSet) + matConsumerPacket := types.ConsumerPacket{ + Type: types.VscMaturedPacket, + Data: ccv.NewVSCMaturedPacketData(1).GetBytes(), + } + + slashConsumerPacket := types.ConsumerPacket{ + Type: types.SlashPacket, + Data: ccv.NewSlashPacketData( + abci.Validator{Address: pubKey.Address(), Power: int64(1)}, + 1, + stakingtypes.Downtime).GetBytes(), + } + + // create default height to validator set update id mapping + heightToValsetUpdateID := []types.HeightToValsetUpdateID{ + {Height: 0, ValsetUpdateId: 0}, + } + cs := ibctmtypes.NewClientState(chainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath, false, false) consensusState := ibctmtypes.NewConsensusState(time.Now(), commitmenttypes.NewMerkleRoot([]byte("apphash")), valHash[:]) @@ -195,7 +276,14 @@ func TestValidateRestartGenesisState(t *testing.T) { }{ { "valid restart consumer genesis state: empty maturing packets", - types.NewRestartGenesisState("ccvclient", "ccvchannel", nil, valUpdates, nil, nil, params), + types.NewRestartGenesisState("ccvclient", "ccvchannel", nil, valUpdates, heightToValsetUpdateID, + types.ConsumerPackets{List: []types.ConsumerPacket{matConsumerPacket, slashConsumerPacket}}, nil, types.LastTransmissionBlockHeight{Height: 100}, params), + false, + }, + { + "valid restart consumer genesis state: handshake in progress ", + types.NewRestartGenesisState("ccvclient", "", nil, valUpdates, heightToValsetUpdateID, + types.ConsumerPackets{List: []types.ConsumerPacket{slashConsumerPacket}}, nil, types.LastTransmissionBlockHeight{}, params), false, }, { @@ -204,31 +292,28 @@ func TestValidateRestartGenesisState(t *testing.T) { {1, uint64(time.Now().UnixNano())}, {3, uint64(time.Now().UnixNano())}, {5, uint64(time.Now().UnixNano())}, - }, valUpdates, nil, nil, params), + }, valUpdates, heightToValsetUpdateID, types.ConsumerPackets{}, + []types.OutstandingDowntime{{ValidatorConsensusAddress: sdk.ConsAddress(validator.Address.Bytes()).String()}}, + types.LastTransmissionBlockHeight{}, params), false, }, { - "invalid restart consumer genesis state: channel id is empty", - types.NewRestartGenesisState("", "ccvchannel", nil, valUpdates, nil, nil, params), - true, - }, - { - "invalid restart consumer genesis state: channel id is empty", - types.NewRestartGenesisState("ccvclient", "", nil, valUpdates, nil, nil, params), + "invalid restart consumer genesis state: provider id is empty", + types.NewRestartGenesisState("", "ccvchannel", nil, valUpdates, heightToValsetUpdateID, types.ConsumerPackets{}, nil, types.LastTransmissionBlockHeight{}, params), true, }, { "invalid restart consumer genesis state: maturing packet vscId is invalid", types.NewRestartGenesisState("ccvclient", "ccvchannel", []types.MaturingVSCPacket{ {0, uint64(time.Now().UnixNano())}, - }, valUpdates, nil, nil, params), + }, valUpdates, nil, types.ConsumerPackets{}, nil, types.LastTransmissionBlockHeight{}, params), true, }, { "invalid restart consumer genesis state: maturing packet time is invalid", types.NewRestartGenesisState("ccvclient", "ccvchannel", []types.MaturingVSCPacket{ {1, 0}, - }, valUpdates, nil, nil, params), + }, valUpdates, nil, types.ConsumerPackets{}, nil, types.LastTransmissionBlockHeight{}, params), true, }, { @@ -244,6 +329,8 @@ func TestValidateRestartGenesisState(t *testing.T) { valUpdates, nil, nil, + types.ConsumerPackets{}, + types.LastTransmissionBlockHeight{}, }, true, }, @@ -260,17 +347,57 @@ func TestValidateRestartGenesisState(t *testing.T) { valUpdates, nil, nil, + types.ConsumerPackets{}, + types.LastTransmissionBlockHeight{}, }, true, }, { "invalid restart consumer genesis state: nil initial validator set", - types.NewRestartGenesisState("ccvclient", "ccvchannel", nil, nil, nil, nil, params), + types.NewRestartGenesisState("ccvclient", "ccvchannel", nil, nil, nil, types.ConsumerPackets{}, nil, types.LastTransmissionBlockHeight{}, params), + true, + }, + { + "invalid restart consumer genesis state: nil height to validator set id mapping", + types.NewRestartGenesisState("ccvclient", "", + []types.MaturingVSCPacket{{1, 0}}, valUpdates, nil, types.ConsumerPackets{}, nil, types.LastTransmissionBlockHeight{}, params), + true, + }, { + "invalid restart consumer genesis state: maturing packet defined when handshake is still in progress", + types.NewRestartGenesisState("ccvclient", "", + []types.MaturingVSCPacket{{1, 0}}, valUpdates, heightToValsetUpdateID, types.ConsumerPackets{}, nil, types.LastTransmissionBlockHeight{}, params), + true, + }, + { + "invalid restart consumer genesis state: outstanding downtime defined when handshake is still in progress", + types.NewRestartGenesisState("ccvclient", "", + nil, valUpdates, heightToValsetUpdateID, types.ConsumerPackets{}, []types.OutstandingDowntime{{ValidatorConsensusAddress: "cosmosvalconsxxx"}}, + types.LastTransmissionBlockHeight{}, params), + true, + }, + { + "invalid restart consumer genesis state: last transmission block height defined when handshake is still in progress", + types.NewRestartGenesisState("ccvclient", "", + nil, valUpdates, heightToValsetUpdateID, types.ConsumerPackets{}, nil, types.LastTransmissionBlockHeight{Height: int64(1)}, params), + true, + }, + { + "invalid restart consumer genesis state: pending maturing packets defined when handshake is still in progress", + types.NewRestartGenesisState("ccvclient", "", + nil, valUpdates, heightToValsetUpdateID, types.ConsumerPackets{ + List: []types.ConsumerPacket{ + + { + Type: types.VscMaturedPacket, + Data: ccv.NewVSCMaturedPacketData(1).GetBytes(), + }, + }, + }, nil, types.LastTransmissionBlockHeight{Height: int64(1)}, params), true, }, { "invalid restart consumer genesis state: invalid params", - types.NewRestartGenesisState("ccvclient", "ccvchannel", nil, valUpdates, nil, nil, + types.NewRestartGenesisState("ccvclient", "ccvchannel", nil, valUpdates, nil, types.ConsumerPackets{}, nil, types.LastTransmissionBlockHeight{}, types.NewParams( true, types.DefaultBlocksPerDistributionTransmission, diff --git a/x/ccv/provider/keeper/genesis.go b/x/ccv/provider/keeper/genesis.go index fdc82ca360..b0318bdbf1 100644 --- a/x/ccv/provider/keeper/genesis.go +++ b/x/ccv/provider/keeper/genesis.go @@ -9,6 +9,7 @@ import ( ccv "github.com/cosmos/interchain-security/x/ccv/types" ) +// InitGenesis initializes the CCV provider state and binds to PortID. func (k Keeper) InitGenesis(ctx sdk.Context, genState *types.GenesisState) { k.SetPort(ctx, ccv.ProviderPortID) @@ -28,15 +29,15 @@ func (k Keeper) InitGenesis(ctx sdk.Context, genState *types.GenesisState) { k.SetValsetUpdateBlockHeight(ctx, v2h.ValsetUpdateId, v2h.Height) } - for _, cccp := range genState.ConsumerAdditionProposals { + for _, prop := range genState.ConsumerAdditionProposals { // prevent implicit memory aliasing - cccp := cccp - if err := k.SetPendingConsumerAdditionProp(ctx, &cccp); err != nil { + prop := prop + if err := k.SetPendingConsumerAdditionProp(ctx, &prop); err != nil { panic(fmt.Errorf("pending create consumer chain proposal could not be persisted: %w", err)) } } - for _, sccp := range genState.ConsumerRemovalProposals { - k.SetPendingConsumerRemovalProp(ctx, sccp.ChainId, sccp.StopTime) + for _, prop := range genState.ConsumerRemovalProposals { + k.SetPendingConsumerRemovalProp(ctx, prop.ChainId, prop.StopTime) } for _, ubdOp := range genState.UnbondingOps { if err := k.SetUnbondingOp(ctx, ubdOp); err != nil { @@ -44,6 +45,8 @@ 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 if genState.MatureUnbondingOps != nil { if err := k.AppendMaturedUnbondingOps(ctx, genState.MatureUnbondingOps.Ids); err != nil { panic(err) @@ -78,6 +81,7 @@ func (k Keeper) InitGenesis(ctx sdk.Context, genState *types.GenesisState) { k.SetParams(ctx, genState.Params) } +// ExportGenesis returns the CCV provider module's exported genesis func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { var consumerStates []types.ConsumerState // export states for each consumer chains diff --git a/x/ccv/provider/keeper/genesis_test.go b/x/ccv/provider/keeper/genesis_test.go index f8fa991b1f..b7fc768bc0 100644 --- a/x/ccv/provider/keeper/genesis_test.go +++ b/x/ccv/provider/keeper/genesis_test.go @@ -17,7 +17,8 @@ import ( "github.com/stretchr/testify/require" ) -func TestIniAndExportGenesis(t *testing.T) { +// TestInitAndExportGenesis tests the export and the initialisation of a provider chain genesis +func TestInitAndExportGenesis(t *testing.T) { // create a provider chain genesis populated with two consumer chains cChainIDs := []string{"c0", "c1"} expClientID := "client" @@ -27,7 +28,7 @@ func TestIniAndExportGenesis(t *testing.T) { params := providertypes.DefaultParams() // create genesis struct - pGenesis := providertypes.NewGenesisState(vscID, + provGenesis := providertypes.NewGenesisState(vscID, []providertypes.ValsetUpdateIdToHeight{{ValsetUpdateId: vscID, Height: initHeight}}, []providertypes.ConsumerState{ providertypes.NewConsumerStates( @@ -82,16 +83,16 @@ func TestIniAndExportGenesis(t *testing.T) { ) // init provider chain - pk.InitGenesis(ctx, pGenesis) + pk.InitGenesis(ctx, provGenesis) // check local provider chain states ubdOps, found := pk.GetUnbondingOp(ctx, vscID) require.True(t, found) - require.Equal(t, pGenesis.UnbondingOps[0], ubdOps) + require.Equal(t, provGenesis.UnbondingOps[0], ubdOps) matureUbdOps, err := pk.GetMaturedUnbondingOps(ctx) require.NoError(t, err) require.Equal(t, ubdIndex, matureUbdOps) - chainID, found := pk.GetChannelToChain(ctx, pGenesis.ConsumerStates[0].ChannelId) + chainID, found := pk.GetChannelToChain(ctx, provGenesis.ConsumerStates[0].ChannelId) require.True(t, found) require.Equal(t, cChainIDs[0], chainID) require.Equal(t, vscID, pk.GetValidatorSetUpdateId(ctx)) @@ -100,15 +101,15 @@ func TestIniAndExportGenesis(t *testing.T) { require.Equal(t, initHeight, height) addProp, found := pk.GetPendingConsumerAdditionProp(ctx, oneHourFromNow, cChainIDs[0]) require.True(t, found) - require.Equal(t, pGenesis.ConsumerAdditionProposals[0], addProp) + require.Equal(t, provGenesis.ConsumerAdditionProposals[0], addProp) require.True(t, pk.GetPendingConsumerRemovalProp(ctx, cChainIDs[0], oneHourFromNow)) - require.Equal(t, pGenesis.Params, pk.GetParams(ctx)) + require.Equal(t, provGenesis.Params, pk.GetParams(ctx)) // check provider chain's consumer chain states - assertConsumerChainStates(ctx, t, pk, pGenesis.ConsumerStates...) + assertConsumerChainStates(ctx, t, pk, provGenesis.ConsumerStates...) // check the exported genesis - require.Equal(t, pGenesis, pk.ExportGenesis(ctx)) + require.Equal(t, provGenesis, pk.ExportGenesis(ctx)) } diff --git a/x/ccv/provider/keeper/proposal.go b/x/ccv/provider/keeper/proposal.go index f274ce64da..56b1058a94 100644 --- a/x/ccv/provider/keeper/proposal.go +++ b/x/ccv/provider/keeper/proposal.go @@ -338,15 +338,12 @@ func (k Keeper) ConsumerAdditionPropsToExecute(ctx sdk.Context) []types.Consumer // store the (to be) executed proposals in order propsToExecute := []types.ConsumerAdditionProposal{} - - iterator := k.PendingConsumerAdditionPropIterator(ctx) - defer iterator.Close() - k.IteratePendingConsumerAdditionProps(ctx, func(spawnTime time.Time, prop types.ConsumerAdditionProposal) bool { if !ctx.BlockTime().Before(spawnTime) { propsToExecute = append(propsToExecute, prop) return true } + // No more proposals to check, since they're stored/ordered by timestamp. return false }) @@ -464,10 +461,9 @@ func (k Keeper) ConsumerRemovalPropsToExecute(ctx sdk.Context) []types.ConsumerR if !ctx.BlockTime().Before(stopTime) { propsToExecute = append(propsToExecute, prop) return true - } else { - // No more proposals to check, since they're stored/ordered by timestamp. - return false } + // No more proposals to check, since they're stored/ordered by timestamp. + return false }) return propsToExecute diff --git a/x/ccv/provider/types/genesis.pb.go b/x/ccv/provider/types/genesis.pb.go index 80545cbb4d..f092530348 100644 --- a/x/ccv/provider/types/genesis.pb.go +++ b/x/ccv/provider/types/genesis.pb.go @@ -135,7 +135,7 @@ func (m *GenesisState) GetParams() Params { // consumer chain type ConsumerState struct { - // ChannelID defines the chain ID for the consumer chain + // ChainID defines the chain ID for the consumer chain ChainId string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` // ChannelID defines the IBC channel ID for the consumer chain ChannelId string `protobuf:"bytes,2,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty"`