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!: initial deposit requirement for proposals #12771

Merged
merged 19 commits into from
Aug 2, 2022
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
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: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (x/gov) [#12631](https://github.com/cosmos/cosmos-sdk/pull/12631) Migrate `x/gov` to self-managed parameters and deprecate it's usage of `x/params`.
* (x/staking) [#12409](https://github.com/cosmos/cosmos-sdk/pull/12409) Migrate `x/staking` to self-managed parameters and deprecate it's usage of `x/params`.
* (x/bank) [#11859](https://github.com/cosmos/cosmos-sdk/pull/11859) Move the SendEnabled information out of the Params and into the state store directly.
* (x/gov) [#12771](https://github.com/cosmos/cosmos-sdk/pull/12771) Initial deposit requirement for proposals at submission time.
Copy link
Member

Choose a reason for hiding this comment

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

could you add a short entry to the upgrading.md file. then lets merge.


### API Breaking Changes

Expand Down
162 changes: 121 additions & 41 deletions api/cosmos/gov/v1/gov.pulsar.go

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions proto/cosmos/bank/v1beta1/authz.proto
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ message SendAuthorization {
repeated cosmos.base.v1beta1.Coin spend_limit = 1
[(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"];

// allow_list specifies an optional list of addresses to whom the grantee can send tokens on behalf of the
// granter. If omitted, any recipient is allowed.
// allow_list specifies an optional list of addresses to whom the grantee can send tokens on behalf of the
// granter. If omitted, any recipient is allowed.
//
// Since: cosmos-sdk 0.47
repeated string allow_list = 2;
Expand Down
6 changes: 3 additions & 3 deletions proto/cosmos/gov/v1/genesis.proto
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ message GenesisState {
repeated Proposal proposals = 4;
// Deprecated: Prefer to use `params` instead.
// deposit_params defines all the paramaters of related to deposit.
DepositParams deposit_params = 5 [deprecated=true];
DepositParams deposit_params = 5 [deprecated = true];
// Deprecated: Prefer to use `params` instead.
// voting_params defines all the paramaters of related to voting.
VotingParams voting_params = 6 [deprecated=true];
VotingParams voting_params = 6 [deprecated = true];
// Deprecated: Prefer to use `params` instead.
// tally_params defines all the paramaters of related to tally.
TallyParams tally_params = 7 [deprecated=true];
TallyParams tally_params = 7 [deprecated = true];
// params defines all the paramaters of x/gov module.
//
// Since: cosmos-sdk 0.47
Expand Down
4 changes: 4 additions & 0 deletions proto/cosmos/gov/v1/gov.proto
Original file line number Diff line number Diff line change
Expand Up @@ -155,4 +155,8 @@ message Params {
// Minimum value of Veto votes to Total votes ratio for proposal to be
// vetoed. Default value: 1/3.
string veto_threshold = 6 [(cosmos_proto.scalar) = "cosmos.Dec"];

// The ratio representing the proportion of the deposit value that must be paid at proposal submission.
string min_initial_deposit_ratio = 7
[(cosmos_proto.scalar) = "cosmos.Dec", (gogoproto.jsontag) = "min_initial_deposit_ratio,omitempty"];
tac0turtle marked this conversation as resolved.
Show resolved Hide resolved
}
6 changes: 3 additions & 3 deletions proto/cosmos/gov/v1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -132,13 +132,13 @@ message QueryParamsRequest {
message QueryParamsResponse {
// Deprecated: Prefer to use `params` instead.
// voting_params defines the parameters related to voting.
VotingParams voting_params = 1 [deprecated=true];
VotingParams voting_params = 1 [deprecated = true];
// Deprecated: Prefer to use `params` instead.
// deposit_params defines the parameters related to deposit.
DepositParams deposit_params = 2 [deprecated=true];
DepositParams deposit_params = 2 [deprecated = true];
// Deprecated: Prefer to use `params` instead.
// tally_params defines the parameters related to tally.
TallyParams tally_params = 3 [deprecated=true];
TallyParams tally_params = 3 [deprecated = true];
// params defines all the paramaters of x/gov module.
//
// Since: cosmos-sdk 0.47
Expand Down
3 changes: 2 additions & 1 deletion x/gov/client/testutil/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func (s *IntegrationTestSuite) TestCmdParams() {
{
"json output",
[]string{fmt.Sprintf("--%s=json", tmcli.OutputFlag)},
`{"voting_params":{"voting_period":"172800s"},"deposit_params":{"min_deposit":[{"denom":"stake","amount":"10000000"}],"max_deposit_period":"172800s"},"tally_params":{"quorum":"0.334000000000000000","threshold":"0.500000000000000000","veto_threshold":"0.334000000000000000"},"params":{"min_deposit":[{"denom":"stake","amount":"10000000"}],"max_deposit_period":"172800s","voting_period":"172800s","quorum":"0.334000000000000000","threshold":"0.500000000000000000","veto_threshold":"0.334000000000000000"}}`,
`{"voting_params":{"voting_period":"172800s"},"deposit_params":{"min_deposit":[{"denom":"stake","amount":"10000000"}],"max_deposit_period":"172800s"},"tally_params":{"quorum":"0.334000000000000000","threshold":"0.500000000000000000","veto_threshold":"0.334000000000000000"},"params":{"min_deposit":[{"denom":"stake","amount":"10000000"}],"max_deposit_period":"172800s","voting_period":"172800s","quorum":"0.334000000000000000","threshold":"0.500000000000000000","veto_threshold":"0.334000000000000000","min_initial_deposit_ratio":"0.000000000000000000"}}`,
},
{
"text output",
Expand All @@ -40,6 +40,7 @@ params:
min_deposit:
- amount: "10000000"
denom: stake
min_initial_deposit_ratio: "0.000000000000000000"
quorum: "0.334000000000000000"
threshold: "0.500000000000000000"
veto_threshold: "0.334000000000000000"
Expand Down
20 changes: 10 additions & 10 deletions x/gov/client/testutil/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,18 +165,18 @@ func (s *IntegrationTestSuite) TestNewCmdSubmitProposal() {
func (s *IntegrationTestSuite) TestNewCmdSubmitLegacyProposal() {
val := s.network.Validators[0]
invalidProp := `{
"title": "",
"description": "Where is the title!?",
"type": "Text",
"deposit": "-324foocoin"
}`
"title": "",
"description": "Where is the title!?",
"type": "Text",
"deposit": "-324foocoin"
}`
invalidPropFile := testutil.WriteToNewTempFile(s.T(), invalidProp)
validProp := fmt.Sprintf(`{
"title": "Text Proposal",
"description": "Hello, World!",
"type": "Text",
"deposit": "%s"
}`, sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(5431)))
"title": "Text Proposal",
"description": "Hello, World!",
"type": "Text",
"deposit": "%s"
}`, sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(5431)))
validPropFile := testutil.WriteToNewTempFile(s.T(), validProp)
testCases := []struct {
name string
Expand Down
22 changes: 22 additions & 0 deletions x/gov/keeper/deposit.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,25 @@ func (keeper Keeper) RefundAndDeleteDeposits(ctx sdk.Context, proposalID uint64)
return false
})
}

// validateInitialDeposit validates if initial deposit is greater than or equal to the minimum
// required at the time of proposal submission. This threshold amount is determined by
// the deposit parameters. Returns nil on success, error otherwise.
func (keeper Keeper) validateInitialDeposit(ctx sdk.Context, initialDeposit sdk.Coins) error {
params := keeper.GetParams(ctx)
minInitialDepositRatio, err := sdk.NewDecFromStr(params.MinInitialDepositRatio)
if err != nil {
return err
}
if minInitialDepositRatio.IsZero() {
return nil
}
minDepositCoins := params.MinDeposit
for i := range minDepositCoins {
minDepositCoins[i].Amount = sdk.NewDecFromInt(minDepositCoins[i].Amount).Mul(minInitialDepositRatio).RoundInt()
}
if !initialDeposit.IsAllGTE(minDepositCoins) {
return sdkerrors.Wrapf(types.ErrMinDepositTooSmall, "was (%s), need (%s)", initialDeposit, minDepositCoins)
}
return nil
}
105 changes: 105 additions & 0 deletions x/gov/keeper/deposit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ import (

"github.com/cosmos/cosmos-sdk/simapp"
sdk "github.com/cosmos/cosmos-sdk/types"
v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
)

const (
baseDepositTestAmount = 100
baseDepositTestPercent = 25
)

func TestDeposits(t *testing.T) {
Expand Down Expand Up @@ -111,3 +117,102 @@ func TestDeposits(t *testing.T) {
require.Len(t, deposits, 0)
require.Equal(t, addr0Initial.Sub(fourStake...), app.BankKeeper.GetAllBalances(ctx, TestAddrs[0]))
}

func TestValidateInitialDeposit(t *testing.T) {
testcases := map[string]struct {
minDeposit sdk.Coins
minInitialDepositPercent int64
initialDeposit sdk.Coins

expectError bool
}{
"min deposit * initial percent == initial deposit: success": {
minDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount))),
minInitialDepositPercent: baseDepositTestPercent,
initialDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount*baseDepositTestPercent/100))),
},
"min deposit * initial percent < initial deposit: success": {
minDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount))),
minInitialDepositPercent: baseDepositTestPercent,
initialDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount*baseDepositTestPercent/100+1))),
},
"min deposit * initial percent > initial deposit: error": {
minDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount))),
minInitialDepositPercent: baseDepositTestPercent,
initialDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount*baseDepositTestPercent/100-1))),

