Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
insumity committed Aug 27, 2024
1 parent f7e5b3c commit 7a44d05
Show file tree
Hide file tree
Showing 15 changed files with 218 additions and 354 deletions.
51 changes: 0 additions & 51 deletions tests/integration/provider_gov_hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,57 +13,6 @@ import (
testkeeper "github.com/cosmos/interchain-security/v5/testutil/keeper"
)

// TestAfterPropSubmissionAndVotingPeriodEnded tests AfterProposalSubmission and AfterProposalVotingPeriodEnded hooks
// require adding a proposal in the gov module and registering a consumer chain with the provider module
func (s *CCVTestSuite) TestAfterPropSubmissionAndVotingPeriodEnded() {
ctx := s.providerChain.GetContext()
providerKeeper := s.providerApp.GetProviderKeeper()
govKeeper := s.providerApp.GetTestGovKeeper()
proposer := s.providerChain.SenderAccount

msgUpdateConsumer := testkeeper.GetTestMsgUpdateConsumer()

proposal, err := v1.NewProposal([]sdk.Msg{&msgUpdateConsumer}, 1, time.Now(), time.Now().Add(1*time.Hour), "metadata", "title", "summary", proposer.GetAddress(), false)
s.Require().NoError(err)

err = govKeeper.SetProposal(ctx, proposal)
s.Require().NoError(err)

// the proposal can only be submitted if the owner of the chain is the gov module
providerKeeper.SetConsumerOwnerAddress(ctx, msgUpdateConsumer.ConsumerId, "some bogus address")

err = providerKeeper.Hooks().AfterProposalSubmission(ctx, proposal.Id)
s.Require().Error(err)

// the proposal can only be submitted if the owner of the chain is the gov module
govModuleAddress := "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn"
providerKeeper.SetConsumerOwnerAddress(ctx, msgUpdateConsumer.ConsumerId, govModuleAddress)

err = providerKeeper.Hooks().AfterProposalSubmission(ctx, proposal.Id)
s.Require().NoError(err)

// verify that the proposal id is created
consumerIdOnProvider, ok := providerKeeper.GetProposalIdToConsumerId(ctx, proposal.Id)
s.Require().True(ok)
s.Require().NotEmpty(consumerIdOnProvider)
s.Require().Equal(msgUpdateConsumer.ConsumerId, consumerIdOnProvider)

providerKeeper.Hooks().AfterProposalVotingPeriodEnded(ctx, proposal.Id)
// verify that the proposal id is deleted
s.Require().Empty(providerKeeper.GetProposalIdToConsumerId(ctx, proposal.Id))

// assert that a proposal with more than one `MsgUpdateConsumer` messages fails
proposal, err = v1.NewProposal([]sdk.Msg{&msgUpdateConsumer, &msgUpdateConsumer}, 1, time.Now(), time.Now().Add(1*time.Hour), "metadata", "title", "summary", proposer.GetAddress(), false)
s.Require().NoError(err)

err = govKeeper.SetProposal(ctx, proposal)
s.Require().NoError(err)

err = providerKeeper.Hooks().AfterProposalSubmission(ctx, proposal.Id)
s.Require().Error(err)

}

