add simulation to validator vesting

This commit is contained in:
Kevin Davis 2019-09-30 22:53:14 -04:00
parent b57b362062
commit f6aec46343
6 changed files with 311 additions and 3 deletions

View File

@ -8,7 +8,7 @@ import (
// InitGenesis stores the account address of each ValidatorVestingAccount in the validator vesting keeper, for faster lookup. // InitGenesis stores the account address of each ValidatorVestingAccount in the validator vesting keeper, for faster lookup.
// CONTRACT: Accounts created by the account keeper must have already been initialized/created by AccountKeeper // CONTRACT: Accounts created by the account keeper must have already been initialized/created by AccountKeeper
func InitGenesis(ctx sdk.Context, keeper Keeper, accountKeeper types.AccountKeeper, data GenesisState) { func InitGenesis(ctx sdk.Context, keeper Keeper, data GenesisState) {
data.Accounts = auth.SanitizeGenesisAccounts(data.Accounts) data.Accounts = auth.SanitizeGenesisAccounts(data.Accounts)
for _, a := range data.Accounts { for _, a := range data.Accounts {
vv, ok := a.(ValidatorVestingAccount) vv, ok := a.(ValidatorVestingAccount)
@ -17,3 +17,8 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, accountKeeper types.AccountKeep
} }
} }
} }
// ExportGenesis returns empty genesis state because auth exports all the genesis state we need.
func ExportGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState {
return types.DefaultGenesisState()
}

View File