expectError: true,
},
"min deposit * initial percent == initial deposit (non-base values and denom): success": {
minDeposit: sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(56912))),
minInitialDepositPercent: 50,
initialDeposit: sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(56912/2+10))),
},
"min deposit * initial percent == initial deposit but different denoms: error": {
minDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount))),
minInitialDepositPercent: baseDepositTestPercent,
initialDeposit: sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(baseDepositTestAmount*baseDepositTestPercent/100))),

expectError: true,
},
"min deposit * initial percent == initial deposit (multiple coins): success": {
minDeposit: sdk.NewCoins(
sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount)),
sdk.NewCoin("uosmo", sdk.NewInt(baseDepositTestAmount*2))),
minInitialDepositPercent: baseDepositTestPercent,
initialDeposit: sdk.NewCoins(
sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount*baseDepositTestPercent/100)),
sdk.NewCoin("uosmo", sdk.NewInt(baseDepositTestAmount*2*baseDepositTestPercent/100)),
),
},
"min deposit * initial percent > initial deposit (multiple coins): error": {
minDeposit: sdk.NewCoins(
sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount)),
sdk.NewCoin("uosmo", sdk.NewInt(baseDepositTestAmount*2))),
minInitialDepositPercent: baseDepositTestPercent,
initialDeposit: sdk.NewCoins(
sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount*baseDepositTestPercent/100)),
sdk.NewCoin("uosmo", sdk.NewInt(baseDepositTestAmount*2*baseDepositTestPercent/100-1)),
),

