0g-chain/x/committee/simulation/genesis.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

124 lines
4.1 KiB
Go

package simulation
import (
"fmt"
"math/rand"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/cosmos-sdk/x/simulation"
"github.com/kava-labs/kava/x/committee/types"
)
const (
// Block time params are un-exported constants in cosmos-sdk/x/simulation.
// Copy them here in lieu of importing them.
minTimePerBlock time.Duration = (10000 / 2) * time.Second
maxTimePerBlock time.Duration = 10000 * time.Second
// Calculate the average block time
AverageBlockTime time.Duration = (maxTimePerBlock - minTimePerBlock) / 2
FallbackCommitteeID uint64 = 0
)
// RandomizedGenState generates a random GenesisState for the module
func RandomizedGenState(simState *module.SimulationState) {
r := simState.Rand
// Create an always present committee with god permissions to ensure any randomly generated proposal can always be submitted.
// Without this, proposals can often not be submitted as there aren't any committees with the right set of permissions available.
// It provides more control over how often different proposal types happen during simulation.
// It also makes the code simpler--proposals can just be randomly generated and submitted without having to comply to permissions that happen to be available at the time.
fallbackCommittee := types.NewCommittee(
FallbackCommitteeID,
"A committee with god permissions that will always be in state and not deleted. It ensures any generated proposal can always be submitted and passed.",
RandomAddresses(r, simState.Accounts),
[]types.Permission{types.GodPermission{}},
sdk.MustNewDecFromStr("0.5"),
AverageBlockTime*10,
)
// Create other committees
numCommittees := r.Intn(100)
committees := []types.Committee{fallbackCommittee}
for i := 0; i < numCommittees; i++ {
com, err := RandomCommittee(r, firstNAccounts(25, simState.Accounts), paramChangeToAllowedParams(simState.ParamChanges))
if err != nil {
panic(err)
}
committees = append(committees, com)
}
// Add genesis state to simState
genesisState := types.NewGenesisState(
types.DefaultNextProposalID,
committees,
[]types.Proposal{},
[]types.Vote{},
)
fmt.Printf("Selected randomly generated %s parameters:\n%s\n", types.ModuleName, []byte{})
simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(genesisState)
}
func RandomCommittee(r *rand.Rand, availableAccs []simulation.Account, allowedParams []types.AllowedParam) (types.Committee, error) {
// pick committee members
if len(availableAccs) < 1 {
return types.Committee{}, fmt.Errorf("must be ≥ 1 addresses")
}
var members []sdk.AccAddress
for len(members) < 1 {
members = RandomAddresses(r, availableAccs)
}
// pick proposal duration
dur, err := RandomPositiveDuration(r, 0, AverageBlockTime*10)
if err != nil {
return types.Committee{}, err
}
// pick committee vote threshold, must be in interval (0,1]
threshold := simulation.RandomDecAmount(r, sdk.MustNewDecFromStr("1").Sub(sdk.SmallestDec())).Add(sdk.SmallestDec())
return types.NewCommittee(
r.Uint64(), // could collide with other committees, but unlikely
simulation.RandStringOfLength(r, r.Intn(types.MaxCommitteeDescriptionLength+1)),
members,
RandomPermissions(r, allowedParams),
threshold,
dur,
), nil
}
func RandomPermissions(r *rand.Rand, allowedParams []types.AllowedParam) []types.Permission {
var permissions []types.Permission
if r.Intn(100) < 50 {
permissions = append(permissions, types.TextPermission{})
}
if r.Intn(100) < 50 {
r.Shuffle(len(allowedParams), func(i, j int) {
allowedParams[i], allowedParams[j] = allowedParams[j], allowedParams[i]
})
permissions = append(permissions,
types.ParamChangePermission{
AllowedParams: allowedParams[:r.Intn(len(allowedParams)+1)],
})
}
return permissions
}
func paramChangeToAllowedParams(paramChanges []simulation.ParamChange) []types.AllowedParam {
var allowedParams []types.AllowedParam
for _, pc := range paramChanges {
allowedParams = append(
allowedParams,
types.AllowedParam{
Subspace: pc.Subspace,
Key: pc.Key,
},
)
}
return allowedParams
}