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): optimistic proposals #18620

Merged
merged 45 commits into from
Dec 20, 2023
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
62ef693
feat(gov): add proposal types and spam votes
julienrbrt Nov 21, 2023
2a536e8
updates
julienrbrt Nov 21, 2023
e8da7ac
wip
julienrbrt Nov 21, 2023
fb281bf
fix tests
julienrbrt Nov 22, 2023
a568dc9
updates
julienrbrt Nov 22, 2023
50f1015
updates
julienrbrt Nov 22, 2023
c9dc8c8
validate previous options
julienrbrt Nov 22, 2023
9013310
Merge branch 'main' into julien/gov-spam-proposal-type
julienrbrt Nov 22, 2023
7eed169
updates
julienrbrt Nov 22, 2023
411378e
proto lint
julienrbrt Nov 22, 2023
f0464f5
nits
julienrbrt Nov 24, 2023
2e62db1
remove duplicate tests
julienrbrt Nov 24, 2023
2bdf2bc
nits
julienrbrt Nov 24, 2023
74c7f77
Merge branch 'main' into julien/gov-spam-proposal-type
julienrbrt Nov 25, 2023
aaf6d2f
Merge branch 'main' into julien/gov-spam-proposal-type
julienrbrt Nov 30, 2023
84a2e4b
Merge branch 'main' into julien/gov-spam-proposal-type
julienrbrt Dec 1, 2023
b000ac0
feedback
julienrbrt Dec 1, 2023
4aa5df7
Merge branch 'main' into julien/gov-spam-proposal-type
julienrbrt Dec 4, 2023
d1ff59b
feat(gov): optimisic proposals
julienrbrt Dec 4, 2023
b5ae484
updates
julienrbrt Dec 4, 2023
f172a45
updates
julienrbrt Dec 4, 2023
96cb2d9
Merge branch 'main' into julien/gov-spam-proposal-type
julienrbrt Dec 6, 2023
2d105ee
updates
julienrbrt Dec 6, 2023
715d5f1
Merge branch 'main' into julien/gov-spam-proposal-type
julienrbrt Dec 6, 2023
87514ed
Merge branch 'julien/gov-spam-proposal-type' into julien/optimistic-p…
julienrbrt Dec 6, 2023
bf109d8
Merge branch 'main' into julien/optimistic-proposals
julienrbrt Dec 11, 2023
eee0945
updates
julienrbrt Dec 11, 2023
d565d5d
tally
julienrbrt Dec 12, 2023
3d9db0e
Merge branch 'main' into julien/optimistic-proposals
julienrbrt Dec 12, 2023
80012dd
changelog
julienrbrt Dec 12, 2023
ca75bee
typos
julienrbrt Dec 13, 2023
05b29c7
lint
julienrbrt Dec 13, 2023
8d3e75b
updates
julienrbrt Dec 14, 2023
d3cf8c1
updates
julienrbrt Dec 14, 2023
8dde530
Merge branch 'main' into julien/optimistic-proposals
julienrbrt Dec 14, 2023
4363e30
lint
julienrbrt Dec 14, 2023
bc30897
Update README.md
julienrbrt Dec 15, 2023
ddc9b2e
feedback
julienrbrt Dec 15, 2023
c0a41a3
updates
julienrbrt Dec 15, 2023
4540b87
feedback
julienrbrt Dec 15, 2023
bb30865
checks done in msg server
julienrbrt Dec 15, 2023
51049d5
revert increase
julienrbrt Dec 15, 2023
b473c11
Merge branch 'main' into julien/optimistic-proposals
julienrbrt Dec 15, 2023
228789f
fix integration
julienrbrt Dec 15, 2023
862ace6
Merge branch 'main' into julien/optimistic-proposals
julienrbrt Dec 19, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,6 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (types) [#17348](https://github.com/cosmos/cosmos-sdk/pull/17348) Remove the `WrapServiceResult` function.
* The `*sdk.Result` returned by the msg server router will not contain the `.Data` field.
* (x/staking) [#17335](https://github.com/cosmos/cosmos-sdk/pull/17335) Remove usage of `"cosmossdk.io/x/staking/types".Infraction_*` in favour of `"cosmossdk.io/api/cosmos/staking/v1beta1".Infraction_` in order to remove dependency between modules on staking
* (x/gov) [#17496](https://github.com/cosmos/cosmos-sdk/pull/17469) in `x/gov/types/v1beta1/vote.go` `NewVote` was removed, constructing the struct is required for this type
* (types) [#17426](https://github.com/cosmos/cosmos-sdk/pull/17426) `NewContext` does not take a `cmtproto.Header{}` any longer.
* `WithChainID` / `WithBlockHeight` / `WithBlockHeader` must be used to set values on the context
* (x/bank) [#17569](https://github.com/cosmos/cosmos-sdk/pull/17569) `BurnCoins` takes an address instead of a module name
Expand Down
936 changes: 705 additions & 231 deletions api/cosmos/gov/v1/gov.pulsar.go

Large diffs are not rendered by default.

437 changes: 255 additions & 182 deletions api/cosmos/gov/v1/tx.pulsar.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/architecture/adr-069-gov-improvements.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

## Status

PROPOSED
ACCEPTED

## Abstract

Expand Down
1 change: 1 addition & 0 deletions proto/buf.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ lint:
- SERVICE_SUFFIX
- PACKAGE_VERSION_SUFFIX
- RPC_REQUEST_STANDARD_NAME
- ENUM_NO_ALLOW_ALIAS
ignore:
- tendermint
65 changes: 56 additions & 9 deletions proto/cosmos/gov/v1/gov.proto
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,45 @@ import "amino/amino.proto";

option go_package = "cosmossdk.io/x/gov/types/v1";

// ProposalType enumerates the valid proposal types.
// All proposal types are v1.Proposal which have different voting periods or tallying logic.
enum ProposalType {
// PROPOSAL_TYPE_UNSPECIFIED defines no proposal type, which fallback to PROPOSAL_TYPE_STANDARD.
PROPOSAL_TYPE_UNSPECIFIED = 0;
// PROPOSAL_TYPE_STANDARD defines the type for a standard proposal.
PROPOSAL_TYPE_STANDARD = 1;
// PROPOSAL_TYPE_MULTIPLE_CHOICE defines the type for a multiple choice proposal.
PROPOSAL_TYPE_MULTIPLE_CHOICE = 2;
// PROPOSAL_TYPE_OPTIMISTIC defines the type for an optimistic proposal.
PROPOSAL_TYPE_OPTIMISTIC = 3;
// PROPOSAL_TYPE_EXPEDITED defines the type for an expedited proposal.
PROPOSAL_TYPE_EXPEDITED = 4;
}

// VoteOption enumerates the valid vote options for a given governance proposal.
enum VoteOption {
option allow_alias = true;

// VOTE_OPTION_UNSPECIFIED defines a no-op vote option.
VOTE_OPTION_UNSPECIFIED = 0;
// VOTE_OPTION_YES defines a yes vote option.
// VOTE_OPTION_ONE defines the first proposal vote option.
VOTE_OPTION_ONE = 1;
// VOTE_OPTION_YES defines the yes proposal vote option.
VOTE_OPTION_YES = 1;
// VOTE_OPTION_ABSTAIN defines an abstain vote option.
// VOTE_OPTION_TWO defines the second proposal vote option.
VOTE_OPTION_TWO = 2;
// VOTE_OPTION_ABSTAIN defines the abstain proposal vote option.
VOTE_OPTION_ABSTAIN = 2;
// VOTE_OPTION_NO defines a no vote option.
// VOTE_OPTION_THREE defines the third proposal vote option.
VOTE_OPTION_THREE = 3;
// VOTE_OPTION_NO defines the no proposal vote option.
VOTE_OPTION_NO = 3;
// VOTE_OPTION_NO_WITH_VETO defines a no with veto vote option.
// VOTE_OPTION_FOUR defines the fourth proposal vote option.
VOTE_OPTION_FOUR = 4;
// VOTE_OPTION_NO_WITH_VETO defines the no with veto proposal vote option.
VOTE_OPTION_NO_WITH_VETO = 4;
// VOTE_OPTION_SPAM defines the spam proposal vote option.
VOTE_OPTION_SPAM = 5;
}

// WeightedVoteOption defines a unit of vote for vote split.
Expand Down Expand Up @@ -102,12 +129,18 @@ message Proposal {
// expedited defines if the proposal is expedited
//
// Since: cosmos-sdk 0.50
bool expedited = 14;
// Deprecated: Use ProposalType instead.
bool expedited = 14 [deprecated = true];

// failed_reason defines the reason why the proposal failed
//
// Since: cosmos-sdk 0.50
string failed_reason = 15;

// proposal_type defines the type of the proposal
//
// Since: cosmos-sdk 0.51
ProposalType proposal_type = 16;
}

// ProposalStatus enumerates the valid statuses of a proposal.
Expand All @@ -134,13 +167,15 @@ enum ProposalStatus {
// TallyResult defines a standard tally for a governance proposal.
message TallyResult {
// yes_count is the number of yes votes on a proposal.
string yes_count = 1 [(cosmos_proto.scalar) = "cosmos.Int"];
string yes_count = 1 [(cosmos_proto.scalar) = "cosmos.Int"]; // option 1
// abstain_count is the number of abstain votes on a proposal.
string abstain_count = 2 [(cosmos_proto.scalar) = "cosmos.Int"];
string abstain_count = 2 [(cosmos_proto.scalar) = "cosmos.Int"]; // option 2
// no_count is the number of no votes on a proposal.
string no_count = 3 [(cosmos_proto.scalar) = "cosmos.Int"];
string no_count = 3 [(cosmos_proto.scalar) = "cosmos.Int"]; // option 3
// no_with_veto_count is the number of no with veto votes on a proposal.
string no_with_veto_count = 4 [(cosmos_proto.scalar) = "cosmos.Int"];
string no_with_veto_count = 4 [(cosmos_proto.scalar) = "cosmos.Int"]; // option 4
// spam_count is the number of spam votes on a proposal.
string spam_count = 5 [(cosmos_proto.scalar) = "cosmos.Int"];
}

// Vote defines a vote on a governance proposal.
Expand Down Expand Up @@ -268,4 +303,16 @@ message Params {
//
// Since: cosmos-sdk 0.50
string min_deposit_ratio = 16 [(cosmos_proto.scalar) = "cosmos.Dec"];

// optimistic_authorized_addreses is an optional governance parameter that limits the authorized accounts than can
// submit optimisitc proposals
//
// Since: x/gov v1.0.0
repeated string optimistic_authorized_addreses = 17 [(cosmos_proto.scalar) = "cosmos.AddressString"];
julienrbrt marked this conversation as resolved.
Show resolved Hide resolved

// optimistic rejected threshold defines at which percentage of NO votes, the optimistic proposal should fail and be
// converted to a standard proposal.
//
// Since: x/gov v1.0.0
string optimistic_rejected_threshold = 18 [(cosmos_proto.scalar) = "cosmos.Dec"];
}
11 changes: 10 additions & 1 deletion proto/cosmos/gov/v1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,16 @@ message MsgSubmitProposal {
// expedited defines if the proposal is expedited or not
//
// Since: cosmos-sdk 0.50
bool expedited = 7;
// Deprecated: Use the PROPOSAL_TYPE_EXPEDITED proposal type instead.
// When this field is set and no proposal_type is set, the proposal_type
// will be set to PROPOSAL_TYPE_EXPEDITED for backwards compatibility.
bool expedited = 7 [deprecated = true];

// proposal_type defines the type of proposal
// When not set defaults to PROPOSAL_TYPE_STANDARD
//
// Since: cosmos-sdk 0.51
ProposalType proposal_type = 8;
}

// MsgSubmitProposalResponse defines the Msg/SubmitProposal response type.
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/bank/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ func TestMsgSetSendEnabled(t *testing.T) {
"set default send enabled to true",
"Change send enabled",
"Modify send enabled and set to true",
false,
govv1.ProposalType_PROPOSAL_TYPE_STANDARD,
)
require.NoError(t, err, "making goodGovProp")

Expand Down
4 changes: 2 additions & 2 deletions tests/integration/gov/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,11 @@ func TestImportExportQueues(t *testing.T) {

ctx = s1.app.BaseApp.NewContext(false)
// Create two proposals, put the second into the voting period
proposal1, err := s1.GovKeeper.SubmitProposal(ctx, []sdk.Msg{mkTestLegacyContent(t)}, "", "test", "description", addrs[0], false)
proposal1, err := s1.GovKeeper.SubmitProposal(ctx, []sdk.Msg{mkTestLegacyContent(t)}, "", "test", "description", addrs[0], v1.ProposalType_PROPOSAL_TYPE_STANDARD)
assert.NilError(t, err)
proposalID1 := proposal1.Id

proposal2, err := s1.GovKeeper.SubmitProposal(ctx, []sdk.Msg{mkTestLegacyContent(t)}, "", "test", "description", addrs[0], false)
proposal2, err := s1.GovKeeper.SubmitProposal(ctx, []sdk.Msg{mkTestLegacyContent(t)}, "", "test", "description", addrs[0], v1.ProposalType_PROPOSAL_TYPE_STANDARD)
assert.NilError(t, err)
proposalID2 := proposal2.Id

Expand Down
194 changes: 0 additions & 194 deletions tests/integration/gov/keeper/grpc_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,128 +12,6 @@ import (
"cosmossdk.io/x/gov/types/v1beta1"
)

func TestGRPCQueryTally(t *testing.T) {
t.Parallel()
f := initFixture(t)

ctx, queryClient := f.ctx, f.queryClient

addrs, _ := createValidators(t, f, []int64{5, 5, 5})

var (
req *v1.QueryTallyResultRequest
expRes *v1.QueryTallyResultResponse
proposal v1.Proposal
)

testCases := []struct {
msg string
malleate func()
expPass bool
expErrMsg string
}{
{
"empty request",
func() {
req = &v1.QueryTallyResultRequest{}
},
false,
"proposal id can not be 0",
},
{
"zero proposal id request",
func() {
req = &v1.QueryTallyResultRequest{ProposalId: 0}
},
false,
"proposal id can not be 0",
},
{
"query non existed proposal",
func() {
req = &v1.QueryTallyResultRequest{ProposalId: 1}
},
false,
"proposal 1 doesn't exist",
},
{
"create a proposal and get tally",
func() {
var err error
proposal, err = f.govKeeper.SubmitProposal(ctx, TestProposal, "", "test", "description", addrs[0], false)
assert.NilError(t, err)
assert.Assert(t, proposal.String() != "")

req = &v1.QueryTallyResultRequest{ProposalId: proposal.Id}

tallyResult := v1.EmptyTallyResult()
expRes = &v1.QueryTallyResultResponse{
Tally: &tallyResult,
}
},
true,
"",
},
{
"request tally after few votes",
func() {
proposal.Status = v1.StatusVotingPeriod
err := f.govKeeper.SetProposal(ctx, proposal)
assert.NilError(t, err)
assert.NilError(t, f.govKeeper.AddVote(ctx, proposal.Id, addrs[0], v1.NewNonSplitVoteOption(v1.OptionYes), ""))
assert.NilError(t, f.govKeeper.AddVote(ctx, proposal.Id, addrs[1], v1.NewNonSplitVoteOption(v1.OptionYes), ""))
assert.NilError(t, f.govKeeper.AddVote(ctx, proposal.Id, addrs[2], v1.NewNonSplitVoteOption(v1.OptionYes), ""))

req = &v1.QueryTallyResultRequest{ProposalId: proposal.Id}

expRes = &v1.QueryTallyResultResponse{
Tally: &v1.TallyResult{
YesCount: math.NewInt(3 * 5 * 1000000).String(),
NoCount: "0",
AbstainCount: "0",
NoWithVetoCount: "0",
},
}
},
true,
"",
},
{
"request final tally after status changed",
func() {
proposal.Status = v1.StatusPassed
err := f.govKeeper.SetProposal(ctx, proposal)
assert.NilError(t, err)
proposal, _ = f.govKeeper.Proposals.Get(ctx, proposal.Id)

req = &v1.QueryTallyResultRequest{ProposalId: proposal.Id}

expRes = &v1.QueryTallyResultResponse{
Tally: proposal.FinalTallyResult,
}
},
true,
"",
},
}

for _, testCase := range testCases {
t.Run(fmt.Sprintf("Case %s", testCase.msg), func(t *testing.T) {
testCase.malleate()

tally, err := queryClient.TallyResult(gocontext.Background(), req)

if testCase.expPass {
assert.NilError(t, err)
assert.Equal(t, expRes.String(), tally.String())
} else {
assert.ErrorContains(t, err, testCase.expErrMsg)
assert.Assert(t, tally == nil)
}
})
}
}

func TestLegacyGRPCQueryTally(t *testing.T) {
t.Parallel()

Expand All @@ -155,48 +33,6 @@ func TestLegacyGRPCQueryTally(t *testing.T) {
expPass bool
expErrMsg string
}{
{
"empty request",
func() {
req = &v1beta1.QueryTallyResultRequest{}
},
false,
"proposal id can not be 0",
},
{
"zero proposal id request",
func() {
req = &v1beta1.QueryTallyResultRequest{ProposalId: 0}
},
false,
"proposal id can not be 0",
},
{
"query non existed proposal",
func() {
req = &v1beta1.QueryTallyResultRequest{ProposalId: 1}
},
false,
"proposal 1 doesn't exist",
},
{
"create a proposal and get tally",
func() {
var err error
proposal, err = f.govKeeper.SubmitProposal(ctx, TestProposal, "", "test", "description", addrs[0], false)
assert.NilError(t, err)
assert.Assert(t, proposal.String() != "")

req = &v1beta1.QueryTallyResultRequest{ProposalId: proposal.Id}

tallyResult := v1beta1.EmptyTallyResult()
expRes = &v1beta1.QueryTallyResultResponse{
Tally: tallyResult,
}
},
true,
"",
},
{
"request tally after few votes",
func() {
Expand All @@ -221,23 +57,6 @@ func TestLegacyGRPCQueryTally(t *testing.T) {
true,
"",
},
{
"request final tally after status changed",
func() {
proposal.Status = v1.StatusPassed
err := f.govKeeper.SetProposal(ctx, proposal)
assert.NilError(t, err)
proposal, _ = f.govKeeper.Proposals.Get(ctx, proposal.Id)

req = &v1beta1.QueryTallyResultRequest{ProposalId: proposal.Id}

expRes = &v1beta1.QueryTallyResultResponse{
Tally: v1TallyToV1Beta1Tally(*proposal.FinalTallyResult),
}
},
true,
"",
},
}

for _, testCase := range testCases {
Expand All @@ -256,16 +75,3 @@ func TestLegacyGRPCQueryTally(t *testing.T) {
})
}
}

func v1TallyToV1Beta1Tally(t v1.TallyResult) v1beta1.TallyResult {
yes, _ := math.NewIntFromString(t.YesCount)
no, _ := math.NewIntFromString(t.NoCount)
noWithVeto, _ := math.NewIntFromString(t.NoWithVetoCount)
abstain, _ := math.NewIntFromString(t.AbstainCount)
return v1beta1.TallyResult{
Yes: yes,
No: no,
NoWithVeto: noWithVeto,
Abstain: abstain,
}
}
Loading
Loading