@ -34,3 +34,8 @@ func (data GenesisState) Equal(data2 GenesisState) bool {
func (data GenesisState) IsEmpty() bool { func (data GenesisState) IsEmpty() bool {
return data.Equal(GenesisState{}) return data.Equal(GenesisState{})
} }
// ValidateGenesis returns nil because accounts are validated by auth
func ValidateGenesis(data GenesisState) error {
return nil
}

View File

@ -0,0 +1,146 @@
package validatorvesting
import (
"encoding/json"
"math/rand"
"github.com/gorilla/mux"
"github.com/spf13/cobra"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
sim "github.com/cosmos/cosmos-sdk/x/simulation"
"github.com/cosmos/cosmos-sdk/x/validator-vesting/internal/types"
"github.com/cosmos/cosmos-sdk/x/validator-vesting/simulation"
)
var (
_ module.AppModule = AppModule{}
_ module.AppModuleBasic = AppModuleBasic{}
_ module.AppModuleSimulation = AppModuleSimulation{}
)
// AppModuleBasic defines the basic application module used by the auth module.
type AppModuleBasic struct{}
// Name returns the auth module's name.
func (AppModuleBasic) Name() string {
return types.ModuleName
}
// RegisterCodec registers the auth module's types for the given codec.
func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) {
types.RegisterCodec(cdc)
}
// DefaultGenesis returns default genesis state as raw bytes for the validator-vesting
// module.
func (AppModuleBasic) DefaultGenesis() json.RawMessage {
return types.ModuleCdc.MustMarshalJSON(types.DefaultGenesisState())
}
// ValidateGenesis performs genesis state validation for the validator-vesting module.
func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error {
var data types.GenesisState
if err := types.ModuleCdc.UnmarshalJSON(bz, &data); err != nil {
return err
}
return types.ValidateGenesis(data)
}
// RegisterRESTRoutes registers no REST routes for the crisis module.
func (AppModuleBasic) RegisterRESTRoutes(_ context.CLIContext, _ *mux.Router) {}
// GetTxCmd returns no root tx command for the validator-vesting module.
func (AppModuleBasic) GetTxCmd(_ *codec.Codec) *cobra.Command { return nil }
// GetQueryCmd returns no root query command for the validator-vesting module.
func (AppModuleBasic) GetQueryCmd(_ *codec.Codec) *cobra.Command { return nil }
// AppModuleSimulation defines the module simulation functions used by the auth module.
type AppModuleSimulation struct{}
// RegisterStoreDecoder registers a decoder for auth module's types
func (AppModuleSimulation) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) {
sdr[StoreKey] = simulation.DecodeStore
}
// GenerateGenesisState creates a randomized GenState of the auth module
func (AppModuleSimulation) GenerateGenesisState(simState *module.SimulationState) {
simulation.RandomizedGenState(simState)
}
// RandomizedParams returns nil because validatorvesting has no params.
func (AppModuleSimulation) RandomizedParams(_ *rand.Rand) []sim.ParamChange {
return []sim.ParamChange{}
}
// AppModule implements an application module for the validator-vesting module.
type AppModule struct {
AppModuleBasic
AppModuleSimulation
keeper Keeper
}
// NewAppModule creates a new AppModule object
func NewAppModule(keeper Keeper) AppModule {
return AppModule{
AppModuleBasic: AppModuleBasic{},
AppModuleSimulation: AppModuleSimulation{},
keeper: keeper,
}
}
// Name returns the auth module's name.
func (AppModule) Name() string {
return types.ModuleName
}
// RegisterInvariants performs a no-op.
func (AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {}
// Route returns the message routing key for the auth module.
func (AppModule) Route() string { return "" }
// NewHandler returns an sdk.Handler for the auth module.
func (AppModule) NewHandler() sdk.Handler { return nil }
// QuerierRoute returns the auth module's querier route name.
func (AppModule) QuerierRoute() string {
return ""
}
// NewQuerierHandler returns the auth module sdk.Querier.
func (am AppModule) NewQuerierHandler() sdk.Querier {
return nil
}
// InitGenesis performs genesis initialization for the auth module. It returns
// no validator updates.
func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate {
var genesisState GenesisState
types.ModuleCdc.MustUnmarshalJSON(data, &genesisState)
InitGenesis(ctx, am.keeper, genesisState)
return []abci.ValidatorUpdate{}
}
// ExportGenesis returns the exported genesis state as raw bytes for the auth
// module.
func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage {
gs := ExportGenesis(ctx, am.keeper)
return types.ModuleCdc.MustMarshalJSON(gs)
}
// BeginBlock returns the begin blocker for the auth module.
func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) {
BeginBlocker(ctx, req, am.keeper)
}
// EndBlock returns the end blocker for the auth module. It returns no validator
// updates.
func (AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate {
return []abci.ValidatorUpdate{}
}

View File

@ -0,0 +1,31 @@
package simulation
import (
"bytes"
"fmt"
"time"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/x/auth/exported"
"github.com/cosmos/cosmos-sdk/x/validator-vesting/internal/types"
)
// DecodeStore unmarshals the KVPair's Value to the corresponding auth type
func DecodeStore(cdc *codec.Codec, kvA, kvB cmn.KVPair) string {
switch {
case bytes.Equal(kvA.Key[:1], types.ValidatorVestingAccountPrefix):
var accA, accB exported.Account
cdc.MustUnmarshalBinaryBare(kvA.Value, &accA)
cdc.MustUnmarshalBinaryBare(kvB.Value, &accB)
return fmt.Sprintf("%v\n%v", accA, accB)
case bytes.Equal(kvA.Key, types.BlocktimeKey):
var btA, btB time.Time
cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &btA)
cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &btB)
return fmt.Sprintf("%v\n%v", btA, btB)
default:
panic(fmt.Sprintf("invalid account key %X", kvA.Key))
}
}

View File