expectError: true,
},
"min deposit * initial percent < initial deposit (multiple coins - coin not required by min deposit): success": {
minDeposit: sdk.NewCoins(
sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount))),
minInitialDepositPercent: baseDepositTestPercent,
initialDeposit: sdk.NewCoins(
sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount*baseDepositTestPercent/100)),
sdk.NewCoin("uosmo", sdk.NewInt(baseDepositTestAmount*baseDepositTestPercent/100-1)),
),
},
"0 initial percent: success": {
minDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount))),
minInitialDepositPercent: 0,
initialDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount*baseDepositTestPercent/100))),
},
}

for name, tc := range testcases {
t.Run(name, func(t *testing.T) {
app := simapp.Setup(t, false)
ctx := app.BaseApp.NewContext(false, tmproto.Header{})

govKeeper := app.GovKeeper

params := v1.DefaultParams()
params.MinDeposit = tc.minDeposit
params.MinInitialDepositRatio = sdk.NewDec(tc.minInitialDepositPercent).Quo(sdk.NewDec(100)).String()

govKeeper.SetParams(ctx, params)

err := govKeeper.ValidateInitialDeposit(ctx, tc.initialDeposit)

if tc.expectError {
require.Error(t, err)
return
}
require.NoError(t, err)
})
}
}
7 changes: 7 additions & 0 deletions x/gov/keeper/export_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package keeper

import sdk "github.com/cosmos/cosmos-sdk/types"

