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): add constitution amendment and law proposals with specific quorum and pass thresholds #11

Merged
merged 10 commits into from
Sep 18, 2024
29 changes: 25 additions & 4 deletions proto/atomone/gov/v1/gov.proto
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,18 @@ message TallyParams {

// Minimum proportion of Yes votes for proposal to pass. Default value: 2/3.
string threshold = 2 [(cosmos_proto.scalar) = "cosmos.Dec"];

// quorum for constitution amendment proposals
string constitution_amendment_quorum = 3 [(cosmos_proto.scalar) = "cosmos.Dec"];

// Minimum proportion of Yes votes for a Constitution Amendment proposal to pass. Default value: 0.9.
string constitution_amendment_threshold = 4 [(cosmos_proto.scalar) = "cosmos.Dec"];

// quorum for law proposals
string law_quorum = 5 [(cosmos_proto.scalar) = "cosmos.Dec"];

// Minimum proportion of Yes votes for a Law proposal to pass. Default value: 0.9.
string law_threshold = 6 [(cosmos_proto.scalar) = "cosmos.Dec"];
}

// Params defines the parameters for the x/gov module.
Expand Down Expand Up @@ -213,15 +225,24 @@ message Params {
// burn deposits if the proposal does not enter voting period
bool burn_proposal_deposit_prevote = 14;

// burn deposits if quorum with vote type no_veto is met
bool burn_vote_veto = 15;

// The ratio representing the proportion of the deposit value minimum that
// must be met when making a deposit. Default value: 0.01. Meaning that for a
// chain with a min_deposit of 100stake, a deposit of 1stake would be
// required.
//
// Since: cosmos-sdk 0.50
// NOTE: backported from v50 (https://github.com/cosmos/cosmos-sdk/pull/18146)
string min_deposit_ratio = 16 [ (cosmos_proto.scalar) = "cosmos.Dec" ];
string min_deposit_ratio = 15 [ (cosmos_proto.scalar) = "cosmos.Dec" ];

// quorum for constitution amendment proposals
string constitution_amendment_quorum = 16 [(cosmos_proto.scalar) = "cosmos.Dec"];

// Minimum proportion of Yes votes for a Constitution Amendment proposal to pass. Default value: 0.9.
string constitution_amendment_threshold = 17 [(cosmos_proto.scalar) = "cosmos.Dec"];

// quorum for law proposals
string law_quorum = 18 [(cosmos_proto.scalar) = "cosmos.Dec"];

// Minimum proportion of Yes votes for a Law proposal to pass. Default value: 0.9.
string law_threshold = 19 [(cosmos_proto.scalar) = "cosmos.Dec"];
}
26 changes: 26 additions & 0 deletions proto/atomone/gov/v1beta1/gov.proto
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,32 @@ message TextProposal {
string description = 2;
}

message LawProposal {
option (cosmos_proto.implements_interface) = "atomone.gov.v1beta1.Content";
option (amino.name) = "atomone/LawProposal";

option (gogoproto.equal) = true;

// title of the proposal.
string title = 1;

// description associated with the proposal.
string description = 2;
}

message ConstitutionAmendmentProposal {
option (cosmos_proto.implements_interface) = "atomone.gov.v1beta1.Content";
option (amino.name) = "atomone/ConstitutionAmendmentProposal";

option (gogoproto.equal) = true;

// title of the proposal.
string title = 1;

// description associated with the proposal.
string description = 2;
}