func (s *CCVTestSuite) TestGetConsumerAdditionFromProp() {
ctx := s.providerChain.GetContext()
proposer := s.providerChain.SenderAccount
Expand Down
2 changes: 1 addition & 1 deletion x/ccv/provider/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func CmdConsumerChains() *cobra.Command {
func CmdProposedConsumerChains() *cobra.Command {
cmd := &cobra.Command{
Use: "list-proposed-consumer-chains",
Short: "Query chainIDs in consumer addition proposal before voting finishes",
Short: "Query consumer chains that try to be added through governance before voting finishes",
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) (err error) {
clientCtx, err := client.GetClientQueryContext(cmd)
Expand Down
2 changes: 0 additions & 2 deletions x/ccv/provider/keeper/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,6 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState {
k.GetValidatorSetUpdateId(ctx),
k.GetAllValsetUpdateBlockHeights(ctx),
consumerStates,
k.GetAllPendingConsumerAdditionProps(ctx),
k.GetAllPendingConsumerRemovalProps(ctx),
params,
k.GetAllValidatorConsumerPubKeys(ctx, nil),
k.GetAllValidatorsByConsumerAddr(ctx, nil),
Expand Down
12 changes: 0 additions & 12 deletions x/ccv/provider/keeper/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,6 @@ func TestInitAndExportGenesis(t *testing.T) {
nil,
),
},
[]providertypes.ConsumerAdditionProposal{{
ChainId: cChainIDs[0],
SpawnTime: oneHourFromNow,
}},
[]providertypes.ConsumerRemovalProposal{{
ChainId: cChainIDs[0],
StopTime: oneHourFromNow,
}},
params,
[]providertypes.ValidatorConsumerPubKey{
{
Expand Down Expand Up @@ -139,10 +131,6 @@ func TestInitAndExportGenesis(t *testing.T) {
height, found := pk.GetValsetUpdateBlockHeight(ctx, vscID)
require.True(t, found)
require.Equal(t, initHeight, height)
addProp, found := pk.GetPendingConsumerAdditionProp(ctx, oneHourFromNow, cChainIDs[0])
require.True(t, found)
require.Equal(t, provGenesis.ConsumerAdditionProposals[0], addProp)
require.True(t, pk.PendingConsumerRemovalPropExists(ctx, cChainIDs[0], oneHourFromNow))
require.Equal(t, provGenesis.Params, pk.GetParams(ctx))

providerConsensusValSet, err := pk.GetLastProviderConsensusValSet(ctx)
Expand Down
53 changes: 38 additions & 15 deletions x/ccv/provider/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package keeper
import (
"context"
"fmt"
govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"

"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
Expand Down Expand Up @@ -111,14 +112,14 @@ func (k Keeper) QueryConsumerChainStarts(goCtx context.Context, req *types.Query
return nil, status.Error(codes.InvalidArgument, "empty request")
}

ctx := sdk.UnwrapSDKContext(goCtx)
//ctx := sdk.UnwrapSDKContext(goCtx)
var props []*types.ConsumerAdditionProposal

for _, prop := range k.GetAllPendingConsumerAdditionProps(ctx) {
// prevent implicit memory aliasing
p := prop
props = append(props, &p)
}
//for _, prop := range k.GetAllPendingConsumerAdditionProps(ctx) {
// // prevent implicit memory aliasing
// p := prop
// props = append(props, &p)
//}

return &types.QueryConsumerChainStartProposalsResponse{Proposals: &types.ConsumerAdditionProposals{Pending: props}}, nil
}
Expand All @@ -129,13 +130,13 @@ func (k Keeper) QueryConsumerChainStops(goCtx context.Context, req *types.QueryC
return nil, status.Error(codes.InvalidArgument, "empty request")
}

ctx := sdk.UnwrapSDKContext(goCtx)
//ctx := sdk.UnwrapSDKContext(goCtx)
var props []*types.ConsumerRemovalProposal
for _, prop := range k.GetAllPendingConsumerRemovalProps(ctx) {
// prevent implicit memory aliasing
p := prop
props = append(props, &p)
}
//for _, prop := range k.GetAllPendingConsumerRemovalProps(ctx) {
// // prevent implicit memory aliasing
// p := prop
// props = append(props, &p)
//}

return &types.QueryConsumerChainStopProposalsResponse{Proposals: &types.ConsumerRemovalProposals{Pending: props}}, nil
}
Expand Down Expand Up @@ -232,18 +233,40 @@ func (k Keeper) QueryRegisteredConsumerRewardDenoms(goCtx context.Context, req *
}, nil
}

// TODO (PERMISSIONLESS)
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)
var proposals []govv1.Proposal
err := k.govKeeper.Proposals.Walk(ctx, nil, func(proposalId uint64, proposal govv1.Proposal) (stop bool, err error) {
proposals = append(proposals, proposal)
return false, nil
})
if err != nil {
return nil, status.Error(codes.Internal, "failed to retrieve proposals")
}

var proposedChains []types.ProposedChain
for _, proposal := range proposals {
status := proposal.Status
if status != govv1.ProposalStatus_PROPOSAL_STATUS_DEPOSIT_PERIOD && status != govv1.ProposalStatus_PROPOSAL_STATUS_VOTING_PERIOD {
// we only care about active proposals
continue
}

messages := proposal.GetMessages()
for _, msg := range messages {
if sdkMsg, isMsgUpdateConsumer := msg.GetCachedValue().(*types.MsgUpdateConsumer); isMsgUpdateConsumer {
proposedChains = append(proposedChains, types.ProposedChain{ProposalID: proposal.Id, ConsumerId: sdkMsg.ConsumerId})
}
}
}

return &types.QueryProposedChainIDsResponse{
ProposedChains: chains,
ProposedChains: proposedChains,
}, nil
}

Expand Down
4 changes: 4 additions & 0 deletions x/ccv/provider/keeper/grpc_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -384,3 +384,7 @@ func TestQueryConsumerIdFromClientId(t *testing.T) {
require.NoError(t, err)
require.Equal(t, expectedConsumerId, res.ConsumerId)
}

func TestQueryProposedConsumerChainIDs(t *testing.T) {
// TODO (PERMISSIONLESS)
}
70 changes: 0 additions & 70 deletions x/ccv/provider/keeper/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package keeper

import (
"context"
"fmt"

"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkgov "github.com/cosmos/cosmos-sdk/x/gov/types"
Expand Down Expand Up @@ -106,79 +104,11 @@ func (h Hooks) BeforeTokenizeShareRecordRemoved(_ context.Context, _ uint64) err
// gov hooks
//

// AfterProposalSubmission - call hook if registered
// If an update consumer message exists in the proposal, a record is created that maps the proposal id to the consumer id
// TODO (PERMISSIONLESS)
func (h Hooks) AfterProposalSubmission(goCtx context.Context, proposalId uint64) error {
ctx := sdk.UnwrapSDKContext(goCtx)

p, err := h.k.govKeeper.Proposals.Get(ctx, proposalId)
if err != nil {
return fmt.Errorf("cannot retrieve proposal with id: %d", proposalId)
}

hasUpdateConsumerMsg := false
for _, msg := range p.GetMessages() {
sdkMsg, isMsgUpdateConsumer := msg.GetCachedValue().(*providertypes.MsgUpdateConsumer)
if isMsgUpdateConsumer {
// A `MsgUpdateConsumer` can only succeed if the owner of the consumer chain is the gov module.
// If that's not the case, we immediately fail the proposal.
// Note that someone could potentially change the owner of a chain to be that of the gov module
// while a proposal is active and before the proposal is executed. Even then, we still do not allow
// `MsgUpdateConsumer` proposals if the owner of the chain is not the gov module to avoid someone forgetting
// to change the owner address while the proposal is active.
ownerAddress, err := h.k.GetConsumerOwnerAddress(ctx, sdkMsg.ConsumerId)
if err != nil {
return fmt.Errorf("cannot find owner address for consumer with consumer id (%s): %s", sdkMsg.ConsumerId, err.Error())
} else if ownerAddress != h.k.GetAuthority() {
return fmt.Errorf("owner address (%s) is not the gov module (%s)", ownerAddress, h.k.GetAuthority())
}

if hasUpdateConsumerMsg {
return fmt.Errorf("proposal can contain at most one `MsgUpdateConsumer` message")
}
hasUpdateConsumerMsg = true
h.k.SetProposalIdToConsumerId(ctx, proposalId, sdkMsg.ConsumerId)
}

// if the proposal contains a deprecated message, cancel the proposal
_, isMsgConsumerAddition := msg.GetCachedValue().(*providertypes.MsgConsumerAddition)
if isMsgConsumerAddition {
return fmt.Errorf("proposal cannot contain deprecated `MsgConsumerAddition`; use `MsgCreateConsumer` instead")
}

_, isMsgConsumerModification := msg.GetCachedValue().(*providertypes.MsgConsumerModification)
if isMsgConsumerModification {
return fmt.Errorf("proposal cannot contain deprecated `MsgConsumerModification`; use `MsgUpdateConsumer` instead")
}
_, isMsgConsumerRemoval := msg.GetCachedValue().(*providertypes.MsgConsumerRemoval)
if isMsgConsumerRemoval {
return fmt.Errorf("proposal cannot contain deprecated `MsgConsumerRemoval`; use `MsgRemoveConsumer` instead")
}
}

return nil
}

// AfterProposalVotingPeriodEnded - call hook if registered
// After proposal voting ends, the consumer to proposal id record in store is deleted.
// TODO (PERMISSIONLESS)
func (h Hooks) AfterProposalVotingPeriodEnded(goCtx context.Context, proposalId uint64) error {
ctx := sdk.UnwrapSDKContext(goCtx)

p, err := h.k.govKeeper.Proposals.Get(ctx, proposalId)
if err != nil {
return fmt.Errorf("cannot retrieve proposal with id: %d", proposalId)
}

for _, msg := range p.GetMessages() {
_, isUpdateConsumer := msg.GetCachedValue().(*providertypes.MsgUpdateConsumer)
if isUpdateConsumer {
h.k.DeleteProposalIdToConsumerId(ctx, proposalId)
return nil
}
}

return nil
}

Expand Down
95 changes: 0 additions & 95 deletions x/ccv/provider/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,101 +284,6 @@ func TestSetSlashLog(t *testing.T) {
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 {
consumerId string
proposalId uint64
}{
{consumerId: "1", proposalId: 1},
{consumerId: "some other ID", proposalId: 12},
{consumerId: "some other other chain ID", proposalId: 123},
{consumerId: "", proposalId: 1234},
}

for _, test := range tests {
providerKeeper.SetProposalIdToConsumerId(ctx, test.proposalId, test.consumerId)
cID, _ := providerKeeper.GetProposalIdToConsumerId(ctx, test.proposalId)
require.Equal(t, cID, test.consumerId)
}
}

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.SetProposalIdToConsumerId(ctx, test.proposalId, test.chainId)
providerKeeper.DeleteProposalIdToConsumerId(ctx, test.deleteProposalId)
cID, found := providerKeeper.GetProposalIdToConsumerId(ctx, test.proposalId)
if test.ok {
require.False(t, found)
} 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{
{},
{
{
ConsumerId: "1",
ProposalID: 1,
},
},
{
{
ConsumerId: "1",
ProposalID: 1,
},
{
ConsumerId: "2",
ProposalID: 2,
},
{
ConsumerId: "",
ProposalID: 3,
},
},
}

for _, test := range tests {
for _, tc := range test {
providerKeeper.SetProposalIdToConsumerId(ctx, tc.ProposalID, tc.ConsumerId)
}

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.DeleteProposalIdToConsumerId(ctx, tc.ProposalID)
}
}
}

// TestTopN tests the `SetTopN`, `GetTopN`, `IsTopN`, and `IsOptIn` methods
func TestTopN(t *testing.T) {
providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t))
Expand Down
Loading

0 comments on commit 7a44d05

Please sign in to comment.