func (k Keeper) ValidateInitialDeposit(ctx sdk.Context, initialDeposit sdk.Coins) error {
return k.validateInitialDeposit(ctx, initialDeposit)
}
7 changes: 6 additions & 1 deletion x/gov/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,16 @@ var _ v1.MsgServer = msgServer{}
func (k msgServer) SubmitProposal(goCtx context.Context, msg *v1.MsgSubmitProposal) (*v1.MsgSubmitProposalResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)

initialDeposit := msg.GetInitialDeposit()

if err := k.validateInitialDeposit(ctx, initialDeposit); err != nil {
return nil, err
}

proposalMsgs, err := msg.GetMsgs()
Fixed Show fixed Hide fixed
if err != nil {
return nil, err
}

proposal, err := k.Keeper.SubmitProposal(ctx, proposalMsgs, msg.Metadata)
if err != nil {
return nil, err
Expand Down
85 changes: 85 additions & 0 deletions x/gov/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@ import (
"strings"
"time"

"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
"github.com/cosmos/cosmos-sdk/simapp"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
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"
"github.com/cosmos/cosmos-sdk/x/gov/keeper"
v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
"github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
)

func (suite *KeeperTestSuite) TestSubmitProposalReq() {
Expand Down Expand Up @@ -1007,3 +1012,83 @@ func (suite *KeeperTestSuite) TestMsgUpdateParams() {
})
}
}

func (suite *KeeperTestSuite) TestSubmitProposal_InitialDeposit() {
const meetsDepositValue = baseDepositTestAmount * baseDepositTestPercent / 100
var baseDepositRatioDec = sdk.NewDec(baseDepositTestPercent).Quo(sdk.NewDec(100))

testcases := map[string]struct {
minDeposit sdk.Coins
minInitialDepositRatio sdk.Dec
initialDeposit sdk.Coins
accountBalance sdk.Coins

expectError bool
}{
"meets initial deposit, enough balance - success": {
minDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount))),
minInitialDepositRatio: baseDepositRatioDec,
initialDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(meetsDepositValue))),
accountBalance: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(meetsDepositValue))),
},
"does not meet initial deposit, enough balance - error": {
minDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount))),
minInitialDepositRatio: baseDepositRatioDec,
initialDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(meetsDepositValue-1))),
accountBalance: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(meetsDepositValue))),

expectError: true,
},
"meets initial deposit, not enough balance - error": {
minDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount))),
minInitialDepositRatio: baseDepositRatioDec,
initialDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(meetsDepositValue))),
accountBalance: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(meetsDepositValue-1))),

expectError: true,
},
"does not meet initial deposit and not enough balance - error": {
minDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount))),
minInitialDepositRatio: baseDepositRatioDec,
initialDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(meetsDepositValue-1))),
accountBalance: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(meetsDepositValue-1))),

expectError: true,
},
}

for name, tc := range testcases {
suite.Run(name, func() {
// Setup
privateKey := secp256k1.GenPrivKey()
address := sdk.AccAddress(privateKey.PubKey().Address())
acc := &authtypes.BaseAccount{
Address: address.String(),
}

genAccs := []authtypes.GenesisAccount{acc}
app := simapp.SetupWithGenesisAccounts(suite.T(), genAccs, banktypes.Balance{Address: acc.Address, Coins: tc.accountBalance})
ctx := app.BaseApp.NewContext(false, tmproto.Header{})
govKeeper := app.GovKeeper
msgServer := keeper.NewMsgServerImpl(govKeeper)

params := v1.DefaultParams()
params.MinDeposit = tc.minDeposit
params.MinInitialDepositRatio = tc.minInitialDepositRatio.String()
govKeeper.SetParams(ctx, params)

msg, err := v1.NewMsgSubmitProposal(TestProposal, tc.initialDeposit, address.String(), "test")
suite.Require().NoError(err)

// System under test
_, err = msgServer.SubmitProposal(sdk.WrapSDKContext(ctx), msg)

// Assertions
if tc.expectError {
suite.Require().Error(err)
return
}
suite.Require().NoError(err)
})
}
}
1 change: 1 addition & 0 deletions x/gov/migrations/v3/json_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ func TestMigrateJSON(t *testing.T) {
"denom": "stake"
}
],
"min_initial_deposit_ratio": "",
"quorum": "0.334000000000000000",
"threshold": "0.500000000000000000",
"veto_threshold": "0.334000000000000000",
Expand Down
Loading