@ -0,0 +1,121 @@
package simulation
import (
"math/rand"
"github.com/cosmos/cosmos-sdk/types/module"
sdk "github.com/cosmos/cosmos-sdk/types"
authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
vestexported "github.com/cosmos/cosmos-sdk/x/auth/vesting/exported"
vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
"github.com/cosmos/cosmos-sdk/x/simulation"
"github.com/cosmos/cosmos-sdk/x/validator-vesting/internal/types"
)
// RandomizedGenState generates a random GenesisState for validator-vesting
func RandomizedGenState(simState *module.SimulationState) {
var authGenState authtypes.GenesisState
authSimState := simState.GenState[authtypes.ModuleName]
simState.Cdc.MustUnmarshalJSON(authSimState, &authGenState)
var newGenesisAccs authexported.GenesisAccounts
for _, acc := range authGenState.Accounts {
va, ok := acc.(vestexported.VestingAccount)
if ok {
// 50% of the time convert the vesting account
if simState.Rand.Intn(100) < 50 {
bacc := authtypes.NewBaseAccountWithAddress(va.GetAddress())
err := bacc.SetCoins(va.GetCoins())
if err != nil {
panic(err)
}
duration := va.GetEndTime() - va.GetStartTime()
vestingPeriods := getRandomVestingPeriods(duration, simState.Rand, va.GetCoins())
vestingCoins := getVestingCoins(vestingPeriods)
bva := vestingtypes.NewBaseVestingAccount(&bacc, vestingCoins, va.GetEndTime())
var gacc authexported.GenesisAccount
if simState.Rand.Intn(100) < 50 {
// convert to periodic vesting account 50%
gacc = vestingtypes.NewPeriodicVestingAccountRaw(bva, va.GetStartTime(), vestingPeriods)
err = gacc.Validate()
if err != nil {
panic(err)
}
} else {
consAdd := getRandomValidatorConsAddr(simState, simulation.RandIntBetween(simState.Rand, 0, int(simState.NumBonded)-1))
// convert to validator vesting account 50%
// set signing threshold to be anywhere between 1 and 100
gacc = types.NewValidatorVestingAccountRaw(
bva, va.GetStartTime(), vestingPeriods, consAdd, nil,
int64(simulation.RandIntBetween(simState.Rand, 1, 100)),
)
err = gacc.Validate()
if err != nil {
panic(err)
}
}
newGenesisAccs = append(newGenesisAccs, gacc)
} else {
newGenesisAccs = append(newGenesisAccs, acc)
}
} else {
newGenesisAccs = append(newGenesisAccs, acc)
}
}
newAuthGenesis := authtypes.NewGenesisState(authGenState.Params, newGenesisAccs)
simState.GenState[authtypes.ModuleName] = simState.Cdc.MustMarshalJSON(newAuthGenesis)
}
func getRandomValidatorConsAddr(simState *module.SimulationState, rint int) sdk.ConsAddress {
acc := simState.Accounts[rint]
return sdk.ConsAddress(acc.PubKey.Address())
}
func getRandomVestingPeriods(duration int64, r *rand.Rand, origCoins sdk.Coins) vestingtypes.Periods {
maxPeriods := int64(50)
if duration < maxPeriods {
maxPeriods = duration
}
numPeriods := simulation.RandIntBetween(r, 1, int(maxPeriods))
lenPeriod := duration / int64(numPeriods)
periodLengths := make([]int64, numPeriods)
totalLength := int64(0)
for i := 0; i < numPeriods; i++ {
periodLengths[i] = lenPeriod
totalLength += lenPeriod
}
if duration-totalLength != 0 {
periodLengths[len(periodLengths)-1] += (duration - totalLength)
}
coinFraction := simulation.RandIntBetween(r, 1, 100)
vestingCoins := sdk.NewCoins()
for _, ic := range origCoins {
amountVesting := ic.Amount.Int64() / int64(coinFraction)
vestingCoins = vestingCoins.Add(sdk.NewCoins(sdk.NewInt64Coin(ic.Denom, amountVesting)))
}
periodCoins := sdk.NewCoins()
for _, c := range vestingCoins {
amountPeriod := c.Amount.Int64() / int64(numPeriods)
periodCoins = periodCoins.Add(sdk.NewCoins(sdk.NewInt64Coin(c.Denom, amountPeriod)))
}
vestingPeriods := make([]vestingtypes.Period, numPeriods)
for i := 0; i < numPeriods; i++ {
vestingPeriods[i] = vestingtypes.Period{Length: int64(periodLengths[i]), Amount: periodCoins}
}
return vestingPeriods
}
func getVestingCoins(periods vestingtypes.Periods) sdk.Coins {
vestingCoins := sdk.NewCoins()
for _, p := range periods {
vestingCoins = vestingCoins.Add(p.Amount)
}
return vestingCoins
}

View File

@ -122,9 +122,9 @@ func getInitChainer(mapp *mock.App, keeper Keeper, stakingKeeper staking.Keeper,
validators := staking.InitGenesis(ctx, stakingKeeper, mapp.AccountKeeper, supplyKeeper, stakingGenesis) validators := staking.InitGenesis(ctx, stakingKeeper, mapp.AccountKeeper, supplyKeeper, stakingGenesis)
if genState.IsEmpty() { if genState.IsEmpty() {
InitGenesis(ctx, keeper, mapp.AccountKeeper, types.DefaultGenesisState()) InitGenesis(ctx, keeper, types.DefaultGenesisState())
} else { } else {
InitGenesis(ctx, keeper, mapp.AccountKeeper, genState) InitGenesis(ctx, keeper, genState)
} }
return abci.ResponseInitChain{ return abci.ResponseInitChain{
Validators: validators, Validators: validators,