Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(x/gov): multiple choice proposals #18762

Merged
merged 9 commits into from
Jan 8, 2024
Merged

feat(x/gov): multiple choice proposals #18762

merged 9 commits into from
Jan 8, 2024

Conversation

julienrbrt
Copy link
Member

@julienrbrt julienrbrt commented Dec 15, 2023

Description

ref: #18498

Implements multiple choice proposals


Author Checklist

All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.

I have...

  • included the correct type prefix in the PR title
  • confirmed ! in the type prefix if API or client breaking change
  • targeted the correct branch (see PR Targeting)
  • provided a link to the relevant issue or specification
  • reviewed "Files changed" and left comments if necessary
  • included the necessary unit and integration tests
  • added a changelog entry to CHANGELOG.md
  • updated the relevant documentation or specification, including comments for documenting Go code
  • confirmed all CI checks have passed

Reviewers Checklist

All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.

I have...

  • confirmed the correct type prefix in the PR title
  • confirmed all author checklist items have been addressed
  • reviewed state machine logic, API design and naming, documentation is accurate, tests and test coverage

@julienrbrt julienrbrt marked this pull request as ready for review December 18, 2023 10:05
@julienrbrt julienrbrt requested a review from a team as a code owner December 18, 2023 10:05
@julienrbrt
Copy link
Member Author

julienrbrt commented Dec 18, 2023

Adding tests later. Concept R4R for review.

Base automatically changed from julien/optimistic-proposals to main December 20, 2023 21:50

This comment has been minimized.

Copy link
Contributor

coderabbitai bot commented Dec 20, 2023

Walkthrough

The Cosmos governance module (x/gov) has been updated to include multiple choice proposals, enhancing the decision-making process. The changes involve adding new message types and RPC methods to support this feature across various proto files. The keeper functions have been refactored for consistency, and the tallying logic has been adapted for multiple choice vote counting. The documentation, changelog, and test suites have been updated to reflect these new capabilities and ensure robust functionality.

Changes

File(s) Change Summary
proto/cosmos/gov/v1/gov.proto
proto/cosmos/gov/v1/query.proto
proto/cosmos/gov/v1/tx.proto
Updated to support multiple choice proposals with new message types and RPC methods.
x/gov/CHANGELOG.md
x/gov/README.md
Documented the addition of multiple choice proposals.
x/gov/keeper/deposit.go
x/gov/keeper/keeper.go
x/gov/keeper/msg_server.go
x/gov/keeper/proposal.go
x/gov/keeper/tally.go
x/gov/keeper/vote.go
Refactored to change the receiver variable from keeper to k and updated method references accordingly.
x/gov/keeper/grpc_query.go
x/gov/keeper/grpc_query_test.go
x/gov/keeper/msg_server_test.go
x/gov/keeper/tally_test.go
x/gov/keeper/vote_test.go
Added new methods, tests, and error handling for multiple choice proposals.
x/gov/types/keys.go
x/gov/types/v1/codec.go
x/gov/types/v1/msgs.go
x/gov/types/v1/vote.go
Introduced new keys, updated codec registration, and added new message types for handling multiple choice proposals.

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share

Tips

Chat

There are 3 ways to chat with CodeRabbit:

Note: Auto-reply has been disabled for this repository by the repository owner. The CodeRabbit bot will not respond to your comments unless it is explicitly tagged.

  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit tests for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository from git and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit tests.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger a review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • The JSON schema for the configuration file is available here.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/coderabbit-overrides.v2.json

CodeRabbit Discord Community

Join our Discord Community to get help, request features, and share feedback.

Comment on lines 1740 to 1743
func (suite *KeeperTestSuite) TestProposalVoteOptions() {
suite.reset()

}
Copy link
Contributor

Choose a reason for hiding this comment

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

The function TestProposalVoteOptions is empty and does not contain any test code. Please implement the test or add a TODO comment if it's a work in progress.

Comment on lines +7051 to +7098
type _MsgSubmitMultipleChoiceProposal_1_list struct {
list *[]*v1beta1.Coin
}

func (x *_MsgSubmitMultipleChoiceProposal_1_list) Len() int {
if x.list == nil {
return 0
}
return len(*x.list)
}

