From 67ec7154bab11f92e66da7d752b52e8b5a150bc6 Mon Sep 17 00:00:00 2001 From: yaruwangway <69694322+yaruwangway@users.noreply.github.com> Date: Mon, 2 Oct 2023 17:09:06 +0200 Subject: [PATCH 01/22] feat!: store proposed chainID before voting finishes (#1289) * feat: store chain in proposal * add govHook * delete GetChainsInProposal * check proposal type * update key * feat: add query proposed chainIDs * feat: set govhook * feat: parse key * refactor: names * feat: add list proposed consumer chains * test: add e2e test * add e2e test * update comments * update ProposeConsumerChains in e2e test * remove wait for block * docs: update changelog * fix: lint * add TestParseProposedConsumerChainKey * refactor gov hook * Update proto/interchain_security/ccv/provider/v1/query.proto Co-authored-by: MSalopek * update proto * add test for set kv * refactor key to be prefix_proposalID * formatting * update e2e test * format * Update x/ccv/provider/keeper/gov_hook.go Co-authored-by: Shawn <44221603+smarshall-spitzbart@users.noreply.github.com> * Update x/ccv/provider/keeper/keeper.go Co-authored-by: Shawn <44221603+smarshall-spitzbart@users.noreply.github.com> * Update x/ccv/provider/keeper/keeper.go Co-authored-by: Shawn <44221603+smarshall-spitzbart@users.noreply.github.com> * fix e2e test * fix gosec * remove type url check * test: add unit test * lint * fix lint * fix err --------- Co-authored-by: MSalopek Co-authored-by: Shawn <44221603+smarshall-spitzbart@users.noreply.github.com> --- CHANGELOG.md | 1 + app/provider/app.go | 9 +- .../ccv/provider/v1/query.proto | 21 + tests/e2e/config.go | 23 +- tests/e2e/state.go | 28 + tests/e2e/steps_start_chains.go | 2 + x/ccv/provider/client/cli/query.go | 28 + x/ccv/provider/keeper/gov_hook.go | 93 +++ x/ccv/provider/keeper/grpc_query.go | 14 + x/ccv/provider/keeper/keeper.go | 44 ++ x/ccv/provider/keeper/keeper_test.go | 95 +++ x/ccv/provider/types/keys.go | 22 + x/ccv/provider/types/keys_test.go | 20 + x/ccv/provider/types/query.pb.go | 719 ++++++++++++++++-- x/ccv/provider/types/query.pb.gw.go | 65 ++ 15 files changed, 1086 insertions(+), 98 deletions(-) create mode 100644 x/ccv/provider/keeper/gov_hook.go diff --git a/CHANGELOG.md b/CHANGELOG.md index a238df4a44..cb2477b334 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Add an entry to the unreleased provider section whenever merging a PR to main th * (deps) [#1258](https://github.com/cosmos/interchain-security/pull/1258) Bump [cosmos-sdk](https://github.com/cosmos/cosmos-sdk) to [v0.47.4](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.47.4). * (deps!) [#1196](https://github.com/cosmos/interchain-security/pull/1196) Bump [ibc-go](https://github.com/cosmos/ibc-go) to [v7.2.0](https://github.com/cosmos/ibc-go/releases/tag/v7.2.0). * `[x/ccv/provider]` (fix) [#1076](https://github.com/cosmos/interchain-security/pull/1076) Add `InitTimeoutTimestamps` and `ExportedVscSendTimestamps` to exported genesis. +* (feature) [#1282](https://github.com/cosmos/interchain-security/issues/1282) In the `ConsumerAdditionProposal`, consumer chainIDs proposed before the voting period finishes are now stored in the state. The gRPC query `/interchain_security/ccv/provider/proposed_consumer_chainids` and CLI command `query provider proposed-consumer-chains` can be used to retrieve this information. ## [Unreleased for Consumer] diff --git a/app/provider/app.go b/app/provider/app.go index 4550ec77d2..377bedd10c 100644 --- a/app/provider/app.go +++ b/app/provider/app.go @@ -443,7 +443,7 @@ func New( AddRoute(ibcclienttypes.RouterKey, ibcclient.NewClientProposalHandler(app.IBCKeeper.ClientKeeper)) govConfig := govtypes.DefaultConfig() - app.GovKeeper = *govkeeper.NewKeeper( + govKeeper := govkeeper.NewKeeper( appCodec, keys[govtypes.StoreKey], app.AccountKeeper, @@ -455,7 +455,12 @@ func New( ) // Set legacy router for backwards compatibility with gov v1beta1 - app.GovKeeper.SetLegacyRouter(govRouter) + govKeeper.SetLegacyRouter(govRouter) + + govHook := app.ProviderKeeper.GovHooks(govKeeper) + app.GovKeeper = *govKeeper.SetHooks( + govtypes.NewMultiGovHooks(govHook), + ) app.TransferKeeper = ibctransferkeeper.NewKeeper( appCodec, diff --git a/proto/interchain_security/ccv/provider/v1/query.proto b/proto/interchain_security/ccv/provider/v1/query.proto index 6dfcdcb320..0136a420e7 100644 --- a/proto/interchain_security/ccv/provider/v1/query.proto +++ b/proto/interchain_security/ccv/provider/v1/query.proto @@ -81,6 +81,15 @@ service Query { option (google.api.http).get = "/interchain_security/ccv/provider/registered_consumer_reward_denoms"; } + + // QueryProposedConsumerChainIDs query chainIDs in consumerAdditionProposals + // before voting period finishes, after voting period finishes, this chainID will be removed from the result + rpc QueryProposedConsumerChainIDs( + QueryProposedChainIDsRequest) + returns (QueryProposedChainIDsResponse) { + option (google.api.http).get = + "/interchain_security/ccv/provider/proposed_consumer_chains"; + } } message QueryConsumerGenesisRequest { string chain_id = 1; } @@ -187,3 +196,15 @@ message QueryRegisteredConsumerRewardDenomsRequest {} message QueryRegisteredConsumerRewardDenomsResponse { repeated string denoms = 1; } + +message QueryProposedChainIDsRequest {} + +message QueryProposedChainIDsResponse { + repeated ProposedChain proposedChains = 1 + [ (gogoproto.nullable) = false ]; +} + +message ProposedChain { + string chainID = 1; + uint64 proposalID = 2; +} diff --git a/tests/e2e/config.go b/tests/e2e/config.go index 0a164c2cec..f17deb9843 100644 --- a/tests/e2e/config.go +++ b/tests/e2e/config.go @@ -395,7 +395,7 @@ func ChangeoverTestConfig() TestConfig { return tr } -func (s *TestConfig) SetDockerConfig(localSdkPath string, useGaia bool, gaiaTag string) { +func (tr *TestRun) SetDockerConfig(localSdkPath string, useGaia bool, gaiaTag string) { if localSdkPath != "" { fmt.Println("USING LOCAL SDK", localSdkPath) } @@ -403,17 +403,17 @@ func (s *TestConfig) SetDockerConfig(localSdkPath string, useGaia bool, gaiaTag fmt.Println("USING GAIA INSTEAD OF ICS provider app", gaiaTag) } - s.useGaia = useGaia - s.gaiaTag = gaiaTag - s.localSdkPath = localSdkPath + tr.useGaia = useGaia + tr.gaiaTag = gaiaTag + tr.localSdkPath = localSdkPath } -func (s *TestConfig) SetCometMockConfig(useCometmock bool) { - s.useCometmock = useCometmock +func (tr *TestRun) SetCometMockConfig(useCometmock bool) { + tr.useCometmock = useCometmock } -func (s *TestConfig) SetRelayerConfig(useRly bool) { - s.useGorelayer = useRly +func (tr *TestRun) SetRelayerConfig(useRly bool) { + tr.useGorelayer = useRly } // validateStringLiterals enforces that configs follow the constraints @@ -423,9 +423,8 @@ func (s *TestConfig) SetRelayerConfig(useRly bool) { // within the container will be named as "$CHAIN_ID-$VAL_ID-out" etc. // where this name is constrained to 15 bytes or less. Therefore each string literal // used as a validatorID or chainID needs to be 5 char or less. -func (s *TestConfig) validateStringLiterals() { - for valID, valConfig := range s.validatorConfigs { - +func (tr *TestRun) validateStringLiterals() { + for valID, valConfig := range tr.validatorConfigs { if len(valID) > 5 { panic("validator id string literal must be 5 char or less") } @@ -448,7 +447,7 @@ func (s *TestConfig) validateStringLiterals() { } } - for chainID, chainConfig := range s.chainConfigs { + for chainID, chainConfig := range tr.chainConfigs { if len(chainID) > 5 { panic("chain id string literal must be 5 char or less") } diff --git a/tests/e2e/state.go b/tests/e2e/state.go index 0febed41f7..f4001b4879 100644 --- a/tests/e2e/state.go +++ b/tests/e2e/state.go @@ -19,6 +19,7 @@ type State map[ChainID]ChainState type ChainState struct { ValBalances *map[ValidatorID]uint Proposals *map[uint]Proposal + ProposedConsumerChains *[]string ValPowers *map[ValidatorID]uint StakedTokens *map[ValidatorID]uint Params *[]Param @@ -132,6 +133,11 @@ func (tr TestConfig) getChainState(chain ChainID, modelState ChainState) ChainSt chainState.Proposals = &proposals } + if modelState.ProposedConsumerChains != nil { + proposedConsumerChains := tr.getProposedConsumerChains(chain) + chainState.ProposedConsumerChains = &proposedConsumerChains + } + if modelState.ValPowers != nil { tr.waitBlocks(chain, 1, 10*time.Second) powers := tr.getValPowers(chain, *modelState.ValPowers) @@ -772,3 +778,25 @@ func (tr TestConfig) curlJsonRPCRequest(method, params, address string) { verbosity := false executeCommandWithVerbosity(cmd, "curlJsonRPCRequest", verbosity) } + +func (tr TestRun) getProposedConsumerChains(chain ChainID) []string { + tr.waitBlocks(chain, 1, 10*time.Second) + //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. + bz, err := exec.Command("docker", "exec", tr.containerConfig.InstanceName, tr.chainConfigs[chain].BinaryName, + "query", "provider", "list-proposed-consumer-chains", + `--node`, tr.getQueryNode(chain), + `-o`, `json`, + ).CombinedOutput() + if err != nil { + log.Fatal(err, "\n", string(bz)) + } + + arr := gjson.Get(string(bz), "proposedChains").Array() + chains := []string{} + for _, c := range arr { + cid := c.Get("chainID").String() + chains = append(chains, cid) + } + + return chains +} diff --git a/tests/e2e/steps_start_chains.go b/tests/e2e/steps_start_chains.go index ace3d6c255..58a5f75162 100644 --- a/tests/e2e/steps_start_chains.go +++ b/tests/e2e/steps_start_chains.go @@ -54,6 +54,7 @@ func stepsStartConsumerChain(consumerName string, proposalIndex, chainIndex uint Status: "PROPOSAL_STATUS_VOTING_PERIOD", }, }, + ProposedConsumerChains: &[]string{consumerName}, }, }, }, @@ -163,6 +164,7 @@ func stepsStartConsumerChain(consumerName string, proposalIndex, chainIndex uint ValidatorID("bob"): 9500000000, ValidatorID("carol"): 9500000000, }, + ProposedConsumerChains: &[]string{}, }, ChainID(consumerName): ChainState{ ValBalances: &map[ValidatorID]uint{ diff --git a/x/ccv/provider/client/cli/query.go b/x/ccv/provider/client/cli/query.go index 1240e242f0..b1cbb2d9ef 100644 --- a/x/ccv/provider/client/cli/query.go +++ b/x/ccv/provider/client/cli/query.go @@ -33,6 +33,7 @@ func NewQueryCmd() *cobra.Command { cmd.AddCommand(CmdThrottleState()) cmd.AddCommand(CmdThrottledConsumerPacketData()) cmd.AddCommand(CmdRegisteredConsumerRewardDenoms()) + cmd.AddCommand(CmdProposedConsumerChains()) return cmd } @@ -93,6 +94,33 @@ func CmdConsumerChains() *cobra.Command { return cmd } +func CmdProposedConsumerChains() *cobra.Command { + cmd := &cobra.Command{ + Use: "list-proposed-consumer-chains", + Short: "Query chainIDs in consumer addition proposal before voting finishes", + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) (err error) { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + + req := &types.QueryProposedChainIDsRequest{} + res, err := queryClient.QueryProposedConsumerChainIDs(cmd.Context(), req) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + func CmdConsumerStartProposals() *cobra.Command { cmd := &cobra.Command{ Use: "list-start-proposals", diff --git a/x/ccv/provider/keeper/gov_hook.go b/x/ccv/provider/keeper/gov_hook.go new file mode 100644 index 0000000000..61f504366f --- /dev/null +++ b/x/ccv/provider/keeper/gov_hook.go @@ -0,0 +1,93 @@ +package keeper + +import ( + "fmt" + + "github.com/cosmos/gogoproto/proto" + + sdk "github.com/cosmos/cosmos-sdk/types" + govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" + sdkgov "github.com/cosmos/cosmos-sdk/x/gov/types" + v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + + "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" +) + +type GovHooks struct { + gk *govkeeper.Keeper + k *Keeper +} + +// Implements GovHooks interface +// GovHooks exist in cosmos-sdk/x/gov/keeper/hooks.go of v0.45.16-lsm-ics and on +var _ sdkgov.GovHooks = GovHooks{} + +func (k *Keeper) GovHooks(gk *govkeeper.Keeper) GovHooks { + return GovHooks{ + gk: gk, + k: k, + } +} + +// AfterProposalSubmission - call hook if registered +// After consumerAddition proposal submission, the consumer chainID is stored +func (gh GovHooks) AfterProposalSubmission(ctx sdk.Context, proposalID uint64) { + p, ok := gh.gk.GetProposal(ctx, proposalID) + if !ok { + panic(fmt.Errorf("failed to get proposal %d in gov hook", proposalID)) + } + msgs := p.GetMessages() + + for _, msg := range msgs { + var msgLegacyContent v1.MsgExecLegacyContent + err := proto.Unmarshal(msg.Value, &msgLegacyContent) + if err != nil { + panic(fmt.Errorf("failed to unmarshal proposal content in gov hook: %w", err)) + } + + // if the consumer addition proposal cannot be unmarshaled, continue + var consAdditionProp types.ConsumerAdditionProposal + if err := proto.Unmarshal(msgLegacyContent.Content.Value, &consAdditionProp); err != nil { + continue + } + + if consAdditionProp.ProposalType() == types.ProposalTypeConsumerAddition { + gh.k.SetProposedConsumerChain(ctx, consAdditionProp.ChainId, proposalID) + } + } +} + +// AfterProposalVotingPeriodEnded - call hook if registered +// After proposal voting ends, the consumer chainID in store is deleted. +// When a proposal passes, this chainID will be available in providerKeeper.GetAllPendingConsumerAdditionProps +// or providerKeeper.GetAllConsumerChains(ctx). +func (gh GovHooks) AfterProposalVotingPeriodEnded(ctx sdk.Context, proposalID uint64) { + p, ok := gh.gk.GetProposal(ctx, proposalID) + if !ok { + panic(fmt.Errorf("failed to get proposal %d in gov hook", proposalID)) + } + msgs := p.GetMessages() + + for _, msg := range msgs { + var msgLegacyContent v1.MsgExecLegacyContent + err := proto.Unmarshal(msg.Value, &msgLegacyContent) + if err != nil { + panic(fmt.Errorf("failed to unmarshal proposal content in gov hook: %w", err)) + } + + var consAdditionProp types.ConsumerAdditionProposal + // if the proposal is not ConsumerAdditionProposal, return + if err := proto.Unmarshal(msgLegacyContent.Content.Value, &consAdditionProp); err != nil { + continue + } + + if consAdditionProp.ProposalType() == types.ProposalTypeConsumerAddition { + gh.k.DeleteProposedConsumerChainInStore(ctx, proposalID) + } + } +} + +func (gh GovHooks) AfterProposalDeposit(ctx sdk.Context, proposalID uint64, depositorAddr sdk.AccAddress) { +} +func (gh GovHooks) AfterProposalVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress) {} +func (gh GovHooks) AfterProposalFailedMinDeposit(ctx sdk.Context, proposalID uint64) {} diff --git a/x/ccv/provider/keeper/grpc_query.go b/x/ccv/provider/keeper/grpc_query.go index 2b522ea9ef..93837df229 100644 --- a/x/ccv/provider/keeper/grpc_query.go +++ b/x/ccv/provider/keeper/grpc_query.go @@ -253,3 +253,17 @@ func (k Keeper) QueryRegisteredConsumerRewardDenoms(goCtx context.Context, req * Denoms: denoms, }, nil } + +func (k Keeper) QueryProposedConsumerChainIDs(goCtx context.Context, req *types.QueryProposedChainIDsRequest) (*types.QueryProposedChainIDsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + ctx := sdk.UnwrapSDKContext(goCtx) + + chains := k.GetAllProposedConsumerChainIDs(ctx) + + return &types.QueryProposedChainIDsResponse{ + ProposedChains: chains, + }, nil +} diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index e9bb1d1bd0..ba2b6f47fe 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -178,6 +178,50 @@ func (k Keeper) DeleteChainToChannel(ctx sdk.Context, chainID string) { store.Delete(types.ChainToChannelKey(chainID)) } +// SetProposedConsumerChain stores a consumer chainId corresponding to a submitted consumer addition proposal +// This consumer chainId is deleted once the voting period for the proposal ends +// does not end. +func (k Keeper) SetProposedConsumerChain(ctx sdk.Context, chainID string, proposalID uint64) { + store := ctx.KVStore(k.storeKey) + store.Set(types.ProposedConsumerChainKey(proposalID), []byte(chainID)) +} + +// GetProposedConsumerChain get the proposed chainID in consumerAddition proposal. +func (k Keeper) GetProposedConsumerChain(ctx sdk.Context, proposalID uint64) string { + store := ctx.KVStore(k.storeKey) + return string(store.Get(types.ProposedConsumerChainKey(proposalID))) +} + +// DeleteProposedConsumerChainInStore deletes the consumer chainID from store +// which is in gov consumerAddition proposal +func (k Keeper) DeleteProposedConsumerChainInStore(ctx sdk.Context, proposalID uint64) { + store := ctx.KVStore(k.storeKey) + store.Delete(types.ProposedConsumerChainKey(proposalID)) +} + +// GetAllProposedConsumerChainIDs get consumer chainId in gov consumerAddition proposal before voting period ends. +func (k Keeper) GetAllProposedConsumerChainIDs(ctx sdk.Context) []types.ProposedChain { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, []byte{types.ProposedConsumerChainByteKey}) + defer iterator.Close() + + proposedChains := []types.ProposedChain{} + for ; iterator.Valid(); iterator.Next() { + proposalID, err := types.ParseProposedConsumerChainKey(types.ProposedConsumerChainByteKey, iterator.Key()) + if err != nil { + panic(fmt.Errorf("proposed chains cannot be parsed: %w", err)) + } + + proposedChains = append(proposedChains, types.ProposedChain{ + ChainID: string(iterator.Value()), + ProposalID: proposalID, + }) + + } + + return proposedChains +} + // GetAllConsumerChains gets all of the consumer chains, for which the provider module // created IBC clients. Consumer chains with created clients are also referred to as registered. // diff --git a/x/ccv/provider/keeper/keeper_test.go b/x/ccv/provider/keeper/keeper_test.go index be3ef4001c..14c86fe775 100644 --- a/x/ccv/provider/keeper/keeper_test.go +++ b/x/ccv/provider/keeper/keeper_test.go @@ -533,3 +533,98 @@ func TestSetSlashLog(t *testing.T) { require.True(t, providerKeeper.GetSlashLog(ctx, addrWithDoubleSigns)) require.False(t, providerKeeper.GetSlashLog(ctx, addrWithoutDoubleSigns)) } + +func TestSetProposedConsumerChains(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + tests := []struct { + chainID string + proposalID uint64 + }{ + {chainID: "1", proposalID: 1}, + {chainID: "some other ID", proposalID: 12}, + {chainID: "some other other chain ID", proposalID: 123}, + {chainID: "", proposalID: 1234}, + } + + for _, test := range tests { + providerKeeper.SetProposedConsumerChain(ctx, test.chainID, test.proposalID) + cID := providerKeeper.GetProposedConsumerChain(ctx, test.proposalID) + require.Equal(t, cID, test.chainID) + } +} + +func TestDeleteProposedConsumerChainInStore(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + tests := []struct { + chainID string + proposalID uint64 + deleteProposalID uint64 + ok bool + }{ + {chainID: "1", proposalID: 1, deleteProposalID: 1, ok: true}, + {chainID: "", proposalID: 12, deleteProposalID: 12, ok: true}, + {chainID: "1", proposalID: 0, deleteProposalID: 1, ok: false}, + } + for _, test := range tests { + providerKeeper.SetProposedConsumerChain(ctx, test.chainID, test.proposalID) + providerKeeper.DeleteProposedConsumerChainInStore(ctx, test.deleteProposalID) + cID := providerKeeper.GetProposedConsumerChain(ctx, test.proposalID) + if test.ok { + require.Equal(t, cID, "") + } else { + require.Equal(t, cID, test.chainID) + } + } +} + +func TestGetAllProposedConsumerChainIDs(t *testing.T) { + providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + tests := [][]types.ProposedChain{ + {}, + { + { + ChainID: "1", + ProposalID: 1, + }, + }, + { + { + ChainID: "1", + ProposalID: 1, + }, + { + ChainID: "2", + ProposalID: 2, + }, + { + ChainID: "", + ProposalID: 3, + }, + }, + } + + for _, test := range tests { + for _, tc := range test { + providerKeeper.SetProposedConsumerChain(ctx, tc.ChainID, tc.ProposalID) + } + + chains := providerKeeper.GetAllProposedConsumerChainIDs(ctx) + + sort.Slice(chains, func(i, j int) bool { + return chains[i].ProposalID < chains[j].ProposalID + }) + sort.Slice(test, func(i, j int) bool { + return test[i].ProposalID < test[j].ProposalID + }) + require.Equal(t, chains, test) + + for _, tc := range test { + providerKeeper.DeleteProposedConsumerChainInStore(ctx, tc.ProposalID) + } + } +} diff --git a/x/ccv/provider/types/keys.go b/x/ccv/provider/types/keys.go index ee4c11015b..fa0428311c 100644 --- a/x/ccv/provider/types/keys.go +++ b/x/ccv/provider/types/keys.go @@ -138,6 +138,8 @@ const ( // handled in the current block VSCMaturedHandledThisBlockBytePrefix + // ProposedConsumerChainByteKey is the byte prefix storing the consumer chainId in consumerAddition gov proposal submitted before voting finishes + ProposedConsumerChainByteKey // NOTE: DO NOT ADD NEW BYTE PREFIXES HERE WITHOUT ADDING THEM TO getAllKeyPrefixes() IN keys_test.go ) @@ -484,6 +486,26 @@ func VSCMaturedHandledThisBlockKey() []byte { return []byte{VSCMaturedHandledThisBlockBytePrefix} } +// ProposedConsumerChainKey returns the key of proposed consumer chainId in consumerAddition gov proposal before voting finishes, the stored key format is prefix|proposalID, value is chainID +func ProposedConsumerChainKey(proposalID uint64) []byte { + return ccvtypes.AppendMany( + []byte{ProposedConsumerChainByteKey}, + sdk.Uint64ToBigEndian(proposalID), + ) +} + +// ParseProposedConsumerChainKey get the proposalID in the key +func ParseProposedConsumerChainKey(prefix byte, bz []byte) (uint64, error) { + expectedPrefix := []byte{prefix} + prefixL := len(expectedPrefix) + if prefix := bz[:prefixL]; !bytes.Equal(prefix, expectedPrefix) { + return 0, fmt.Errorf("invalid prefix; expected: %X, got: %X", expectedPrefix, prefix) + } + proposalID := sdk.BigEndianToUint64(bz[prefixL:]) + + return proposalID, nil +} + // // End of generic helpers section // diff --git a/x/ccv/provider/types/keys_test.go b/x/ccv/provider/types/keys_test.go index 9f470f4a82..6d32e49178 100644 --- a/x/ccv/provider/types/keys_test.go +++ b/x/ccv/provider/types/keys_test.go @@ -54,6 +54,7 @@ func getAllKeyPrefixes() []byte { providertypes.ConsumerAddrsToPruneBytePrefix, providertypes.SlashLogBytePrefix, providertypes.VSCMaturedHandledThisBlockBytePrefix, + providertypes.ProposedConsumerChainByteKey, } } @@ -309,3 +310,22 @@ func TestKeysWithUint64Payload(t *testing.T) { } } } + +func TestParseProposedConsumerChainKey(t *testing.T) { + tests := []struct { + chainID string + proposalID uint64 + }{ + {chainID: "1", proposalID: 1}, + {chainID: "some other ID", proposalID: 12}, + {chainID: "some other other chain ID", proposalID: 123}, + } + + for _, test := range tests { + key := providertypes.ProposedConsumerChainKey(test.proposalID) + pID, err := providertypes.ParseProposedConsumerChainKey( + providertypes.ProposedConsumerChainByteKey, key) + require.NoError(t, err) + require.Equal(t, pID, test.proposalID) + } +} diff --git a/x/ccv/provider/types/query.pb.go b/x/ccv/provider/types/query.pb.go index eac75ded70..229bb7c7e0 100644 --- a/x/ccv/provider/types/query.pb.go +++ b/x/ccv/provider/types/query.pb.go @@ -1039,6 +1039,138 @@ func (m *QueryRegisteredConsumerRewardDenomsResponse) GetDenoms() []string { return nil } +type QueryProposedChainIDsRequest struct { +} + +func (m *QueryProposedChainIDsRequest) Reset() { *m = QueryProposedChainIDsRequest{} } +func (m *QueryProposedChainIDsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryProposedChainIDsRequest) ProtoMessage() {} +func (*QueryProposedChainIDsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_422512d7b7586cd7, []int{21} +} +func (m *QueryProposedChainIDsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryProposedChainIDsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryProposedChainIDsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryProposedChainIDsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryProposedChainIDsRequest.Merge(m, src) +} +func (m *QueryProposedChainIDsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryProposedChainIDsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryProposedChainIDsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryProposedChainIDsRequest proto.InternalMessageInfo + +type QueryProposedChainIDsResponse struct { + ProposedChains []ProposedChain `protobuf:"bytes,1,rep,name=proposedChains,proto3" json:"proposedChains"` +} + +func (m *QueryProposedChainIDsResponse) Reset() { *m = QueryProposedChainIDsResponse{} } +func (m *QueryProposedChainIDsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryProposedChainIDsResponse) ProtoMessage() {} +func (*QueryProposedChainIDsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_422512d7b7586cd7, []int{22} +} +func (m *QueryProposedChainIDsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryProposedChainIDsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryProposedChainIDsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryProposedChainIDsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryProposedChainIDsResponse.Merge(m, src) +} +func (m *QueryProposedChainIDsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryProposedChainIDsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryProposedChainIDsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryProposedChainIDsResponse proto.InternalMessageInfo + +func (m *QueryProposedChainIDsResponse) GetProposedChains() []ProposedChain { + if m != nil { + return m.ProposedChains + } + return nil +} + +type ProposedChain struct { + ChainID string `protobuf:"bytes,1,opt,name=chainID,proto3" json:"chainID,omitempty"` + ProposalID uint64 `protobuf:"varint,2,opt,name=proposalID,proto3" json:"proposalID,omitempty"` +} + +func (m *ProposedChain) Reset() { *m = ProposedChain{} } +func (m *ProposedChain) String() string { return proto.CompactTextString(m) } +func (*ProposedChain) ProtoMessage() {} +func (*ProposedChain) Descriptor() ([]byte, []int) { + return fileDescriptor_422512d7b7586cd7, []int{23} +} +func (m *ProposedChain) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ProposedChain) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ProposedChain.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ProposedChain) XXX_Merge(src proto.Message) { + xxx_messageInfo_ProposedChain.Merge(m, src) +} +func (m *ProposedChain) XXX_Size() int { + return m.Size() +} +func (m *ProposedChain) XXX_DiscardUnknown() { + xxx_messageInfo_ProposedChain.DiscardUnknown(m) +} + +var xxx_messageInfo_ProposedChain proto.InternalMessageInfo + +func (m *ProposedChain) GetChainID() string { + if m != nil { + return m.ChainID + } + return "" +} + +func (m *ProposedChain) GetProposalID() uint64 { + if m != nil { + return m.ProposalID + } + return 0 +} + func init() { proto.RegisterType((*QueryConsumerGenesisRequest)(nil), "interchain_security.ccv.provider.v1.QueryConsumerGenesisRequest") proto.RegisterType((*QueryConsumerGenesisResponse)(nil), "interchain_security.ccv.provider.v1.QueryConsumerGenesisResponse") @@ -1061,6 +1193,9 @@ func init() { proto.RegisterType((*ThrottledPacketDataWrapper)(nil), "interchain_security.ccv.provider.v1.ThrottledPacketDataWrapper") proto.RegisterType((*QueryRegisteredConsumerRewardDenomsRequest)(nil), "interchain_security.ccv.provider.v1.QueryRegisteredConsumerRewardDenomsRequest") proto.RegisterType((*QueryRegisteredConsumerRewardDenomsResponse)(nil), "interchain_security.ccv.provider.v1.QueryRegisteredConsumerRewardDenomsResponse") + proto.RegisterType((*QueryProposedChainIDsRequest)(nil), "interchain_security.ccv.provider.v1.QueryProposedChainIDsRequest") + proto.RegisterType((*QueryProposedChainIDsResponse)(nil), "interchain_security.ccv.provider.v1.QueryProposedChainIDsResponse") + proto.RegisterType((*ProposedChain)(nil), "interchain_security.ccv.provider.v1.ProposedChain") } func init() { @@ -1068,90 +1203,96 @@ func init() { } var fileDescriptor_422512d7b7586cd7 = []byte{ - // 1321 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x57, 0xcf, 0x8f, 0x14, 0x45, - 0x18, 0x9d, 0x9e, 0x5d, 0x96, 0xdd, 0x5a, 0x14, 0x2c, 0x10, 0x87, 0x86, 0xcc, 0x60, 0x13, 0x75, - 0x01, 0xed, 0x66, 0x87, 0x98, 0x00, 0xba, 0x0c, 0x33, 0xcb, 0xba, 0x10, 0x20, 0xac, 0xbd, 0x04, - 0x12, 0x35, 0xb4, 0xb5, 0xdd, 0xe5, 0x4c, 0xc7, 0x9e, 0xae, 0xa6, 0xaa, 0x66, 0x96, 0x95, 0x78, - 0x50, 0x13, 0xe5, 0x48, 0x62, 0xbc, 0x79, 0xe0, 0xe4, 0x7f, 0xe1, 0x9d, 0x9b, 0x44, 0x2e, 0x9c, - 0xd0, 0x2c, 0x1e, 0x3c, 0x1a, 0xef, 0x26, 0xa6, 0xab, 0xab, 0x7b, 0x7e, 0xf5, 0xce, 0xf4, 0x0c, - 0xdc, 0x66, 0xaa, 0xeb, 0x7b, 0xdf, 0x7b, 0x6f, 0xbe, 0xaa, 0x7e, 0x03, 0x0c, 0xd7, 0xe7, 0x98, - 0xda, 0x0d, 0xe4, 0xfa, 0x16, 0xc3, 0x76, 0x8b, 0xba, 0x7c, 0xcb, 0xb0, 0xed, 0xb6, 0x11, 0x50, - 0xd2, 0x76, 0x1d, 0x4c, 0x8d, 0xf6, 0xa2, 0x71, 0xa7, 0x85, 0xe9, 0x96, 0x1e, 0x50, 0xc2, 0x09, - 0x3c, 0x96, 0x52, 0xa0, 0xdb, 0x76, 0x5b, 0x8f, 0x0b, 0xf4, 0xf6, 0xa2, 0x7a, 0xa4, 0x4e, 0x48, - 0xdd, 0xc3, 0x06, 0x0a, 0x5c, 0x03, 0xf9, 0x3e, 0xe1, 0x88, 0xbb, 0xc4, 0x67, 0x11, 0x84, 0x7a, - 0xa0, 0x4e, 0xea, 0x44, 0x7c, 0x34, 0xc2, 0x4f, 0x72, 0xb5, 0x24, 0x6b, 0xc4, 0xb7, 0x8d, 0xd6, - 0x17, 0x06, 0x77, 0x9b, 0x98, 0x71, 0xd4, 0x0c, 0xe4, 0x86, 0x72, 0x16, 0xaa, 0x09, 0x8b, 0xa8, - 0xe6, 0xd4, 0x4e, 0x35, 0xed, 0x45, 0x83, 0x35, 0x10, 0xc5, 0x8e, 0x65, 0x13, 0x9f, 0xb5, 0x9a, - 0x49, 0xc5, 0x5b, 0x43, 0x2a, 0x36, 0x5d, 0x8a, 0xa3, 0x6d, 0xda, 0x19, 0x70, 0xf8, 0xe3, 0xd0, - 0x95, 0x65, 0x59, 0xbd, 0x8a, 0x7d, 0xcc, 0x5c, 0x66, 0xe2, 0x3b, 0x2d, 0xcc, 0x38, 0x3c, 0x04, - 0x66, 0x23, 0x08, 0xd7, 0x29, 0x28, 0x47, 0x95, 0x85, 0x39, 0x73, 0xb7, 0xf8, 0x7e, 0xd9, 0xd1, - 0xee, 0x81, 0x23, 0xe9, 0x95, 0x2c, 0x20, 0x3e, 0xc3, 0xf0, 0x53, 0xf0, 0x4a, 0x3d, 0x5a, 0xb2, - 0x18, 0x47, 0x1c, 0x8b, 0xfa, 0xf9, 0xf2, 0x29, 0x7d, 0x27, 0xe3, 0xdb, 0x8b, 0x7a, 0x1f, 0xd6, - 0x7a, 0x58, 0x57, 0x9b, 0x7e, 0xf4, 0xac, 0x94, 0x33, 0xf7, 0xd4, 0xbb, 0xd6, 0xb4, 0x23, 0x40, - 0xed, 0x69, 0xbe, 0x1c, 0xc2, 0xc5, 0xac, 0x35, 0xd4, 0x27, 0x2a, 0x7e, 0x2a, 0x99, 0xd5, 0xc0, - 0x8c, 0x68, 0xcf, 0x0a, 0xca, 0xd1, 0xa9, 0x85, 0xf9, 0xf2, 0x09, 0x3d, 0xc3, 0x2c, 0xe8, 0x02, - 0xc4, 0x94, 0x95, 0xda, 0x71, 0xf0, 0xce, 0x60, 0x8b, 0x75, 0x8e, 0x28, 0x5f, 0xa3, 0x24, 0x20, - 0x0c, 0x79, 0x09, 0x9b, 0xfb, 0x0a, 0x58, 0x18, 0xbd, 0x57, 0x72, 0xfb, 0x0c, 0xcc, 0x05, 0xf1, - 0xa2, 0x74, 0xec, 0x7c, 0x36, 0x7a, 0x12, 0xbc, 0xea, 0x38, 0x6e, 0x38, 0xa4, 0x1d, 0xe8, 0x0e, - 0xa0, 0xb6, 0x00, 0xde, 0x4e, 0x63, 0x42, 0x82, 0x01, 0xd2, 0xdf, 0x2b, 0xe9, 0x02, 0x7b, 0xb6, - 0x26, 0xbf, 0xf4, 0x00, 0xe7, 0xa5, 0xb1, 0x38, 0x9b, 0xb8, 0x49, 0xda, 0xc8, 0x4b, 0xa5, 0x5c, - 0x01, 0xbb, 0x44, 0xeb, 0x21, 0xa3, 0x08, 0x0f, 0x83, 0x39, 0xdb, 0x73, 0xb1, 0xcf, 0xc3, 0x67, - 0x79, 0xf1, 0x6c, 0x36, 0x5a, 0xb8, 0xec, 0x68, 0x3f, 0x28, 0xe0, 0x4d, 0xa1, 0xe4, 0x26, 0xf2, - 0x5c, 0x07, 0x71, 0x42, 0xbb, 0xac, 0xa2, 0xa3, 0x07, 0x1d, 0x2e, 0x81, 0x7d, 0x31, 0x69, 0x0b, - 0x39, 0x0e, 0xc5, 0x8c, 0x45, 0x4d, 0x6a, 0xf0, 0xdf, 0x67, 0xa5, 0x57, 0xb7, 0x50, 0xd3, 0x3b, - 0xa7, 0xc9, 0x07, 0x9a, 0xb9, 0x37, 0xde, 0x5b, 0x8d, 0x56, 0xce, 0xcd, 0xde, 0x7f, 0x58, 0xca, - 0xfd, 0xfd, 0xb0, 0x94, 0xd3, 0xae, 0x03, 0x6d, 0x18, 0x11, 0xe9, 0xe6, 0x71, 0xb0, 0x2f, 0x3e, - 0xca, 0x49, 0xbb, 0x88, 0xd1, 0x5e, 0xbb, 0x6b, 0x7f, 0xd8, 0x6c, 0x50, 0xda, 0x5a, 0x57, 0xf3, - 0x6c, 0xd2, 0x06, 0x7a, 0x0d, 0x91, 0xd6, 0xd7, 0x7f, 0x98, 0xb4, 0x5e, 0x22, 0x1d, 0x69, 0x03, - 0x4e, 0x4a, 0x69, 0x7d, 0xae, 0x69, 0x87, 0xc1, 0x21, 0x01, 0x78, 0xa3, 0x41, 0x09, 0xe7, 0x1e, - 0x16, 0xc7, 0x3e, 0x1e, 0xce, 0x5f, 0xf2, 0xf2, 0xf8, 0xf7, 0x3d, 0x95, 0x6d, 0x4a, 0x60, 0x9e, - 0x79, 0x88, 0x35, 0xac, 0x26, 0xe6, 0x98, 0x8a, 0x0e, 0x53, 0x26, 0x10, 0x4b, 0xd7, 0xc2, 0x15, - 0x58, 0x06, 0xaf, 0x77, 0x6d, 0xb0, 0x90, 0xe7, 0x91, 0x4d, 0xe4, 0xdb, 0x58, 0x68, 0x9f, 0x32, - 0xf7, 0x77, 0xb6, 0x56, 0xe3, 0x47, 0xf0, 0x36, 0x28, 0xf8, 0xf8, 0x2e, 0xb7, 0x28, 0x0e, 0x3c, - 0xec, 0xbb, 0xac, 0x61, 0xd9, 0xc8, 0x77, 0x42, 0xb1, 0xb8, 0x30, 0x25, 0x66, 0x5e, 0xd5, 0xa3, - 0x9b, 0x5f, 0x8f, 0x6f, 0x7e, 0xfd, 0x46, 0x7c, 0xf3, 0xd7, 0x66, 0xc3, 0x3b, 0xec, 0xc1, 0x1f, - 0x25, 0xc5, 0x3c, 0x18, 0xa2, 0x98, 0x31, 0xc8, 0x72, 0x8c, 0x01, 0xd7, 0xc1, 0xee, 0x00, 0xd9, - 0x5f, 0x62, 0xce, 0x0a, 0xd3, 0xe2, 0x56, 0x3a, 0x9b, 0xe9, 0x08, 0xc5, 0x0e, 0x38, 0xeb, 0x21, - 0xe7, 0x35, 0x81, 0x60, 0xc6, 0x48, 0xda, 0x45, 0x79, 0x88, 0x93, 0x5d, 0xf1, 0xc4, 0x45, 0x1b, - 0x2f, 0x22, 0x8e, 0x32, 0xdc, 0xf4, 0xbf, 0xc7, 0x17, 0xd8, 0x50, 0x18, 0x69, 0xfe, 0x90, 0x69, - 0x83, 0x60, 0x9a, 0xb9, 0x5f, 0x45, 0x2e, 0x4f, 0x9b, 0xe2, 0x33, 0xdc, 0x04, 0xfb, 0x83, 0x04, - 0xe4, 0xb2, 0xcf, 0x78, 0x68, 0x36, 0x2b, 0x4c, 0x09, 0x0b, 0x2a, 0xe3, 0x59, 0xd0, 0x61, 0x73, - 0x8b, 0xa2, 0x20, 0xc0, 0x54, 0xbe, 0x3a, 0xd2, 0x3a, 0x68, 0xbf, 0x2a, 0xe0, 0x40, 0x9a, 0x79, - 0xf0, 0x36, 0xd8, 0x53, 0xf7, 0xc8, 0x06, 0xf2, 0x2c, 0xec, 0x73, 0xba, 0x25, 0x2f, 0xb4, 0xf7, - 0x33, 0x51, 0x59, 0x15, 0x85, 0x02, 0x6d, 0x25, 0x2c, 0x96, 0x04, 0xe6, 0x23, 0x40, 0xb1, 0x04, - 0x57, 0xc0, 0xb4, 0x83, 0x38, 0x12, 0x2e, 0xcc, 0x97, 0x4f, 0x0e, 0x7b, 0x1d, 0x76, 0xd1, 0x0a, - 0xc9, 0x4b, 0x34, 0x51, 0xae, 0x3d, 0x55, 0x80, 0xba, 0xb3, 0x72, 0xb8, 0x06, 0xf6, 0x44, 0x23, - 0x1e, 0x69, 0x97, 0x2a, 0xc6, 0xe9, 0x76, 0x29, 0x67, 0x46, 0xc7, 0x48, 0xfa, 0xf2, 0x39, 0x80, - 0x6d, 0x66, 0x5b, 0x4d, 0xc4, 0x5b, 0x61, 0xdc, 0x90, 0xb8, 0xf9, 0xd1, 0x2f, 0xf5, 0x9b, 0xeb, - 0xcb, 0xd7, 0xa2, 0xa2, 0x1e, 0xf0, 0x7d, 0x6d, 0x66, 0xf7, 0xac, 0xd7, 0x66, 0x22, 0x67, 0xb4, - 0x77, 0xc1, 0x09, 0x31, 0x6e, 0x26, 0xae, 0xbb, 0x8c, 0x63, 0xda, 0x99, 0x37, 0x13, 0x6f, 0x22, - 0xea, 0x5c, 0xc4, 0x3e, 0x69, 0x26, 0x6f, 0xaa, 0x15, 0x70, 0x32, 0xd3, 0x6e, 0x39, 0x9f, 0x07, - 0xc1, 0x8c, 0x23, 0x56, 0xc4, 0xcb, 0x7f, 0xce, 0x94, 0xdf, 0xca, 0x3f, 0xbf, 0x06, 0x76, 0x09, - 0x1c, 0xb8, 0xad, 0x80, 0x03, 0x69, 0xc9, 0x06, 0x5e, 0xc8, 0x34, 0x03, 0x43, 0xe2, 0x94, 0x5a, - 0x7d, 0x01, 0x84, 0x88, 0xbf, 0xb6, 0xf2, 0xed, 0x93, 0xbf, 0x7e, 0xcc, 0x57, 0xe0, 0xd2, 0xe8, - 0xc4, 0x9b, 0x5c, 0xed, 0x32, 0x3a, 0x19, 0xf7, 0xe2, 0x93, 0xf9, 0x35, 0x7c, 0xa2, 0x80, 0xfd, - 0x29, 0x19, 0x09, 0x56, 0xc6, 0x67, 0xd8, 0x93, 0xbd, 0xd4, 0x0b, 0x93, 0x03, 0x48, 0x85, 0x67, - 0x85, 0xc2, 0xd3, 0x70, 0x71, 0x0c, 0x85, 0x51, 0x2a, 0x83, 0xdf, 0xe4, 0x41, 0x61, 0x87, 0xa8, - 0xc5, 0xe0, 0xd5, 0x09, 0x99, 0xa5, 0xa6, 0x3a, 0xf5, 0xda, 0x4b, 0x42, 0x93, 0xa2, 0x2f, 0x09, - 0xd1, 0x35, 0x78, 0x61, 0x5c, 0xd1, 0x61, 0xb8, 0xa6, 0xdc, 0x4a, 0x02, 0x13, 0xfc, 0x4f, 0x01, - 0x6f, 0xa4, 0x27, 0x37, 0x06, 0xaf, 0x4c, 0x4c, 0x7a, 0x30, 0x22, 0xaa, 0x57, 0x5f, 0x0e, 0x98, - 0x34, 0x60, 0x55, 0x18, 0x50, 0x85, 0x95, 0x09, 0x0c, 0x20, 0x41, 0x97, 0xfe, 0x7f, 0x14, 0x19, - 0x0e, 0x52, 0x63, 0x16, 0xfc, 0x28, 0x3b, 0xeb, 0x61, 0x81, 0x51, 0x5d, 0x7d, 0x61, 0x1c, 0x29, - 0xbc, 0x2a, 0x84, 0x7f, 0x00, 0xcf, 0x66, 0xf8, 0x0b, 0x1b, 0x03, 0x59, 0x3d, 0xa9, 0x2d, 0x45, - 0x72, 0x77, 0xfc, 0x9a, 0x48, 0x72, 0x4a, 0x90, 0x9c, 0x48, 0x72, 0x5a, 0x0e, 0x9c, 0x4c, 0x72, - 0x4f, 0x72, 0x84, 0xbf, 0x29, 0x00, 0x0e, 0x46, 0x40, 0x78, 0x3e, 0x3b, 0xc5, 0xb4, 0x64, 0xa9, - 0x56, 0x26, 0xae, 0x97, 0xd2, 0xce, 0x08, 0x69, 0x65, 0x78, 0x6a, 0xb4, 0x34, 0x2e, 0x01, 0xa2, - 0xbf, 0xc7, 0xf0, 0xbb, 0x3c, 0x38, 0x3a, 0x2a, 0x65, 0x8d, 0x73, 0x87, 0x8d, 0xce, 0x7c, 0xe3, - 0xdc, 0x61, 0x19, 0xa2, 0x9f, 0x56, 0x13, 0xda, 0x3f, 0x84, 0xe7, 0x46, 0x6b, 0x0f, 0xb0, 0xef, - 0xb8, 0x7e, 0xbd, 0x33, 0xc7, 0x32, 0xb1, 0xc2, 0x9f, 0xf2, 0xe0, 0x58, 0x86, 0xd7, 0x39, 0xbc, - 0x9e, 0x9d, 0x7a, 0xa6, 0x18, 0xa1, 0xae, 0xbd, 0x3c, 0x40, 0x69, 0xc7, 0x15, 0x61, 0xc7, 0x0a, - 0x5c, 0x1e, 0x6d, 0x07, 0x4d, 0x10, 0x3b, 0x8e, 0x50, 0x81, 0x69, 0x45, 0xf1, 0xa4, 0x76, 0xeb, - 0xd1, 0x76, 0x51, 0x79, 0xbc, 0x5d, 0x54, 0xfe, 0xdc, 0x2e, 0x2a, 0x0f, 0x9e, 0x17, 0x73, 0x8f, - 0x9f, 0x17, 0x73, 0x4f, 0x9f, 0x17, 0x73, 0x9f, 0x2c, 0xd5, 0x5d, 0xde, 0x68, 0x6d, 0xe8, 0x36, - 0x69, 0x1a, 0x36, 0x61, 0x4d, 0xc2, 0xba, 0xfa, 0xbd, 0x97, 0xf4, 0x6b, 0x9f, 0x36, 0xee, 0xf6, - 0xcd, 0xdf, 0x56, 0x80, 0xd9, 0xc6, 0x8c, 0xf8, 0xb7, 0x72, 0xfa, 0xff, 0x00, 0x00, 0x00, 0xff, - 0xff, 0x03, 0xbe, 0x75, 0x9c, 0x41, 0x13, 0x00, 0x00, + // 1418 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x58, 0xcf, 0x6f, 0x13, 0xc7, + 0x17, 0xf7, 0x3a, 0x21, 0x24, 0x2f, 0xfc, 0xd2, 0x84, 0x2f, 0x5f, 0xb3, 0x50, 0x9b, 0x2e, 0x6a, + 0x1b, 0xa0, 0xf5, 0x12, 0xa3, 0x4a, 0x40, 0x1b, 0x42, 0x1c, 0xa7, 0xc1, 0x02, 0x44, 0xba, 0x41, + 0x20, 0xb5, 0x15, 0xcb, 0x64, 0x77, 0xea, 0xac, 0xba, 0xde, 0x5d, 0x76, 0xc6, 0x0e, 0x29, 0xea, + 0x81, 0x56, 0x6a, 0xe9, 0x0d, 0xa9, 0xea, 0x9d, 0x53, 0xff, 0x8b, 0xde, 0xb9, 0x15, 0x95, 0x0b, + 0x27, 0x5a, 0x85, 0x1e, 0xaa, 0x9e, 0xaa, 0xde, 0x2b, 0x55, 0x3b, 0x3b, 0xbb, 0xfe, 0xb5, 0xb1, + 0xd7, 0x26, 0x37, 0x7b, 0xe6, 0xbd, 0xcf, 0xfb, 0x7c, 0x5e, 0xde, 0xcc, 0x7c, 0x1c, 0x50, 0x2d, + 0x87, 0x11, 0xdf, 0xd8, 0xc0, 0x96, 0xa3, 0x53, 0x62, 0x34, 0x7c, 0x8b, 0x6d, 0xa9, 0x86, 0xd1, + 0x54, 0x3d, 0xdf, 0x6d, 0x5a, 0x26, 0xf1, 0xd5, 0xe6, 0x9c, 0x7a, 0xaf, 0x41, 0xfc, 0xad, 0xa2, + 0xe7, 0xbb, 0xcc, 0x45, 0x27, 0x13, 0x12, 0x8a, 0x86, 0xd1, 0x2c, 0x46, 0x09, 0xc5, 0xe6, 0x9c, + 0x7c, 0xbc, 0xe6, 0xba, 0x35, 0x9b, 0xa8, 0xd8, 0xb3, 0x54, 0xec, 0x38, 0x2e, 0xc3, 0xcc, 0x72, + 0x1d, 0x1a, 0x42, 0xc8, 0x87, 0x6b, 0x6e, 0xcd, 0xe5, 0x1f, 0xd5, 0xe0, 0x93, 0x58, 0x2d, 0x88, + 0x1c, 0xfe, 0x6d, 0xbd, 0xf1, 0xb9, 0xca, 0xac, 0x3a, 0xa1, 0x0c, 0xd7, 0x3d, 0x11, 0x50, 0x4a, + 0x43, 0x35, 0x66, 0x11, 0xe6, 0x9c, 0xdd, 0x29, 0xa7, 0x39, 0xa7, 0xd2, 0x0d, 0xec, 0x13, 0x53, + 0x37, 0x5c, 0x87, 0x36, 0xea, 0x71, 0xc6, 0x5b, 0x7d, 0x32, 0x36, 0x2d, 0x9f, 0x84, 0x61, 0xca, + 0x79, 0x38, 0xf6, 0x71, 0xd0, 0x95, 0x25, 0x91, 0xbd, 0x42, 0x1c, 0x42, 0x2d, 0xaa, 0x91, 0x7b, + 0x0d, 0x42, 0x19, 0x3a, 0x0a, 0x93, 0x21, 0x84, 0x65, 0xe6, 0xa4, 0x13, 0xd2, 0xec, 0x94, 0xb6, + 0x97, 0x7f, 0xaf, 0x9a, 0xca, 0x03, 0x38, 0x9e, 0x9c, 0x49, 0x3d, 0xd7, 0xa1, 0x04, 0x7d, 0x0a, + 0xfb, 0x6b, 0xe1, 0x92, 0x4e, 0x19, 0x66, 0x84, 0xe7, 0x4f, 0x97, 0xce, 0x16, 0x77, 0x6a, 0x7c, + 0x73, 0xae, 0xd8, 0x85, 0xb5, 0x16, 0xe4, 0x95, 0xc7, 0x9f, 0xbe, 0x2c, 0x64, 0xb4, 0x7d, 0xb5, + 0xb6, 0x35, 0xe5, 0x38, 0xc8, 0x1d, 0xc5, 0x97, 0x02, 0xb8, 0x88, 0xb5, 0x82, 0xbb, 0x44, 0x45, + 0xbb, 0x82, 0x59, 0x19, 0x26, 0x78, 0x79, 0x9a, 0x93, 0x4e, 0x8c, 0xcd, 0x4e, 0x97, 0x4e, 0x17, + 0x53, 0xcc, 0x42, 0x91, 0x83, 0x68, 0x22, 0x53, 0x39, 0x05, 0xef, 0xf4, 0x96, 0x58, 0x63, 0xd8, + 0x67, 0xab, 0xbe, 0xeb, 0xb9, 0x14, 0xdb, 0x31, 0x9b, 0x47, 0x12, 0xcc, 0x0e, 0x8e, 0x15, 0xdc, + 0x3e, 0x83, 0x29, 0x2f, 0x5a, 0x14, 0x1d, 0xbb, 0x94, 0x8e, 0x9e, 0x00, 0x5f, 0x34, 0x4d, 0x2b, + 0x18, 0xd2, 0x16, 0x74, 0x0b, 0x50, 0x99, 0x85, 0xb7, 0x93, 0x98, 0xb8, 0x5e, 0x0f, 0xe9, 0x6f, + 0xa5, 0x64, 0x81, 0x1d, 0xa1, 0xf1, 0x5f, 0xba, 0x87, 0xf3, 0xfc, 0x50, 0x9c, 0x35, 0x52, 0x77, + 0x9b, 0xd8, 0x4e, 0xa4, 0xbc, 0x00, 0x7b, 0x78, 0xe9, 0x3e, 0xa3, 0x88, 0x8e, 0xc1, 0x94, 0x61, + 0x5b, 0xc4, 0x61, 0xc1, 0x5e, 0x96, 0xef, 0x4d, 0x86, 0x0b, 0x55, 0x53, 0xf9, 0x4e, 0x82, 0x37, + 0xb9, 0x92, 0x5b, 0xd8, 0xb6, 0x4c, 0xcc, 0x5c, 0xbf, 0xad, 0x55, 0xfe, 0xe0, 0x41, 0x47, 0xf3, + 0x70, 0x28, 0x22, 0xad, 0x63, 0xd3, 0xf4, 0x09, 0xa5, 0x61, 0x91, 0x32, 0xfa, 0xe7, 0x65, 0xe1, + 0xc0, 0x16, 0xae, 0xdb, 0x17, 0x15, 0xb1, 0xa1, 0x68, 0x07, 0xa3, 0xd8, 0xc5, 0x70, 0xe5, 0xe2, + 0xe4, 0xa3, 0x27, 0x85, 0xcc, 0x9f, 0x4f, 0x0a, 0x19, 0xe5, 0x06, 0x28, 0xfd, 0x88, 0x88, 0x6e, + 0x9e, 0x82, 0x43, 0xd1, 0x51, 0x8e, 0xcb, 0x85, 0x8c, 0x0e, 0x1a, 0x6d, 0xf1, 0x41, 0xb1, 0x5e, + 0x69, 0xab, 0x6d, 0xc5, 0xd3, 0x49, 0xeb, 0xa9, 0xd5, 0x47, 0x5a, 0x57, 0xfd, 0x7e, 0xd2, 0x3a, + 0x89, 0xb4, 0xa4, 0xf5, 0x74, 0x52, 0x48, 0xeb, 0xea, 0x9a, 0x72, 0x0c, 0x8e, 0x72, 0xc0, 0x9b, + 0x1b, 0xbe, 0xcb, 0x98, 0x4d, 0xf8, 0xb1, 0x8f, 0x86, 0xf3, 0xa7, 0xac, 0x38, 0xfe, 0x5d, 0xbb, + 0xa2, 0x4c, 0x01, 0xa6, 0xa9, 0x8d, 0xe9, 0x86, 0x5e, 0x27, 0x8c, 0xf8, 0xbc, 0xc2, 0x98, 0x06, + 0x7c, 0xe9, 0x7a, 0xb0, 0x82, 0x4a, 0xf0, 0xbf, 0xb6, 0x00, 0x1d, 0xdb, 0xb6, 0xbb, 0x89, 0x1d, + 0x83, 0x70, 0xed, 0x63, 0xda, 0x4c, 0x2b, 0x74, 0x31, 0xda, 0x42, 0x77, 0x20, 0xe7, 0x90, 0xfb, + 0x4c, 0xf7, 0x89, 0x67, 0x13, 0xc7, 0xa2, 0x1b, 0xba, 0x81, 0x1d, 0x33, 0x10, 0x4b, 0x72, 0x63, + 0x7c, 0xe6, 0xe5, 0x62, 0x78, 0xf3, 0x17, 0xa3, 0x9b, 0xbf, 0x78, 0x33, 0xba, 0xf9, 0xcb, 0x93, + 0xc1, 0x1d, 0xf6, 0xf8, 0xb7, 0x82, 0xa4, 0x1d, 0x09, 0x50, 0xb4, 0x08, 0x64, 0x29, 0xc2, 0x40, + 0x6b, 0xb0, 0xd7, 0xc3, 0xc6, 0x17, 0x84, 0xd1, 0xdc, 0x38, 0xbf, 0x95, 0x2e, 0xa4, 0x3a, 0x42, + 0x51, 0x07, 0xcc, 0xb5, 0x80, 0xf3, 0x2a, 0x47, 0xd0, 0x22, 0x24, 0xa5, 0x22, 0x0e, 0x71, 0x1c, + 0x15, 0x4d, 0x5c, 0x18, 0x58, 0xc1, 0x0c, 0xa7, 0xb8, 0xe9, 0x7f, 0x8d, 0x2e, 0xb0, 0xbe, 0x30, + 0xa2, 0xf9, 0x7d, 0xa6, 0x0d, 0xc1, 0x38, 0xb5, 0xbe, 0x0c, 0xbb, 0x3c, 0xae, 0xf1, 0xcf, 0x68, + 0x13, 0x66, 0xbc, 0x18, 0xa4, 0xea, 0x50, 0x16, 0x34, 0x9b, 0xe6, 0xc6, 0x78, 0x0b, 0x16, 0x86, + 0x6b, 0x41, 0x8b, 0xcd, 0x6d, 0x1f, 0x7b, 0x1e, 0xf1, 0xc5, 0xd3, 0x91, 0x54, 0x41, 0xf9, 0x59, + 0x82, 0xc3, 0x49, 0xcd, 0x43, 0x77, 0x60, 0x5f, 0xcd, 0x76, 0xd7, 0xb1, 0xad, 0x13, 0x87, 0xf9, + 0x5b, 0xe2, 0x42, 0x7b, 0x3f, 0x15, 0x95, 0x15, 0x9e, 0xc8, 0xd1, 0x96, 0x83, 0x64, 0x41, 0x60, + 0x3a, 0x04, 0xe4, 0x4b, 0x68, 0x19, 0xc6, 0x4d, 0xcc, 0x30, 0xef, 0xc2, 0x74, 0xe9, 0x4c, 0xbf, + 0xe7, 0xb0, 0x8d, 0x56, 0x40, 0x5e, 0xa0, 0xf1, 0x74, 0xe5, 0x85, 0x04, 0xf2, 0xce, 0xca, 0xd1, + 0x2a, 0xec, 0x0b, 0x47, 0x3c, 0xd4, 0x2e, 0x54, 0x0c, 0x53, 0xed, 0x4a, 0x46, 0x0b, 0x8f, 0x91, + 0xe8, 0xcb, 0x5d, 0x40, 0x4d, 0x6a, 0xe8, 0x75, 0xcc, 0x1a, 0x81, 0xdd, 0x10, 0xb8, 0xd9, 0xc1, + 0x8f, 0xfa, 0xad, 0xb5, 0xa5, 0xeb, 0x61, 0x52, 0x07, 0xf8, 0xa1, 0x26, 0x35, 0x3a, 0xd6, 0xcb, + 0x13, 0x61, 0x67, 0x94, 0x77, 0xe1, 0x34, 0x1f, 0x37, 0x8d, 0xd4, 0x2c, 0xca, 0x88, 0xdf, 0x9a, + 0x37, 0x8d, 0x6c, 0x62, 0xdf, 0xac, 0x10, 0xc7, 0xad, 0xc7, 0x2f, 0xd5, 0x32, 0x9c, 0x49, 0x15, + 0x2d, 0xe6, 0xf3, 0x08, 0x4c, 0x98, 0x7c, 0x85, 0x3f, 0xfe, 0x53, 0x9a, 0xf8, 0xa6, 0xe4, 0x85, + 0x9d, 0x09, 0x1f, 0x21, 0x62, 0xf2, 0x47, 0xa7, 0x5a, 0x89, 0xcb, 0x3c, 0x94, 0xe0, 0x8d, 0x1d, + 0x02, 0x04, 0xf2, 0x5d, 0x38, 0xe0, 0xb5, 0xef, 0x45, 0xf6, 0xa2, 0x94, 0x6a, 0x74, 0x3a, 0x60, + 0xc5, 0x5f, 0xba, 0x0b, 0x4f, 0xa9, 0xc2, 0xfe, 0x8e, 0x30, 0x94, 0x03, 0x71, 0xb8, 0x2a, 0x9d, + 0x67, 0xad, 0x82, 0xf2, 0x00, 0xd1, 0x1b, 0x5a, 0xad, 0x88, 0x13, 0xd7, 0xb6, 0x52, 0xfa, 0x7e, + 0x06, 0xf6, 0x70, 0x39, 0x68, 0x5b, 0x82, 0xc3, 0x49, 0x46, 0x0e, 0x5d, 0x4e, 0xc5, 0xbb, 0x8f, + 0x7b, 0x94, 0x17, 0x5f, 0x03, 0x21, 0x6c, 0xaa, 0xb2, 0xfc, 0xf5, 0xf3, 0x3f, 0x7e, 0xc8, 0x2e, + 0xa0, 0xf9, 0xc1, 0x06, 0x3f, 0x7e, 0xc9, 0x84, 0x53, 0x54, 0x1f, 0x44, 0x17, 0xd1, 0x57, 0xe8, + 0xb9, 0x04, 0x33, 0x09, 0x96, 0x10, 0x2d, 0x0c, 0xcf, 0xb0, 0xc3, 0x6a, 0xca, 0x97, 0x47, 0x07, + 0x10, 0x0a, 0x2f, 0x70, 0x85, 0xe7, 0xd0, 0xdc, 0x10, 0x0a, 0x43, 0x13, 0x8a, 0x1e, 0x66, 0x21, + 0xb7, 0x83, 0xb3, 0xa4, 0xe8, 0xda, 0x88, 0xcc, 0x12, 0x4d, 0xac, 0x7c, 0x7d, 0x97, 0xd0, 0x84, + 0xe8, 0x2b, 0x5c, 0x74, 0x19, 0x5d, 0x1e, 0x56, 0x74, 0xf0, 0x5b, 0xc2, 0x67, 0x7a, 0xec, 0x0f, + 0xd1, 0xbf, 0x12, 0xfc, 0x3f, 0xd9, 0xa8, 0x52, 0x74, 0x75, 0x64, 0xd2, 0xbd, 0x8e, 0x58, 0xbe, + 0xb6, 0x3b, 0x60, 0xa2, 0x01, 0x2b, 0xbc, 0x01, 0x8b, 0x68, 0x61, 0x84, 0x06, 0xb8, 0x5e, 0x9b, + 0xfe, 0xbf, 0x25, 0xe1, 0x85, 0x12, 0x5d, 0x25, 0xfa, 0x28, 0x3d, 0xeb, 0x7e, 0xfe, 0x58, 0x5e, + 0x79, 0x6d, 0x1c, 0x21, 0x7c, 0x91, 0x0b, 0xff, 0x00, 0x5d, 0x48, 0xf1, 0x8b, 0x3d, 0x02, 0xd2, + 0x3b, 0x4c, 0x6a, 0x82, 0xe4, 0x76, 0xb7, 0x39, 0x92, 0xe4, 0x04, 0xdf, 0x3c, 0x92, 0xe4, 0x24, + 0xdb, 0x3b, 0x9a, 0xe4, 0x0e, 0xa3, 0x8c, 0x7e, 0x91, 0x00, 0xf5, 0x3a, 0x5e, 0x74, 0x29, 0x3d, + 0xc5, 0x24, 0x23, 0x2d, 0x2f, 0x8c, 0x9c, 0x2f, 0xa4, 0x9d, 0xe7, 0xd2, 0x4a, 0xe8, 0xec, 0x60, + 0x69, 0x4c, 0x00, 0x84, 0xff, 0x0d, 0x40, 0xdf, 0x64, 0xe1, 0xc4, 0x20, 0x53, 0x39, 0xcc, 0x1d, + 0x36, 0xd8, 0xe2, 0x0e, 0x73, 0x87, 0xa5, 0x70, 0xba, 0x4a, 0x99, 0x6b, 0xff, 0x10, 0x5d, 0x1c, + 0xac, 0xdd, 0x23, 0x8e, 0x69, 0x39, 0xb5, 0xd6, 0x1c, 0x0b, 0x83, 0x8e, 0x7e, 0xcc, 0xc2, 0xc9, + 0x14, 0xee, 0x05, 0xdd, 0x48, 0x4f, 0x3d, 0x95, 0x6b, 0x92, 0x57, 0x77, 0x0f, 0x50, 0xb4, 0xe3, + 0x2a, 0x6f, 0xc7, 0x32, 0x5a, 0x1a, 0xdc, 0x0e, 0x3f, 0x46, 0x6c, 0x75, 0xc4, 0xe7, 0x98, 0x7a, + 0xe8, 0xc6, 0xd0, 0x5f, 0x3d, 0x6e, 0xab, 0xfd, 0x4a, 0xad, 0x56, 0x28, 0x1a, 0xc2, 0x5b, 0xec, + 0x60, 0xe9, 0xe4, 0xf2, 0xeb, 0x40, 0x8c, 0x30, 0x04, 0x02, 0x43, 0xef, 0x7a, 0xc6, 0xcb, 0xb7, + 0x9f, 0x6e, 0xe7, 0xa5, 0x67, 0xdb, 0x79, 0xe9, 0xf7, 0xed, 0xbc, 0xf4, 0xf8, 0x55, 0x3e, 0xf3, + 0xec, 0x55, 0x3e, 0xf3, 0xe2, 0x55, 0x3e, 0xf3, 0xc9, 0x7c, 0xcd, 0x62, 0x1b, 0x8d, 0xf5, 0xa2, + 0xe1, 0xd6, 0x55, 0xc3, 0xa5, 0x75, 0x97, 0xb6, 0x95, 0x79, 0x2f, 0x2e, 0xd3, 0x3c, 0xa7, 0xde, + 0xef, 0x3a, 0x6c, 0x5b, 0x1e, 0xa1, 0xeb, 0x13, 0xfc, 0x97, 0xe8, 0xb9, 0xff, 0x02, 0x00, 0x00, + 0xff, 0xff, 0x1c, 0xfd, 0x42, 0x7e, 0x1d, 0x15, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1191,6 +1332,9 @@ type QueryClient interface { // QueryRegisteredConsumerRewardDenoms returns a list of consumer reward // denoms that are registered QueryRegisteredConsumerRewardDenoms(ctx context.Context, in *QueryRegisteredConsumerRewardDenomsRequest, opts ...grpc.CallOption) (*QueryRegisteredConsumerRewardDenomsResponse, error) + // QueryProposedConsumerChainIDs query chainIDs in consumerAdditionProposals + // before voting period finishes, after voting period finishes, this chainID will be removed from the result + QueryProposedConsumerChainIDs(ctx context.Context, in *QueryProposedChainIDsRequest, opts ...grpc.CallOption) (*QueryProposedChainIDsResponse, error) } type queryClient struct { @@ -1282,6 +1426,15 @@ func (c *queryClient) QueryRegisteredConsumerRewardDenoms(ctx context.Context, i return out, nil } +func (c *queryClient) QueryProposedConsumerChainIDs(ctx context.Context, in *QueryProposedChainIDsRequest, opts ...grpc.CallOption) (*QueryProposedChainIDsResponse, error) { + out := new(QueryProposedChainIDsResponse) + err := c.cc.Invoke(ctx, "/interchain_security.ccv.provider.v1.Query/QueryProposedConsumerChainIDs", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // QueryServer is the server API for Query service. type QueryServer interface { // ConsumerGenesis queries the genesis state needed to start a consumer chain @@ -1309,6 +1462,9 @@ type QueryServer interface { // QueryRegisteredConsumerRewardDenoms returns a list of consumer reward // denoms that are registered QueryRegisteredConsumerRewardDenoms(context.Context, *QueryRegisteredConsumerRewardDenomsRequest) (*QueryRegisteredConsumerRewardDenomsResponse, error) + // QueryProposedConsumerChainIDs query chainIDs in consumerAdditionProposals + // before voting period finishes, after voting period finishes, this chainID will be removed from the result + QueryProposedConsumerChainIDs(context.Context, *QueryProposedChainIDsRequest) (*QueryProposedChainIDsResponse, error) } // UnimplementedQueryServer can be embedded to have forward compatible implementations. @@ -1342,6 +1498,9 @@ func (*UnimplementedQueryServer) QueryThrottledConsumerPacketData(ctx context.Co func (*UnimplementedQueryServer) QueryRegisteredConsumerRewardDenoms(ctx context.Context, req *QueryRegisteredConsumerRewardDenomsRequest) (*QueryRegisteredConsumerRewardDenomsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method QueryRegisteredConsumerRewardDenoms not implemented") } +func (*UnimplementedQueryServer) QueryProposedConsumerChainIDs(ctx context.Context, req *QueryProposedChainIDsRequest) (*QueryProposedChainIDsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method QueryProposedConsumerChainIDs not implemented") +} func RegisterQueryServer(s grpc1.Server, srv QueryServer) { s.RegisterService(&_Query_serviceDesc, srv) @@ -1509,6 +1668,24 @@ func _Query_QueryRegisteredConsumerRewardDenoms_Handler(srv interface{}, ctx con return interceptor(ctx, in, info, handler) } +func _Query_QueryProposedConsumerChainIDs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryProposedChainIDsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).QueryProposedConsumerChainIDs(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/interchain_security.ccv.provider.v1.Query/QueryProposedConsumerChainIDs", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).QueryProposedConsumerChainIDs(ctx, req.(*QueryProposedChainIDsRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _Query_serviceDesc = grpc.ServiceDesc{ ServiceName: "interchain_security.ccv.provider.v1.Query", HandlerType: (*QueryServer)(nil), @@ -1549,6 +1726,10 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "QueryRegisteredConsumerRewardDenoms", Handler: _Query_QueryRegisteredConsumerRewardDenoms_Handler, }, + { + MethodName: "QueryProposedConsumerChainIDs", + Handler: _Query_QueryProposedConsumerChainIDs_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "interchain_security/ccv/provider/v1/query.proto", @@ -2293,6 +2474,101 @@ func (m *QueryRegisteredConsumerRewardDenomsResponse) MarshalToSizedBuffer(dAtA return len(dAtA) - i, nil } +func (m *QueryProposedChainIDsRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryProposedChainIDsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryProposedChainIDsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryProposedChainIDsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryProposedChainIDsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryProposedChainIDsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ProposedChains) > 0 { + for iNdEx := len(m.ProposedChains) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ProposedChains[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *ProposedChain) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ProposedChain) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ProposedChain) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ProposalID != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.ProposalID)) + i-- + dAtA[i] = 0x10 + } + if len(m.ChainID) > 0 { + i -= len(m.ChainID) + copy(dAtA[i:], m.ChainID) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ChainID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { offset -= sovQuery(v) base := offset @@ -2613,6 +2889,46 @@ func (m *QueryRegisteredConsumerRewardDenomsResponse) Size() (n int) { return n } +func (m *QueryProposedChainIDsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryProposedChainIDsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.ProposedChains) > 0 { + for _, e := range m.ProposedChains { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + +func (m *ProposedChain) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ChainID) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.ProposalID != 0 { + n += 1 + sovQuery(uint64(m.ProposalID)) + } + return n +} + func sovQuery(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -4486,6 +4802,241 @@ func (m *QueryRegisteredConsumerRewardDenomsResponse) Unmarshal(dAtA []byte) err } return nil } +func (m *QueryProposedChainIDsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryProposedChainIDsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryProposedChainIDsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryProposedChainIDsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryProposedChainIDsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryProposedChainIDsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProposedChains", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProposedChains = append(m.ProposedChains, ProposedChain{}) + if err := m.ProposedChains[len(m.ProposedChains)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ProposedChain) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ProposedChain: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ProposedChain: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ChainID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ProposalID", wireType) + } + m.ProposalID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ProposalID |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipQuery(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/ccv/provider/types/query.pb.gw.go b/x/ccv/provider/types/query.pb.gw.go index 6a249ca2a2..c417c4688c 100644 --- a/x/ccv/provider/types/query.pb.gw.go +++ b/x/ccv/provider/types/query.pb.gw.go @@ -285,6 +285,24 @@ func local_request_Query_QueryRegisteredConsumerRewardDenoms_0(ctx context.Conte } +func request_Query_QueryProposedConsumerChainIDs_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryProposedChainIDsRequest + var metadata runtime.ServerMetadata + + msg, err := client.QueryProposedConsumerChainIDs(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_QueryProposedConsumerChainIDs_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryProposedChainIDsRequest + var metadata runtime.ServerMetadata + + msg, err := server.QueryProposedConsumerChainIDs(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -498,6 +516,29 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) + mux.Handle("GET", pattern_Query_QueryProposedConsumerChainIDs_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_QueryProposedConsumerChainIDs_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryProposedConsumerChainIDs_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -719,6 +760,26 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) + mux.Handle("GET", pattern_Query_QueryProposedConsumerChainIDs_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_QueryProposedConsumerChainIDs_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_QueryProposedConsumerChainIDs_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -740,6 +801,8 @@ var ( pattern_Query_QueryThrottledConsumerPacketData_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"interchain_security", "ccv", "provider", "pending_consumer_packets"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Query_QueryRegisteredConsumerRewardDenoms_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"interchain_security", "ccv", "provider", "registered_consumer_reward_denoms"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_QueryProposedConsumerChainIDs_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"interchain_security", "ccv", "provider", "proposed_consumer_chains"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( @@ -760,4 +823,6 @@ var ( forward_Query_QueryThrottledConsumerPacketData_0 = runtime.ForwardResponseMessage forward_Query_QueryRegisteredConsumerRewardDenoms_0 = runtime.ForwardResponseMessage + + forward_Query_QueryProposedConsumerChainIDs_0 = runtime.ForwardResponseMessage ) From 3a3e157b79cfde1aa1213fbb33db67a2ea25e1a3 Mon Sep 17 00:00:00 2001 From: Yaru Wang Date: Mon, 2 Oct 2023 17:19:40 +0200 Subject: [PATCH 02/22] fix test --- tests/e2e/config.go | 22 +++++++++++----------- tests/e2e/state.go | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/e2e/config.go b/tests/e2e/config.go index f17deb9843..f58af0de65 100644 --- a/tests/e2e/config.go +++ b/tests/e2e/config.go @@ -395,7 +395,7 @@ func ChangeoverTestConfig() TestConfig { return tr } -func (tr *TestRun) SetDockerConfig(localSdkPath string, useGaia bool, gaiaTag string) { +func (s *TestConfig) SetDockerConfig(localSdkPath string, useGaia bool, gaiaTag string) { if localSdkPath != "" { fmt.Println("USING LOCAL SDK", localSdkPath) } @@ -403,17 +403,17 @@ func (tr *TestRun) SetDockerConfig(localSdkPath string, useGaia bool, gaiaTag st fmt.Println("USING GAIA INSTEAD OF ICS provider app", gaiaTag) } - tr.useGaia = useGaia - tr.gaiaTag = gaiaTag - tr.localSdkPath = localSdkPath + s.useGaia = useGaia + s.gaiaTag = gaiaTag + s.localSdkPath = localSdkPath } -func (tr *TestRun) SetCometMockConfig(useCometmock bool) { - tr.useCometmock = useCometmock +func (s *TestConfig) SetCometMockConfig(useCometmock bool) { + s.useCometmock = useCometmock } -func (tr *TestRun) SetRelayerConfig(useRly bool) { - tr.useGorelayer = useRly +func (s *TestConfig) SetRelayerConfig(useRly bool) { + s.useGorelayer = useRly } // validateStringLiterals enforces that configs follow the constraints @@ -423,8 +423,8 @@ func (tr *TestRun) SetRelayerConfig(useRly bool) { // within the container will be named as "$CHAIN_ID-$VAL_ID-out" etc. // where this name is constrained to 15 bytes or less. Therefore each string literal // used as a validatorID or chainID needs to be 5 char or less. -func (tr *TestRun) validateStringLiterals() { - for valID, valConfig := range tr.validatorConfigs { +func (s *TestConfig) validateStringLiterals() { + for valID, valConfig := range s.validatorConfigs { if len(valID) > 5 { panic("validator id string literal must be 5 char or less") } @@ -447,7 +447,7 @@ func (tr *TestRun) validateStringLiterals() { } } - for chainID, chainConfig := range tr.chainConfigs { + for chainID, chainConfig := range s.chainConfigs { if len(chainID) > 5 { panic("chain id string literal must be 5 char or less") } diff --git a/tests/e2e/state.go b/tests/e2e/state.go index f4001b4879..41a20bccc8 100644 --- a/tests/e2e/state.go +++ b/tests/e2e/state.go @@ -779,7 +779,7 @@ func (tr TestConfig) curlJsonRPCRequest(method, params, address string) { executeCommandWithVerbosity(cmd, "curlJsonRPCRequest", verbosity) } -func (tr TestRun) getProposedConsumerChains(chain ChainID) []string { +func (tr TestConfig) getProposedConsumerChains(chain ChainID) []string { tr.waitBlocks(chain, 1, 10*time.Second) //#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments. bz, err := exec.Command("docker", "exec", tr.containerConfig.InstanceName, tr.chainConfigs[chain].BinaryName, From 9fb387177fa18f7fefec1b7b83bd34de970b54b8 Mon Sep 17 00:00:00 2001 From: yaruwangway <69694322+yaruwangway@users.noreply.github.com> Date: Mon, 2 Oct 2023 18:23:43 +0200 Subject: [PATCH 03/22] feat: update GetAllValidatorsByConsumerAddr for fast find consensus key in use (#1279) * update GetAllValidatorsByConsumerAddr * fix test * update ValidatorConsensusKeyInUse --- tests/e2e/config.go | 2 +- x/ccv/provider/keeper/hooks.go | 26 +++++++++++++++++++------- x/ccv/provider/keeper/hooks_test.go | 4 ++++ x/ccv/provider/keeper/keeper.go | 11 +++++++++++ 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/tests/e2e/config.go b/tests/e2e/config.go index f58af0de65..bbd2278d75 100644 --- a/tests/e2e/config.go +++ b/tests/e2e/config.go @@ -417,7 +417,7 @@ func (s *TestConfig) SetRelayerConfig(useRly bool) { } // validateStringLiterals enforces that configs follow the constraints -// necessary to to execute the tests +// necessary to execute the tests // // Note: Network interfaces (name of virtual ethernet interfaces for ip link) // within the container will be named as "$CHAIN_ID-$VAL_ID-out" etc. diff --git a/x/ccv/provider/keeper/hooks.go b/x/ccv/provider/keeper/hooks.go index 35c9b96301..56c8d1e094 100644 --- a/x/ccv/provider/keeper/hooks.go +++ b/x/ccv/provider/keeper/hooks.go @@ -98,16 +98,28 @@ func ValidatorConsensusKeyInUse(k *Keeper, ctx sdk.Context, valAddr sdk.ValAddre panic("could not get validator cons addr ") } - inUse := false - - for _, validatorConsumerAddrs := range k.GetAllValidatorsByConsumerAddr(ctx, nil) { - if sdk.ConsAddress(validatorConsumerAddrs.ConsumerAddr).Equals(consensusAddr) { - inUse = true - break + allConsumerChains := []string{} + consumerChains := k.GetAllConsumerChains(ctx) + for _, consumerChain := range consumerChains { + allConsumerChains = append(allConsumerChains, consumerChain.ChainId) + } + proposedChains := k.GetAllProposedConsumerChainIDs(ctx) + for _, proposedChain := range proposedChains { + allConsumerChains = append(allConsumerChains, proposedChain.ChainID) + } + pendingChainIDs := k.GetAllPendingConsumerChainIDs(ctx) + allConsumerChains = append(allConsumerChains, pendingChainIDs...) + + for _, c := range allConsumerChains { + if _, exist := k.GetValidatorByConsumerAddr( + ctx, + c, + providertypes.NewConsumerConsAddress(consensusAddr)); exist { + return true } } - return inUse + return false } func (h Hooks) AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) error { diff --git a/x/ccv/provider/keeper/hooks_test.go b/x/ccv/provider/keeper/hooks_test.go index 83dbfe9622..14720bd970 100644 --- a/x/ccv/provider/keeper/hooks_test.go +++ b/x/ccv/provider/keeper/hooks_test.go @@ -38,6 +38,7 @@ func TestValidatorConsensusKeyInUse(t *testing.T) { newValidator.ConsumerConsAddress(), anotherValidator0.ProviderConsAddress(), ) + k.SetConsumerClientId(ctx, "chainid", "clientID") }, expect: true, }, @@ -50,10 +51,13 @@ func TestValidatorConsensusKeyInUse(t *testing.T) { newValidator.ConsumerConsAddress(), anotherValidator0.ProviderConsAddress(), ) + k.SetConsumerClientId(ctx, "chainid0", "clientID0") + k.SetValidatorByConsumerAddr(ctx, "chainid1", anotherValidator1.ConsumerConsAddress(), anotherValidator1.ProviderConsAddress(), ) + k.SetConsumerClientId(ctx, "chainid1", "clientID1") }, expect: true, }, diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index ba2b6f47fe..562e4903fa 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -222,6 +222,17 @@ func (k Keeper) GetAllProposedConsumerChainIDs(ctx sdk.Context) []types.Proposed return proposedChains } +// GetAllPendingConsumerChainIDs gets pending consumer chains have not reach spawn time +func (k Keeper) GetAllPendingConsumerChainIDs(ctx sdk.Context) []string { + chainIDs := []string{} + props := k.GetAllPendingConsumerAdditionProps(ctx) + for _, prop := range props { + chainIDs = append(chainIDs, prop.ChainId) + } + + return chainIDs +} + // GetAllConsumerChains gets all of the consumer chains, for which the provider module // created IBC clients. Consumer chains with created clients are also referred to as registered. // From 35b4a5cb9fadc7cde443c392708da050b74ef0c7 Mon Sep 17 00:00:00 2001 From: Yaru Wang Date: Mon, 2 Oct 2023 18:38:09 +0200 Subject: [PATCH 04/22] update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb2477b334..cd6d198fc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ Add an entry to the unreleased provider section whenever merging a PR to main th * (deps) [#1258](https://github.com/cosmos/interchain-security/pull/1258) Bump [cosmos-sdk](https://github.com/cosmos/cosmos-sdk) to [v0.47.4](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.47.4). * (deps!) [#1196](https://github.com/cosmos/interchain-security/pull/1196) Bump [ibc-go](https://github.com/cosmos/ibc-go) to [v7.2.0](https://github.com/cosmos/ibc-go/releases/tag/v7.2.0). * `[x/ccv/provider]` (fix) [#1076](https://github.com/cosmos/interchain-security/pull/1076) Add `InitTimeoutTimestamps` and `ExportedVscSendTimestamps` to exported genesis. -* (feature) [#1282](https://github.com/cosmos/interchain-security/issues/1282) In the `ConsumerAdditionProposal`, consumer chainIDs proposed before the voting period finishes are now stored in the state. The gRPC query `/interchain_security/ccv/provider/proposed_consumer_chainids` and CLI command `query provider proposed-consumer-chains` can be used to retrieve this information. +* (feature!) [#1282](https://github.com/cosmos/interchain-security/issues/1282) In the `ConsumerAdditionProposal`, consumer chainIDs proposed before the voting period finishes are now stored in the state. The gRPC query `/interchain_security/ccv/provider/proposed_consumer_chainids` and CLI command `query provider proposed-consumer-chains` can be used to retrieve this information. ## [Unreleased for Consumer] From e09d9304d48a2063d7c444fbd175dfb11fbd0f22 Mon Sep 17 00:00:00 2001 From: Simon Noetzlin Date: Tue, 10 Oct 2023 11:30:30 +0200 Subject: [PATCH 05/22] Update proto/interchain_security/ccv/provider/v1/query.proto --- proto/interchain_security/ccv/provider/v1/query.proto | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proto/interchain_security/ccv/provider/v1/query.proto b/proto/interchain_security/ccv/provider/v1/query.proto index 0136a420e7..d908247480 100644 --- a/proto/interchain_security/ccv/provider/v1/query.proto +++ b/proto/interchain_security/ccv/provider/v1/query.proto @@ -82,8 +82,8 @@ service Query { "/interchain_security/ccv/provider/registered_consumer_reward_denoms"; } - // QueryProposedConsumerChainIDs query chainIDs in consumerAdditionProposals - // before voting period finishes, after voting period finishes, this chainID will be removed from the result + // QueryProposedConsumerChainIDs returns the chain IDs of the proposed consumer chain addition proposals + // that are still in the voting period rpc QueryProposedConsumerChainIDs( QueryProposedChainIDsRequest) returns (QueryProposedChainIDsResponse) { From b97c1cbc8949e665d19a2d48618bd423dd138b64 Mon Sep 17 00:00:00 2001 From: Simon Noetzlin Date: Tue, 10 Oct 2023 11:30:40 +0200 Subject: [PATCH 06/22] Update x/ccv/provider/keeper/gov_hook.go --- x/ccv/provider/keeper/gov_hook.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/ccv/provider/keeper/gov_hook.go b/x/ccv/provider/keeper/gov_hook.go index 61f504366f..125181d022 100644 --- a/x/ccv/provider/keeper/gov_hook.go +++ b/x/ccv/provider/keeper/gov_hook.go @@ -19,7 +19,7 @@ type GovHooks struct { } // Implements GovHooks interface -// GovHooks exist in cosmos-sdk/x/gov/keeper/hooks.go of v0.45.16-lsm-ics and on +// see https://github.com/cosmos/cosmos-sdk/blob/release/v0.47.x/x/gov/types/hooks.go var _ sdkgov.GovHooks = GovHooks{} func (k *Keeper) GovHooks(gk *govkeeper.Keeper) GovHooks { From 0ae0649a4d7812d35e4318758b842385c5e0d97e Mon Sep 17 00:00:00 2001 From: Simon Noetzlin Date: Tue, 10 Oct 2023 11:31:00 +0200 Subject: [PATCH 07/22] Update x/ccv/provider/keeper/keeper.go --- x/ccv/provider/keeper/keeper.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index 562e4903fa..0948dbd118 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -179,8 +179,7 @@ func (k Keeper) DeleteChainToChannel(ctx sdk.Context, chainID string) { } // SetProposedConsumerChain stores a consumer chainId corresponding to a submitted consumer addition proposal -// This consumer chainId is deleted once the voting period for the proposal ends -// does not end. +// This consumer chainId is deleted once the voting period for the proposal ends. func (k Keeper) SetProposedConsumerChain(ctx sdk.Context, chainID string, proposalID uint64) { store := ctx.KVStore(k.storeKey) store.Set(types.ProposedConsumerChainKey(proposalID), []byte(chainID)) From 880eaefd136f97f49d0bc519972949be18f6e052 Mon Sep 17 00:00:00 2001 From: Simon Noetzlin Date: Tue, 10 Oct 2023 11:31:12 +0200 Subject: [PATCH 08/22] Update x/ccv/provider/keeper/keeper.go --- x/ccv/provider/keeper/keeper.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index 0948dbd118..3f4ae8e448 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -185,7 +185,7 @@ func (k Keeper) SetProposedConsumerChain(ctx sdk.Context, chainID string, propos store.Set(types.ProposedConsumerChainKey(proposalID), []byte(chainID)) } -// GetProposedConsumerChain get the proposed chainID in consumerAddition proposal. +// GetProposedConsumerChain returns the proposed chainID for the given consumerAddition proposal ID. func (k Keeper) GetProposedConsumerChain(ctx sdk.Context, proposalID uint64) string { store := ctx.KVStore(k.storeKey) return string(store.Get(types.ProposedConsumerChainKey(proposalID))) From c95faeae3105bb084ec80bdd83b976c6e33aaac0 Mon Sep 17 00:00:00 2001 From: Simon Noetzlin Date: Tue, 10 Oct 2023 11:31:26 +0200 Subject: [PATCH 09/22] Update x/ccv/provider/keeper/keeper.go --- x/ccv/provider/keeper/keeper.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index 3f4ae8e448..f76d419b0a 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -198,7 +198,7 @@ func (k Keeper) DeleteProposedConsumerChainInStore(ctx sdk.Context, proposalID u store.Delete(types.ProposedConsumerChainKey(proposalID)) } -// GetAllProposedConsumerChainIDs get consumer chainId in gov consumerAddition proposal before voting period ends. +// GetAllProposedConsumerChainIDs returns the proposed chainID of all gov consumerAddition proposals that are still in the voting period. func (k Keeper) GetAllProposedConsumerChainIDs(ctx sdk.Context) []types.ProposedChain { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, []byte{types.ProposedConsumerChainByteKey}) From 584ce4e6b6653817cb9b6c4b264555028a8d2bff Mon Sep 17 00:00:00 2001 From: Simon Noetzlin Date: Wed, 11 Oct 2023 11:13:54 +0200 Subject: [PATCH 10/22] fix gov hooks --- app/provider/app.go | 5 +- testutil/keeper/mocks.go | 57 +++++++++++++++-- testutil/keeper/unit_test_helpers.go | 3 + x/ccv/provider/keeper/gov_hook.go | 93 ---------------------------- x/ccv/provider/keeper/hooks.go | 83 ++++++++++++++++++++++++- x/ccv/provider/keeper/hooks_test.go | 69 ++++++++++++++++++++- x/ccv/provider/keeper/keeper.go | 11 ++-- x/ccv/types/expected_keepers.go | 5 ++ 8 files changed, 218 insertions(+), 108 deletions(-) delete mode 100644 x/ccv/provider/keeper/gov_hook.go diff --git a/app/provider/app.go b/app/provider/app.go index 377bedd10c..f021e0797b 100644 --- a/app/provider/app.go +++ b/app/provider/app.go @@ -427,6 +427,7 @@ func New( app.EvidenceKeeper, app.DistrKeeper, app.BankKeeper, + app.GovKeeper, authtypes.FeeCollectorName, ) @@ -456,10 +457,8 @@ func New( // Set legacy router for backwards compatibility with gov v1beta1 govKeeper.SetLegacyRouter(govRouter) - - govHook := app.ProviderKeeper.GovHooks(govKeeper) app.GovKeeper = *govKeeper.SetHooks( - govtypes.NewMultiGovHooks(govHook), + govtypes.NewMultiGovHooks(app.ProviderKeeper.Hooks()), ) app.TransferKeeper = ibctransferkeeper.NewKeeper( diff --git a/testutil/keeper/mocks.go b/testutil/keeper/mocks.go index b075819cc4..308aa2c9ab 100644 --- a/testutil/keeper/mocks.go +++ b/testutil/keeper/mocks.go @@ -15,6 +15,7 @@ import ( types1 "github.com/cosmos/cosmos-sdk/x/auth/types" types2 "github.com/cosmos/cosmos-sdk/x/capability/types" types3 "github.com/cosmos/cosmos-sdk/x/evidence/types" + v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" types4 "github.com/cosmos/cosmos-sdk/x/slashing/types" types5 "github.com/cosmos/cosmos-sdk/x/staking/types" types6 "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" @@ -564,20 +565,26 @@ func (m *MockChannelKeeper) GetChannel(ctx types0.Context, srcPort, srcChan stri return ret0, ret1 } +// GetChannel indicates an expected call of GetChannel. +func (mr *MockChannelKeeperMockRecorder) GetChannel(ctx, srcPort, srcChan interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChannel", reflect.TypeOf((*MockChannelKeeper)(nil).GetChannel), ctx, srcPort, srcChan) +} + +// GetChannelConnection mocks base method. func (m *MockChannelKeeper) GetChannelConnection(ctx types0.Context, portID, channelID string) (string, exported.ConnectionI, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetChannelConnection", ctx, portID, channelID) ret0, _ := ret[0].(string) - ret1, _ := ret[0].(exported.ConnectionI) - ret2, _ := ret[1].(error) - + ret1, _ := ret[1].(exported.ConnectionI) + ret2, _ := ret[2].(error) return ret0, ret1, ret2 } -// GetChannel indicates an expected call of GetChannel. -func (mr *MockChannelKeeperMockRecorder) GetChannel(ctx, srcPort, srcChan interface{}) *gomock.Call { +// GetChannelConnection indicates an expected call of GetChannelConnection. +func (mr *MockChannelKeeperMockRecorder) GetChannelConnection(ctx, portID, channelID interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChannel", reflect.TypeOf((*MockChannelKeeper)(nil).GetChannel), ctx, srcPort, srcChan) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChannelConnection", reflect.TypeOf((*MockChannelKeeper)(nil).GetChannelConnection), ctx, portID, channelID) } // GetNextSequenceSend mocks base method. @@ -1099,3 +1106,41 @@ func (mr *MockScopedKeeperMockRecorder) GetCapability(ctx, name interface{}) *go mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCapability", reflect.TypeOf((*MockScopedKeeper)(nil).GetCapability), ctx, name) } + +// MockGovKeeper is a mock of GovKeeper interface. +type MockGovKeeper struct { + ctrl *gomock.Controller + recorder *MockGovKeeperMockRecorder +} + +// MockGovKeeperMockRecorder is the mock recorder for MockGovKeeper. +type MockGovKeeperMockRecorder struct { + mock *MockGovKeeper +} + +// NewMockGovKeeper creates a new mock instance. +func NewMockGovKeeper(ctrl *gomock.Controller) *MockGovKeeper { + mock := &MockGovKeeper{ctrl: ctrl} + mock.recorder = &MockGovKeeperMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockGovKeeper) EXPECT() *MockGovKeeperMockRecorder { + return m.recorder +} + +// GetProposal mocks base method. +func (m *MockGovKeeper) GetProposal(ctx types0.Context, proposalID uint64) (v1.Proposal, bool) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetProposal", ctx, proposalID) + ret0, _ := ret[0].(v1.Proposal) + ret1, _ := ret[1].(bool) + return ret0, ret1 +} + +// GetProposal indicates an expected call of GetProposal. +func (mr *MockGovKeeperMockRecorder) GetProposal(ctx, proposalID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetProposal", reflect.TypeOf((*MockGovKeeper)(nil).GetProposal), ctx, proposalID) +} diff --git a/testutil/keeper/unit_test_helpers.go b/testutil/keeper/unit_test_helpers.go index b58d6d2471..f085a2855b 100644 --- a/testutil/keeper/unit_test_helpers.go +++ b/testutil/keeper/unit_test_helpers.go @@ -89,6 +89,7 @@ type MockedKeepers struct { *MockIBCCoreKeeper *MockEvidenceKeeper *MockDistributionKeeper + *MockGovKeeper } // NewMockedKeepers instantiates a struct with pointers to properly instantiated mocked keepers. @@ -107,6 +108,7 @@ func NewMockedKeepers(ctrl *gomock.Controller) MockedKeepers { MockIBCCoreKeeper: NewMockIBCCoreKeeper(ctrl), MockEvidenceKeeper: NewMockEvidenceKeeper(ctrl), MockDistributionKeeper: NewMockDistributionKeeper(ctrl), + MockGovKeeper: NewMockGovKeeper(ctrl), } } @@ -127,6 +129,7 @@ func NewInMemProviderKeeper(params InMemKeeperParams, mocks MockedKeepers) provi mocks.MockEvidenceKeeper, mocks.MockDistributionKeeper, mocks.MockBankKeeper, + mocks.MockGovKeeper, authtypes.FeeCollectorName, ) } diff --git a/x/ccv/provider/keeper/gov_hook.go b/x/ccv/provider/keeper/gov_hook.go deleted file mode 100644 index 125181d022..0000000000 --- a/x/ccv/provider/keeper/gov_hook.go +++ /dev/null @@ -1,93 +0,0 @@ -package keeper - -import ( - "fmt" - - "github.com/cosmos/gogoproto/proto" - - sdk "github.com/cosmos/cosmos-sdk/types" - govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" - sdkgov "github.com/cosmos/cosmos-sdk/x/gov/types" - v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" - - "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" -) - -type GovHooks struct { - gk *govkeeper.Keeper - k *Keeper -} - -// Implements GovHooks interface -// see https://github.com/cosmos/cosmos-sdk/blob/release/v0.47.x/x/gov/types/hooks.go -var _ sdkgov.GovHooks = GovHooks{} - -func (k *Keeper) GovHooks(gk *govkeeper.Keeper) GovHooks { - return GovHooks{ - gk: gk, - k: k, - } -} - -// AfterProposalSubmission - call hook if registered -// After consumerAddition proposal submission, the consumer chainID is stored -func (gh GovHooks) AfterProposalSubmission(ctx sdk.Context, proposalID uint64) { - p, ok := gh.gk.GetProposal(ctx, proposalID) - if !ok { - panic(fmt.Errorf("failed to get proposal %d in gov hook", proposalID)) - } - msgs := p.GetMessages() - - for _, msg := range msgs { - var msgLegacyContent v1.MsgExecLegacyContent - err := proto.Unmarshal(msg.Value, &msgLegacyContent) - if err != nil { - panic(fmt.Errorf("failed to unmarshal proposal content in gov hook: %w", err)) - } - - // if the consumer addition proposal cannot be unmarshaled, continue - var consAdditionProp types.ConsumerAdditionProposal - if err := proto.Unmarshal(msgLegacyContent.Content.Value, &consAdditionProp); err != nil { - continue - } - - if consAdditionProp.ProposalType() == types.ProposalTypeConsumerAddition { - gh.k.SetProposedConsumerChain(ctx, consAdditionProp.ChainId, proposalID) - } - } -} - -// AfterProposalVotingPeriodEnded - call hook if registered -// After proposal voting ends, the consumer chainID in store is deleted. -// When a proposal passes, this chainID will be available in providerKeeper.GetAllPendingConsumerAdditionProps -// or providerKeeper.GetAllConsumerChains(ctx). -func (gh GovHooks) AfterProposalVotingPeriodEnded(ctx sdk.Context, proposalID uint64) { - p, ok := gh.gk.GetProposal(ctx, proposalID) - if !ok { - panic(fmt.Errorf("failed to get proposal %d in gov hook", proposalID)) - } - msgs := p.GetMessages() - - for _, msg := range msgs { - var msgLegacyContent v1.MsgExecLegacyContent - err := proto.Unmarshal(msg.Value, &msgLegacyContent) - if err != nil { - panic(fmt.Errorf("failed to unmarshal proposal content in gov hook: %w", err)) - } - - var consAdditionProp types.ConsumerAdditionProposal - // if the proposal is not ConsumerAdditionProposal, return - if err := proto.Unmarshal(msgLegacyContent.Content.Value, &consAdditionProp); err != nil { - continue - } - - if consAdditionProp.ProposalType() == types.ProposalTypeConsumerAddition { - gh.k.DeleteProposedConsumerChainInStore(ctx, proposalID) - } - } -} - -func (gh GovHooks) AfterProposalDeposit(ctx sdk.Context, proposalID uint64, depositorAddr sdk.AccAddress) { -} -func (gh GovHooks) AfterProposalVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress) {} -func (gh GovHooks) AfterProposalFailedMinDeposit(ctx sdk.Context, proposalID uint64) {} diff --git a/x/ccv/provider/keeper/hooks.go b/x/ccv/provider/keeper/hooks.go index 56c8d1e094..6ff6c69f00 100644 --- a/x/ccv/provider/keeper/hooks.go +++ b/x/ccv/provider/keeper/hooks.go @@ -1,9 +1,14 @@ package keeper import ( + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkgov "github.com/cosmos/cosmos-sdk/x/gov/types" + v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" providertypes "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" ccvtypes "github.com/cosmos/interchain-security/v3/x/ccv/types" ) @@ -13,13 +18,20 @@ type Hooks struct { k *Keeper } -var _ stakingtypes.StakingHooks = Hooks{} +var ( + _ stakingtypes.StakingHooks = Hooks{} + _ sdkgov.GovHooks = Hooks{} +) // Returns new provider hooks func (k *Keeper) Hooks() Hooks { return Hooks{k} } +// +// staking hooks +// + // This stores a record of each unbonding op from staking, allowing us to track which consumer chains have unbonded func (h Hooks) AfterUnbondingInitiated(ctx sdk.Context, id uint64) error { var consumerChainIDS []string @@ -179,3 +191,72 @@ func (h Hooks) AfterValidatorBeginUnbonding(_ sdk.Context, _ sdk.ConsAddress, _ func (h Hooks) BeforeDelegationRemoved(_ sdk.Context, _ sdk.AccAddress, _ sdk.ValAddress) error { return nil } + +// +// gov hooks +// + +// AfterProposalSubmission - call hook if registered +// After consumerAddition proposal submission, the consumer chainID is stored +func (h Hooks) AfterProposalSubmission(ctx sdk.Context, proposalID uint64) { + p, ok := h.k.govKeeper.GetProposal(ctx, proposalID) + if !ok { + panic(fmt.Errorf("failed to get proposal %d in gov hook", proposalID)) + } + msgs := p.GetMessages() + + for _, msg := range msgs { + sdkMsg, isLegacyProposal := msg.GetCachedValue().(*v1.MsgExecLegacyContent) + if !isLegacyProposal { + continue + } + + content, err := v1.LegacyContentFromMessage(sdkMsg) + if err != nil { + panic(fmt.Errorf("failed to get legacy proposal %d content in gov hook", proposalID)) + } + + consProp, ok := content.(*types.ConsumerAdditionProposal) + if ok { + h.k.SetProposedConsumerChain(ctx, consProp.ChainId, proposalID) + } + } +} + +// AfterProposalVotingPeriodEnded - call hook if registered +// After proposal voting ends, the consumer chainID in store is deleted. +// When a proposal passes, this chainID will be available in providerKeeper.GetAllPendingConsumerAdditionProps +// or providerKeeper.GetAllConsumerChains(ctx). +func (h Hooks) AfterProposalVotingPeriodEnded(ctx sdk.Context, proposalID uint64) { + p, ok := h.k.govKeeper.GetProposal(ctx, proposalID) + if !ok { + panic(fmt.Errorf("failed to get proposal %d in gov hook", proposalID)) + } + msgs := p.GetMessages() + + for _, msg := range msgs { + if sdkMsg, isLegacyProposal := msg.GetCachedValue().(*v1.MsgExecLegacyContent); isLegacyProposal { + content, err := v1.LegacyContentFromMessage(sdkMsg) + if err != nil { + panic(fmt.Errorf("failed to get legacy proposal %d content in gov hook", proposalID)) + } + + if content.ProposalType() != types.ProposalTypeConsumerAddition { + continue + } + + h.k.DeleteProposedConsumerChainInStore(ctx, proposalID) + } + + continue + } +} + +func (h Hooks) AfterProposalDeposit(ctx sdk.Context, proposalID uint64, depositorAddr sdk.AccAddress) { +} + +func (h Hooks) AfterProposalVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress) { +} + +func (h Hooks) AfterProposalFailedMinDeposit(ctx sdk.Context, proposalID uint64) { +} diff --git a/x/ccv/provider/keeper/hooks_test.go b/x/ccv/provider/keeper/hooks_test.go index 14720bd970..f73b9e5165 100644 --- a/x/ccv/provider/keeper/hooks_test.go +++ b/x/ccv/provider/keeper/hooks_test.go @@ -1,15 +1,23 @@ package keeper_test import ( + "fmt" "testing" + "time" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" sdk "github.com/cosmos/cosmos-sdk/types" + v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" cryptotestutil "github.com/cosmos/interchain-security/v3/testutil/crypto" testkeeper "github.com/cosmos/interchain-security/v3/testutil/keeper" providerkeeper "github.com/cosmos/interchain-security/v3/x/ccv/provider/keeper" + "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" + ccvtypes "github.com/cosmos/interchain-security/v3/x/ccv/types" ) func TestValidatorConsensusKeyInUse(t *testing.T) { @@ -63,7 +71,8 @@ func TestValidatorConsensusKeyInUse(t *testing.T) { }, } for _, tt := range tests { - k, ctx, _, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() gomock.InOrder( mocks.MockStakingKeeper.EXPECT().GetValidator(ctx, @@ -80,3 +89,61 @@ func TestValidatorConsensusKeyInUse(t *testing.T) { }) } } + +func TestAfterProposalSubmission(t *testing.T) { + // encodingConfig := appparams.MakeTestEncodingConfig() + // v1beta1.RegisterInterfaces(encodingConfig.InterfaceRegistry) + // v1.RegisterInterfaces(encodingConfig.InterfaceRegistry) + // cdc := encodingConfig.Codec + + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + newValidator := cryptotestutil.NewCryptoIdentityFromIntSeed(0) + + content := types.NewConsumerAdditionProposal( + "chainID", + "description", + "chainID", + clienttypes.NewHeight(4, 5), + []byte("gen_hash"), + []byte("bin_hash"), + time.Now(), + ccvtypes.DefaultConsumerRedistributeFrac, + ccvtypes.DefaultBlocksPerDistributionTransmission, + "", + ccvtypes.DefaultHistoricalEntries, + ccvtypes.DefaultCCVTimeoutPeriod, + ccvtypes.DefaultTransferTimeoutPeriod, + ccvtypes.DefaultConsumerUnbondingPeriod, + ) + + propContent, err := v1.NewLegacyContent(content, authtypes.NewModuleAddress("gov").String()) + require.NoError(t, err) + + // _, err = v1.LegacyContentFromMessage(propContent) + // require.NoError(t, err) + + prop, err := v1.NewProposal( + []sdk.Msg{propContent}, + 0, + time.Now(), + time.Now(), + "", + "", + "", + // sdk.NewCoins(sdk.NewCoin("stake", math.OneInt())), + sdk.AccAddress(newValidator.SDKValOpAddress()), + ) + + require.NoError(t, err) + + gomock.InOrder( + mocks.MockGovKeeper.EXPECT().GetProposal(ctx, uint64(0)).Return(prop, true), + ) + + tHooks := k.Hooks() + tHooks.AfterProposalSubmission(ctx, 0) + + fmt.Println(len(k.GetAllProposedConsumerChainIDs(ctx))) +} diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index f76d419b0a..0dc7c9706c 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -44,6 +44,7 @@ type Keeper struct { evidenceKeeper ccv.EvidenceKeeper distributionKeeper ccv.DistributionKeeper bankKeeper ccv.BankKeeper + govKeeper ccv.GovKeeper feeCollectorName string } @@ -55,7 +56,7 @@ func NewKeeper( stakingKeeper ccv.StakingKeeper, slashingKeeper ccv.SlashingKeeper, accountKeeper ccv.AccountKeeper, evidenceKeeper ccv.EvidenceKeeper, distributionKeeper ccv.DistributionKeeper, bankKeeper ccv.BankKeeper, - feeCollectorName string, + govKeeper ccv.GovKeeper, feeCollectorName string, ) Keeper { // set KeyTable if it has not already been set if !paramSpace.HasKeyTable() { @@ -77,6 +78,7 @@ func NewKeeper( evidenceKeeper: evidenceKeeper, distributionKeeper: distributionKeeper, bankKeeper: bankKeeper, + govKeeper: govKeeper, feeCollectorName: feeCollectorName, } @@ -94,8 +96,8 @@ func (k *Keeper) SetParamSpace(ctx sdk.Context, ps paramtypes.Subspace) { // 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 provider keeper is not 16") } ccv.PanicIfZeroOrNil(k.cdc, "cdc") // 1 @@ -112,7 +114,8 @@ func (k Keeper) mustValidateFields() { ccv.PanicIfZeroOrNil(k.evidenceKeeper, "evidenceKeeper") // 12 ccv.PanicIfZeroOrNil(k.distributionKeeper, "distributionKeeper") // 13 ccv.PanicIfZeroOrNil(k.bankKeeper, "bankKeeper") // 14 - ccv.PanicIfZeroOrNil(k.feeCollectorName, "feeCollectorName") // 15 + ccv.PanicIfZeroOrNil(k.govKeeper, "govKeeper") // 15 + ccv.PanicIfZeroOrNil(k.feeCollectorName, "feeCollectorName") // 16 } // Logger returns a module-specific logger. diff --git a/x/ccv/types/expected_keepers.go b/x/ccv/types/expected_keepers.go index 2f203a47ea..070b4f2a6e 100644 --- a/x/ccv/types/expected_keepers.go +++ b/x/ccv/types/expected_keepers.go @@ -20,6 +20,7 @@ import ( stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" abci "github.com/cometbft/cometbft/abci/types" + v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" ) // StakingKeeper defines the contract expected by provider-chain ccv module from a Staking Module that will keep track @@ -145,3 +146,7 @@ type ScopedKeeper interface { AuthenticateCapability(ctx sdk.Context, cap *capabilitytypes.Capability, name string) bool ClaimCapability(ctx sdk.Context, cap *capabilitytypes.Capability, name string) error } + +type GovKeeper interface { + GetProposal(ctx sdk.Context, proposalID uint64) (v1.Proposal, bool) +} From b26b4533ac9f007254c42b6038fbc037f8fa4d1d Mon Sep 17 00:00:00 2001 From: Simon Noetzlin Date: Thu, 12 Oct 2023 14:00:41 +0200 Subject: [PATCH 11/22] fix bug and add tests --- x/ccv/provider/keeper/hooks.go | 82 +++++++------- x/ccv/provider/keeper/hooks_test.go | 168 +++++++++++++++++++++------- 2 files changed, 163 insertions(+), 87 deletions(-) diff --git a/x/ccv/provider/keeper/hooks.go b/x/ccv/provider/keeper/hooks.go index 6ff6c69f00..0a6f595630 100644 --- a/x/ccv/provider/keeper/hooks.go +++ b/x/ccv/provider/keeper/hooks.go @@ -197,58 +197,21 @@ func (h Hooks) BeforeDelegationRemoved(_ sdk.Context, _ sdk.AccAddress, _ sdk.Va // // AfterProposalSubmission - call hook if registered -// After consumerAddition proposal submission, the consumer chainID is stored +// After a consumerAddition proposal submission, a record is created +// that maps the proposal ID to the consumer chain ID. func (h Hooks) AfterProposalSubmission(ctx sdk.Context, proposalID uint64) { - p, ok := h.k.govKeeper.GetProposal(ctx, proposalID) - if !ok { - panic(fmt.Errorf("failed to get proposal %d in gov hook", proposalID)) - } - msgs := p.GetMessages() - - for _, msg := range msgs { - sdkMsg, isLegacyProposal := msg.GetCachedValue().(*v1.MsgExecLegacyContent) - if !isLegacyProposal { - continue - } - - content, err := v1.LegacyContentFromMessage(sdkMsg) - if err != nil { - panic(fmt.Errorf("failed to get legacy proposal %d content in gov hook", proposalID)) - } - - consProp, ok := content.(*types.ConsumerAdditionProposal) - if ok { - h.k.SetProposedConsumerChain(ctx, consProp.ChainId, proposalID) - } + if p, ok := h.GetConsumerAdditionLegacyPropFromProp(ctx, proposalID); ok { + h.k.SetProposedConsumerChain(ctx, p.ChainId, proposalID) } } // AfterProposalVotingPeriodEnded - call hook if registered // After proposal voting ends, the consumer chainID in store is deleted. -// When a proposal passes, this chainID will be available in providerKeeper.GetAllPendingConsumerAdditionProps +// When a consumerAddition proposal passes, the consumer chainID is available in providerKeeper.GetAllPendingConsumerAdditionProps // or providerKeeper.GetAllConsumerChains(ctx). func (h Hooks) AfterProposalVotingPeriodEnded(ctx sdk.Context, proposalID uint64) { - p, ok := h.k.govKeeper.GetProposal(ctx, proposalID) - if !ok { - panic(fmt.Errorf("failed to get proposal %d in gov hook", proposalID)) - } - msgs := p.GetMessages() - - for _, msg := range msgs { - if sdkMsg, isLegacyProposal := msg.GetCachedValue().(*v1.MsgExecLegacyContent); isLegacyProposal { - content, err := v1.LegacyContentFromMessage(sdkMsg) - if err != nil { - panic(fmt.Errorf("failed to get legacy proposal %d content in gov hook", proposalID)) - } - - if content.ProposalType() != types.ProposalTypeConsumerAddition { - continue - } - - h.k.DeleteProposedConsumerChainInStore(ctx, proposalID) - } - - continue + if _, ok := h.GetConsumerAdditionLegacyPropFromProp(ctx, proposalID); ok { + h.k.DeleteProposedConsumerChainInStore(ctx, proposalID) } } @@ -260,3 +223,34 @@ func (h Hooks) AfterProposalVote(ctx sdk.Context, proposalID uint64, voterAddr s func (h Hooks) AfterProposalFailedMinDeposit(ctx sdk.Context, proposalID uint64) { } + +func (h Hooks) GetConsumerAdditionLegacyPropFromProp( + ctx sdk.Context, + proposalID uint64, +) (types.ConsumerAdditionProposal, bool) { + p, ok := h.k.govKeeper.GetProposal(ctx, proposalID) + if !ok { + panic(fmt.Errorf("failed to get proposal %d from store", proposalID)) + } + + // Iterate over the messages in the proposal + // Note that only ONE message can contain a consumer addition proposal + for _, msg := range p.GetMessages() { + sdkMsg, isLegacyProposal := msg.GetCachedValue().(*v1.MsgExecLegacyContent) + if !isLegacyProposal { + continue + } + + content, err := v1.LegacyContentFromMessage(sdkMsg) + if err != nil { + panic(fmt.Errorf("failed to get legacy proposal %d from prop message", proposalID)) + } + + // returns if legacy prop is of ConsumerAddition proposal type + prop, ok := content.(*types.ConsumerAdditionProposal) + if ok { + return *prop, true + } + } + return types.ConsumerAdditionProposal{}, false +} diff --git a/x/ccv/provider/keeper/hooks_test.go b/x/ccv/provider/keeper/hooks_test.go index f73b9e5165..d8f30115f0 100644 --- a/x/ccv/provider/keeper/hooks_test.go +++ b/x/ccv/provider/keeper/hooks_test.go @@ -1,23 +1,24 @@ package keeper_test import ( - "fmt" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "testing" "time" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "cosmossdk.io/math" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" sdk "github.com/cosmos/cosmos-sdk/types" v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" cryptotestutil "github.com/cosmos/interchain-security/v3/testutil/crypto" testkeeper "github.com/cosmos/interchain-security/v3/testutil/keeper" providerkeeper "github.com/cosmos/interchain-security/v3/x/ccv/provider/keeper" - "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" - ccvtypes "github.com/cosmos/interchain-security/v3/x/ccv/types" ) func TestValidatorConsensusKeyInUse(t *testing.T) { @@ -90,60 +91,141 @@ func TestValidatorConsensusKeyInUse(t *testing.T) { } } -func TestAfterProposalSubmission(t *testing.T) { - // encodingConfig := appparams.MakeTestEncodingConfig() - // v1beta1.RegisterInterfaces(encodingConfig.InterfaceRegistry) - // v1.RegisterInterfaces(encodingConfig.InterfaceRegistry) - // cdc := encodingConfig.Codec +func TestAfterSubmissionAndAfterProposalVotingPeriodEnded(t *testing.T) { + acct := cryptotestutil.NewCryptoIdentityFromIntSeed(0) - k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) - defer ctrl.Finish() - - newValidator := cryptotestutil.NewCryptoIdentityFromIntSeed(0) - - content := types.NewConsumerAdditionProposal( - "chainID", - "description", - "chainID", - clienttypes.NewHeight(4, 5), - []byte("gen_hash"), - []byte("bin_hash"), - time.Now(), - ccvtypes.DefaultConsumerRedistributeFrac, - ccvtypes.DefaultBlocksPerDistributionTransmission, - "", - ccvtypes.DefaultHistoricalEntries, - ccvtypes.DefaultCCVTimeoutPeriod, - ccvtypes.DefaultTransferTimeoutPeriod, - ccvtypes.DefaultConsumerUnbondingPeriod, + propMsg, err := v1.NewLegacyContent( + testkeeper.GetTestConsumerAdditionProp(), + authtypes.NewModuleAddress("gov").String(), ) - - propContent, err := v1.NewLegacyContent(content, authtypes.NewModuleAddress("gov").String()) require.NoError(t, err) - // _, err = v1.LegacyContentFromMessage(propContent) - // require.NoError(t, err) - prop, err := v1.NewProposal( - []sdk.Msg{propContent}, + []sdk.Msg{propMsg}, 0, time.Now(), time.Now(), "", "", "", - // sdk.NewCoins(sdk.NewCoin("stake", math.OneInt())), - sdk.AccAddress(newValidator.SDKValOpAddress()), + sdk.AccAddress(acct.SDKValOpAddress()), ) - require.NoError(t, err) + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + // pass the prop to the mocked gov keeper, + // which is called by both the AfterProposalVotingPeriodEnded and + // AfterProposalSubmission gov hooks gomock.InOrder( - mocks.MockGovKeeper.EXPECT().GetProposal(ctx, uint64(0)).Return(prop, true), + mocks.MockGovKeeper.EXPECT().GetProposal(ctx, prop.Id).Return(prop, true).Times(2), ) - tHooks := k.Hooks() - tHooks.AfterProposalSubmission(ctx, 0) + k.Hooks().AfterProposalSubmission(ctx, prop.Id) + // verify that the proposal ID is created + require.NotEmpty(t, k.GetProposedConsumerChain(ctx, prop.Id)) + + k.Hooks().AfterProposalVotingPeriodEnded(ctx, prop.Id) + // verify that the proposal ID is deleted + require.Empty(t, k.GetProposedConsumerChain(ctx, prop.Id)) +} + +func TestGetConsumerAdditionLegacyPropFromProp(t *testing.T) { + acct := cryptotestutil.NewCryptoIdentityFromIntSeed(0) + anotherAcct := cryptotestutil.NewCryptoIdentityFromIntSeed(1) + + // create a dummy bank send message + dummyMsg := &banktypes.MsgSend{ + FromAddress: sdk.AccAddress(acct.SDKValOpAddress()).String(), + ToAddress: sdk.AccAddress(anotherAcct.SDKValOpAddress()).String(), + Amount: sdk.NewCoins(sdk.NewCoin("stake", math.OneInt())), + } + + testCases := map[string]struct { + propMsg func() sdk.Msg + // setup func(sdk.Context, k providerkeeper, proposalID uint64) + expPanic bool + expConsuAddProp bool + }{ + + "prop not found": { + propMsg: func() sdk.Msg { + return dummyMsg + }, + expPanic: true, + }, + "msgs in prop contain no legacy props": { + propMsg: func() sdk.Msg { + return dummyMsg + }, + expConsuAddProp: false, + }, + "msgs contain a legacy prop but not of ConsumerAdditionProposal type": { + propMsg: func() sdk.Msg { + textProp, err := v1.NewLegacyContent( + v1beta1.NewTextProposal("a title", "a legacy text prop"), + authtypes.NewModuleAddress("gov").String(), + ) + require.NoError(t, err) + + return textProp + }, + expConsuAddProp: false, + }, + "msg contains a prop of ConsumerAdditionProposal type - hook should create a new proposed chain": { + propMsg: func() sdk.Msg { + // create a dummy consumer addition prop + consuProp, err := v1.NewLegacyContent( + testkeeper.GetTestConsumerAdditionProp(), + authtypes.NewModuleAddress("gov").String(), + ) + require.NoError(t, err) + + return consuProp + }, + expConsuAddProp: true, + }, + } - fmt.Println(len(k.GetAllProposedConsumerChainIDs(ctx))) + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) + defer ctrl.Finish() + + // create a dummy prop for each test case + prop, err := v1.NewProposal( + []sdk.Msg{tc.propMsg()}, + 0, + time.Now(), + time.Now(), + "", + "", + "", + sdk.AccAddress(acct.SDKValOpAddress()), + ) + require.NoError(t, err) + + if !tc.expPanic { + // pass the prop to the mocked gov keeper, + // which is called by the AfterProposalSubmission gov hook + gomock.InOrder( + mocks.MockGovKeeper.EXPECT().GetProposal(ctx, prop.Id).Return(prop, true), + ) + } else { + gomock.InOrder( + mocks.MockGovKeeper.EXPECT().GetProposal(ctx, prop.Id).Return(v1.Proposal{}, false), + ) + defer func() { + if r := recover(); r == nil { + require.Fail(t, r.(string)) + } + }() + } + + // retrieve consumer addition proposal + _, ok := k.Hooks().GetConsumerAdditionLegacyPropFromProp(ctx, prop.Id) + require.Equal(t, tc.expConsuAddProp, ok) + }) + } } From a240ab582c9dd3003f5ffc17d27f0eac0af7beca Mon Sep 17 00:00:00 2001 From: Simon Noetzlin Date: Tue, 14 Nov 2023 15:10:01 +0100 Subject: [PATCH 12/22] finish unit testing of consu addition legacy prop getter --- x/ccv/provider/keeper/hooks.go | 4 +- x/ccv/provider/keeper/hooks_test.go | 95 ++++++++++++++--------------- 2 files changed, 48 insertions(+), 51 deletions(-) diff --git a/x/ccv/provider/keeper/hooks.go b/x/ccv/provider/keeper/hooks.go index 0a6f595630..fead7b06d6 100644 --- a/x/ccv/provider/keeper/hooks.go +++ b/x/ccv/provider/keeper/hooks.go @@ -224,6 +224,8 @@ func (h Hooks) AfterProposalVote(ctx sdk.Context, proposalID uint64, voterAddr s func (h Hooks) AfterProposalFailedMinDeposit(ctx sdk.Context, proposalID uint64) { } +// GetConsumerAdditionLegacyPropFromProp extracts a consumer addition legacy proposal from +// the proposal with given ID func (h Hooks) GetConsumerAdditionLegacyPropFromProp( ctx sdk.Context, proposalID uint64, @@ -234,7 +236,7 @@ func (h Hooks) GetConsumerAdditionLegacyPropFromProp( } // Iterate over the messages in the proposal - // Note that only ONE message can contain a consumer addition proposal + // Note that it's assumed that at most ONE message can contain a consumer addition proposal for _, msg := range p.GetMessages() { sdkMsg, isLegacyProposal := msg.GetCachedValue().(*v1.MsgExecLegacyContent) if !isLegacyProposal { diff --git a/x/ccv/provider/keeper/hooks_test.go b/x/ccv/provider/keeper/hooks_test.go index d8f30115f0..fd389e3267 100644 --- a/x/ccv/provider/keeper/hooks_test.go +++ b/x/ccv/provider/keeper/hooks_test.go @@ -91,7 +91,7 @@ func TestValidatorConsensusKeyInUse(t *testing.T) { } } -func TestAfterSubmissionAndAfterProposalVotingPeriodEnded(t *testing.T) { +func TestAfterPropSubmissionAndVotingPeriodEnded(t *testing.T) { acct := cryptotestutil.NewCryptoIdentityFromIntSeed(0) propMsg, err := v1.NewLegacyContent( @@ -142,48 +142,40 @@ func TestGetConsumerAdditionLegacyPropFromProp(t *testing.T) { Amount: sdk.NewCoins(sdk.NewCoin("stake", math.OneInt())), } + textProp, err := v1.NewLegacyContent( + v1beta1.NewTextProposal("a title", "a legacy text prop"), + authtypes.NewModuleAddress("gov").String(), + ) + require.NoError(t, err) + + consuProp, err := v1.NewLegacyContent( + testkeeper.GetTestConsumerAdditionProp(), + authtypes.NewModuleAddress("gov").String(), + ) + require.NoError(t, err) + testCases := map[string]struct { - propMsg func() sdk.Msg + propMsg sdk.Msg // setup func(sdk.Context, k providerkeeper, proposalID uint64) expPanic bool expConsuAddProp bool }{ - "prop not found": { - propMsg: func() sdk.Msg { - return dummyMsg - }, + propMsg: nil, expPanic: true, }, "msgs in prop contain no legacy props": { - propMsg: func() sdk.Msg { - return dummyMsg - }, - expConsuAddProp: false, + propMsg: dummyMsg, }, "msgs contain a legacy prop but not of ConsumerAdditionProposal type": { - propMsg: func() sdk.Msg { - textProp, err := v1.NewLegacyContent( - v1beta1.NewTextProposal("a title", "a legacy text prop"), - authtypes.NewModuleAddress("gov").String(), - ) - require.NoError(t, err) - - return textProp - }, - expConsuAddProp: false, + propMsg: textProp, + }, + "msgs contain an invalid legacy prop": { + propMsg: &v1.MsgExecLegacyContent{}, + expPanic: true, }, "msg contains a prop of ConsumerAdditionProposal type - hook should create a new proposed chain": { - propMsg: func() sdk.Msg { - // create a dummy consumer addition prop - consuProp, err := v1.NewLegacyContent( - testkeeper.GetTestConsumerAdditionProp(), - authtypes.NewModuleAddress("gov").String(), - ) - require.NoError(t, err) - - return consuProp - }, + propMsg: consuProp, expConsuAddProp: true, }, } @@ -193,30 +185,33 @@ func TestGetConsumerAdditionLegacyPropFromProp(t *testing.T) { k, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() - // create a dummy prop for each test case - prop, err := v1.NewProposal( - []sdk.Msg{tc.propMsg()}, - 0, - time.Now(), - time.Now(), - "", - "", - "", - sdk.AccAddress(acct.SDKValOpAddress()), + var ( + prop v1.Proposal + propFound bool ) - require.NoError(t, err) - if !tc.expPanic { - // pass the prop to the mocked gov keeper, - // which is called by the AfterProposalSubmission gov hook - gomock.InOrder( - mocks.MockGovKeeper.EXPECT().GetProposal(ctx, prop.Id).Return(prop, true), - ) - } else { - gomock.InOrder( - mocks.MockGovKeeper.EXPECT().GetProposal(ctx, prop.Id).Return(v1.Proposal{}, false), + if tc.propMsg != nil { + propFound = true + prop, err = v1.NewProposal( + []sdk.Msg{tc.propMsg}, + 0, + time.Now(), + time.Now(), + "", + "", + "", + sdk.AccAddress(acct.SDKValOpAddress()), ) + require.NoError(t, err) + } + + gomock.InOrder( + mocks.MockGovKeeper.EXPECT().GetProposal(ctx, prop.Id).Return(prop, propFound), + ) + + if tc.expPanic { defer func() { + // fail test if not panic was recovered if r := recover(); r == nil { require.Fail(t, r.(string)) } From 94fd342a846ced55ace63a84bb956b6e824db530 Mon Sep 17 00:00:00 2001 From: Simon Noetzlin Date: Wed, 15 Nov 2023 09:49:47 +0100 Subject: [PATCH 13/22] nit --- app/provider/app.go | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/app/provider/app.go b/app/provider/app.go index f021e0797b..ebca50a2cc 100644 --- a/app/provider/app.go +++ b/app/provider/app.go @@ -204,7 +204,7 @@ type App struct { // nolint: golint // different fee-pool from the consumer chain ConsumerKeeper DistrKeeper distrkeeper.Keeper - GovKeeper govkeeper.Keeper + GovKeeper *govkeeper.Keeper // Gov Keeper must be a pointer in the app, so we can SetRouter on it correctly CrisisKeeper crisiskeeper.Keeper UpgradeKeeper upgradekeeper.Keeper ParamsKeeper paramskeeper.Keeper @@ -412,6 +412,17 @@ func New( app.SlashingKeeper, ) + app.GovKeeper = govkeeper.NewKeeper( + appCodec, + keys[govtypes.StoreKey], + app.AccountKeeper, + app.BankKeeper, + app.StakingKeeper, + app.MsgServiceRouter(), + govtypes.DefaultConfig(), + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + app.ProviderKeeper = ibcproviderkeeper.NewKeeper( appCodec, keys[providertypes.StoreKey], @@ -442,22 +453,11 @@ func New( AddRoute(ibchost.RouterKey, ibcclient.NewClientProposalHandler(app.IBCKeeper.ClientKeeper)). AddRoute(providertypes.RouterKey, ibcprovider.NewProviderProposalHandler(app.ProviderKeeper)). AddRoute(ibcclienttypes.RouterKey, ibcclient.NewClientProposalHandler(app.IBCKeeper.ClientKeeper)) - govConfig := govtypes.DefaultConfig() - - govKeeper := govkeeper.NewKeeper( - appCodec, - keys[govtypes.StoreKey], - app.AccountKeeper, - app.BankKeeper, - app.StakingKeeper, - app.MsgServiceRouter(), - govConfig, - authtypes.NewModuleAddress(govtypes.ModuleName).String(), - ) // Set legacy router for backwards compatibility with gov v1beta1 - govKeeper.SetLegacyRouter(govRouter) - app.GovKeeper = *govKeeper.SetHooks( + app.GovKeeper.SetLegacyRouter(govRouter) + + app.GovKeeper = app.GovKeeper.SetHooks( govtypes.NewMultiGovHooks(app.ProviderKeeper.Hooks()), ) @@ -497,7 +497,7 @@ func New( bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper, app.GetSubspace(banktypes.ModuleName)), capability.NewAppModule(appCodec, *app.CapabilityKeeper, false), crisis.NewAppModule(&app.CrisisKeeper, skipGenesisInvariants, app.GetSubspace(crisistypes.ModuleName)), - gov.NewAppModule(appCodec, &app.GovKeeper, app.AccountKeeper, app.BankKeeper, app.GetSubspace(govtypes.ModuleName)), + gov.NewAppModule(appCodec, app.GovKeeper, app.AccountKeeper, app.BankKeeper, app.GetSubspace(govtypes.ModuleName)), mint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper, nil, app.GetSubspace(minttypes.ModuleName)), slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.GetSubspace(slashingtypes.ModuleName)), distr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.GetSubspace(distrtypes.ModuleName)), @@ -594,7 +594,7 @@ func New( auth.NewAppModule(appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts, app.GetSubspace(authtypes.ModuleName)), bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper, app.GetSubspace(banktypes.ModuleName)), capability.NewAppModule(appCodec, *app.CapabilityKeeper, false), - gov.NewAppModule(appCodec, &app.GovKeeper, app.AccountKeeper, app.BankKeeper, app.GetSubspace(govtypes.ModuleName)), + gov.NewAppModule(appCodec, app.GovKeeper, app.AccountKeeper, app.BankKeeper, app.GetSubspace(govtypes.ModuleName)), mint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper, nil, app.GetSubspace(minttypes.ModuleName)), staking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.GetSubspace(stakingtypes.ModuleName)), distr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.GetSubspace(distrtypes.ModuleName)), From f196cc982d20f44ed9739718bc8f3d22610e98c4 Mon Sep 17 00:00:00 2001 From: Simon Noetzlin Date: Wed, 15 Nov 2023 10:35:22 +0100 Subject: [PATCH 14/22] update changelog --- .../provider/1339-check-key-assignment-in-use.md | 3 +++ .../features/provider/1339-check-key-assignment-in-use.md | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 .changelog/unreleased/consensus-breaking/provider/1339-check-key-assignment-in-use.md create mode 100644 .changelog/unreleased/features/provider/1339-check-key-assignment-in-use.md diff --git a/.changelog/unreleased/consensus-breaking/provider/1339-check-key-assignment-in-use.md b/.changelog/unreleased/consensus-breaking/provider/1339-check-key-assignment-in-use.md new file mode 100644 index 0000000000..2890582ba8 --- /dev/null +++ b/.changelog/unreleased/consensus-breaking/provider/1339-check-key-assignment-in-use.md @@ -0,0 +1,3 @@ +- Change the states by adding a consumer key for each chain that is + not yet registered meaning for which the gov proposal has not passed. + ([\#1339](https://github.com/cosmos/interchain-security/pull/1339)) \ No newline at end of file diff --git a/.changelog/unreleased/features/provider/1339-check-key-assignment-in-use.md b/.changelog/unreleased/features/provider/1339-check-key-assignment-in-use.md new file mode 100644 index 0000000000..994e637963 --- /dev/null +++ b/.changelog/unreleased/features/provider/1339-check-key-assignment-in-use.md @@ -0,0 +1,3 @@ +- Update how consumer-assigned keys are checked when a validator is + created on the provider. ([\#1339](https://github.com/cosmos/interchain- + security/pull/1339)) \ No newline at end of file From 642ca2980467fa37e03797a3fb5f17ee652185ae Mon Sep 17 00:00:00 2001 From: Simon Noetzlin Date: Wed, 15 Nov 2023 10:52:58 +0100 Subject: [PATCH 15/22] lint --- x/ccv/provider/keeper/hooks.go | 7 +++---- x/ccv/provider/keeper/hooks_test.go | 9 ++++----- x/ccv/types/expected_keepers.go | 2 +- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/x/ccv/provider/keeper/hooks.go b/x/ccv/provider/keeper/hooks.go index 81e2f252aa..3be2be04eb 100644 --- a/x/ccv/provider/keeper/hooks.go +++ b/x/ccv/provider/keeper/hooks.go @@ -8,7 +8,6 @@ import ( v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" providertypes "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" ccvtypes "github.com/cosmos/interchain-security/v3/x/ccv/types" ) @@ -229,7 +228,7 @@ func (h Hooks) AfterProposalFailedMinDeposit(ctx sdk.Context, proposalID uint64) func (h Hooks) GetConsumerAdditionLegacyPropFromProp( ctx sdk.Context, proposalID uint64, -) (types.ConsumerAdditionProposal, bool) { +) (providertypes.ConsumerAdditionProposal, bool) { p, ok := h.k.govKeeper.GetProposal(ctx, proposalID) if !ok { panic(fmt.Errorf("failed to get proposal %d from store", proposalID)) @@ -249,10 +248,10 @@ func (h Hooks) GetConsumerAdditionLegacyPropFromProp( } // returns if legacy prop is of ConsumerAddition proposal type - prop, ok := content.(*types.ConsumerAdditionProposal) + prop, ok := content.(*providertypes.ConsumerAdditionProposal) if ok { return *prop, true } } - return types.ConsumerAdditionProposal{}, false + return providertypes.ConsumerAdditionProposal{}, false } diff --git a/x/ccv/provider/keeper/hooks_test.go b/x/ccv/provider/keeper/hooks_test.go index fd389e3267..0bdf9b26fa 100644 --- a/x/ccv/provider/keeper/hooks_test.go +++ b/x/ccv/provider/keeper/hooks_test.go @@ -1,18 +1,17 @@ package keeper_test import ( - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - "testing" "time" - "cosmossdk.io/math" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" diff --git a/x/ccv/types/expected_keepers.go b/x/ccv/types/expected_keepers.go index 070b4f2a6e..825aabe920 100644 --- a/x/ccv/types/expected_keepers.go +++ b/x/ccv/types/expected_keepers.go @@ -16,11 +16,11 @@ import ( auth "github.com/cosmos/cosmos-sdk/x/auth/types" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" + v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" abci "github.com/cometbft/cometbft/abci/types" - v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" ) // StakingKeeper defines the contract expected by provider-chain ccv module from a Staking Module that will keep track From 4b3b8ed4d0abd39cc014141c84aac8f51cf57b21 Mon Sep 17 00:00:00 2001 From: Simon Noetzlin Date: Wed, 15 Nov 2023 14:31:14 +0100 Subject: [PATCH 16/22] update changelog entry --- .../provider/1339-check-key-assignment-in-use.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .changelog/unreleased/{consensus-breaking => state-breaking}/provider/1339-check-key-assignment-in-use.md (100%) diff --git a/.changelog/unreleased/consensus-breaking/provider/1339-check-key-assignment-in-use.md b/.changelog/unreleased/state-breaking/provider/1339-check-key-assignment-in-use.md similarity index 100% rename from .changelog/unreleased/consensus-breaking/provider/1339-check-key-assignment-in-use.md rename to .changelog/unreleased/state-breaking/provider/1339-check-key-assignment-in-use.md From 50cb42ac7546acec0b105312e2ef3f83189f9712 Mon Sep 17 00:00:00 2001 From: Simon Noetzlin Date: Wed, 22 Nov 2023 09:07:01 +0100 Subject: [PATCH 17/22] Update .changelog/unreleased/features/provider/1339-check-key-assignment-in-use.md Co-authored-by: Marius Poke --- .../features/provider/1339-check-key-assignment-in-use.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changelog/unreleased/features/provider/1339-check-key-assignment-in-use.md b/.changelog/unreleased/features/provider/1339-check-key-assignment-in-use.md index 994e637963..9f274f7bb4 100644 --- a/.changelog/unreleased/features/provider/1339-check-key-assignment-in-use.md +++ b/.changelog/unreleased/features/provider/1339-check-key-assignment-in-use.md @@ -1,3 +1,3 @@ - Update how consumer-assigned keys are checked when a validator is - created on the provider. ([\#1339](https://github.com/cosmos/interchain- - security/pull/1339)) \ No newline at end of file + created on the provider. + ([\#1339](https://github.com/cosmos/interchain-security/pull/1339)) \ No newline at end of file From 441bcee2f8161e6c8b0d07a30f5f89713da57bbb Mon Sep 17 00:00:00 2001 From: Simon Noetzlin Date: Wed, 22 Nov 2023 18:32:39 +0100 Subject: [PATCH 18/22] fix #1282 --- x/ccv/provider/keeper/hooks.go | 44 +---------------- x/ccv/provider/keeper/hooks_test.go | 2 +- x/ccv/provider/keeper/keeper.go | 16 ++++++ x/ccv/provider/keeper/key_assignment.go | 52 ++++++++++++++++++++ x/ccv/provider/keeper/key_assignment_test.go | 51 +++++++++++++------ 5 files changed, 105 insertions(+), 60 deletions(-) diff --git a/x/ccv/provider/keeper/hooks.go b/x/ccv/provider/keeper/hooks.go index 3be2be04eb..cfb9090981 100644 --- a/x/ccv/provider/keeper/hooks.go +++ b/x/ccv/provider/keeper/hooks.go @@ -91,50 +91,8 @@ func (h Hooks) AfterUnbondingInitiated(ctx sdk.Context, id uint64) error { return nil } -// ValidatorConsensusKeyInUse is called when a new validator is created -// in the x/staking module of cosmos-sdk. In case it panics, the TX aborts -// and thus, the validator is not created. See AfterValidatorCreated hook. -func ValidatorConsensusKeyInUse(k *Keeper, ctx sdk.Context, valAddr sdk.ValAddress) bool { - // Get the validator being added in the staking module. - val, found := k.stakingKeeper.GetValidator(ctx, valAddr) - if !found { - // Abort TX, do NOT allow validator to be created - panic("did not find newly created validator in staking module") - } - - // Get the consensus address of the validator being added - consensusAddr, err := val.GetConsAddr() - if err != nil { - // Abort TX, do NOT allow validator to be created - panic("could not get validator cons addr ") - } - - allConsumerChains := []string{} - consumerChains := k.GetAllConsumerChains(ctx) - for _, consumerChain := range consumerChains { - allConsumerChains = append(allConsumerChains, consumerChain.ChainId) - } - proposedChains := k.GetAllProposedConsumerChainIDs(ctx) - for _, proposedChain := range proposedChains { - allConsumerChains = append(allConsumerChains, proposedChain.ChainID) - } - pendingChainIDs := k.GetAllPendingConsumerChainIDs(ctx) - allConsumerChains = append(allConsumerChains, pendingChainIDs...) - - for _, c := range allConsumerChains { - if _, exist := k.GetValidatorByConsumerAddr( - ctx, - c, - providertypes.NewConsumerConsAddress(consensusAddr)); exist { - return true - } - } - - return false -} - func (h Hooks) AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) error { - if ValidatorConsensusKeyInUse(h.k, ctx, valAddr) { + if h.k.ValidatorConsensusKeyInUse(ctx, valAddr) { // Abort TX, do NOT allow validator to be created panic("cannot create a validator with a consensus key that is already in use or was recently in use as an assigned consumer chain key") } diff --git a/x/ccv/provider/keeper/hooks_test.go b/x/ccv/provider/keeper/hooks_test.go index 0bdf9b26fa..47175eb907 100644 --- a/x/ccv/provider/keeper/hooks_test.go +++ b/x/ccv/provider/keeper/hooks_test.go @@ -83,7 +83,7 @@ func TestValidatorConsensusKeyInUse(t *testing.T) { tt.setup(ctx, k) t.Run(tt.name, func(t *testing.T) { - if actual := providerkeeper.ValidatorConsensusKeyInUse(&k, ctx, newValidator.SDKStakingValidator().GetOperator()); actual != tt.expect { + if actual := k.ValidatorConsensusKeyInUse(ctx, newValidator.SDKStakingValidator().GetOperator()); actual != tt.expect { t.Errorf("validatorConsensusKeyInUse() = %v, want %v", actual, tt.expect) } }) diff --git a/x/ccv/provider/keeper/keeper.go b/x/ccv/provider/keeper/keeper.go index 0b9fa0420d..f0c6195cfe 100644 --- a/x/ccv/provider/keeper/keeper.go +++ b/x/ccv/provider/keeper/keeper.go @@ -1123,3 +1123,19 @@ func (k Keeper) GetSlashLog( func (k Keeper) BondDenom(ctx sdk.Context) string { return k.stakingKeeper.BondDenom(ctx) } + +func (k Keeper) GetAllRegisteredAndProposedChainIDs(ctx sdk.Context) []string { + allConsumerChains := []string{} + consumerChains := k.GetAllConsumerChains(ctx) + for _, consumerChain := range consumerChains { + allConsumerChains = append(allConsumerChains, consumerChain.ChainId) + } + proposedChains := k.GetAllProposedConsumerChainIDs(ctx) + for _, proposedChain := range proposedChains { + allConsumerChains = append(allConsumerChains, proposedChain.ChainID) + } + pendingChainIDs := k.GetAllPendingConsumerChainIDs(ctx) + allConsumerChains = append(allConsumerChains, pendingChainIDs...) + + return allConsumerChains +} diff --git a/x/ccv/provider/keeper/key_assignment.go b/x/ccv/provider/keeper/key_assignment.go index d440848bbf..49d940a947 100644 --- a/x/ccv/provider/keeper/key_assignment.go +++ b/x/ccv/provider/keeper/key_assignment.go @@ -377,6 +377,14 @@ func (k Keeper) AssignConsumerKey( validator stakingtypes.Validator, consumerKey tmprotocrypto.PublicKey, ) error { + // check that the consumer chain is either registered or that + // ConsumerAdditionProposal was voted on. + if !k.CheckIfConsumerIsProposedOrRegistered(ctx, chainID) { + return errorsmod.Wrapf( + types.ErrUnknownConsumerChainId, chainID, + ) + } + consAddrTmp, err := ccvtypes.TMCryptoPublicKeyToConsAddr(consumerKey) if err != nil { return err @@ -629,3 +637,47 @@ func (k Keeper) DeleteKeyAssignments(ctx sdk.Context, chainID string) { k.DeleteConsumerAddrsToPrune(ctx, chainID, consumerAddrsToPrune.VscId) } } + +// CheckIfConsumerIsProposedOrRegistered checks if a consumer chain is either registered, meaning either already running +// or will run soon, or proposed its ConsumerAdditionProposal was submitted but the chain was not yet added to ICS yet. +func (k Keeper) CheckIfConsumerIsProposedOrRegistered(ctx sdk.Context, chainID string) bool { + allConsumerChains := k.GetAllRegisteredAndProposedChainIDs(ctx) + for _, c := range allConsumerChains { + if c == chainID { + return true + } + } + + return false +} + +// ValidatorConsensusKeyInUse checks if the given consensus key is already +// used by validator in a consumer chain. +// Note that this method is called when a new validator is created in the x/staking module of cosmos-sdk. +// In case it panics, the TX aborts and thus, the validator is not created. See AfterValidatorCreated hook. +func (k Keeper) ValidatorConsensusKeyInUse(ctx sdk.Context, valAddr sdk.ValAddress) bool { + // Get the validator being added in the staking module. + val, found := k.stakingKeeper.GetValidator(ctx, valAddr) + if !found { + // Abort TX, do NOT allow validator to be created + panic("did not find newly created validator in staking module") + } + + // Get the consensus address of the validator being added + consensusAddr, err := val.GetConsAddr() + if err != nil { + // Abort TX, do NOT allow validator to be created + panic("could not get validator cons addr ") + } + + allConsumerChains := k.GetAllRegisteredAndProposedChainIDs(ctx) + for _, c := range allConsumerChains { + if _, exist := k.GetValidatorByConsumerAddr(ctx, + c, + types.NewConsumerConsAddress(consensusAddr), + ); exist { + return true + } + } + return false +} diff --git a/x/ccv/provider/keeper/key_assignment_test.go b/x/ccv/provider/keeper/key_assignment_test.go index e9cb1dd646..b9a20e6b1b 100644 --- a/x/ccv/provider/keeper/key_assignment_test.go +++ b/x/ccv/provider/keeper/key_assignment_test.go @@ -389,17 +389,32 @@ func TestAssignConsensusKeyForConsumerChain(t *testing.T) { doActions func(sdk.Context, providerkeeper.Keeper) }{ /* - 0. Consumer registered: Assign PK0->CK0 and retrieve PK0->CK0 - 1. Consumer registered: Assign PK0->CK0, PK0->CK1 and retrieve PK0->CK1 - 2. Consumer registered: Assign PK0->CK0, PK1->CK0 and error - 3. Consumer registered: Assign PK1->PK0 and error - 4. Consumer not registered: Assign PK0->CK0 and retrieve PK0->CK0 - 5. Consumer not registered: Assign PK0->CK0, PK0->CK1 and retrieve PK0->CK1 - 6. Consumer not registered: Assign PK0->CK0, PK1->CK0 and error - 7. Consumer not registered: Assign PK1->PK0 and error + 0. Consumer not registered: Assign PK0->CK0 and error + 1. Consumer registered: Assign PK0->CK0 and retrieve PK0->CK0 + 2. Consumer registered: Assign PK0->CK0, PK0->CK1 and retrieve PK0->CK1 + 3. Consumer registered: Assign PK0->CK0, PK1->CK0 and error + 4. Consumer registered: Assign PK1->PK0 and error + 5. Consumer proposed: Assign Assign PK0->CK0 and retrieve PK0->CK0 + 6. Consumer proposed: Assign PK0->CK0, PK0->CK1 and retrieve PK0->CK1 + 7. Consumer proposed: Assign PK0->CK0, PK1->CK0 and error + 8. Consumer proposed: Assign PK1->PK0 and error */ { - name: "0", + name: "0", + mockSetup: func(ctx sdk.Context, k providerkeeper.Keeper, mocks testkeeper.MockedKeepers) {}, + doActions: func(ctx sdk.Context, k providerkeeper.Keeper) { + err := k.AssignConsumerKey(ctx, chainID, + providerIdentities[0].SDKStakingValidator(), + consumerIdentities[0].TMProtoCryptoPublicKey(), + ) + require.Error(t, err) + _, found := k.GetValidatorByConsumerAddr(ctx, chainID, + consumerIdentities[0].ConsumerConsAddress()) + require.False(t, found) + }, + }, + { + name: "1", mockSetup: func(ctx sdk.Context, k providerkeeper.Keeper, mocks testkeeper.MockedKeepers) { gomock.InOrder( mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, @@ -424,7 +439,7 @@ func TestAssignConsensusKeyForConsumerChain(t *testing.T) { }, }, { - name: "1", + name: "2", mockSetup: func(ctx sdk.Context, k providerkeeper.Keeper, mocks testkeeper.MockedKeepers) { gomock.InOrder( mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, @@ -460,7 +475,7 @@ func TestAssignConsensusKeyForConsumerChain(t *testing.T) { }, }, { - name: "2", + name: "3", mockSetup: func(ctx sdk.Context, k providerkeeper.Keeper, mocks testkeeper.MockedKeepers) { gomock.InOrder( mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, @@ -493,7 +508,7 @@ func TestAssignConsensusKeyForConsumerChain(t *testing.T) { }, }, { - name: "3", + name: "4", mockSetup: func(ctx sdk.Context, k providerkeeper.Keeper, mocks testkeeper.MockedKeepers) { gomock.InOrder( mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, @@ -511,7 +526,7 @@ func TestAssignConsensusKeyForConsumerChain(t *testing.T) { }, }, { - name: "4", + name: "5", mockSetup: func(ctx sdk.Context, k providerkeeper.Keeper, mocks testkeeper.MockedKeepers) { gomock.InOrder( mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, @@ -520,6 +535,7 @@ func TestAssignConsensusKeyForConsumerChain(t *testing.T) { ) }, doActions: func(ctx sdk.Context, k providerkeeper.Keeper) { + k.SetProposedConsumerChain(ctx, chainID, 0) err := k.AssignConsumerKey(ctx, chainID, providerIdentities[0].SDKStakingValidator(), consumerIdentities[0].TMProtoCryptoPublicKey(), @@ -532,7 +548,7 @@ func TestAssignConsensusKeyForConsumerChain(t *testing.T) { }, }, { - name: "5", + name: "6", mockSetup: func(ctx sdk.Context, k providerkeeper.Keeper, mocks testkeeper.MockedKeepers) { gomock.InOrder( mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, @@ -544,6 +560,7 @@ func TestAssignConsensusKeyForConsumerChain(t *testing.T) { ) }, doActions: func(ctx sdk.Context, k providerkeeper.Keeper) { + k.SetProposedConsumerChain(ctx, chainID, 0) err := k.AssignConsumerKey(ctx, chainID, providerIdentities[0].SDKStakingValidator(), consumerIdentities[0].TMProtoCryptoPublicKey(), @@ -561,7 +578,7 @@ func TestAssignConsensusKeyForConsumerChain(t *testing.T) { }, }, { - name: "6", + name: "7", mockSetup: func(ctx sdk.Context, k providerkeeper.Keeper, mocks testkeeper.MockedKeepers) { gomock.InOrder( mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, @@ -573,6 +590,7 @@ func TestAssignConsensusKeyForConsumerChain(t *testing.T) { ) }, doActions: func(ctx sdk.Context, k providerkeeper.Keeper) { + k.SetProposedConsumerChain(ctx, chainID, 0) err := k.AssignConsumerKey(ctx, chainID, providerIdentities[0].SDKStakingValidator(), consumerIdentities[0].TMProtoCryptoPublicKey(), @@ -590,7 +608,7 @@ func TestAssignConsensusKeyForConsumerChain(t *testing.T) { }, }, { - name: "7", + name: "8", mockSetup: func(ctx sdk.Context, k providerkeeper.Keeper, mocks testkeeper.MockedKeepers) { gomock.InOrder( mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, @@ -599,6 +617,7 @@ func TestAssignConsensusKeyForConsumerChain(t *testing.T) { ) }, doActions: func(ctx sdk.Context, k providerkeeper.Keeper) { + k.SetProposedConsumerChain(ctx, chainID, 0) err := k.AssignConsumerKey(ctx, chainID, providerIdentities[1].SDKStakingValidator(), providerIdentities[0].TMProtoCryptoPublicKey(), From 167a74d21d7c00f9459e4a6100ce59a13612639e Mon Sep 17 00:00:00 2001 From: Simon Noetzlin Date: Thu, 23 Nov 2023 11:45:59 +0100 Subject: [PATCH 19/22] remove todos and update doc --- x/ccv/provider/keeper/key_assignment.go | 11 ++++++----- x/ccv/provider/keeper/msg_server.go | 9 ++------- x/ccv/provider/types/msg.go | 3 +-- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/x/ccv/provider/keeper/key_assignment.go b/x/ccv/provider/keeper/key_assignment.go index 49d940a947..da6aa8de94 100644 --- a/x/ccv/provider/keeper/key_assignment.go +++ b/x/ccv/provider/keeper/key_assignment.go @@ -370,7 +370,8 @@ func (k Keeper) DeleteConsumerAddrsToPrune(ctx sdk.Context, chainID string, vscI } // AssignConsumerKey assigns the consumerKey to the validator with providerAddr -// on the consumer chain with ID chainID +// on the consumer chain with ID chainID, if it is either registered or currently +// voted on in a ConsumerAddition governance proposal func (k Keeper) AssignConsumerKey( ctx sdk.Context, chainID string, @@ -398,15 +399,15 @@ func (k Keeper) AssignConsumerKey( providerAddr := types.NewProviderConsAddress(consAddrTmp) if existingVal, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, consumerAddr.ToSdkConsAddr()); found { - // If there is a validator using the consumer key to validate on the provider - // we prevent assigning the consumer key, unless the validator is assigning validator. - // This ensures that a validator joining the active set who has not explicitly assigned - // a consumer key, will be able to use their provider key as consumer key (as per default). + // If there is already a different validator using the consumer key to validate on the provider + // we prevent assigning the consumer key. if existingVal.OperatorAddress != validator.OperatorAddress { return errorsmod.Wrapf( types.ErrConsumerKeyInUse, "a different validator already uses the consumer key", ) } + // We prevent a validator from assigning the default provider key as a consumer key + // if it has not already assigned a different consumer key _, found := k.GetValidatorConsumerPubKey(ctx, chainID, providerAddr) if !found { return errorsmod.Wrapf( diff --git a/x/ccv/provider/keeper/msg_server.go b/x/ccv/provider/keeper/msg_server.go index fecbdc8c36..3808b8f0f7 100644 --- a/x/ccv/provider/keeper/msg_server.go +++ b/x/ccv/provider/keeper/msg_server.go @@ -26,16 +26,11 @@ func NewMsgServerImpl(keeper *Keeper) types.MsgServer { var _ types.MsgServer = msgServer{} -// CreateValidator defines a method for creating a new validator +// AssignConsumerKey defines a method to assign a consensus key on a consumer chain +// for a given validator on the provider func (k msgServer) AssignConsumerKey(goCtx context.Context, msg *types.MsgAssignConsumerKey) (*types.MsgAssignConsumerKeyResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - // It is possible to assign keys for consumer chains that are not yet approved. - // TODO: In future, a mechanism will be added to limit assigning keys to chains - // which are approved or pending approval, only. - // Note that current attack potential is restricted because validators must sign - // the transaction, and the chainID size is limited. - providerValidatorAddr, err := sdk.ValAddressFromBech32(msg.ProviderAddr) if err != nil { return nil, err diff --git a/x/ccv/provider/types/msg.go b/x/ccv/provider/types/msg.go index 901aa03600..cb124b10b7 100644 --- a/x/ccv/provider/types/msg.go +++ b/x/ccv/provider/types/msg.go @@ -61,8 +61,7 @@ func (msg MsgAssignConsumerKey) ValidateBasic() error { // It is possible to assign keys for consumer chains that are not yet approved. // This can only be done by a signing validator, but it is still sensible // to limit the chainID size to prevent abuse. - // TODO: In future, a mechanism will be added to limit assigning keys to chains - // which are approved or pending approval, only. + if 128 < len(msg.ChainId) { return ErrBlankConsumerChainID } From efc3f957f127e29c9fc29ae2dc00078f6db734f4 Mon Sep 17 00:00:00 2001 From: MSalopek Date: Mon, 27 Nov 2023 22:19:41 +0100 Subject: [PATCH 20/22] tests: fix broken unit tests --- x/ccv/provider/handler_test.go | 24 ++++++++++++++++++++ x/ccv/provider/keeper/key_assignment_test.go | 4 ++++ 2 files changed, 28 insertions(+) diff --git a/x/ccv/provider/handler_test.go b/x/ccv/provider/handler_test.go index e4835cf14a..3ff2d5a26a 100644 --- a/x/ccv/provider/handler_test.go +++ b/x/ccv/provider/handler_test.go @@ -51,6 +51,9 @@ func TestAssignConsensusKeyForConsumerChain(t *testing.T) { setup: func(ctx sdk.Context, k keeper.Keeper, mocks testkeeper.MockedKeepers, ) { + k.SetPendingConsumerAdditionProp(ctx, &providertypes.ConsumerAdditionProposal{ + ChainId: "chainid", + }) gomock.InOrder( mocks.MockStakingKeeper.EXPECT().GetValidator( ctx, providerCryptoId.SDKValOpAddress(), @@ -64,11 +67,29 @@ func TestAssignConsensusKeyForConsumerChain(t *testing.T) { expError: false, chainID: "chainid", }, + { + name: "fail: chain ID not registered", + setup: func(ctx sdk.Context, + k keeper.Keeper, mocks testkeeper.MockedKeepers, + ) { + gomock.InOrder( + mocks.MockStakingKeeper.EXPECT().GetValidator( + ctx, providerCryptoId.SDKValOpAddress(), + // Return a valid validator, found! + ).Return(providerCryptoId.SDKStakingValidator(), true).Times(1), + ) + }, + expError: true, + chainID: "chainid", + }, { name: "fail: missing validator", setup: func(ctx sdk.Context, k keeper.Keeper, mocks testkeeper.MockedKeepers, ) { + k.SetPendingConsumerAdditionProp(ctx, &providertypes.ConsumerAdditionProposal{ + ChainId: "chainid", + }) gomock.InOrder( mocks.MockStakingKeeper.EXPECT().GetValidator( ctx, providerCryptoId.SDKValOpAddress(), @@ -84,6 +105,9 @@ func TestAssignConsensusKeyForConsumerChain(t *testing.T) { setup: func(ctx sdk.Context, k keeper.Keeper, mocks testkeeper.MockedKeepers, ) { + k.SetPendingConsumerAdditionProp(ctx, &providertypes.ConsumerAdditionProposal{ + ChainId: "chainid", + }) // Use the consumer key already k.SetValidatorByConsumerAddr(ctx, "chainid", consumerConsAddr, providerConsAddr) diff --git a/x/ccv/provider/keeper/key_assignment_test.go b/x/ccv/provider/keeper/key_assignment_test.go index b9a20e6b1b..0009680864 100644 --- a/x/ccv/provider/keeper/key_assignment_test.go +++ b/x/ccv/provider/keeper/key_assignment_test.go @@ -653,6 +653,10 @@ func TestCannotReassignDefaultKeyAssignment(t *testing.T) { providerKeeper, ctx, ctrl, mocks := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t)) defer ctrl.Finish() + providerKeeper.SetPendingConsumerAdditionProp(ctx, &types.ConsumerAdditionProposal{ + ChainId: "chain", + }) + // Mock that the validator is validating with the single key, as confirmed by provider's staking keeper gomock.InOrder( mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, From 29528b238a5e541daf722f4700ccd86684de6d32 Mon Sep 17 00:00:00 2001 From: MSalopek Date: Mon, 27 Nov 2023 22:26:40 +0100 Subject: [PATCH 21/22] tests: fix broken integration test consumer registration --- tests/integration/setup.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/integration/setup.go b/tests/integration/setup.go index eee8fb1a22..4628ca3111 100644 --- a/tests/integration/setup.go +++ b/tests/integration/setup.go @@ -24,6 +24,7 @@ import ( testutil "github.com/cosmos/interchain-security/v3/testutil/integration" "github.com/cosmos/interchain-security/v3/testutil/simibc" consumertypes "github.com/cosmos/interchain-security/v3/x/ccv/consumer/types" + "github.com/cosmos/interchain-security/v3/x/ccv/provider/types" ccv "github.com/cosmos/interchain-security/v3/x/ccv/types" ) @@ -129,6 +130,9 @@ func (suite *CCVTestSuite) SetupTest() { providerKeeper := suite.providerApp.GetProviderKeeper() // re-assign all validator keys for the first consumer chain + providerKeeper.SetPendingConsumerAdditionProp(suite.providerCtx(), &types.ConsumerAdditionProposal{ + ChainId: icstestingutils.FirstConsumerChainID, + }) preProposalKeyAssignment(suite, icstestingutils.FirstConsumerChainID) // start consumer chains From 4a38b9aeae16c4ac1ee2bf662a5ce339cc242ed4 Mon Sep 17 00:00:00 2001 From: MSalopek Date: Mon, 27 Nov 2023 22:30:00 +0100 Subject: [PATCH 22/22] tests: update names --- x/ccv/provider/keeper/key_assignment.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/x/ccv/provider/keeper/key_assignment.go b/x/ccv/provider/keeper/key_assignment.go index da6aa8de94..46447790df 100644 --- a/x/ccv/provider/keeper/key_assignment.go +++ b/x/ccv/provider/keeper/key_assignment.go @@ -379,8 +379,8 @@ func (k Keeper) AssignConsumerKey( consumerKey tmprotocrypto.PublicKey, ) error { // check that the consumer chain is either registered or that - // ConsumerAdditionProposal was voted on. - if !k.CheckIfConsumerIsProposedOrRegistered(ctx, chainID) { + // a ConsumerAdditionProposal was submitted. + if !k.IsConsumerProposedOrRegistered(ctx, chainID) { return errorsmod.Wrapf( types.ErrUnknownConsumerChainId, chainID, ) @@ -639,9 +639,9 @@ func (k Keeper) DeleteKeyAssignments(ctx sdk.Context, chainID string) { } } -// CheckIfConsumerIsProposedOrRegistered checks if a consumer chain is either registered, meaning either already running +// IsConsumerProposedOrRegistered checks if a consumer chain is either registered, meaning either already running // or will run soon, or proposed its ConsumerAdditionProposal was submitted but the chain was not yet added to ICS yet. -func (k Keeper) CheckIfConsumerIsProposedOrRegistered(ctx sdk.Context, chainID string) bool { +func (k Keeper) IsConsumerProposedOrRegistered(ctx sdk.Context, chainID string) bool { allConsumerChains := k.GetAllRegisteredAndProposedChainIDs(ctx) for _, c := range allConsumerChains { if c == chainID {