0g-chain/x/committee/simulation/proposals.go
Ruaridh 23a5c7b969
Add committee simulations (#431)
* first pass at genesis and msgs

* add proposal generation

* add permission generation

* add decoder

* add invariants

* add committee change proposal generator

* improve committee change proposal generation

* fix error formatting

* update sims to v0.38

* Update x/committee/keeper/invariants.go

Co-Authored-By: Denali Marsh <denali@kava.io>

* Update x/committee/keeper/invariants.go

Co-Authored-By: Denali Marsh <denali@kava.io>

* tidy up comments

* tidy up random helpers

* add committee to ImportExport test

* add member check to vote invariant

* fix comment wording

Co-authored-by: Kevin Davis <karzak@users.noreply.github.com>

Co-authored-by: Denali Marsh <denali@kava.io>
Co-authored-by: Kevin Davis <karzak@users.noreply.github.com>
2020-05-04 17:17:20 -04:00

174 lines
5.3 KiB
Go

package simulation
import (
"fmt"
"math/rand"
sdk "github.com/cosmos/cosmos-sdk/types"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
"github.com/cosmos/cosmos-sdk/x/simulation"
appparams "github.com/kava-labs/kava/app/params"
"github.com/kava-labs/kava/x/committee/keeper"
"github.com/kava-labs/kava/x/committee/types"
)
const OpWeightSubmitCommitteeChangeProposal = "op_weight_submit_committee_change_proposal"
// ProposalContents defines the module weighted proposals' contents
func ProposalContents(k keeper.Keeper, paramChanges []simulation.ParamChange) []simulation.WeightedProposalContent {
return []simulation.WeightedProposalContent{
{
AppParamsKey: OpWeightSubmitCommitteeChangeProposal,
DefaultWeight: appparams.OpWeightSubmitCommitteeChangeProposal,
ContentSimulatorFn: SimulateCommitteeChangeProposalContent(k, paramChanges),
},
}
}
// SimulateCommitteeChangeProposalContent generates gov proposal contents that either:
// - create new committees
// - change existing committees
// - delete committees
// It does not alter the fallback committee.
func SimulateCommitteeChangeProposalContent(k keeper.Keeper, paramChanges []simulation.ParamChange) simulation.ContentSimulatorFn {
return func(r *rand.Rand, ctx sdk.Context, accs []simulation.Account) govtypes.Content {
allowedParams := paramChangeToAllowedParams(paramChanges)
// get current committees, ignoring the fallback committee
var committees []types.Committee
k.IterateCommittees(ctx, func(com types.Committee) bool {
if com.ID != FallbackCommitteeID {
committees = append(committees, com)
}
return false
})
if len(committees) < 1 { // create a committee if none exist
com, err := RandomCommittee(r, firstNAccounts(25, accs), allowedParams) // limit num members to avoid overflowing hardcoded gov ops gas limit
if err != nil {
panic(err)
}
return types.NewCommitteeChangeProposal(
simulation.RandStringOfLength(r, 10),
simulation.RandStringOfLength(r, 100),
com,
)
}
// create a proposal content
var content govtypes.Content
switch choice := r.Intn(100); {
// create committee
case choice < 20:
com, err := RandomCommittee(r, firstNAccounts(25, accs), allowedParams) // limit num members to avoid overflowing hardcoded gov ops gas limit
if err != nil {
panic(err)
}
content = types.NewCommitteeChangeProposal(
simulation.RandStringOfLength(r, 10),
simulation.RandStringOfLength(r, 100),
com,
)
// update committee
case choice < 80:
com := committees[r.Intn(len(committees))]
// update members
if r.Intn(100) < 50 {
if len(accs) == 0 {
panic("must have at least one account availabel to use as committee member")
}
var members []sdk.AccAddress
for len(members) < 1 {
members = RandomAddresses(r, firstNAccounts(25, accs)) // limit num members to avoid overflowing hardcoded gov ops gas limit
}
com.Members = members
}
// update permissions
if r.Intn(100) < 50 {
com.Permissions = RandomPermissions(r, allowedParams)
}
// update proposal duration
if r.Intn(100) < 50 {
dur, err := RandomPositiveDuration(r, 0, AverageBlockTime*100)
if err != nil {
panic(err)
}
com.ProposalDuration = dur
}
// update vote threshold
if r.Intn(100) < 50 {
// VoteThreshold must be in interval (0,1]
com.VoteThreshold = simulation.RandomDecAmount(r, sdk.MustNewDecFromStr("1").Sub(sdk.SmallestDec())).Add(sdk.SmallestDec())
}
content = types.NewCommitteeChangeProposal(
simulation.RandStringOfLength(r, 10),
simulation.RandStringOfLength(r, 100),
com,
)
// delete committee
default:
com := committees[r.Intn(len(committees))]
content = types.NewCommitteeDeleteProposal(
simulation.RandStringOfLength(r, 10),
simulation.RandStringOfLength(r, 100),
com.ID,
)
}
return content
}
}
/*
// Example custom ParamChangeProposal generator to only generate change to interesting cdp params.
// This allows more control over what params are changed within a simulation.
func SimulateCDPParamChangeProposalContent(cdpKeeper cdpkeeper.Keeper, paramChangePool []simulation.ParamChange) simulation.ContentSimulatorFn {
return func(r *rand.Rand, ctx sdk.Context, _ []simulation.Account) govtypes.Content {
var paramChanges []paramstypes.ParamChange
// alter sub params
cp := cdpKeeper.GetParams(ctx).CollateralParams
if len(cp) == 0 {
return nil
}
cp[0].StabilityFee = sdk.MustNewDecFromStr("0.000001") // TODO generate
paramChanges = append(
paramChanges,
paramstypes.NewParamChange(cdptypes.ModuleName, "?", string(cdptypes.ModuleCdc.MustMarshalJSON(cp))),
)
// alter normal param
for _, pc := range paramChangePool {
if pc.Subspace == cdptypes.ModuleName && pc.Key == string(cdptypes.KeyGlobalDebtLimit) {
paramChanges = append(
paramChanges,
paramstypes.NewParamChange(pc.Subspace, pc.Key, pc.SimValue(r)),
)
}
}
return paramstypes.NewParameterChangeProposal(
simulation.RandStringOfLength(r, 140), // title
simulation.RandStringOfLength(r, 5000), // description
paramChanges, // set of changes
)
}
}
*/
func firstNAccounts(n int, accs []simulation.Account) []simulation.Account {
if n < 0 {
panic(fmt.Sprintf("n must be ≥ 0"))
}
if n > len(accs) {
return accs
}
return accs[:n]
}