func (x *_MsgSubmitMultipleChoiceProposal_1_list) Get(i int) protoreflect.Value {
return protoreflect.ValueOfMessage((*x.list)[i].ProtoReflect())
}

func (x *_MsgSubmitMultipleChoiceProposal_1_list) Set(i int, value protoreflect.Value) {
valueUnwrapped := value.Message()
concreteValue := valueUnwrapped.Interface().(*v1beta1.Coin)
(*x.list)[i] = concreteValue
}

func (x *_MsgSubmitMultipleChoiceProposal_1_list) Append(value protoreflect.Value) {
valueUnwrapped := value.Message()
concreteValue := valueUnwrapped.Interface().(*v1beta1.Coin)
*x.list = append(*x.list, concreteValue)
}

func (x *_MsgSubmitMultipleChoiceProposal_1_list) AppendMutable() protoreflect.Value {
v := new(v1beta1.Coin)
*x.list = append(*x.list, v)
return protoreflect.ValueOfMessage(v.ProtoReflect())
}

func (x *_MsgSubmitMultipleChoiceProposal_1_list) Truncate(n int) {
for i := n; i < len(*x.list); i++ {
(*x.list)[i] = nil
}
*x.list = (*x.list)[:n]
}

func (x *_MsgSubmitMultipleChoiceProposal_1_list) NewElement() protoreflect.Value {
v := new(v1beta1.Coin)
return protoreflect.ValueOfMessage(v.ProtoReflect())
}

func (x *_MsgSubmitMultipleChoiceProposal_1_list) IsValid() bool {
return x.list != nil
}
Copy link
Contributor

Choose a reason for hiding this comment

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

The _MsgSubmitMultipleChoiceProposal_1_list type and its methods seem to implement the protoreflect.List interface correctly. However, ensure that the Set and Append methods handle the case where the passed protoreflect.Value is not of the expected type, which could lead to a panic if not properly checked.

Copy link
Member Author

Choose a reason for hiding this comment

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

don't look at proto generated files!

Comment on lines 943 to 945
func TestTally_Multiple_Choice(t *testing.T) {

}
Copy link
Contributor

Choose a reason for hiding this comment

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

The function TestTally_Multiple_Choice is empty. Please implement the test logic or add a TODO comment if it's intended to be completed later.

x/gov/keeper/vote_test.go Show resolved Hide resolved
Comment on lines 301 to 303
func (suite *KeeperTestSuite) TestSubmitMultipleChoiceProposal() {

}
Copy link
Contributor

Choose a reason for hiding this comment

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

The TestSubmitMultipleChoiceProposal function is currently empty. It should be implemented to test the new functionality or marked with a TODO if it's a work in progress.

@github-actions github-actions bot removed the C:x/tx label Dec 28, 2023
Comment on lines +167 to +186
// ProposalVoteOptions defines the stringified vote options for proposals.
// This allows to support multiple choice options for a given proposal.
//
// Since: x/gov v1.0.0
message ProposalVoteOptions {
// option_one is the first option of the proposal
string option_one = 1;

// option_two is the second option of the proposal
string option_two = 2;

// option_three is the third option of the proposal
string option_three = 3;

// option_four is the fourth option of the proposal
string option_four = 4;

// option_spam is always present for all proposals.
string option_spam = 5;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

The introduction of the ProposalVoteOptions message with fields for multiple options and a spam option is consistent with the PR's objective to support multiple choice proposals. However, the naming of the options as option_one, option_two, etc., and option_spam may not be descriptive enough for future maintainers or users of the SDK. Consider using more descriptive names that convey the meaning or context of each option.

api/cosmos/gov/v1/gov.pulsar.go Show resolved Hide resolved
api/cosmos/gov/v1/gov.pulsar.go Outdated Show resolved Hide resolved
x/gov/keeper/grpc_query_test.go Outdated Show resolved Hide resolved
Comment on lines 943 to 944
func TestTally_Multiple_Choice(t *testing.T) {
}
Copy link
Contributor

Choose a reason for hiding this comment

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

The function TestTally_Multiple_Choice is currently empty. If the implementation is pending, consider adding a TODO comment to indicate that tests need to be written. Otherwise, please implement the test cases for multiple choice tallying.

func TestTally_Multiple_Choice(t *testing.T) {
+   // TODO: Implement test cases for multiple choice tallying
}

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
func TestTally_Multiple_Choice(t *testing.T) {
}
func TestTally_Multiple_Choice(t *testing.T) {
// TODO: Implement test cases for multiple choice tallying
}

x/gov/keeper/grpc_query.go Outdated Show resolved Hide resolved
Copy link
Member

@facundomedica facundomedica left a comment

Choose a reason for hiding this comment

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

LGTM 👌

@@ -939,3 +939,329 @@ func TestTally_Optimistic(t *testing.T) {
})
}
}