// Deposit defines an amount deposited by an account address to an active
// proposal.
message Deposit {
Expand Down
5 changes: 5 additions & 0 deletions tests/e2e/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ func modifyGenesis(path, moniker, amountStr string, addrAll []sdk.AccAddress, de
amnt := sdk.NewInt(10000)
quorum, _ := sdk.NewDecFromStr("0.000000000000000001")
threshold, _ := sdk.NewDecFromStr("0.000000000000000001")
lawQuorum, _ := sdk.NewDecFromStr("0.000000000000000001")
lawThreshold, _ := sdk.NewDecFromStr("0.000000000000000001")
amendmentsQuorum, _ := sdk.NewDecFromStr("0.000000000000000001")
amendmentsThreshold, _ := sdk.NewDecFromStr("0.000000000000000001")

maxDepositPeriod := 10 * time.Minute
votingPeriod := 15 * time.Second
Expand All @@ -133,6 +137,7 @@ func modifyGenesis(path, moniker, amountStr string, addrAll []sdk.AccAddress, de
sdk.NewCoins(sdk.NewCoin(denom, amnt)), maxDepositPeriod,
votingPeriod,
quorum.String(), threshold.String(),
amendmentsQuorum.String(), amendmentsThreshold.String(), lawQuorum.String(), lawThreshold.String(),
sdk.ZeroDec().String(),
false, false, govv1.DefaultMinDepositRatio.String(),
),
Expand Down
34 changes: 31 additions & 3 deletions x/gov/keeper/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@ import (
)

var (
_, _, addr = testdata.KeyTestPubAddr()
govAcct = authtypes.NewModuleAddress(types.ModuleName)
TestProposal = getTestProposal()
_, _, addr = testdata.KeyTestPubAddr()
govAcct = authtypes.NewModuleAddress(types.ModuleName)
TestProposal = getTestProposal()
TestAmendmentProposal = getTestConstitutionAmendmentProposal()
TestLawProposal = getTestLawProposal()
)

// getTestProposal creates and returns a test proposal message.
Expand All @@ -46,6 +48,32 @@ func getTestProposal() []sdk.Msg {
}
}

// getTestConstitutionAmendmentProposal creates and returns a test constitution amendment proposal message.
func getTestConstitutionAmendmentProposal() []sdk.Msg {
legacyProposalMsg, err := v1.NewLegacyContent(v1beta1.NewConstitutionAmendmentProposal("Title", "description"), authtypes.NewModuleAddress(types.ModuleName).String())
if err != nil {
panic(err)
}

return []sdk.Msg{
banktypes.NewMsgSend(govAcct, addr, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(1000)))),
legacyProposalMsg,
}
}

// getTestLawProposal creates and returns a test law proposal message.
func getTestLawProposal() []sdk.Msg {
legacyProposalMsg, err := v1.NewLegacyContent(v1beta1.NewLawProposal("Title", "description"), authtypes.NewModuleAddress(types.ModuleName).String())
if err != nil {
panic(err)
}

return []sdk.Msg{
banktypes.NewMsgSend(govAcct, addr, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(1000)))),
legacyProposalMsg,
}
}

