Merge PR #4403: Paramchange proposal skips omitempty fields
This commit is contained in:
parent
8fecc7724b
commit
91e75cb74a
|
@ -0,0 +1,2 @@
|
||||||
|
#4403 Allow for parameter change proposals to supply only desired fields to be updated
|
||||||
|
in objects instead of the entire object (only applies to values that are objects).
|
|
@ -11,26 +11,12 @@ import (
|
||||||
"github.com/tendermint/tendermint/libs/log"
|
"github.com/tendermint/tendermint/libs/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Parameter store key
|
|
||||||
var (
|
var (
|
||||||
ParamStoreKeyDepositParams = []byte("depositparams")
|
|
||||||
ParamStoreKeyVotingParams = []byte("votingparams")
|
|
||||||
ParamStoreKeyTallyParams = []byte("tallyparams")
|
|
||||||
|
|
||||||
// TODO: Find another way to implement this without using accounts, or find a cleaner way to implement it using accounts.
|
// TODO: Find another way to implement this without using accounts, or find a cleaner way to implement it using accounts.
|
||||||
DepositedCoinsAccAddr = sdk.AccAddress(crypto.AddressHash([]byte("govDepositedCoins")))
|
DepositedCoinsAccAddr = sdk.AccAddress(crypto.AddressHash([]byte("govDepositedCoins")))
|
||||||
BurnedDepositCoinsAccAddr = sdk.AccAddress(crypto.AddressHash([]byte("govBurnedDepositCoins")))
|
BurnedDepositCoinsAccAddr = sdk.AccAddress(crypto.AddressHash([]byte("govBurnedDepositCoins")))
|
||||||
)
|
)
|
||||||
|
|
||||||
// Key declaration for parameters
|
|
||||||
func ParamKeyTable() params.KeyTable {
|
|
||||||
return params.NewKeyTable(
|
|
||||||
ParamStoreKeyDepositParams, DepositParams{},
|
|
||||||
ParamStoreKeyVotingParams, VotingParams{},
|
|
||||||
ParamStoreKeyTallyParams, TallyParams{},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Governance Keeper
|
// Governance Keeper
|
||||||
type Keeper struct {
|
type Keeper struct {
|
||||||
// The reference to the Param Keeper to get and set Global Params
|
// The reference to the Param Keeper to get and set Global Params
|
||||||
|
|
|
@ -5,12 +5,29 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Parameter store key
|
||||||
|
var (
|
||||||
|
ParamStoreKeyDepositParams = []byte("depositparams")
|
||||||
|
ParamStoreKeyVotingParams = []byte("votingparams")
|
||||||
|
ParamStoreKeyTallyParams = []byte("tallyparams")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Key declaration for parameters
|
||||||
|
func ParamKeyTable() params.KeyTable {
|
||||||
|
return params.NewKeyTable(
|
||||||
|
ParamStoreKeyDepositParams, DepositParams{},
|
||||||
|
ParamStoreKeyVotingParams, VotingParams{},
|
||||||
|
ParamStoreKeyTallyParams, TallyParams{},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Param around deposits for governance
|
// Param around deposits for governance
|
||||||
type DepositParams struct {
|
type DepositParams struct {
|
||||||
MinDeposit sdk.Coins `json:"min_deposit"` // Minimum deposit for a proposal to enter voting period.
|
MinDeposit sdk.Coins `json:"min_deposit,omitempty"` // Minimum deposit for a proposal to enter voting period.
|
||||||
MaxDepositPeriod time.Duration `json:"max_deposit_period"` // Maximum period for Atom holders to deposit on a proposal. Initial value: 2 months
|
MaxDepositPeriod time.Duration `json:"max_deposit_period,omitempty"` // Maximum period for Atom holders to deposit on a proposal. Initial value: 2 months
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDepositParams creates a new DepositParams object
|
// NewDepositParams creates a new DepositParams object
|
||||||
|
@ -34,9 +51,9 @@ func (dp DepositParams) Equal(dp2 DepositParams) bool {
|
||||||
|
|
||||||
// Param around Tallying votes in governance
|
// Param around Tallying votes in governance
|
||||||
type TallyParams struct {
|
type TallyParams struct {
|
||||||
Quorum sdk.Dec `json:"quorum"` // Minimum percentage of total stake needed to vote for a result to be considered valid
|
Quorum sdk.Dec `json:"quorum,omitempty"` // Minimum percentage of total stake needed to vote for a result to be considered valid
|
||||||
Threshold sdk.Dec `json:"threshold"` // Minimum proportion of Yes votes for proposal to pass. Initial value: 0.5
|
Threshold sdk.Dec `json:"threshold,omitempty"` // Minimum proportion of Yes votes for proposal to pass. Initial value: 0.5
|
||||||
Veto sdk.Dec `json:"veto"` // Minimum value of Veto votes to Total votes ratio for proposal to be vetoed. Initial value: 1/3
|
Veto sdk.Dec `json:"veto,omitempty"` // Minimum value of Veto votes to Total votes ratio for proposal to be vetoed. Initial value: 1/3
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTallyParams creates a new TallyParams object
|
// NewTallyParams creates a new TallyParams object
|
||||||
|
@ -58,7 +75,7 @@ func (tp TallyParams) String() string {
|
||||||
|
|
||||||
// Param around Voting in governance
|
// Param around Voting in governance
|
||||||
type VotingParams struct {
|
type VotingParams struct {
|
||||||
VotingPeriod time.Duration `json:"voting_period"` // Length of the voting period.
|
VotingPeriod time.Duration `json:"voting_period,omitempty"` // Length of the voting period.
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewVotingParams creates a new VotingParams object
|
// NewVotingParams creates a new VotingParams object
|
||||||
|
|
|
@ -40,9 +40,11 @@ func getMockApp(t *testing.T, numGenAccs int, genState GenesisState, genAccs []a
|
||||||
tKeyStaking := sdk.NewTransientStoreKey(staking.TStoreKey)
|
tKeyStaking := sdk.NewTransientStoreKey(staking.TStoreKey)
|
||||||
keyGov := sdk.NewKVStoreKey(StoreKey)
|
keyGov := sdk.NewKVStoreKey(StoreKey)
|
||||||
|
|
||||||
rtr := NewRouter().AddRoute(RouterKey, ProposalHandler)
|
|
||||||
|
|
||||||
pk := mApp.ParamsKeeper
|
pk := mApp.ParamsKeeper
|
||||||
|
|
||||||
|
rtr := NewRouter().
|
||||||
|
AddRoute(RouterKey, ProposalHandler)
|
||||||
|
|
||||||
ck := bank.NewBaseKeeper(mApp.AccountKeeper, mApp.ParamsKeeper.Subspace(bank.DefaultParamspace), bank.DefaultCodespace)
|
ck := bank.NewBaseKeeper(mApp.AccountKeeper, mApp.ParamsKeeper.Subspace(bank.DefaultParamspace), bank.DefaultCodespace)
|
||||||
sk := staking.NewKeeper(mApp.Cdc, keyStaking, tKeyStaking, ck, pk.Subspace(staking.DefaultParamspace), staking.DefaultCodespace)
|
sk := staking.NewKeeper(mApp.Cdc, keyStaking, tKeyStaking, ck, pk.Subspace(staking.DefaultParamspace), staking.DefaultCodespace)
|
||||||
keeper := NewKeeper(mApp.Cdc, keyGov, pk, pk.Subspace("testgov"), ck, sk, DefaultCodespace, rtr)
|
keeper := NewKeeper(mApp.Cdc, keyGov, pk, pk.Subspace("testgov"), ck, sk, DefaultCodespace, rtr)
|
||||||
|
|
|
@ -37,6 +37,7 @@ var (
|
||||||
ErrEmptyValue = types.ErrEmptyValue
|
ErrEmptyValue = types.ErrEmptyValue
|
||||||
NewParameterChangeProposal = types.NewParameterChangeProposal
|
NewParameterChangeProposal = types.NewParameterChangeProposal
|
||||||
NewParamChange = types.NewParamChange
|
NewParamChange = types.NewParamChange
|
||||||
|
NewParamChangeWithSubkey = types.NewParamChangeWithSubkey
|
||||||
ValidateChanges = types.ValidateChanges
|
ValidateChanges = types.ValidateChanges
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,8 @@ func GetCmdSubmitProposal(cdc *codec.Codec) *cobra.Command {
|
||||||
Short: "Submit a parameter change proposal",
|
Short: "Submit a parameter change proposal",
|
||||||
Long: strings.TrimSpace(
|
Long: strings.TrimSpace(
|
||||||
fmt.Sprintf(`Submit a parameter proposal along with an initial deposit.
|
fmt.Sprintf(`Submit a parameter proposal along with an initial deposit.
|
||||||
The proposal details must be supplied via a JSON file.
|
The proposal details must be supplied via a JSON file. For values that contains
|
||||||
|
objects, only non-empty fields will be updated.
|
||||||
|
|
||||||
IMPORTANT: Currently parameter changes are evaluated but not validated, so it is
|
IMPORTANT: Currently parameter changes are evaluated but not validated, so it is
|
||||||
very important that any "value" change is valid (ie. correct type and within bounds)
|
very important that any "value" change is valid (ie. correct type and within bounds)
|
||||||
|
@ -34,7 +35,7 @@ for its respective parameter, eg. "MaxValidators" should be an integer and not a
|
||||||
|
|
||||||
Proper vetting of a parameter change proposal should prevent this from happening
|
Proper vetting of a parameter change proposal should prevent this from happening
|
||||||
(no deposits should occur during the governance process), but it should be noted
|
(no deposits should occur during the governance process), but it should be noted
|
||||||
regardless.
|
regardless.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
$ %s tx gov submit-proposal param-change <path/to/proposal.json> --from=<key_or_address>
|
$ %s tx gov submit-proposal param-change <path/to/proposal.json> --from=<key_or_address>
|
||||||
|
|
|
@ -51,7 +51,7 @@ func NewParamChangeJSON(subspace, key, subkey string, value json.RawMessage) Par
|
||||||
|
|
||||||
// ToParamChange converts a ParamChangeJSON object to ParamChange.
|
// ToParamChange converts a ParamChangeJSON object to ParamChange.
|
||||||
func (pcj ParamChangeJSON) ToParamChange() params.ParamChange {
|
func (pcj ParamChangeJSON) ToParamChange() params.ParamChange {
|
||||||
return params.NewParamChange(pcj.Subspace, pcj.Key, pcj.Subkey, string(pcj.Value))
|
return params.NewParamChangeWithSubkey(pcj.Subspace, pcj.Key, pcj.Subkey, string(pcj.Value))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToParamChanges converts a slice of ParamChangeJSON objects to a slice of
|
// ToParamChanges converts a slice of ParamChangeJSON objects to a slice of
|
||||||
|
|
|
@ -6,40 +6,10 @@ import (
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
|
||||||
dbm "github.com/tendermint/tendermint/libs/db"
|
|
||||||
"github.com/tendermint/tendermint/libs/log"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
|
||||||
"github.com/cosmos/cosmos-sdk/store"
|
|
||||||
"github.com/cosmos/cosmos-sdk/store/prefix"
|
"github.com/cosmos/cosmos-sdk/store/prefix"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func defaultContext(key sdk.StoreKey, tkey sdk.StoreKey) sdk.Context {
|
|
||||||
db := dbm.NewMemDB()
|
|
||||||
cms := store.NewCommitMultiStore(db)
|
|
||||||
cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db)
|
|
||||||
cms.MountStoreWithDB(tkey, sdk.StoreTypeTransient, db)
|
|
||||||
cms.LoadLatestVersion()
|
|
||||||
ctx := sdk.NewContext(cms, abci.Header{}, false, log.NewNopLogger())
|
|
||||||
return ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
type invalid struct{}
|
|
||||||
|
|
||||||
type s struct {
|
|
||||||
I int
|
|
||||||
}
|
|
||||||
|
|
||||||
func createTestCodec() *codec.Codec {
|
|
||||||
cdc := codec.New()
|
|
||||||
sdk.RegisterCodec(cdc)
|
|
||||||
cdc.RegisterConcrete(s{}, "test/s", nil)
|
|
||||||
cdc.RegisterConcrete(invalid{}, "test/invalid", nil)
|
|
||||||
return cdc
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestKeeper(t *testing.T) {
|
func TestKeeper(t *testing.T) {
|
||||||
kvs := []struct {
|
kvs := []struct {
|
||||||
key string
|
key string
|
||||||
|
@ -66,11 +36,8 @@ func TestKeeper(t *testing.T) {
|
||||||
[]byte("extra2"), string(""),
|
[]byte("extra2"), string(""),
|
||||||
)
|
)
|
||||||
|
|
||||||
cdc := codec.New()
|
cdc, ctx, skey, _, keeper := testComponents()
|
||||||
skey := sdk.NewKVStoreKey("test")
|
|
||||||
tkey := sdk.NewTransientStoreKey("transient_test")
|
|
||||||
ctx := defaultContext(skey, tkey)
|
|
||||||
keeper := NewKeeper(cdc, skey, tkey, DefaultCodespace)
|
|
||||||
store := prefix.NewStore(ctx.KVStore(skey), []byte("test/"))
|
store := prefix.NewStore(ctx.KVStore(skey), []byte("test/"))
|
||||||
space := keeper.Subspace("test").WithKeyTable(table)
|
space := keeper.Subspace("test").WithKeyTable(table)
|
||||||
|
|
||||||
|
@ -137,11 +104,7 @@ func indirect(ptr interface{}) interface{} {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSubspace(t *testing.T) {
|
func TestSubspace(t *testing.T) {
|
||||||
cdc := createTestCodec()
|
cdc, ctx, key, _, keeper := testComponents()
|
||||||
key := sdk.NewKVStoreKey("test")
|
|
||||||
tkey := sdk.NewTransientStoreKey("transient_test")
|
|
||||||
ctx := defaultContext(key, tkey)
|
|
||||||
keeper := NewKeeper(cdc, key, tkey, DefaultCodespace)
|
|
||||||
|
|
||||||
kvs := []struct {
|
kvs := []struct {
|
||||||
key string
|
key string
|
||||||
|
@ -216,3 +179,34 @@ func TestSubspace(t *testing.T) {
|
||||||
require.Equal(t, kv.param, indirect(kv.ptr), "stored param not equal, tc #%d", i)
|
require.Equal(t, kv.param, indirect(kv.ptr), "stored param not equal, tc #%d", i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type paramJSON struct {
|
||||||
|
Param1 int64 `json:"param1,omitempty"`
|
||||||
|
Param2 string `json:"param2,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJSONUpdate(t *testing.T) {
|
||||||
|
_, ctx, _, _, keeper := testComponents()
|
||||||
|
|
||||||
|
key := []byte("key")
|
||||||
|
|
||||||
|
space := keeper.Subspace("test").WithKeyTable(NewKeyTable(key, paramJSON{}))
|
||||||
|
|
||||||
|
var param paramJSON
|
||||||
|
|
||||||
|
space.Update(ctx, key, []byte(`{"param1": "10241024"}`))
|
||||||
|
space.Get(ctx, key, ¶m)
|
||||||
|
require.Equal(t, paramJSON{10241024, ""}, param)
|
||||||
|
|
||||||
|
space.Update(ctx, key, []byte(`{"param2": "helloworld"}`))
|
||||||
|
space.Get(ctx, key, ¶m)
|
||||||
|
require.Equal(t, paramJSON{10241024, "helloworld"}, param)
|
||||||
|
|
||||||
|
space.Update(ctx, key, []byte(`{"param1": "20482048"}`))
|
||||||
|
space.Get(ctx, key, ¶m)
|
||||||
|
require.Equal(t, paramJSON{20482048, "helloworld"}, param)
|
||||||
|
|
||||||
|
space.Update(ctx, key, []byte(`{"param1": "40964096", "param2": "goodbyeworld"}`))
|
||||||
|
space.Get(ctx, key, ¶m)
|
||||||
|
require.Equal(t, paramJSON{40964096, "goodbyeworld"}, param)
|
||||||
|
}
|
||||||
|
|
|
@ -32,12 +32,13 @@ func handleParameterChangeProposal(ctx sdk.Context, k Keeper, p ParameterChangeP
|
||||||
k.Logger(ctx).Info(
|
k.Logger(ctx).Info(
|
||||||
fmt.Sprintf("setting new parameter; key: %s, value: %s", c.Key, c.Value),
|
fmt.Sprintf("setting new parameter; key: %s, value: %s", c.Key, c.Value),
|
||||||
)
|
)
|
||||||
err = ss.SetRaw(ctx, []byte(c.Key), []byte(c.Value))
|
|
||||||
|
err = ss.Update(ctx, []byte(c.Key), []byte(c.Value))
|
||||||
} else {
|
} else {
|
||||||
k.Logger(ctx).Info(
|
k.Logger(ctx).Info(
|
||||||
fmt.Sprintf("setting new parameter; key: %s, subkey: %s, value: %s", c.Key, c.Subspace, c.Value),
|
fmt.Sprintf("setting new parameter; key: %s, subkey: %s, value: %s", c.Key, c.Subspace, c.Value),
|
||||||
)
|
)
|
||||||
err = ss.SetRawWithSubkey(ctx, []byte(c.Key), []byte(c.Subkey), []byte(c.Value))
|
err = ss.UpdateWithSubkey(ctx, []byte(c.Key), []byte(c.Subkey), []byte(c.Value))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -27,16 +27,24 @@ var (
|
||||||
_ subspace.ParamSet = (*testParams)(nil)
|
_ subspace.ParamSet = (*testParams)(nil)
|
||||||
|
|
||||||
keyMaxValidators = "MaxValidators"
|
keyMaxValidators = "MaxValidators"
|
||||||
|
keySlashingRate = "SlashingRate"
|
||||||
testSubspace = "TestSubspace"
|
testSubspace = "TestSubspace"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type testParamsSlashingRate struct {
|
||||||
|
DoubleSign uint16 `json:"double_sign,omitempty"`
|
||||||
|
Downtime uint16 `json:"downtime,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type testParams struct {
|
type testParams struct {
|
||||||
MaxValidators uint16 `json:"max_validators"` // maximum number of validators (max uint16 = 65535)
|
MaxValidators uint16 `json:"max_validators"` // maximum number of validators (max uint16 = 65535)
|
||||||
|
SlashingRate testParamsSlashingRate `json:"slashing_rate"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tp *testParams) ParamSetPairs() subspace.ParamSetPairs {
|
func (tp *testParams) ParamSetPairs() subspace.ParamSetPairs {
|
||||||
return subspace.ParamSetPairs{
|
return subspace.ParamSetPairs{
|
||||||
{[]byte(keyMaxValidators), &tp.MaxValidators},
|
{[]byte(keyMaxValidators), &tp.MaxValidators},
|
||||||
|
{[]byte(keySlashingRate), &tp.SlashingRate},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +84,7 @@ func TestProposalHandlerPassed(t *testing.T) {
|
||||||
params.NewKeyTable().RegisterParamSet(&testParams{}),
|
params.NewKeyTable().RegisterParamSet(&testParams{}),
|
||||||
)
|
)
|
||||||
|
|
||||||
tp := testProposal(params.NewParamChange(testSubspace, keyMaxValidators, "", "1"))
|
tp := testProposal(params.NewParamChange(testSubspace, keyMaxValidators, "1"))
|
||||||
hdlr := params.NewParamChangeProposalHandler(input.keeper)
|
hdlr := params.NewParamChangeProposalHandler(input.keeper)
|
||||||
require.NoError(t, hdlr(input.ctx, tp))
|
require.NoError(t, hdlr(input.ctx, tp))
|
||||||
|
|
||||||
|
@ -91,9 +99,31 @@ func TestProposalHandlerFailed(t *testing.T) {
|
||||||
params.NewKeyTable().RegisterParamSet(&testParams{}),
|
params.NewKeyTable().RegisterParamSet(&testParams{}),
|
||||||
)
|
)
|
||||||
|
|
||||||
tp := testProposal(params.NewParamChange(testSubspace, keyMaxValidators, "", "invalidType"))
|
tp := testProposal(params.NewParamChange(testSubspace, keyMaxValidators, "invalidType"))
|
||||||
hdlr := params.NewParamChangeProposalHandler(input.keeper)
|
hdlr := params.NewParamChangeProposalHandler(input.keeper)
|
||||||
require.Error(t, hdlr(input.ctx, tp))
|
require.Error(t, hdlr(input.ctx, tp))
|
||||||
|
|
||||||
require.False(t, ss.Has(input.ctx, []byte(keyMaxValidators)))
|
require.False(t, ss.Has(input.ctx, []byte(keyMaxValidators)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestProposalHandlerUpdateOmitempty(t *testing.T) {
|
||||||
|
input := newTestInput(t)
|
||||||
|
ss := input.keeper.Subspace(testSubspace).WithKeyTable(
|
||||||
|
params.NewKeyTable().RegisterParamSet(&testParams{}),
|
||||||
|
)
|
||||||
|
|
||||||
|
hdlr := params.NewParamChangeProposalHandler(input.keeper)
|
||||||
|
var param testParamsSlashingRate
|
||||||
|
|
||||||
|
tp := testProposal(params.NewParamChange(testSubspace, keySlashingRate, `{"downtime": 7}`))
|
||||||
|
require.NoError(t, hdlr(input.ctx, tp))
|
||||||
|
|
||||||
|
ss.Get(input.ctx, []byte(keySlashingRate), ¶m)
|
||||||
|
require.Equal(t, testParamsSlashingRate{0, 7}, param)
|
||||||
|
|
||||||
|
tp = testProposal(params.NewParamChange(testSubspace, keySlashingRate, `{"double_sign": 10}`))
|
||||||
|
require.NoError(t, hdlr(input.ctx, tp))
|
||||||
|
|
||||||
|
ss.Get(input.ctx, []byte(keySlashingRate), ¶m)
|
||||||
|
require.Equal(t, testParamsSlashingRate{10, 7}, param)
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package simulation
|
package simulation
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"time"
|
"time"
|
||||||
|
@ -26,8 +27,6 @@ func (spc simParamChange) compKey() string {
|
||||||
// paramChangePool defines a static slice of possible simulated parameter changes
|
// paramChangePool defines a static slice of possible simulated parameter changes
|
||||||
// where each simParamChange corresponds to a ParamChange with a simValue
|
// where each simParamChange corresponds to a ParamChange with a simValue
|
||||||
// function to generate a simulated new value.
|
// function to generate a simulated new value.
|
||||||
//
|
|
||||||
// TODO: governance parameters (blocked on an upgrade to go-amino)
|
|
||||||
var paramChangePool = []simParamChange{
|
var paramChangePool = []simParamChange{
|
||||||
// staking parameters
|
// staking parameters
|
||||||
{
|
{
|
||||||
|
@ -80,6 +79,55 @@ var paramChangePool = []simParamChange{
|
||||||
return fmt.Sprintf("\"%s\"", simulation.ModuleParamSimulator["InflationRateChange"](r).(sdk.Dec))
|
return fmt.Sprintf("\"%s\"", simulation.ModuleParamSimulator["InflationRateChange"](r).(sdk.Dec))
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// gov parameters
|
||||||
|
{
|
||||||
|
"gov",
|
||||||
|
"votingparams",
|
||||||
|
"",
|
||||||
|
func(r *rand.Rand) string {
|
||||||
|
return fmt.Sprintf(`{"voting_period": "%d"}`, simulation.ModuleParamSimulator["VotingParams/VotingPeriod"](r).(time.Duration))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gov",
|
||||||
|
"depositparams",
|
||||||
|
"",
|
||||||
|
func(r *rand.Rand) string {
|
||||||
|
return fmt.Sprintf(`{"max_deposit_period": "%d"}`, simulation.ModuleParamSimulator["VotingParams/VotingPeriod"](r).(time.Duration))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gov",
|
||||||
|
"tallyparams",
|
||||||
|
"",
|
||||||
|
func(r *rand.Rand) string {
|
||||||
|
changes := []struct {
|
||||||
|
key string
|
||||||
|
value sdk.Dec
|
||||||
|
}{
|
||||||
|
{"quorum", simulation.ModuleParamSimulator["TallyParams/Quorum"](r).(sdk.Dec)},
|
||||||
|
{"threshold", simulation.ModuleParamSimulator["TallyParams/Threshold"](r).(sdk.Dec)},
|
||||||
|
{"veto", simulation.ModuleParamSimulator["TallyParams/Veto"](r).(sdk.Dec)},
|
||||||
|
}
|
||||||
|
|
||||||
|
pc := make(map[string]string)
|
||||||
|
numChanges := simulation.RandIntBetween(r, 1, len(changes))
|
||||||
|
for i := 0; i < numChanges; i++ {
|
||||||
|
c := changes[r.Intn(len(changes))]
|
||||||
|
|
||||||
|
_, ok := pc[c.key]
|
||||||
|
for ok {
|
||||||
|
c := changes[r.Intn(len(changes))]
|
||||||
|
_, ok = pc[c.key]
|
||||||
|
}
|
||||||
|
|
||||||
|
pc[c.key] = c.value.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
bz, _ := json.Marshal(pc)
|
||||||
|
return string(bz)
|
||||||
|
},
|
||||||
|
},
|
||||||
// auth parameters
|
// auth parameters
|
||||||
{
|
{
|
||||||
"auth",
|
"auth",
|
||||||
|
@ -126,7 +174,7 @@ func SimulateParamChangeProposalContent(r *rand.Rand, _ *baseapp.BaseApp, _ sdk.
|
||||||
}
|
}
|
||||||
|
|
||||||
paramChangesKeys[spc.compKey()] = struct{}{}
|
paramChangesKeys[spc.compKey()] = struct{}{}
|
||||||
paramChanges[i] = params.NewParamChange(spc.subspace, spc.key, spc.subkey, spc.simValue(r))
|
paramChanges[i] = params.NewParamChangeWithSubkey(spc.subspace, spc.key, spc.subkey, spc.simValue(r))
|
||||||
}
|
}
|
||||||
|
|
||||||
return params.NewParameterChangeProposal(
|
return params.NewParameterChangeProposal(
|
||||||
|
|
|
@ -177,10 +177,10 @@ func (s Subspace) Set(ctx sdk.Context, key []byte, param interface{}) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetRaw stores raw parameter bytes. It returns error if the stored parameter
|
// Update stores raw parameter bytes. It returns error if the stored parameter
|
||||||
// has a different type from the input. It also sets to the transient store to
|
// has a different type from the input. It also sets to the transient store to
|
||||||
// record change.
|
// record change.
|
||||||
func (s Subspace) SetRaw(ctx sdk.Context, key []byte, param []byte) error {
|
func (s Subspace) Update(ctx sdk.Context, key []byte, param []byte) error {
|
||||||
attr, ok := s.table.m[string(key)]
|
attr, ok := s.table.m[string(key)]
|
||||||
if !ok {
|
if !ok {
|
||||||
panic("Parameter not registered")
|
panic("Parameter not registered")
|
||||||
|
@ -188,13 +188,13 @@ func (s Subspace) SetRaw(ctx sdk.Context, key []byte, param []byte) error {
|
||||||
|
|
||||||
ty := attr.ty
|
ty := attr.ty
|
||||||
dest := reflect.New(ty).Interface()
|
dest := reflect.New(ty).Interface()
|
||||||
|
s.GetIfExists(ctx, key, dest)
|
||||||
err := s.cdc.UnmarshalJSON(param, dest)
|
err := s.cdc.UnmarshalJSON(param, dest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
store := s.kvStore(ctx)
|
s.Set(ctx, key, dest)
|
||||||
store.Set(key, param)
|
|
||||||
tStore := s.transientStore(ctx)
|
tStore := s.transientStore(ctx)
|
||||||
tStore.Set(key, []byte{})
|
tStore.Set(key, []byte{})
|
||||||
|
|
||||||
|
@ -220,9 +220,9 @@ func (s Subspace) SetWithSubkey(ctx sdk.Context, key []byte, subkey []byte, para
|
||||||
tstore.Set(newkey, []byte{})
|
tstore.Set(newkey, []byte{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetRawWithSubkey stores raw parameter bytes with a key and subkey. It checks
|
// UpdateWithSubkey stores raw parameter bytes with a key and subkey. It checks
|
||||||
// the parameter type only over the key.
|
// the parameter type only over the key.
|
||||||
func (s Subspace) SetRawWithSubkey(ctx sdk.Context, key []byte, subkey []byte, param []byte) error {
|
func (s Subspace) UpdateWithSubkey(ctx sdk.Context, key []byte, subkey []byte, param []byte) error {
|
||||||
concatkey := concatKeys(key, subkey)
|
concatkey := concatKeys(key, subkey)
|
||||||
|
|
||||||
attr, ok := s.table.m[string(concatkey)]
|
attr, ok := s.table.m[string(concatkey)]
|
||||||
|
@ -232,13 +232,13 @@ func (s Subspace) SetRawWithSubkey(ctx sdk.Context, key []byte, subkey []byte, p
|
||||||
|
|
||||||
ty := attr.ty
|
ty := attr.ty
|
||||||
dest := reflect.New(ty).Interface()
|
dest := reflect.New(ty).Interface()
|
||||||
err := s.cdc.UnmarshalJSON(param, &dest)
|
s.GetWithSubkeyIfExists(ctx, key, subkey, dest)
|
||||||
|
err := s.cdc.UnmarshalJSON(param, dest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
store := s.kvStore(ctx)
|
s.SetWithSubkey(ctx, key, subkey, dest)
|
||||||
store.Set(concatkey, param)
|
|
||||||
tStore := s.transientStore(ctx)
|
tStore := s.transientStore(ctx)
|
||||||
tStore.Set(concatkey, []byte{})
|
tStore.Set(concatkey, []byte{})
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
package params
|
||||||
|
|
||||||
|
import (
|
||||||
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
|
dbm "github.com/tendermint/tendermint/libs/db"
|
||||||
|
"github.com/tendermint/tendermint/libs/log"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
"github.com/cosmos/cosmos-sdk/store"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type invalid struct{}
|
||||||
|
|
||||||
|
type s struct {
|
||||||
|
I int
|
||||||
|
}
|
||||||
|
|
||||||
|
func createTestCodec() *codec.Codec {
|
||||||
|
cdc := codec.New()
|
||||||
|
sdk.RegisterCodec(cdc)
|
||||||
|
cdc.RegisterConcrete(s{}, "test/s", nil)
|
||||||
|
cdc.RegisterConcrete(invalid{}, "test/invalid", nil)
|
||||||
|
return cdc
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultContext(key sdk.StoreKey, tkey sdk.StoreKey) sdk.Context {
|
||||||
|
db := dbm.NewMemDB()
|
||||||
|
cms := store.NewCommitMultiStore(db)
|
||||||
|
cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db)
|
||||||
|
cms.MountStoreWithDB(tkey, sdk.StoreTypeTransient, db)
|
||||||
|
err := cms.LoadLatestVersion()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
ctx := sdk.NewContext(cms, abci.Header{}, false, log.NewNopLogger())
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func testComponents() (*codec.Codec, sdk.Context, sdk.StoreKey, sdk.StoreKey, Keeper) {
|
||||||
|
cdc := createTestCodec()
|
||||||
|
mkey := sdk.NewKVStoreKey("test")
|
||||||
|
tkey := sdk.NewTransientStoreKey("transient_test")
|
||||||
|
ctx := defaultContext(mkey, tkey)
|
||||||
|
keeper := NewKeeper(cdc, mkey, tkey, DefaultCodespace)
|
||||||
|
|
||||||
|
return cdc, ctx, mkey, tkey, keeper
|
||||||
|
}
|
|
@ -85,7 +85,11 @@ type ParamChange struct {
|
||||||
Value string `json:"value"`
|
Value string `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewParamChange(subspace, key, subkey, value string) ParamChange {
|
func NewParamChange(subspace, key, value string) ParamChange {
|
||||||
|
return ParamChange{subspace, key, "", value}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewParamChangeWithSubkey(subspace, key, subkey, value string) ParamChange {
|
||||||
return ParamChange{subspace, key, subkey, value}
|
return ParamChange{subspace, key, subkey, value}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestParameterChangeProposal(t *testing.T) {
|
func TestParameterChangeProposal(t *testing.T) {
|
||||||
pc1 := NewParamChange("sub", "foo", "", "baz")
|
pc1 := NewParamChange("sub", "foo", "baz")
|
||||||
pc2 := NewParamChange("sub", "bar", "cat", "dog")
|
pc2 := NewParamChangeWithSubkey("sub", "bar", "cat", "dog")
|
||||||
pcp := NewParameterChangeProposal("test title", "test description", []ParamChange{pc1, pc2})
|
pcp := NewParameterChangeProposal("test title", "test description", []ParamChange{pc1, pc2})
|
||||||
|
|
||||||
require.Equal(t, "test title", pcp.GetTitle())
|
require.Equal(t, "test title", pcp.GetTitle())
|
||||||
|
@ -17,15 +17,15 @@ func TestParameterChangeProposal(t *testing.T) {
|
||||||
require.Equal(t, ProposalTypeChange, pcp.ProposalType())
|
require.Equal(t, ProposalTypeChange, pcp.ProposalType())
|
||||||
require.Nil(t, pcp.ValidateBasic())
|
require.Nil(t, pcp.ValidateBasic())
|
||||||
|
|
||||||
pc3 := NewParamChange("", "bar", "cat", "dog")
|
pc3 := NewParamChangeWithSubkey("", "bar", "cat", "dog")
|
||||||
pcp = NewParameterChangeProposal("test title", "test description", []ParamChange{pc3})
|
pcp = NewParameterChangeProposal("test title", "test description", []ParamChange{pc3})
|
||||||
require.Error(t, pcp.ValidateBasic())
|
require.Error(t, pcp.ValidateBasic())
|
||||||
|
|
||||||
pc4 := NewParamChange("sub", "", "cat", "dog")
|
pc4 := NewParamChangeWithSubkey("sub", "", "cat", "dog")
|
||||||
pcp = NewParameterChangeProposal("test title", "test description", []ParamChange{pc4})
|
pcp = NewParameterChangeProposal("test title", "test description", []ParamChange{pc4})
|
||||||
require.Error(t, pcp.ValidateBasic())
|
require.Error(t, pcp.ValidateBasic())
|
||||||
|
|
||||||
pc5 := NewParamChange("sub", "foo", "cat", "")
|
pc5 := NewParamChangeWithSubkey("sub", "foo", "cat", "")
|
||||||
pcp = NewParameterChangeProposal("test title", "test description", []ParamChange{pc5})
|
pcp = NewParameterChangeProposal("test title", "test description", []ParamChange{pc5})
|
||||||
require.Error(t, pcp.ValidateBasic())
|
require.Error(t, pcp.ValidateBasic())
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,13 +59,13 @@ var (
|
||||||
return time.Duration(RandIntBetween(r, 1, 2*60*60*24*2)) * time.Second
|
return time.Duration(RandIntBetween(r, 1, 2*60*60*24*2)) * time.Second
|
||||||
},
|
},
|
||||||
"TallyParams/Quorum": func(r *rand.Rand) interface{} {
|
"TallyParams/Quorum": func(r *rand.Rand) interface{} {
|
||||||
return sdk.NewDecWithPrec(334, 3)
|
return sdk.NewDecWithPrec(int64(RandIntBetween(r, 334, 500)), 3)
|
||||||
},
|
},
|
||||||
"TallyParams/Threshold": func(r *rand.Rand) interface{} {
|
"TallyParams/Threshold": func(r *rand.Rand) interface{} {
|
||||||
return sdk.NewDecWithPrec(5, 1)
|
return sdk.NewDecWithPrec(int64(RandIntBetween(r, 450, 550)), 3)
|
||||||
},
|
},
|
||||||
"TallyParams/Veto": func(r *rand.Rand) interface{} {
|
"TallyParams/Veto": func(r *rand.Rand) interface{} {
|
||||||
return sdk.NewDecWithPrec(334, 3)
|
return sdk.NewDecWithPrec(int64(RandIntBetween(r, 250, 334)), 3)
|
||||||
},
|
},
|
||||||
"UnbondingTime": func(r *rand.Rand) interface{} {
|
"UnbondingTime": func(r *rand.Rand) interface{} {
|
||||||
return time.Duration(RandIntBetween(r, 60, 60*60*24*3*2)) * time.Second
|
return time.Duration(RandIntBetween(r, 60, 60*60*24*3*2)) * time.Second
|
||||||
|
|
Loading…
Reference in New Issue