// TODO(@julienrbrt): refactor tally result to fit all proposal types
Copy link
Contributor

Choose a reason for hiding this comment

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

The TODO comment on line 943 suggests that there is an intention to refactor the tally result to fit all proposal types. This should be tracked in the project's issue tracker to ensure it is not forgotten.

Comment on lines +944 to +1267
expectedTally: v1.TallyResult{
YesCount: "2000000",
AbstainCount: "0",
NoCount: "2000000",
NoWithVetoCount: "0",
SpamCount: "0",
},
},
{
name: "quorum reached with spam > all other votes: prop fails/burn deposit",
setup: func(s tallyFixture) {
setTotalBonded(s, 10000000)
validatorVote(s, s.valAddrs[0], v1.VoteOption_VOTE_OPTION_ONE)
// spam votes
validatorVote(s, s.valAddrs[1], v1.VoteOption_VOTE_OPTION_SPAM)
validatorVote(s, s.valAddrs[2], v1.VoteOption_VOTE_OPTION_SPAM)
validatorVote(s, s.valAddrs[3], v1.VoteOption_VOTE_OPTION_SPAM)
validatorVote(s, s.valAddrs[4], v1.VoteOption_VOTE_OPTION_SPAM)
validatorVote(s, s.valAddrs[5], v1.VoteOption_VOTE_OPTION_SPAM)
validatorVote(s, s.valAddrs[6], v1.VoteOption_VOTE_OPTION_SPAM)
},
expectedPass: false,
expectedBurn: true,
expectedTally: v1.TallyResult{
YesCount: "1000000",
AbstainCount: "0",
NoCount: "0",
NoWithVetoCount: "0",
SpamCount: "6000000",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
govKeeper, mocks, _, ctx := setupGovKeeper(t, mockAccountKeeperExpectations)
params := v1.DefaultParams()
// Ensure params value are different than false
params.BurnVoteQuorum = true
params.BurnVoteVeto = true
err := govKeeper.Params.Set(ctx, params)
require.NoError(t, err)
var (
numVals = 10
numDelegators = 5
addrs = simtestutil.CreateRandomAccounts(numVals + numDelegators)
valAddrs = simtestutil.ConvertAddrsToValAddrs(addrs[:numVals])
delAddrs = addrs[numVals:]
)
// Mocks a bunch of validators
mocks.stakingKeeper.EXPECT().
IterateBondedValidatorsByPower(ctx, gomock.Any()).
DoAndReturn(
func(ctx context.Context, fn func(index int64, validator sdk.ValidatorI) bool) error {
for i := int64(0); i < int64(numVals); i++ {
fn(i, stakingtypes.Validator{
OperatorAddress: valAddrs[i].String(),
Status: stakingtypes.Bonded,
Tokens: sdkmath.NewInt(1000000),
DelegatorShares: sdkmath.LegacyNewDec(1000000),
})
}
return nil
})

// Submit and activate a proposal
proposal, err := govKeeper.SubmitProposal(ctx, nil, "", "title", "summary", delAddrs[0], v1.ProposalType_PROPOSAL_TYPE_MULTIPLE_CHOICE)
require.NoError(t, err)
err = govKeeper.ProposalVoteOptions.Set(ctx, proposal.Id, v1.ProposalVoteOptions{
OptionOne: "Vote Option 1",
OptionTwo: "Vote Option 2",
OptionThree: "Vote Option 3",
OptionFour: "Vote Option 4",
})
require.NoError(t, err)
err = govKeeper.ActivateVotingPeriod(ctx, proposal)
require.NoError(t, err)
suite := tallyFixture{
t: t,
proposal: proposal,
valAddrs: valAddrs,
delAddrs: delAddrs,
ctx: ctx,
keeper: govKeeper,
mocks: mocks,
}
tt.setup(suite)

pass, burn, tally, err := govKeeper.Tally(ctx, proposal)

require.NoError(t, err)
assert.Equal(t, tt.expectedPass, pass, "wrong pass")
assert.Equal(t, tt.expectedBurn, burn, "wrong burn")
assert.Equal(t, tt.expectedTally, tally)
// Assert votes removal after tally
rng := collections.NewPrefixedPairRange[uint64, sdk.AccAddress](proposal.Id)
_, err = suite.keeper.Votes.Iterate(suite.ctx, rng)
assert.NoError(t, err)
})
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