type mocks struct {
acctKeeper *govtestutil.MockAccountKeeper
bankKeeper *govtestutil.MockBankKeeper
Expand Down
116 changes: 114 additions & 2 deletions x/gov/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -905,7 +905,7 @@ func (suite *KeeperTestSuite) TestMsgUpdateParams() {
}
},
expErr: true,
expErrMsg: "quorom cannot be negative",
expErrMsg: "quorum must be positive",
},
{
name: "quorum > 1",
Expand All @@ -919,7 +919,7 @@ func (suite *KeeperTestSuite) TestMsgUpdateParams() {
}
},
expErr: true,
expErrMsg: "quorom too large",
expErrMsg: "quorum too large",
},
{
name: "invalid threshold",
Expand Down Expand Up @@ -963,6 +963,118 @@ func (suite *KeeperTestSuite) TestMsgUpdateParams() {
expErr: true,
expErrMsg: "vote threshold too large",
},
{
name: "negative constitution amendment quorum",
input: func() *v1.MsgUpdateParams {
params1 := params
params1.ConstitutionAmendmentQuorum = "-0.1"

return &v1.MsgUpdateParams{
Authority: authority,
Params: params1,
}
},
expErr: true,
expErrMsg: "constitution amendment quorum must be positive",
},
{
name: "constitution amendments quorum > 1",
input: func() *v1.MsgUpdateParams {
params1 := params
params1.ConstitutionAmendmentQuorum = "2"

return &v1.MsgUpdateParams{
Authority: authority,
Params: params1,
}
},
expErr: true,
expErrMsg: "constitution amendment quorum too large",
},
{
name: "negative constitution amendment threshold",
input: func() *v1.MsgUpdateParams {
params1 := params
params1.ConstitutionAmendmentThreshold = "-0.1"

return &v1.MsgUpdateParams{
Authority: authority,
Params: params1,
}
},
expErr: true,
expErrMsg: "constitution amendment threshold must be positive",
},
{
name: "constitution amendments threshold > 1",
input: func() *v1.MsgUpdateParams {
params1 := params
params1.ConstitutionAmendmentThreshold = "2"

return &v1.MsgUpdateParams{
Authority: authority,
Params: params1,
}
},
expErr: true,
expErrMsg: "constitution amendment threshold too large",
},
{
name: "negative law quorum",
input: func() *v1.MsgUpdateParams {
params1 := params
params1.LawQuorum = "-0.1"

return &v1.MsgUpdateParams{
Authority: authority,
Params: params1,
}
},
expErr: true,
expErrMsg: "law quorum must be positive",
},
{
name: "law quorum > 1",
input: func() *v1.MsgUpdateParams {
params1 := params
params1.LawQuorum = "2"

return &v1.MsgUpdateParams{
Authority: authority,
Params: params1,
}
},
expErr: true,
expErrMsg: "law quorum too large",
},
{
name: "negative law threshold",
input: func() *v1.MsgUpdateParams {
params1 := params
params1.LawThreshold = "-0.1"

return &v1.MsgUpdateParams{
Authority: authority,
Params: params1,
}
},
expErr: true,
expErrMsg: "law threshold must be positive",
},
{
name: "law threshold > 1",
input: func() *v1.MsgUpdateParams {
params1 := params
params1.LawThreshold = "2"

return &v1.MsgUpdateParams{
Authority: authority,
Params: params1,
}
},
expErr: true,
expErrMsg: "law threshold too large",
},
{
name: "invalid voting period",
input: func() *v1.MsgUpdateParams {
Expand Down
44 changes: 43 additions & 1 deletion x/gov/keeper/tally.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"

v1 "github.com/atomone-hub/atomone/x/gov/types/v1"
"github.com/atomone-hub/atomone/x/gov/types/v1beta1"
)

// TODO: Break into several smaller functions for clarity
Expand Down Expand Up @@ -85,6 +86,48 @@ func (keeper Keeper) Tally(ctx sdk.Context, proposal v1.Proposal) (passes bool,
// If there is not enough quorum of votes, the proposal fails
percentVoting := totalVotingPower.Quo(sdk.NewDecFromInt(totalBondedTokens))
quorum, _ := sdk.NewDecFromStr(params.Quorum)
threshold, _ := sdk.NewDecFromStr(params.Threshold)

// Check if a proposal message is an ExecLegacyContent message
if len(proposal.Messages) > 0 {
var sdkMsg sdk.Msg
for _, msg := range proposal.Messages {
tbruyelle marked this conversation as resolved.
Show resolved Hide resolved
if err := keeper.cdc.UnpackAny(msg, &sdkMsg); err == nil {
execMsg, ok := sdkMsg.(*v1.MsgExecLegacyContent)
if !ok {
continue
}
var content v1beta1.Content
if err := keeper.cdc.UnpackAny(execMsg.Content, &content); err != nil {
return false, false, tallyResults
}

// Check if proposal is a law or constitution amendment and adjust the
// quorum and threshold accordingly
switch content.(type) {
case *v1beta1.ConstitutionAmendmentProposal:
q, _ := sdk.NewDecFromStr(params.ConstitutionAmendmentQuorum)
if quorum.LT(q) {
quorum = q
}
t, _ := sdk.NewDecFromStr(params.ConstitutionAmendmentThreshold)
if threshold.LT(t) {
threshold = t
}
case *v1beta1.LawProposal:
q, _ := sdk.NewDecFromStr(params.LawQuorum)
if quorum.LT(q) {
quorum = q
}
t, _ := sdk.NewDecFromStr(params.LawThreshold)
if threshold.LT(t) {
threshold = t
}
}
}
}
}

if percentVoting.LT(quorum) {
return false, params.BurnVoteQuorum, tallyResults
}
Expand All @@ -95,7 +138,6 @@ func (keeper Keeper) Tally(ctx sdk.Context, proposal v1.Proposal) (passes bool,
}

// If more than 2/3 of non-abstaining voters vote Yes, proposal passes
threshold, _ := sdk.NewDecFromStr(params.Threshold)
if results[v1.OptionYes].Quo(totalVotingPower.Sub(results[v1.OptionAbstain])).GT(threshold) {
return true, false, tallyResults
}
Expand Down
Loading
Loading