The test cases within TestTally_MultipleChoice appear to be comprehensive and should effectively validate the tallying logic for multiple-choice proposals. However, it is important to ensure that the test setup does not perform unnecessary computations that could slow down the test suite. For example, if the setup for each test case is similar, consider using a shared setup function to reduce redundancy and improve test execution time.

@@ -2,6 +2,7 @@ package keeper

import (
"context"
stderrors "errors"
Copy link
Contributor

Choose a reason for hiding this comment

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

The import alias stderrors for the standard errors package is unconventional. Consider using a more descriptive alias if necessary to avoid confusion with the cosmossdk.io/errors package.

Comment on lines +110 to +138
func TestVotes_MultipleChoiceProposal(t *testing.T) {
govKeeper, mocks, _, ctx := setupGovKeeper(t)
authKeeper, bankKeeper, stakingKeeper := mocks.acctKeeper, mocks.bankKeeper, mocks.stakingKeeper
addrs := simtestutil.AddTestAddrsIncremental(bankKeeper, stakingKeeper, ctx, 2, sdkmath.NewInt(10000000))
authKeeper.EXPECT().AddressCodec().Return(address.NewBech32Codec("cosmos")).AnyTimes()

proposal, err := govKeeper.SubmitProposal(ctx, nil, "", "title", "description", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r"), v1.ProposalType_PROPOSAL_TYPE_MULTIPLE_CHOICE)
require.NoError(t, err)
err = govKeeper.ProposalVoteOptions.Set(ctx, proposal.Id, v1.ProposalVoteOptions{
OptionOne: "Vote for @tac0turle",
OptionTwo: "Vote for @facudomedica",
OptionThree: "Vote for @alexanderbez",
})
require.NoError(t, err)

proposal.Status = v1.StatusVotingPeriod
require.NoError(t, govKeeper.SetProposal(ctx, proposal))

proposalID := proposal.Id

// invalid options
require.Error(t, govKeeper.AddVote(ctx, proposalID, addrs[0], v1.NewNonSplitVoteOption(invalidOption), ""), "invalid option")
require.Error(t, govKeeper.AddVote(ctx, proposalID, addrs[0], v1.NewNonSplitVoteOption(v1.OptionFour), ""), "invalid option") // option four is not defined.

// valid options
require.NoError(t, govKeeper.AddVote(ctx, proposalID, addrs[0], v1.NewNonSplitVoteOption(v1.OptionOne), ""))
require.NoError(t, govKeeper.AddVote(ctx, proposalID, addrs[1], v1.NewNonSplitVoteOption(v1.OptionTwo), ""))
require.NoError(t, govKeeper.AddVote(ctx, proposalID, addrs[0], v1.NewNonSplitVoteOption(v1.OptionThree), ""))
}
Copy link
Contributor

Choose a reason for hiding this comment

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

The new test function TestVotes_MultipleChoiceProposal seems to cover the basic scenarios for multiple-choice proposals, including submitting a proposal, setting proposal vote options, and adding votes with both valid and invalid options. However, it does not appear to test the actual vote tallying or the behavior when a vote is cast for an option that is not part of the proposal. It would be beneficial to add such test cases to ensure the tallying logic handles these scenarios correctly.

Copy link
Member Author

Choose a reason for hiding this comment

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

because it then tests voting instead of tallying, which isn't the purpose of this test. this is properly tested in vote testes

@julienrbrt julienrbrt added this pull request to the merge queue Jan 8, 2024
Merged via the queue into main with commit 41c84d6 Jan 8, 2024
58 of 61 checks passed
@julienrbrt julienrbrt deleted the julien/mc branch January 8, 2024 11:56
relyt29 pushed a commit to relyt29/cosmos-sdk that referenced this pull request Jan 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants