update simulation operations to use BaseApp (#4946)
* update operations to use baseapp * updates and cleanup operations * update operations * restructure sim ops params * rename sim /operations/msg.go to /operations.go * move GenTx to a helper pkg to avoid circle deps * rm msg.ValidateBasic * changelog * random fees; delete auth's DeductFees sim operation * add chain-id for sig verification * Update x/simulation/account.go Co-Authored-By: colin axner <colinaxner@berkeley.edu> * fix bank, gov and distr errors * fix staking and slashing errors; increase prob for send enabled * increase gas x10 * make format * fix some distr and staking edge cases * fix all edge cases * golang ci * rename acc vars; default no fees to 0stake * cleanup; check for exchange rate and skip invalid ops * fixes * check for max entries * add pubkey to genaccounts * fix gov bug * update staking sim ops * fix small redelegation error * fix small self delegation on unjail * rm inf loop on random val/accs * copy array * add ok boolean to RandomValidator return values * format * Update x/bank/simulation/operations.go Co-Authored-By: colin axner <colinaxner@berkeley.edu> * Update simapp/helpers/test_helpers.go Co-Authored-By: colin axner <colinaxner@berkeley.edu> * address @colin-axner comments * add genaccount pubkey validation * fix test * update operations and move RandomFees to x/simulation * update gov ops * address @alexanderbez comments * avoid modifications to config * reorder params * changelog * Update x/distribution/simulation/genesis.go Co-Authored-By: Alexander Bezobchuk <alexanderbez@users.noreply.github.com> * remove named return values * ensure all operations are simulated * golangci * add nolint * disable whitespace and funlen linter * disable godox * add TODO on unjail * update ops weights * remove dup * update godoc * x/slashing/simulation/operations.go linting * x/staking/simulation/operations.go linting * update operations format * x/bank/simulation/operations.go linting * x/distribution/simulation/operations.go linting * x/staking/simulation/operations.go linting * start changes: make bank simulate send multiple coins, code cleanup * fix nondeterminism bug * fix txsiglimit err * fix multisend bug * simplify simulation, cleanup opt privkey args * make slashing test invalid unjail msgs * Update simapp/state.go * golangCI changes
This commit is contained in:
parent
7581871b9b
commit
8344c0aef8
|
@ -167,6 +167,7 @@ generalized genesis accounts through the `GenesisAccount` interface.
|
|||
`ParamChangeProposal`s
|
||||
* (simulation) [\#4893](https://github.com/cosmos/cosmos-sdk/issues/4893) Change SimApp keepers to be public and add getter functions for keys and codec
|
||||
* (simulation) [\#4906](https://github.com/cosmos/cosmos-sdk/issues/4906) Add simulation `Config` struct that wraps simulation flags
|
||||
* (simulation) [\#4935](https://github.com/cosmos/cosmos-sdk/issues/4935) Update simulation to reflect a proper `ABCI` application without bypassing `BaseApp` semantics
|
||||
* (store) [\#4792](https://github.com/cosmos/cosmos-sdk/issues/4792) panic on non-registered store
|
||||
* (types) [\#4821](https://github.com/cosmos/cosmos-sdk/issues/4821) types/errors package added with support for stacktraces. It is meant as a more feature-rich replacement for sdk.Errors in the mid-term.
|
||||
* (store) [\#1947](https://github.com/cosmos/cosmos-sdk/issues/1947) Implement inter-block (persistent)
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/types/module"
|
||||
"github.com/cosmos/cosmos-sdk/version"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/ante"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/vesting"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
"github.com/cosmos/cosmos-sdk/x/crisis"
|
||||
|
@ -229,7 +230,7 @@ func NewSimApp(
|
|||
// initialize BaseApp
|
||||
app.SetInitChainer(app.InitChainer)
|
||||
app.SetBeginBlocker(app.BeginBlocker)
|
||||
app.SetAnteHandler(auth.NewAnteHandler(app.AccountKeeper, app.SupplyKeeper, auth.DefaultSigVerificationGasConsumer))
|
||||
app.SetAnteHandler(ante.NewAnteHandler(app.AccountKeeper, app.SupplyKeeper, auth.DefaultSigVerificationGasConsumer))
|
||||
app.SetEndBlocker(app.EndBlocker)
|
||||
|
||||
if loadLatest {
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
package helpers
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||
)
|
||||
|
||||
// SimAppChainID hardcoded chainID for simulation
|
||||
const SimAppChainID = "simulation-app"
|
||||
|
||||
// GenTx generates a signed mock transaction.
|
||||
func GenTx(msgs []sdk.Msg, feeAmt sdk.Coins, chainID string, accnums []uint64, seq []uint64, priv ...crypto.PrivKey) auth.StdTx {
|
||||
fee := auth.StdFee{
|
||||
Amount: feeAmt,
|
||||
Gas: 1000000, // TODO: this should be a param
|
||||
}
|
||||
|
||||
sigs := make([]auth.StdSignature, len(priv))
|
||||
|
||||
// create a random length memo
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
memo := simulation.RandStringOfLength(r, simulation.RandIntBetween(r, 0, 100))
|
||||
|
||||
for i, p := range priv {
|
||||
// use a empty chainID for ease of testing
|
||||
sig, err := p.Sign(auth.StdSignBytes(chainID, accnums[i], seq[i], fee, msgs, memo))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
sigs[i] = auth.StdSignature{
|
||||
PubKey: p.PubKey(),
|
||||
Signature: sig,
|
||||
}
|
||||
}
|
||||
|
||||
return auth.NewStdTx(msgs, fee, sigs, memo)
|
||||
}
|
|
@ -2,22 +2,23 @@ package simapp
|
|||
|
||||
// Simulation parameter constants
|
||||
const (
|
||||
StakePerAccount = "stake_per_account"
|
||||
InitiallyBondedValidators = "initially_bonded_validators"
|
||||
OpWeightDeductFee = "op_weight_deduct_fee"
|
||||
OpWeightMsgSend = "op_weight_msg_send"
|
||||
OpWeightSingleInputMsgMultiSend = "op_weight_single_input_msg_multisend"
|
||||
OpWeightMsgSetWithdrawAddress = "op_weight_msg_set_withdraw_address"
|
||||
OpWeightMsgWithdrawDelegationReward = "op_weight_msg_withdraw_delegation_reward"
|
||||
OpWeightMsgWithdrawValidatorCommission = "op_weight_msg_withdraw_validator_commission"
|
||||
OpWeightSubmitVotingSlashingTextProposal = "op_weight_submit_voting_slashing_text_proposal"
|
||||
OpWeightSubmitVotingSlashingCommunitySpendProposal = "op_weight_submit_voting_slashing_community_spend_proposal"
|
||||
OpWeightSubmitVotingSlashingParamChangeProposal = "op_weight_submit_voting_slashing_param_change_proposal"
|
||||
OpWeightMsgDeposit = "op_weight_msg_deposit"
|
||||
OpWeightMsgCreateValidator = "op_weight_msg_create_validator"
|
||||
OpWeightMsgEditValidator = "op_weight_msg_edit_validator"
|
||||
OpWeightMsgDelegate = "op_weight_msg_delegate"
|
||||
OpWeightMsgUndelegate = "op_weight_msg_undelegate"
|
||||
OpWeightMsgBeginRedelegate = "op_weight_msg_begin_redelegate"
|
||||
OpWeightMsgUnjail = "op_weight_msg_unjail"
|
||||
StakePerAccount = "stake_per_account"
|
||||
InitiallyBondedValidators = "initially_bonded_validators"
|
||||
OpWeightDeductFee = "op_weight_deduct_fee"
|
||||
OpWeightMsgSend = "op_weight_msg_send"
|
||||
OpWeightMsgMultiSend = "op_weight_msg_multisend"
|
||||
OpWeightMsgSetWithdrawAddress = "op_weight_msg_set_withdraw_address"
|
||||
OpWeightMsgWithdrawDelegationReward = "op_weight_msg_withdraw_delegation_reward"
|
||||
OpWeightMsgWithdrawValidatorCommission = "op_weight_msg_withdraw_validator_commission"
|
||||
OpWeightSubmitTextProposal = "op_weight_submit_text_proposal"
|
||||
OpWeightSubmitCommunitySpendProposal = "op_weight_submit_community_spend_proposal"
|
||||
OpWeightSubmitParamChangeProposal = "op_weight_submit_param_change_proposal"
|
||||
OpWeightMsgDeposit = "op_weight_msg_deposit"
|
||||
OpWeightMsgVote = "op_weight_msg_vote"
|
||||
OpWeightMsgCreateValidator = "op_weight_msg_create_validator"
|
||||
OpWeightMsgEditValidator = "op_weight_msg_edit_validator"
|
||||
OpWeightMsgDelegate = "op_weight_msg_delegate"
|
||||
OpWeightMsgUndelegate = "op_weight_msg_undelegate"
|
||||
OpWeightMsgBeginRedelegate = "op_weight_msg_begin_redelegate"
|
||||
OpWeightMsgUnjail = "op_weight_msg_unjail"
|
||||
)
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/tendermint/tendermint/libs/log"
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/simapp/helpers"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||
)
|
||||
|
@ -19,6 +20,7 @@ import (
|
|||
func BenchmarkFullAppSimulation(b *testing.B) {
|
||||
logger := log.NewNopLogger()
|
||||
config := NewConfigFromFlags()
|
||||
config.ChainID = helpers.SimAppChainID
|
||||
|
||||
var db dbm.DB
|
||||
dir, _ := ioutil.TempDir("", "goleveldb-app-sim")
|
||||
|
@ -69,6 +71,7 @@ func BenchmarkInvariants(b *testing.B) {
|
|||
|
||||
config := NewConfigFromFlags()
|
||||
config.AllInvariants = false
|
||||
config.ChainID = helpers.SimAppChainID
|
||||
|
||||
dir, _ := ioutil.TempDir("", "goleveldb-app-invariant-bench")
|
||||
db, _ := sdk.NewLevelDB("simulation", dir)
|
||||
|
|
|
@ -14,23 +14,23 @@ import (
|
|||
dbm "github.com/tendermint/tm-db"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/simapp/helpers"
|
||||
"github.com/cosmos/cosmos-sdk/store"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
authsimops "github.com/cosmos/cosmos-sdk/x/auth/simulation/operations"
|
||||
banksimops "github.com/cosmos/cosmos-sdk/x/bank/simulation/operations"
|
||||
banksim "github.com/cosmos/cosmos-sdk/x/bank/simulation"
|
||||
distr "github.com/cosmos/cosmos-sdk/x/distribution"
|
||||
distrsimops "github.com/cosmos/cosmos-sdk/x/distribution/simulation/operations"
|
||||
distrsim "github.com/cosmos/cosmos-sdk/x/distribution/simulation"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov"
|
||||
govsimops "github.com/cosmos/cosmos-sdk/x/gov/simulation/operations"
|
||||
govsim "github.com/cosmos/cosmos-sdk/x/gov/simulation"
|
||||
"github.com/cosmos/cosmos-sdk/x/mint"
|
||||
"github.com/cosmos/cosmos-sdk/x/params"
|
||||
paramsimops "github.com/cosmos/cosmos-sdk/x/params/simulation/operations"
|
||||
paramsim "github.com/cosmos/cosmos-sdk/x/params/simulation"
|
||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||
"github.com/cosmos/cosmos-sdk/x/slashing"
|
||||
slashingsimops "github.com/cosmos/cosmos-sdk/x/slashing/simulation/operations"
|
||||
slashingsim "github.com/cosmos/cosmos-sdk/x/slashing/simulation"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking"
|
||||
stakingsimops "github.com/cosmos/cosmos-sdk/x/staking/simulation/operations"
|
||||
stakingsim "github.com/cosmos/cosmos-sdk/x/staking/simulation"
|
||||
"github.com/cosmos/cosmos-sdk/x/supply"
|
||||
)
|
||||
|
||||
|
@ -39,7 +39,6 @@ func init() {
|
|||
GetSimulatorFlags()
|
||||
}
|
||||
|
||||
// TODO: add description
|
||||
func testAndRunTxs(app *SimApp, config simulation.Config) []simulation.WeightedOperation {
|
||||
ap := make(simulation.AppParams)
|
||||
|
||||
|
@ -56,17 +55,6 @@ func testAndRunTxs(app *SimApp, config simulation.Config) []simulation.WeightedO
|
|||
|
||||
// nolint: govet
|
||||
return []simulation.WeightedOperation{
|
||||
{
|
||||
func(_ *rand.Rand) int {
|
||||
var v int
|
||||
ap.GetOrGenerate(app.cdc, OpWeightDeductFee, &v, nil,
|
||||
func(_ *rand.Rand) {
|
||||
v = 5
|
||||
})
|
||||
return v
|
||||
}(nil),
|
||||
authsimops.SimulateDeductFee(app.AccountKeeper, app.SupplyKeeper),
|
||||
},
|
||||
{
|
||||
func(_ *rand.Rand) int {
|
||||
var v int
|
||||
|
@ -76,18 +64,18 @@ func testAndRunTxs(app *SimApp, config simulation.Config) []simulation.WeightedO
|
|||
})
|
||||
return v
|
||||
}(nil),
|
||||
banksimops.SimulateMsgSend(app.AccountKeeper, app.BankKeeper),
|
||||
banksim.SimulateMsgSend(app.AccountKeeper, app.BankKeeper),
|
||||
},
|
||||
{
|
||||
func(_ *rand.Rand) int {
|
||||
var v int
|
||||
ap.GetOrGenerate(app.cdc, OpWeightSingleInputMsgMultiSend, &v, nil,
|
||||
ap.GetOrGenerate(app.cdc, OpWeightMsgMultiSend, &v, nil,
|
||||
func(_ *rand.Rand) {
|
||||
v = 10
|
||||
v = 40
|
||||
})
|
||||
return v
|
||||
}(nil),
|
||||
banksimops.SimulateSingleInputMsgMultiSend(app.AccountKeeper, app.BankKeeper),
|
||||
banksim.SimulateMsgMultiSend(app.AccountKeeper, app.BankKeeper),
|
||||
},
|
||||
{
|
||||
func(_ *rand.Rand) int {
|
||||
|
@ -98,7 +86,7 @@ func testAndRunTxs(app *SimApp, config simulation.Config) []simulation.WeightedO
|
|||
})
|
||||
return v
|
||||
}(nil),
|
||||
distrsimops.SimulateMsgSetWithdrawAddress(app.DistrKeeper),
|
||||
distrsim.SimulateMsgSetWithdrawAddress(app.AccountKeeper, app.DistrKeeper),
|
||||
},
|
||||
{
|
||||
func(_ *rand.Rand) int {
|
||||
|
@ -109,7 +97,7 @@ func testAndRunTxs(app *SimApp, config simulation.Config) []simulation.WeightedO
|
|||
})
|
||||
return v
|
||||
}(nil),
|
||||
distrsimops.SimulateMsgWithdrawDelegatorReward(app.DistrKeeper),
|
||||
distrsim.SimulateMsgWithdrawDelegatorReward(app.AccountKeeper, app.DistrKeeper, app.StakingKeeper),
|
||||
},
|
||||
{
|
||||
func(_ *rand.Rand) int {
|
||||
|
@ -120,40 +108,40 @@ func testAndRunTxs(app *SimApp, config simulation.Config) []simulation.WeightedO
|
|||
})
|
||||
return v
|
||||
}(nil),
|
||||
distrsimops.SimulateMsgWithdrawValidatorCommission(app.DistrKeeper),
|
||||
distrsim.SimulateMsgWithdrawValidatorCommission(app.AccountKeeper, app.DistrKeeper, app.StakingKeeper),
|
||||
},
|
||||
{
|
||||
func(_ *rand.Rand) int {
|
||||
var v int
|
||||
ap.GetOrGenerate(app.cdc, OpWeightSubmitVotingSlashingTextProposal, &v, nil,
|
||||
ap.GetOrGenerate(app.cdc, OpWeightSubmitTextProposal, &v, nil,
|
||||
func(_ *rand.Rand) {
|
||||
v = 5
|
||||
v = 20
|
||||
})
|
||||
return v
|
||||
}(nil),
|
||||
govsimops.SimulateSubmittingVotingAndSlashingForProposal(app.GovKeeper, govsimops.SimulateTextProposalContent),
|
||||
govsim.SimulateSubmitProposal(app.AccountKeeper, app.GovKeeper, govsim.SimulateTextProposalContent),
|
||||
},
|
||||
{
|
||||
func(_ *rand.Rand) int {
|
||||
var v int
|
||||
ap.GetOrGenerate(app.cdc, OpWeightSubmitVotingSlashingCommunitySpendProposal, &v, nil,
|
||||
ap.GetOrGenerate(app.cdc, OpWeightSubmitCommunitySpendProposal, &v, nil,
|
||||
func(_ *rand.Rand) {
|
||||
v = 5
|
||||
v = 20
|
||||
})
|
||||
return v
|
||||
}(nil),
|
||||
govsimops.SimulateSubmittingVotingAndSlashingForProposal(app.GovKeeper, distrsimops.SimulateCommunityPoolSpendProposalContent(app.DistrKeeper)),
|
||||
govsim.SimulateSubmitProposal(app.AccountKeeper, app.GovKeeper, distrsim.SimulateCommunityPoolSpendProposalContent(app.DistrKeeper)),
|
||||
},
|
||||
{
|
||||
func(_ *rand.Rand) int {
|
||||
var v int
|
||||
ap.GetOrGenerate(app.cdc, OpWeightSubmitVotingSlashingParamChangeProposal, &v, nil,
|
||||
ap.GetOrGenerate(app.cdc, OpWeightSubmitParamChangeProposal, &v, nil,
|
||||
func(_ *rand.Rand) {
|
||||
v = 5
|
||||
v = 20
|
||||
})
|
||||
return v
|
||||
}(nil),
|
||||
govsimops.SimulateSubmittingVotingAndSlashingForProposal(app.GovKeeper, paramsimops.SimulateParamChangeProposalContent(paramChanges)),
|
||||
govsim.SimulateSubmitProposal(app.AccountKeeper, app.GovKeeper, paramsim.SimulateParamChangeProposalContent(paramChanges)),
|
||||
},
|
||||
{
|
||||
func(_ *rand.Rand) int {
|
||||
|
@ -164,7 +152,18 @@ func testAndRunTxs(app *SimApp, config simulation.Config) []simulation.WeightedO
|
|||
})
|
||||
return v
|
||||
}(nil),
|
||||
govsimops.SimulateMsgDeposit(app.GovKeeper),
|
||||
govsim.SimulateMsgDeposit(app.AccountKeeper, app.GovKeeper),
|
||||
},
|
||||
{
|
||||
func(_ *rand.Rand) int {
|
||||
var v int
|
||||
ap.GetOrGenerate(app.cdc, OpWeightMsgVote, &v, nil,
|
||||
func(_ *rand.Rand) {
|
||||
v = 100
|
||||
})
|
||||
return v
|
||||
}(nil),
|
||||
govsim.SimulateMsgVote(app.AccountKeeper, app.GovKeeper),
|
||||
},
|
||||
{
|
||||
func(_ *rand.Rand) int {
|
||||
|
@ -175,18 +174,18 @@ func testAndRunTxs(app *SimApp, config simulation.Config) []simulation.WeightedO
|
|||
})
|
||||
return v
|
||||
}(nil),
|
||||
stakingsimops.SimulateMsgCreateValidator(app.AccountKeeper, app.StakingKeeper),
|
||||
stakingsim.SimulateMsgCreateValidator(app.AccountKeeper, app.StakingKeeper),
|
||||
},
|
||||
{
|
||||
func(_ *rand.Rand) int {
|
||||
var v int
|
||||
ap.GetOrGenerate(app.cdc, OpWeightMsgEditValidator, &v, nil,
|
||||
func(_ *rand.Rand) {
|
||||
v = 5
|
||||
v = 20
|
||||
})
|
||||
return v
|
||||
}(nil),
|
||||
stakingsimops.SimulateMsgEditValidator(app.StakingKeeper),
|
||||
stakingsim.SimulateMsgEditValidator(app.AccountKeeper, app.StakingKeeper),
|
||||
},
|
||||
{
|
||||
func(_ *rand.Rand) int {
|
||||
|
@ -197,7 +196,7 @@ func testAndRunTxs(app *SimApp, config simulation.Config) []simulation.WeightedO
|
|||
})
|
||||
return v
|
||||
}(nil),
|
||||
stakingsimops.SimulateMsgDelegate(app.AccountKeeper, app.StakingKeeper),
|
||||
stakingsim.SimulateMsgDelegate(app.AccountKeeper, app.StakingKeeper),
|
||||
},
|
||||
{
|
||||
func(_ *rand.Rand) int {
|
||||
|
@ -208,7 +207,7 @@ func testAndRunTxs(app *SimApp, config simulation.Config) []simulation.WeightedO
|
|||
})
|
||||
return v
|
||||
}(nil),
|
||||
stakingsimops.SimulateMsgUndelegate(app.AccountKeeper, app.StakingKeeper),
|
||||
stakingsim.SimulateMsgUndelegate(app.AccountKeeper, app.StakingKeeper),
|
||||
},
|
||||
{
|
||||
func(_ *rand.Rand) int {
|
||||
|
@ -219,7 +218,7 @@ func testAndRunTxs(app *SimApp, config simulation.Config) []simulation.WeightedO
|
|||
})
|
||||
return v
|
||||
}(nil),
|
||||
stakingsimops.SimulateMsgBeginRedelegate(app.AccountKeeper, app.StakingKeeper),
|
||||
stakingsim.SimulateMsgBeginRedelegate(app.AccountKeeper, app.StakingKeeper),
|
||||
},
|
||||
{
|
||||
func(_ *rand.Rand) int {
|
||||
|
@ -230,7 +229,7 @@ func testAndRunTxs(app *SimApp, config simulation.Config) []simulation.WeightedO
|
|||
})
|
||||
return v
|
||||
}(nil),
|
||||
slashingsimops.SimulateMsgUnjail(app.SlashingKeeper),
|
||||
slashingsim.SimulateMsgUnjail(app.AccountKeeper, app.SlashingKeeper, app.StakingKeeper),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -254,6 +253,7 @@ func TestFullAppSimulation(t *testing.T) {
|
|||
|
||||
var logger log.Logger
|
||||
config := NewConfigFromFlags()
|
||||
config.ChainID = helpers.SimAppChainID
|
||||
|
||||
if FlagVerboseValue {
|
||||
logger = log.TestingLogger()
|
||||
|
@ -308,6 +308,7 @@ func TestAppImportExport(t *testing.T) {
|
|||
|
||||
var logger log.Logger
|
||||
config := NewConfigFromFlags()
|
||||
config.ChainID = helpers.SimAppChainID
|
||||
|
||||
if FlagVerboseValue {
|
||||
logger = log.TestingLogger()
|
||||
|
@ -414,7 +415,7 @@ func TestAppImportExport(t *testing.T) {
|
|||
require.Equal(t, len(failedKVAs), len(failedKVBs), "unequal sets of key-values to compare")
|
||||
|
||||
fmt.Printf("compared %d key/value pairs between %s and %s\n", len(failedKVAs), storeKeyA, storeKeyB)
|
||||
require.Len(t, failedKVAs, 0, GetSimulationLog(storeKeyA.Name(), app.sm.StoreDecoders, app.cdc, failedKVAs, failedKVBs))
|
||||
require.Equal(t, len(failedKVAs), 0, GetSimulationLog(storeKeyA.Name(), app.sm.StoreDecoders, app.cdc, failedKVAs, failedKVBs))
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -426,6 +427,7 @@ func TestAppSimulationAfterImport(t *testing.T) {
|
|||
|
||||
var logger log.Logger
|
||||
config := NewConfigFromFlags()
|
||||
config.ChainID = helpers.SimAppChainID
|
||||
|
||||
if FlagVerboseValue {
|
||||
logger = log.TestingLogger()
|
||||
|
@ -520,6 +522,7 @@ func TestAppStateDeterminism(t *testing.T) {
|
|||
config.ExportParamsPath = ""
|
||||
config.OnOperation = false
|
||||
config.AllInvariants = false
|
||||
config.ChainID = helpers.SimAppChainID
|
||||
|
||||
numSeeds := 3
|
||||
numTimesToRunPerSeed := 5
|
||||
|
@ -529,7 +532,13 @@ func TestAppStateDeterminism(t *testing.T) {
|
|||
config.Seed = rand.Int63()
|
||||
|
||||
for j := 0; j < numTimesToRunPerSeed; j++ {
|
||||
logger := log.NewNopLogger()
|
||||
var logger log.Logger
|
||||
if FlagVerboseValue {
|
||||
logger = log.TestingLogger()
|
||||
} else {
|
||||
logger = log.NewNopLogger()
|
||||
}
|
||||
|
||||
db := dbm.NewMemDB()
|
||||
|
||||
app := NewSimApp(logger, db, nil, true, FlagPeriodValue, interBlockCacheOpt())
|
||||
|
|
|
@ -35,6 +35,7 @@ func AppStateFn(cdc *codec.Codec, simManager *module.SimulationManager) simulati
|
|||
panic("cannot provide both a genesis file and a params file")
|
||||
|
||||
case config.GenesisFile != "":
|
||||
// override the default chain-id from simapp to set it later to the config
|
||||
genesisDoc, accounts := AppStateFromGenesisFileFn(r, cdc, config.GenesisFile)
|
||||
|
||||
if FlagGenesisTimeValue == 0 {
|
||||
|
@ -54,11 +55,11 @@ func AppStateFn(cdc *codec.Codec, simManager *module.SimulationManager) simulati
|
|||
}
|
||||
|
||||
cdc.MustUnmarshalJSON(bz, &appParams)
|
||||
appState, simAccs, chainID = AppStateRandomizedFn(simManager, r, cdc, accs, genesisTimestamp, appParams)
|
||||
appState, simAccs = AppStateRandomizedFn(simManager, r, cdc, accs, genesisTimestamp, appParams)
|
||||
|
||||
default:
|
||||
appParams := make(simulation.AppParams)
|
||||
appState, simAccs, chainID = AppStateRandomizedFn(simManager, r, cdc, accs, genesisTimestamp, appParams)
|
||||
appState, simAccs = AppStateRandomizedFn(simManager, r, cdc, accs, genesisTimestamp, appParams)
|
||||
}
|
||||
|
||||
return appState, simAccs, chainID, genesisTimestamp
|
||||
|
@ -70,7 +71,7 @@ func AppStateFn(cdc *codec.Codec, simManager *module.SimulationManager) simulati
|
|||
func AppStateRandomizedFn(
|
||||
simManager *module.SimulationManager, r *rand.Rand, cdc *codec.Codec,
|
||||
accs []simulation.Account, genesisTimestamp time.Time, appParams simulation.AppParams,
|
||||
) (json.RawMessage, []simulation.Account, string) {
|
||||
) (json.RawMessage, []simulation.Account) {
|
||||
numAccs := int64(len(accs))
|
||||
genesisState := NewDefaultGenesisState()
|
||||
|
||||
|
@ -83,7 +84,7 @@ func AppStateRandomizedFn(
|
|||
)
|
||||
appParams.GetOrGenerate(
|
||||
cdc, InitiallyBondedValidators, &numInitiallyBonded, r,
|
||||
func(r *rand.Rand) { numInitiallyBonded = int64(r.Intn(250)) },
|
||||
func(r *rand.Rand) { numInitiallyBonded = int64(r.Intn(300)) },
|
||||
)
|
||||
|
||||
if numInitiallyBonded > numAccs {
|
||||
|
@ -117,7 +118,7 @@ func AppStateRandomizedFn(
|
|||
panic(err)
|
||||
}
|
||||
|
||||
return appState, accs, "simulation"
|
||||
return appState, accs
|
||||
}
|
||||
|
||||
// AppStateFromGenesisFileFn util function to generate the genesis AppState
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
|
||||
bam "github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/simapp/helpers"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
|
||||
|
@ -107,33 +108,6 @@ func CheckBalance(t *testing.T, app *SimApp, addr sdk.AccAddress, exp sdk.Coins)
|
|||
require.Equal(t, exp, res.GetCoins())
|
||||
}
|
||||
|
||||
// GenTx generates a signed mock transaction.
|
||||
func GenTx(msgs []sdk.Msg, accnums []uint64, seq []uint64, priv ...crypto.PrivKey) auth.StdTx {
|
||||
// Make the transaction free
|
||||
fee := auth.StdFee{
|
||||
Amount: sdk.NewCoins(sdk.NewInt64Coin("foocoin", 0)),
|
||||
Gas: 100000,
|
||||
}
|
||||
|
||||
sigs := make([]auth.StdSignature, len(priv))
|
||||
memo := "testmemotestmemo"
|
||||
|
||||
for i, p := range priv {
|
||||
// use a empty chainID for ease of testing
|
||||
sig, err := p.Sign(auth.StdSignBytes("", accnums[i], seq[i], fee, msgs, memo))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
sigs[i] = auth.StdSignature{
|
||||
PubKey: p.PubKey(),
|
||||
Signature: sig,
|
||||
}
|
||||
}
|
||||
|
||||
return auth.NewStdTx(msgs, fee, sigs, memo)
|
||||
}
|
||||
|
||||
// SignCheckDeliver checks a generated signed transaction and simulates a
|
||||
// block commitment with the given transaction. A test assertion is made using
|
||||
// the parameter 'expPass' against the result. A corresponding result is
|
||||
|
@ -143,7 +117,14 @@ func SignCheckDeliver(
|
|||
accNums, seq []uint64, expSimPass, expPass bool, priv ...crypto.PrivKey,
|
||||
) sdk.Result {
|
||||
|
||||
tx := GenTx(msgs, accNums, seq, priv...)
|
||||
tx := helpers.GenTx(
|
||||
msgs,
|
||||
sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 0)},
|
||||
"",
|
||||
accNums,
|
||||
seq,
|
||||
priv...,
|
||||
)
|
||||
|
||||
txBytes, err := cdc.MarshalBinaryLengthPrefixed(tx)
|
||||
require.Nil(t, err)
|
||||
|
@ -176,10 +157,17 @@ func SignCheckDeliver(
|
|||
// GenSequenceOfTxs generates a set of signed transactions of messages, such
|
||||
// that they differ only by having the sequence numbers incremented between
|
||||
// every transaction.
|
||||
func GenSequenceOfTxs(msgs []sdk.Msg, accnums []uint64, initSeqNums []uint64, numToGenerate int, priv ...crypto.PrivKey) []auth.StdTx {
|
||||
func GenSequenceOfTxs(msgs []sdk.Msg, accNums []uint64, initSeqNums []uint64, numToGenerate int, priv ...crypto.PrivKey) []auth.StdTx {
|
||||
txs := make([]auth.StdTx, numToGenerate)
|
||||
for i := 0; i < numToGenerate; i++ {
|
||||
txs[i] = GenTx(msgs, accnums, initSeqNums, priv...)
|
||||
txs[i] = helpers.GenTx(
|
||||
msgs,
|
||||
sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 0)},
|
||||
"",
|
||||
accNums,
|
||||
initSeqNums,
|
||||
priv...,
|
||||
)
|
||||
incrementAllSequenceNumbers(initSeqNums)
|
||||
}
|
||||
|
||||
|
|
|
@ -30,8 +30,11 @@ func GenMaxMemoChars(r *rand.Rand) uint64 {
|
|||
}
|
||||
|
||||
// GenTxSigLimit randomized TxSigLimit
|
||||
// make sure that sigLimit is always high
|
||||
// so that arbitrarily simulated messages from other
|
||||
// modules can still create valid transactions
|
||||
func GenTxSigLimit(r *rand.Rand) uint64 {
|
||||
return uint64(r.Intn(7) + 1)
|
||||
return uint64(r.Intn(7) + 5)
|
||||
}
|
||||
|
||||
// GenTxSizeCostPerByte randomized TxSizeCostPerByte
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
package operations
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||
)
|
||||
|
||||
// SimulateDeductFee
|
||||
func SimulateDeductFee(ak auth.AccountKeeper, supplyKeeper types.SupplyKeeper) simulation.Operation {
|
||||
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
|
||||
accs []simulation.Account) (
|
||||
opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) {
|
||||
|
||||
account := simulation.RandomAcc(r, accs)
|
||||
stored := ak.GetAccount(ctx, account.Address)
|
||||
initCoins := stored.GetCoins()
|
||||
opMsg = simulation.NewOperationMsgBasic(types.ModuleName, "deduct_fee", "", false, nil)
|
||||
|
||||
feeCollector := ak.GetAccount(ctx, supplyKeeper.GetModuleAddress(types.FeeCollectorName))
|
||||
if feeCollector == nil {
|
||||
panic(fmt.Errorf("fee collector account hasn't been set"))
|
||||
}
|
||||
|
||||
if len(initCoins) == 0 {
|
||||
return opMsg, nil, nil
|
||||
}
|
||||
|
||||
denomIndex := r.Intn(len(initCoins))
|
||||
randCoin := initCoins[denomIndex]
|
||||
|
||||
amt, err := randPositiveInt(r, randCoin.Amount)
|
||||
if err != nil {
|
||||
return opMsg, nil, nil
|
||||
}
|
||||
|
||||
// Create a random fee and verify the fees are within the account's spendable
|
||||
// balance.
|
||||
fees := sdk.NewCoins(sdk.NewCoin(randCoin.Denom, amt))
|
||||
spendableCoins := stored.SpendableCoins(ctx.BlockHeader().Time)
|
||||
if _, hasNeg := spendableCoins.SafeSub(fees); hasNeg {
|
||||
return opMsg, nil, nil
|
||||
}
|
||||
|
||||
// get the new account balance
|
||||
_, hasNeg := initCoins.SafeSub(fees)
|
||||
if hasNeg {
|
||||
return opMsg, nil, nil
|
||||
}
|
||||
|
||||
err = supplyKeeper.SendCoinsFromAccountToModule(ctx, stored.GetAddress(), types.FeeCollectorName, fees)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
opMsg.OK = true
|
||||
return opMsg, nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func randPositiveInt(r *rand.Rand, max sdk.Int) (sdk.Int, error) {
|
||||
if !max.GT(sdk.OneInt()) {
|
||||
return sdk.Int{}, errors.New("max too small")
|
||||
}
|
||||
max = max.Sub(sdk.OneInt())
|
||||
return sdk.NewIntFromBigInt(new(big.Int).Rand(r, max.BigInt())).Add(sdk.OneInt()), nil
|
||||
}
|
|
@ -18,7 +18,7 @@ const (
|
|||
|
||||
// GenSendEnabled randomized SendEnabled
|
||||
func GenSendEnabled(r *rand.Rand) bool {
|
||||
return r.Int63n(2) == 0
|
||||
return r.Int63n(101) <= 95 // 95% chance of transfers being enabled
|
||||
}
|
||||
|
||||
// RandomizedGenState generates a random GenesisState for bank
|
||||
|
|
|
@ -0,0 +1,252 @@
|
|||
package simulation
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/rand"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/simapp/helpers"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank/internal/keeper"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank/internal/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||
)
|
||||
|
||||
// SimulateMsgSend tests and runs a single msg send where both
|
||||
// accounts already exist.
|
||||
// nolint: funlen
|
||||
func SimulateMsgSend(ak types.AccountKeeper, bk keeper.Keeper) simulation.Operation {
|
||||
return func(
|
||||
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
|
||||
accs []simulation.Account, chainID string,
|
||||
) (simulation.OperationMsg, []simulation.FutureOperation, error) {
|
||||
|
||||
if !bk.GetSendEnabled(ctx) {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
||||
}
|
||||
|
||||
simAccount, toSimAcc, coins, skip, err := randomSendFields(r, ctx, accs, ak)
|
||||
if err != nil {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, err
|
||||
}
|
||||
|
||||
if skip {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
||||
}
|
||||
|
||||
msg := types.NewMsgSend(simAccount.Address, toSimAcc.Address, coins)
|
||||
|
||||
err = sendMsgSend(r, app, ak, msg, ctx, chainID, []crypto.PrivKey{simAccount.PrivKey})
|
||||
if err != nil {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, err
|
||||
}
|
||||
|
||||
return simulation.NewOperationMsg(msg, true, ""), nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// sendMsgSend sends a transaction with a MsgSend from a provided random account.
|
||||
func sendMsgSend(
|
||||
r *rand.Rand, app *baseapp.BaseApp, ak types.AccountKeeper,
|
||||
msg types.MsgSend, ctx sdk.Context, chainID string, privkeys []crypto.PrivKey,
|
||||
) (err error) {
|
||||
|
||||
account := ak.GetAccount(ctx, msg.FromAddress)
|
||||
coins := account.SpendableCoins(ctx.BlockTime())
|
||||
|
||||
var fees sdk.Coins
|
||||
coins, hasNeg := coins.SafeSub(msg.Amount)
|
||||
if !hasNeg {
|
||||
fees, err = simulation.RandomFees(r, ctx, coins)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
tx := helpers.GenTx(
|
||||
[]sdk.Msg{msg},
|
||||
fees,
|
||||
chainID,
|
||||
[]uint64{account.GetAccountNumber()},
|
||||
[]uint64{account.GetSequence()},
|
||||
privkeys...,
|
||||
)
|
||||
|
||||
res := app.Deliver(tx)
|
||||
if !res.IsOK() {
|
||||
return errors.New(res.Log)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SimulateMsgMultiSend tests and runs a single msg multisend, with randomized, capped number of inputs/outputs.
|
||||
// all accounts in msg fields exist in state
|
||||
// nolint: funlen
|
||||
func SimulateMsgMultiSend(ak types.AccountKeeper, bk keeper.Keeper) simulation.Operation {
|
||||
return func(
|
||||
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
|
||||
accs []simulation.Account, chainID string,
|
||||
) (simulation.OperationMsg, []simulation.FutureOperation, error) {
|
||||
|
||||
if !bk.GetSendEnabled(ctx) {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
||||
}
|
||||
|
||||
// random number of inputs/outputs between [1, 3]
|
||||
inputs := make([]types.Input, r.Intn(3)+1)
|
||||
outputs := make([]types.Output, r.Intn(3)+1)
|
||||
|
||||
// collect signer privKeys
|
||||
privs := make([]crypto.PrivKey, len(inputs))
|
||||
|
||||
// use map to check if address already exists as input
|
||||
usedAddrs := make(map[string]bool)
|
||||
|
||||
var totalSentCoins sdk.Coins
|
||||
for i := range inputs {
|
||||
// generate random input fields, ignore to address
|
||||
simAccount, _, coins, skip, err := randomSendFields(r, ctx, accs, ak)
|
||||
|
||||
// make sure account is fresh and not used in previous input
|
||||
for usedAddrs[simAccount.Address.String()] {
|
||||
simAccount, _, coins, skip, err = randomSendFields(r, ctx, accs, ak)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, err
|
||||
}
|
||||
if skip {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
||||
}
|
||||
|
||||
// set input address in used address map
|
||||
usedAddrs[simAccount.Address.String()] = true
|
||||
|
||||
// set signer privkey
|
||||
privs[i] = simAccount.PrivKey
|
||||
|
||||
// set next input and accumulate total sent coins
|
||||
inputs[i] = types.NewInput(simAccount.Address, coins)
|
||||
totalSentCoins = totalSentCoins.Add(coins)
|
||||
}
|
||||
|
||||
for o := range outputs {
|
||||
outAddr, _ := simulation.RandomAcc(r, accs)
|
||||
|
||||
var outCoins sdk.Coins
|
||||
// split total sent coins into random subsets for output
|
||||
if o == len(outputs)-1 {
|
||||
outCoins = totalSentCoins
|
||||
} else {
|
||||
// take random subset of remaining coins for output
|
||||
// and update remaining coins
|
||||
outCoins = simulation.RandSubsetCoins(r, totalSentCoins)
|
||||
totalSentCoins = totalSentCoins.Sub(outCoins)
|
||||
}
|
||||
|
||||
outputs[o] = types.NewOutput(outAddr.Address, outCoins)
|
||||
}
|
||||
|
||||
// remove any output that has no coins
|
||||
i := 0
|
||||
for i < len(outputs) {
|
||||
if outputs[i].Coins.Empty() {
|
||||
outputs[i] = outputs[len(outputs)-1]
|
||||
outputs = outputs[:len(outputs)-1]
|
||||
} else {
|
||||
// continue onto next coin
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
msg := types.MsgMultiSend{
|
||||
Inputs: inputs,
|
||||
Outputs: outputs,
|
||||
}
|
||||
|
||||
err := sendMsgMultiSend(r, app, ak, msg, ctx, chainID, privs)
|
||||
if err != nil {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, err
|
||||
}
|
||||
|
||||
return simulation.NewOperationMsg(msg, true, ""), nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// sendMsgMultiSend sends a transaction with a MsgMultiSend from a provided random
|
||||
// account.
|
||||
func sendMsgMultiSend(
|
||||
r *rand.Rand, app *baseapp.BaseApp, ak types.AccountKeeper,
|
||||
msg types.MsgMultiSend, ctx sdk.Context, chainID string, privkeys []crypto.PrivKey,
|
||||
) (err error) {
|
||||
|
||||
accountNumbers := make([]uint64, len(msg.Inputs))
|
||||
sequenceNumbers := make([]uint64, len(msg.Inputs))
|
||||
|
||||
for i := 0; i < len(msg.Inputs); i++ {
|
||||
acc := ak.GetAccount(ctx, msg.Inputs[i].Address)
|
||||
accountNumbers[i] = acc.GetAccountNumber()
|
||||
sequenceNumbers[i] = acc.GetSequence()
|
||||
}
|
||||
|
||||
// feePayer is the first signer, i.e. first input address
|
||||
feePayer := ak.GetAccount(ctx, msg.Inputs[0].Address)
|
||||
coins := feePayer.SpendableCoins(ctx.BlockTime())
|
||||
|
||||
var fees sdk.Coins
|
||||
coins, hasNeg := coins.SafeSub(msg.Inputs[0].Coins)
|
||||
if !hasNeg {
|
||||
fees, err = simulation.RandomFees(r, ctx, coins)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
tx := helpers.GenTx(
|
||||
[]sdk.Msg{msg},
|
||||
fees,
|
||||
chainID,
|
||||
accountNumbers,
|
||||
sequenceNumbers,
|
||||
privkeys...,
|
||||
)
|
||||
|
||||
res := app.Deliver(tx)
|
||||
if !res.IsOK() {
|
||||
return errors.New(res.Log)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// randomSendFields returns the sender and recipient simulation accounts as well
|
||||
// as the transferred amount.
|
||||
func randomSendFields(
|
||||
r *rand.Rand, ctx sdk.Context, accs []simulation.Account, ak types.AccountKeeper,
|
||||
) (simulation.Account, simulation.Account, sdk.Coins, bool, error) {
|
||||
|
||||
simAccount, _ := simulation.RandomAcc(r, accs)
|
||||
toSimAcc, _ := simulation.RandomAcc(r, accs)
|
||||
|
||||
// disallow sending money to yourself
|
||||
for simAccount.PubKey.Equals(toSimAcc.PubKey) {
|
||||
toSimAcc, _ = simulation.RandomAcc(r, accs)
|
||||
}
|
||||
|
||||
acc := ak.GetAccount(ctx, simAccount.Address)
|
||||
if acc == nil {
|
||||
return simAccount, toSimAcc, nil, true, nil // skip error
|
||||
}
|
||||
|
||||
coins := acc.SpendableCoins(ctx.BlockHeader().Time)
|
||||
|
||||
sendCoins := simulation.RandSubsetCoins(r, coins)
|
||||
if sendCoins.Empty() {
|
||||
return simAccount, toSimAcc, nil, true, nil // skip error
|
||||
}
|
||||
|
||||
return simAccount, toSimAcc, sendCoins, false, nil
|
||||
}
|
|
@ -1,218 +0,0 @@
|
|||
package operations
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank/internal/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/mock"
|
||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||
)
|
||||
|
||||
// SimulateMsgSend tests and runs a single msg send where both
|
||||
// accounts already exist.
|
||||
func SimulateMsgSend(mapper types.AccountKeeper, bk bank.Keeper) simulation.Operation {
|
||||
handler := bank.NewHandler(bk)
|
||||
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account) (
|
||||
opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) {
|
||||
|
||||
fromAcc, comment, msg, ok := createMsgSend(r, ctx, accs, mapper)
|
||||
opMsg = simulation.NewOperationMsg(msg, ok, comment)
|
||||
if !ok {
|
||||
return opMsg, nil, nil
|
||||
}
|
||||
err = sendAndVerifyMsgSend(app, mapper, msg, ctx, []crypto.PrivKey{fromAcc.PrivKey}, handler)
|
||||
if err != nil {
|
||||
return opMsg, nil, err
|
||||
}
|
||||
return opMsg, nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func createMsgSend(r *rand.Rand, ctx sdk.Context, accs []simulation.Account, mapper types.AccountKeeper) (
|
||||
fromAcc simulation.Account, comment string, msg types.MsgSend, ok bool) {
|
||||
|
||||
fromAcc = simulation.RandomAcc(r, accs)
|
||||
toAcc := simulation.RandomAcc(r, accs)
|
||||
// Disallow sending money to yourself
|
||||
for {
|
||||
if !fromAcc.PubKey.Equals(toAcc.PubKey) {
|
||||
break
|
||||
}
|
||||
toAcc = simulation.RandomAcc(r, accs)
|
||||
}
|
||||
initFromCoins := mapper.GetAccount(ctx, fromAcc.Address).SpendableCoins(ctx.BlockHeader().Time)
|
||||
|
||||
if len(initFromCoins) == 0 {
|
||||
return fromAcc, "skipping, no coins at all", msg, false
|
||||
}
|
||||
|
||||
denomIndex := r.Intn(len(initFromCoins))
|
||||
amt, goErr := simulation.RandPositiveInt(r, initFromCoins[denomIndex].Amount)
|
||||
if goErr != nil {
|
||||
return fromAcc, "skipping bank send due to account having no coins of denomination " + initFromCoins[denomIndex].Denom, msg, false
|
||||
}
|
||||
|
||||
coins := sdk.Coins{sdk.NewCoin(initFromCoins[denomIndex].Denom, amt)}
|
||||
msg = types.NewMsgSend(fromAcc.Address, toAcc.Address, coins)
|
||||
return fromAcc, "", msg, true
|
||||
}
|
||||
|
||||
// Sends and verifies the transition of a msg send.
|
||||
func sendAndVerifyMsgSend(app *baseapp.BaseApp, mapper types.AccountKeeper, msg types.MsgSend, ctx sdk.Context, privkeys []crypto.PrivKey, handler sdk.Handler) error {
|
||||
fromAcc := mapper.GetAccount(ctx, msg.FromAddress)
|
||||
AccountNumbers := []uint64{fromAcc.GetAccountNumber()}
|
||||
SequenceNumbers := []uint64{fromAcc.GetSequence()}
|
||||
initialFromAddrCoins := fromAcc.GetCoins()
|
||||
|
||||
toAcc := mapper.GetAccount(ctx, msg.ToAddress)
|
||||
initialToAddrCoins := toAcc.GetCoins()
|
||||
|
||||
if handler != nil {
|
||||
res := handler(ctx, msg)
|
||||
if !res.IsOK() {
|
||||
if res.Code == types.CodeSendDisabled {
|
||||
return nil
|
||||
}
|
||||
// TODO: Do this in a more 'canonical' way
|
||||
return fmt.Errorf("handling msg failed %v", res)
|
||||
}
|
||||
} else {
|
||||
tx := mock.GenTx([]sdk.Msg{msg},
|
||||
AccountNumbers,
|
||||
SequenceNumbers,
|
||||
privkeys...)
|
||||
res := app.Deliver(tx)
|
||||
if !res.IsOK() {
|
||||
// TODO: Do this in a more 'canonical' way
|
||||
return fmt.Errorf("deliver failed %v", res)
|
||||
}
|
||||
}
|
||||
|
||||
fromAcc = mapper.GetAccount(ctx, msg.FromAddress)
|
||||
toAcc = mapper.GetAccount(ctx, msg.ToAddress)
|
||||
|
||||
if !initialFromAddrCoins.Sub(msg.Amount).IsEqual(fromAcc.GetCoins()) {
|
||||
return fmt.Errorf("fromAddress %s had an incorrect amount of coins", fromAcc.GetAddress())
|
||||
}
|
||||
|
||||
if !initialToAddrCoins.Add(msg.Amount).IsEqual(toAcc.GetCoins()) {
|
||||
return fmt.Errorf("toAddress %s had an incorrect amount of coins", toAcc.GetAddress())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SimulateSingleInputMsgMultiSend tests and runs a single msg multisend, with one input and one output, where both
|
||||
// accounts already exist.
|
||||
func SimulateSingleInputMsgMultiSend(mapper types.AccountKeeper, bk bank.Keeper) simulation.Operation {
|
||||
handler := bank.NewHandler(bk)
|
||||
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account) (
|
||||
opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) {
|
||||
|
||||
fromAcc, comment, msg, ok := createSingleInputMsgMultiSend(r, ctx, accs, mapper)
|
||||
opMsg = simulation.NewOperationMsg(msg, ok, comment)
|
||||
if !ok {
|
||||
return opMsg, nil, nil
|
||||
}
|
||||
err = sendAndVerifyMsgMultiSend(app, mapper, msg, ctx, []crypto.PrivKey{fromAcc.PrivKey}, handler)
|
||||
if err != nil {
|
||||
return opMsg, nil, err
|
||||
}
|
||||
return opMsg, nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func createSingleInputMsgMultiSend(r *rand.Rand, ctx sdk.Context, accs []simulation.Account, mapper types.AccountKeeper) (
|
||||
fromAcc simulation.Account, comment string, msg types.MsgMultiSend, ok bool) {
|
||||
|
||||
fromAcc = simulation.RandomAcc(r, accs)
|
||||
toAcc := simulation.RandomAcc(r, accs)
|
||||
// Disallow sending money to yourself
|
||||
for {
|
||||
if !fromAcc.PubKey.Equals(toAcc.PubKey) {
|
||||
break
|
||||
}
|
||||
toAcc = simulation.RandomAcc(r, accs)
|
||||
}
|
||||
toAddr := toAcc.Address
|
||||
initFromCoins := mapper.GetAccount(ctx, fromAcc.Address).SpendableCoins(ctx.BlockHeader().Time)
|
||||
|
||||
if len(initFromCoins) == 0 {
|
||||
return fromAcc, "skipping, no coins at all", msg, false
|
||||
}
|
||||
|
||||
denomIndex := r.Intn(len(initFromCoins))
|
||||
amt, goErr := simulation.RandPositiveInt(r, initFromCoins[denomIndex].Amount)
|
||||
if goErr != nil {
|
||||
return fromAcc, "skipping bank send due to account having no coins of denomination " + initFromCoins[denomIndex].Denom, msg, false
|
||||
}
|
||||
|
||||
coins := sdk.Coins{sdk.NewCoin(initFromCoins[denomIndex].Denom, amt)}
|
||||
msg = types.MsgMultiSend{
|
||||
Inputs: []types.Input{types.NewInput(fromAcc.Address, coins)},
|
||||
Outputs: []types.Output{types.NewOutput(toAddr, coins)},
|
||||
}
|
||||
return fromAcc, "", msg, true
|
||||
}
|
||||
|
||||
// Sends and verifies the transition of a msg multisend. This fails if there are repeated inputs or outputs
|
||||
// pass in handler as nil to handle txs, otherwise handle msgs
|
||||
func sendAndVerifyMsgMultiSend(app *baseapp.BaseApp, mapper types.AccountKeeper, msg types.MsgMultiSend,
|
||||
ctx sdk.Context, privkeys []crypto.PrivKey, handler sdk.Handler) error {
|
||||
|
||||
initialInputAddrCoins := make([]sdk.Coins, len(msg.Inputs))
|
||||
initialOutputAddrCoins := make([]sdk.Coins, len(msg.Outputs))
|
||||
AccountNumbers := make([]uint64, len(msg.Inputs))
|
||||
SequenceNumbers := make([]uint64, len(msg.Inputs))
|
||||
|
||||
for i := 0; i < len(msg.Inputs); i++ {
|
||||
acc := mapper.GetAccount(ctx, msg.Inputs[i].Address)
|
||||
AccountNumbers[i] = acc.GetAccountNumber()
|
||||
SequenceNumbers[i] = acc.GetSequence()
|
||||
initialInputAddrCoins[i] = acc.GetCoins()
|
||||
}
|
||||
for i := 0; i < len(msg.Outputs); i++ {
|
||||
acc := mapper.GetAccount(ctx, msg.Outputs[i].Address)
|
||||
initialOutputAddrCoins[i] = acc.GetCoins()
|
||||
}
|
||||
if handler != nil {
|
||||
res := handler(ctx, msg)
|
||||
if !res.IsOK() {
|
||||
if res.Code == types.CodeSendDisabled {
|
||||
return nil
|
||||
}
|
||||
// TODO: Do this in a more 'canonical' way
|
||||
return fmt.Errorf("handling msg failed %v", res)
|
||||
}
|
||||
} else {
|
||||
tx := mock.GenTx([]sdk.Msg{msg},
|
||||
AccountNumbers,
|
||||
SequenceNumbers,
|
||||
privkeys...)
|
||||
res := app.Deliver(tx)
|
||||
if !res.IsOK() {
|
||||
// TODO: Do this in a more 'canonical' way
|
||||
return fmt.Errorf("deliver failed %v", res)
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < len(msg.Inputs); i++ {
|
||||
terminalInputCoins := mapper.GetAccount(ctx, msg.Inputs[i].Address).GetCoins()
|
||||
if !initialInputAddrCoins[i].Sub(msg.Inputs[i].Coins).IsEqual(terminalInputCoins) {
|
||||
return fmt.Errorf("input #%d had an incorrect amount of coins", i)
|
||||
}
|
||||
}
|
||||
for i := 0; i < len(msg.Outputs); i++ {
|
||||
terminalOutputCoins := mapper.GetAccount(ctx, msg.Outputs[i].Address).GetCoins()
|
||||
if !terminalOutputCoins.IsEqual(initialOutputAddrCoins[i].Add(msg.Outputs[i].Coins)) {
|
||||
return fmt.Errorf("output #%d had an incorrect amount of coins", i)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -7,7 +7,7 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/x/distribution/keeper"
|
||||
)
|
||||
|
||||
// set the proposer for determining distribution during endblock
|
||||
// BeginBlocker sets the proposer for determining distribution during endblock
|
||||
// and distribute rewards for the previous block
|
||||
func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k keeper.Keeper) {
|
||||
// determine the total power signing the block
|
||||
|
|
|
@ -18,6 +18,7 @@ const (
|
|||
CommunityTax = "community_tax"
|
||||
BaseProposerReward = "base_proposer_reward"
|
||||
BonusProposerReward = "bonus_proposer_reward"
|
||||
WithdrawEnabled = "withdraw_enabled"
|
||||
)
|
||||
|
||||
// GenCommunityTax randomized CommunityTax
|
||||
|
@ -35,6 +36,11 @@ func GenBonusProposerReward(r *rand.Rand) sdk.Dec {
|
|||
return sdk.NewDecWithPrec(1, 2).Add(sdk.NewDecWithPrec(int64(r.Intn(30)), 2))
|
||||
}
|
||||
|
||||
// GenWithdrawEnabled returns a randomized WithdrawEnabled parameter.
|
||||
func GenWithdrawEnabled(r *rand.Rand) bool {
|
||||
return r.Int63n(101) <= 95 // 95% chance of withdraws being enabled
|
||||
}
|
||||
|
||||
// RandomizedGenState generates a random GenesisState for distribution
|
||||
func RandomizedGenState(simState *module.SimulationState) {
|
||||
var communityTax sdk.Dec
|
||||
|
@ -55,11 +61,18 @@ func RandomizedGenState(simState *module.SimulationState) {
|
|||
func(r *rand.Rand) { bonusProposerReward = GenBonusProposerReward(r) },
|
||||
)
|
||||
|
||||
var withdrawEnabled bool
|
||||
simState.AppParams.GetOrGenerate(
|
||||
simState.Cdc, WithdrawEnabled, &withdrawEnabled, simState.Rand,
|
||||
func(r *rand.Rand) { withdrawEnabled = GenWithdrawEnabled(r) },
|
||||
)
|
||||
|
||||
distrGenesis := types.GenesisState{
|
||||
FeePool: types.InitialFeePool(),
|
||||
CommunityTax: communityTax,
|
||||
BaseProposerReward: baseProposerReward,
|
||||
BonusProposerReward: bonusProposerReward,
|
||||
WithdrawAddrEnabled: withdrawEnabled,
|
||||
}
|
||||
|
||||
fmt.Printf("Selected randomly generated distribution parameters:\n%s\n", codec.MustMarshalJSONIndent(simState.Cdc, distrGenesis))
|
||||
|
|
|
@ -0,0 +1,176 @@
|
|||
package simulation
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/simapp/helpers"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/distribution/keeper"
|
||||
"github.com/cosmos/cosmos-sdk/x/distribution/types"
|
||||
govsim "github.com/cosmos/cosmos-sdk/x/gov/simulation"
|
||||
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
|
||||
)
|
||||
|
||||
// SimulateMsgSetWithdrawAddress generates a MsgSetWithdrawAddress with random values.
|
||||
// nolint: funlen
|
||||
func SimulateMsgSetWithdrawAddress(ak types.AccountKeeper, k keeper.Keeper) simulation.Operation {
|
||||
return func(
|
||||
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string,
|
||||
) (simulation.OperationMsg, []simulation.FutureOperation, error) {
|
||||
|
||||
if !k.GetWithdrawAddrEnabled(ctx) {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
||||
}
|
||||
|
||||
simAccount, _ := simulation.RandomAcc(r, accs)
|
||||
simToAccount, _ := simulation.RandomAcc(r, accs)
|
||||
account := ak.GetAccount(ctx, simAccount.Address)
|
||||
|
||||
fees, err := simulation.RandomFees(r, ctx, account.SpendableCoins(ctx.BlockTime()))
|
||||
if err != nil {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, err
|
||||
}
|
||||
|
||||
msg := types.NewMsgSetWithdrawAddress(simAccount.Address, simToAccount.Address)
|
||||
|
||||
tx := helpers.GenTx(
|
||||
[]sdk.Msg{msg},
|
||||
fees,
|
||||
chainID,
|
||||
[]uint64{account.GetAccountNumber()},
|
||||
[]uint64{account.GetSequence()},
|
||||
simAccount.PrivKey,
|
||||
)
|
||||
|
||||
res := app.Deliver(tx)
|
||||
if !res.IsOK() {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, errors.New(res.Log)
|
||||
}
|
||||
|
||||
return simulation.NewOperationMsg(msg, true, ""), nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// SimulateMsgWithdrawDelegatorReward generates a MsgWithdrawDelegatorReward with random values.
|
||||
// nolint: funlen
|
||||
func SimulateMsgWithdrawDelegatorReward(ak types.AccountKeeper, k keeper.Keeper, sk stakingkeeper.Keeper) simulation.Operation {
|
||||
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string,
|
||||
) (simulation.OperationMsg, []simulation.FutureOperation, error) {
|
||||
|
||||
simAccount, _ := simulation.RandomAcc(r, accs)
|
||||
delegations := sk.GetAllDelegatorDelegations(ctx, simAccount.Address)
|
||||
if len(delegations) == 0 {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
||||
}
|
||||
|
||||
delegation := delegations[r.Intn(len(delegations))]
|
||||
|
||||
validator := sk.Validator(ctx, delegation.GetValidatorAddr())
|
||||
if validator == nil {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, fmt.Errorf("validator %s not found", delegation.GetValidatorAddr())
|
||||
}
|
||||
|
||||
account := ak.GetAccount(ctx, simAccount.Address)
|
||||
fees, err := simulation.RandomFees(r, ctx, account.SpendableCoins(ctx.BlockTime()))
|
||||
if err != nil {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, err
|
||||
}
|
||||
|
||||
msg := types.NewMsgWithdrawDelegatorReward(simAccount.Address, validator.GetOperator())
|
||||
|
||||
tx := helpers.GenTx(
|
||||
[]sdk.Msg{msg},
|
||||
fees,
|
||||
chainID,
|
||||
[]uint64{account.GetAccountNumber()},
|
||||
[]uint64{account.GetSequence()},
|
||||
simAccount.PrivKey,
|
||||
)
|
||||
|
||||
res := app.Deliver(tx)
|
||||
if !res.IsOK() {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, errors.New(res.Log)
|
||||
}
|
||||
|
||||
return simulation.NewOperationMsg(msg, true, ""), nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// SimulateMsgWithdrawValidatorCommission generates a MsgWithdrawValidatorCommission with random values.
|
||||
// nolint: funlen
|
||||
func SimulateMsgWithdrawValidatorCommission(ak types.AccountKeeper, k keeper.Keeper, sk stakingkeeper.Keeper) simulation.Operation {
|
||||
return func(
|
||||
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string,
|
||||
) (simulation.OperationMsg, []simulation.FutureOperation, error) {
|
||||
|
||||
validator, ok := stakingkeeper.RandomValidator(r, sk, ctx)
|
||||
if !ok {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
||||
}
|
||||
|
||||
commission := k.GetValidatorAccumulatedCommission(ctx, validator.GetOperator())
|
||||
if commission.IsZero() {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
||||
}
|
||||
|
||||
simAccount, found := simulation.FindAccount(accs, sdk.AccAddress(validator.GetOperator()))
|
||||
if !found {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, fmt.Errorf("validator %s not found", validator.GetOperator())
|
||||
}
|
||||
|
||||
account := ak.GetAccount(ctx, simAccount.Address)
|
||||
fees, err := simulation.RandomFees(r, ctx, account.SpendableCoins(ctx.BlockTime()))
|
||||
if err != nil {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, err
|
||||
}
|
||||
|
||||
msg := types.NewMsgWithdrawValidatorCommission(validator.GetOperator())
|
||||
|
||||
tx := helpers.GenTx(
|
||||
[]sdk.Msg{msg},
|
||||
fees,
|
||||
chainID,
|
||||
[]uint64{account.GetAccountNumber()},
|
||||
[]uint64{account.GetSequence()},
|
||||
simAccount.PrivKey,
|
||||
)
|
||||
|
||||
res := app.Deliver(tx)
|
||||
if !res.IsOK() {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, errors.New(res.Log)
|
||||
}
|
||||
|
||||
return simulation.NewOperationMsg(msg, true, ""), nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// SimulateCommunityPoolSpendProposalContent generates random community-pool-spend proposal content
|
||||
// nolint: funlen
|
||||
func SimulateCommunityPoolSpendProposalContent(k keeper.Keeper) govsim.ContentSimulator {
|
||||
return func(r *rand.Rand, ctx sdk.Context, accs []simulation.Account) govtypes.Content {
|
||||
simAccount, _ := simulation.RandomAcc(r, accs)
|
||||
|
||||
balance := k.GetFeePool(ctx).CommunityPool
|
||||
if balance.Empty() {
|
||||
return nil
|
||||
}
|
||||
|
||||
denomIndex := r.Intn(len(balance))
|
||||
amount, err := simulation.RandPositiveInt(r, balance[denomIndex].Amount.TruncateInt())
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return types.NewCommunityPoolSpendProposal(
|
||||
simulation.RandStringOfLength(r, 10),
|
||||
simulation.RandStringOfLength(r, 100),
|
||||
simAccount.Address,
|
||||
sdk.NewCoins(sdk.NewCoin(balance[denomIndex].Denom, amount)),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,114 +0,0 @@
|
|||
package operations
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/distribution"
|
||||
govsimops "github.com/cosmos/cosmos-sdk/x/gov/simulation/operations"
|
||||
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||
)
|
||||
|
||||
// SimulateMsgSetWithdrawAddress generates a MsgSetWithdrawAddress with random values.
|
||||
func SimulateMsgSetWithdrawAddress(k distribution.Keeper) simulation.Operation {
|
||||
handler := distribution.NewHandler(k)
|
||||
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
|
||||
accs []simulation.Account) (opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) {
|
||||
|
||||
accountOrigin := simulation.RandomAcc(r, accs)
|
||||
accountDestination := simulation.RandomAcc(r, accs)
|
||||
msg := distribution.NewMsgSetWithdrawAddress(accountOrigin.Address, accountDestination.Address)
|
||||
|
||||
if msg.ValidateBasic() != nil {
|
||||
return simulation.NoOpMsg(distribution.ModuleName), nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
|
||||
}
|
||||
|
||||
ctx, write := ctx.CacheContext()
|
||||
ok := handler(ctx, msg).IsOK()
|
||||
if ok {
|
||||
write()
|
||||
}
|
||||
|
||||
opMsg = simulation.NewOperationMsg(msg, ok, "")
|
||||
return opMsg, nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// SimulateMsgWithdrawDelegatorReward generates a MsgWithdrawDelegatorReward with random values.
|
||||
func SimulateMsgWithdrawDelegatorReward(k distribution.Keeper) simulation.Operation {
|
||||
handler := distribution.NewHandler(k)
|
||||
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
|
||||
accs []simulation.Account) (opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) {
|
||||
|
||||
delegatorAccount := simulation.RandomAcc(r, accs)
|
||||
validatorAccount := simulation.RandomAcc(r, accs)
|
||||
msg := distribution.NewMsgWithdrawDelegatorReward(delegatorAccount.Address, sdk.ValAddress(validatorAccount.Address))
|
||||
|
||||
if msg.ValidateBasic() != nil {
|
||||
return simulation.NoOpMsg(distribution.ModuleName), nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
|
||||
}
|
||||
|
||||
ctx, write := ctx.CacheContext()
|
||||
ok := handler(ctx, msg).IsOK()
|
||||
if ok {
|
||||
write()
|
||||
}
|
||||
|
||||
opMsg = simulation.NewOperationMsg(msg, ok, "")
|
||||
return opMsg, nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// SimulateMsgWithdrawValidatorCommission generates a MsgWithdrawValidatorCommission with random values.
|
||||
func SimulateMsgWithdrawValidatorCommission(k distribution.Keeper) simulation.Operation {
|
||||
handler := distribution.NewHandler(k)
|
||||
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
|
||||
accs []simulation.Account) (opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) {
|
||||
|
||||
account := simulation.RandomAcc(r, accs)
|
||||
msg := distribution.NewMsgWithdrawValidatorCommission(sdk.ValAddress(account.Address))
|
||||
|
||||
if msg.ValidateBasic() != nil {
|
||||
return simulation.NoOpMsg(distribution.ModuleName), nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
|
||||
}
|
||||
|
||||
ctx, write := ctx.CacheContext()
|
||||
ok := handler(ctx, msg).IsOK()
|
||||
if ok {
|
||||
write()
|
||||
}
|
||||
|
||||
opMsg = simulation.NewOperationMsg(msg, ok, "")
|
||||
return opMsg, nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// SimulateCommunityPoolSpendProposalContent generates random community-pool-spend proposal content
|
||||
func SimulateCommunityPoolSpendProposalContent(k distribution.Keeper) govsimops.ContentSimulator {
|
||||
return func(r *rand.Rand, ctx sdk.Context, accs []simulation.Account) govtypes.Content {
|
||||
var coins sdk.Coins
|
||||
|
||||
recipientAcc := simulation.RandomAcc(r, accs)
|
||||
balance := k.GetFeePool(ctx).CommunityPool
|
||||
|
||||
if len(balance) > 0 {
|
||||
denomIndex := r.Intn(len(balance))
|
||||
amount, err := simulation.RandPositiveInt(r, balance[denomIndex].Amount.TruncateInt())
|
||||
|
||||
if err == nil {
|
||||
denom := balance[denomIndex].Denom
|
||||
coins = sdk.NewCoins(sdk.NewCoin(denom, amount.Mul(sdk.NewInt(2))))
|
||||
}
|
||||
}
|
||||
|
||||
return distribution.NewCommunityPoolSpendProposal(
|
||||
simulation.RandStringOfLength(r, 10),
|
||||
simulation.RandStringOfLength(r, 100),
|
||||
recipientAcc.Address,
|
||||
coins,
|
||||
)
|
||||
}
|
||||
}
|
|
@ -2,11 +2,17 @@ package types
|
|||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking"
|
||||
stakingexported "github.com/cosmos/cosmos-sdk/x/staking/exported"
|
||||
supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported"
|
||||
)
|
||||
|
||||
// AccountKeeper defines the expected account keeper used for simulations (noalias)
|
||||
type AccountKeeper interface {
|
||||
GetAccount(ctx sdk.Context, addr sdk.AccAddress) authexported.Account
|
||||
}
|
||||
|
||||
// StakingKeeper expected staking keeper (noalias)
|
||||
type StakingKeeper interface {
|
||||
// iterate through validators by operator address, execute func for each validator
|
||||
|
|
|
@ -0,0 +1,324 @@
|
|||
package simulation
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/simapp/helpers"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov/keeper"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||
)
|
||||
|
||||
var initialProposalID = uint64(100000000000000)
|
||||
|
||||
// ContentSimulator defines a function type alias for generating random proposal
|
||||
// content.
|
||||
type ContentSimulator func(r *rand.Rand, ctx sdk.Context, accs []simulation.Account) types.Content
|
||||
|
||||
// SimulateSubmitProposal simulates creating a msg Submit Proposal
|
||||
// voting on the proposal, and subsequently slashing the proposal. It is implemented using
|
||||
// future operations.
|
||||
// nolint: funlen
|
||||
func SimulateSubmitProposal(ak types.AccountKeeper, k keeper.Keeper,
|
||||
contentSim ContentSimulator) simulation.Operation {
|
||||
// The states are:
|
||||
// column 1: All validators vote
|
||||
// column 2: 90% vote
|
||||
// column 3: 75% vote
|
||||
// column 4: 40% vote
|
||||
// column 5: 15% vote
|
||||
// column 6: noone votes
|
||||
// All columns sum to 100 for simplicity, values chosen by @valardragon semi-arbitrarily,
|
||||
// feel free to change.
|
||||
numVotesTransitionMatrix, _ := simulation.CreateTransitionMatrix([][]int{
|
||||
{20, 10, 0, 0, 0, 0},
|
||||
{55, 50, 20, 10, 0, 0},
|
||||
{25, 25, 30, 25, 30, 15},
|
||||
{0, 15, 30, 25, 30, 30},
|
||||
{0, 0, 20, 30, 30, 30},
|
||||
{0, 0, 0, 10, 10, 25},
|
||||
})
|
||||
|
||||
statePercentageArray := []float64{1, .9, .75, .4, .15, 0}
|
||||
curNumVotesState := 1
|
||||
|
||||
return func(
|
||||
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
|
||||
accs []simulation.Account, chainID string,
|
||||
) (simulation.OperationMsg, []simulation.FutureOperation, error) {
|
||||
// 1) submit proposal now
|
||||
content := contentSim(r, ctx, accs)
|
||||
if content == nil {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
||||
}
|
||||
|
||||
simAccount, _ := simulation.RandomAcc(r, accs)
|
||||
deposit, skip, err := randomDeposit(r, ctx, ak, k, simAccount.Address)
|
||||
switch {
|
||||
case skip:
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
||||
case err != nil:
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, err
|
||||
}
|
||||
|
||||
msg := types.NewMsgSubmitProposal(content, deposit, simAccount.Address)
|
||||
|
||||
account := ak.GetAccount(ctx, simAccount.Address)
|
||||
coins := account.SpendableCoins(ctx.BlockTime())
|
||||
|
||||
var fees sdk.Coins
|
||||
coins, hasNeg := coins.SafeSub(deposit)
|
||||
if !hasNeg {
|
||||
fees, err = simulation.RandomFees(r, ctx, coins)
|
||||
if err != nil {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, err
|
||||
}
|
||||
}
|
||||
|
||||
tx := helpers.GenTx(
|
||||
[]sdk.Msg{msg},
|
||||
fees,
|
||||
chainID,
|
||||
[]uint64{account.GetAccountNumber()},
|
||||
[]uint64{account.GetSequence()},
|
||||
simAccount.PrivKey,
|
||||
)
|
||||
|
||||
res := app.Deliver(tx)
|
||||
if !res.IsOK() {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, errors.New(res.Log)
|
||||
}
|
||||
|
||||
opMsg := simulation.NewOperationMsg(msg, true, "")
|
||||
|
||||
// get the submitted proposal ID
|
||||
proposalID, err := k.GetProposalID(ctx)
|
||||
if err != nil {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, err
|
||||
}
|
||||
|
||||
// 2) Schedule operations for votes
|
||||
// 2.1) first pick a number of people to vote.
|
||||
curNumVotesState = numVotesTransitionMatrix.NextState(r, curNumVotesState)
|
||||
numVotes := int(math.Ceil(float64(len(accs)) * statePercentageArray[curNumVotesState]))
|
||||
|
||||
// 2.2) select who votes and when
|
||||
whoVotes := r.Perm(len(accs))
|
||||
|
||||
// didntVote := whoVotes[numVotes:]
|
||||
whoVotes = whoVotes[:numVotes]
|
||||
votingPeriod := k.GetVotingParams(ctx).VotingPeriod
|
||||
|
||||
fops := make([]simulation.FutureOperation, numVotes+1)
|
||||
for i := 0; i < numVotes; i++ {
|
||||
whenVote := ctx.BlockHeader().Time.Add(time.Duration(r.Int63n(int64(votingPeriod.Seconds()))) * time.Second)
|
||||
fops[i] = simulation.FutureOperation{
|
||||
BlockTime: whenVote,
|
||||
Op: operationSimulateMsgVote(ak, k, accs[whoVotes[i]], int64(proposalID)),
|
||||
}
|
||||
}
|
||||
|
||||
return opMsg, fops, nil
|
||||
}
|
||||
}
|
||||
|
||||
// SimulateTextProposalContent returns random text proposal content.
|
||||
func SimulateTextProposalContent(r *rand.Rand, _ sdk.Context, _ []simulation.Account) types.Content {
|
||||
return types.NewTextProposal(
|
||||
simulation.RandStringOfLength(r, 140),
|
||||
simulation.RandStringOfLength(r, 5000),
|
||||
)
|
||||
}
|
||||
|
||||
// SimulateMsgDeposit generates a MsgDeposit with random values.
|
||||
// nolint: funlen
|
||||
func SimulateMsgDeposit(ak types.AccountKeeper, k keeper.Keeper) simulation.Operation {
|
||||
return func(
|
||||
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
|
||||
accs []simulation.Account, chainID string,
|
||||
) (simulation.OperationMsg, []simulation.FutureOperation, error) {
|
||||
simAccount, _ := simulation.RandomAcc(r, accs)
|
||||
proposalID, ok := randomProposalID(r, k, ctx, types.StatusDepositPeriod)
|
||||
if !ok {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
||||
}
|
||||
|
||||
deposit, skip, err := randomDeposit(r, ctx, ak, k, simAccount.Address)
|
||||
switch {
|
||||
case skip:
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
||||
case err != nil:
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, err
|
||||
}
|
||||
|
||||
msg := types.NewMsgDeposit(simAccount.Address, proposalID, deposit)
|
||||
|
||||
account := ak.GetAccount(ctx, simAccount.Address)
|
||||
coins := account.SpendableCoins(ctx.BlockTime())
|
||||
|
||||
var fees sdk.Coins
|
||||
coins, hasNeg := coins.SafeSub(deposit)
|
||||
if !hasNeg {
|
||||
fees, err = simulation.RandomFees(r, ctx, coins)
|
||||
if err != nil {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, err
|
||||
}
|
||||
}
|
||||
|
||||
tx := helpers.GenTx(
|
||||
[]sdk.Msg{msg},
|
||||
fees,
|
||||
chainID,
|
||||
[]uint64{account.GetAccountNumber()},
|
||||
[]uint64{account.GetSequence()},
|
||||
simAccount.PrivKey,
|
||||
)
|
||||
|
||||
res := app.Deliver(tx)
|
||||
if !res.IsOK() {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, errors.New(res.Log)
|
||||
}
|
||||
|
||||
return simulation.NewOperationMsg(msg, true, ""), nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// SimulateMsgVote generates a MsgVote with random values.
|
||||
// nolint: funlen
|
||||
func SimulateMsgVote(ak types.AccountKeeper, k keeper.Keeper) simulation.Operation {
|
||||
return operationSimulateMsgVote(ak, k, simulation.Account{}, -1)
|
||||
}
|
||||
|
||||
func operationSimulateMsgVote(ak types.AccountKeeper, k keeper.Keeper,
|
||||
simAccount simulation.Account, proposalIDInt int64) simulation.Operation {
|
||||
return func(
|
||||
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
|
||||
accs []simulation.Account, chainID string,
|
||||
) (simulation.OperationMsg, []simulation.FutureOperation, error) {
|
||||
if simAccount.Equals(simulation.Account{}) {
|
||||
simAccount, _ = simulation.RandomAcc(r, accs)
|
||||
}
|
||||
|
||||
var proposalID uint64
|
||||
|
||||
switch {
|
||||
case proposalIDInt < 0:
|
||||
var ok bool
|
||||
proposalID, ok = randomProposalID(r, k, ctx, types.StatusVotingPeriod)
|
||||
if !ok {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
||||
}
|
||||
default:
|
||||
proposalID = uint64(proposalIDInt)
|
||||
}
|
||||
|
||||
option := randomVotingOption(r)
|
||||
|
||||
msg := types.NewMsgVote(simAccount.Address, proposalID, option)
|
||||
|
||||
account := ak.GetAccount(ctx, simAccount.Address)
|
||||
fees, err := simulation.RandomFees(r, ctx, account.SpendableCoins(ctx.BlockTime()))
|
||||
if err != nil {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, err
|
||||
}
|
||||
|
||||
tx := helpers.GenTx(
|
||||
[]sdk.Msg{msg},
|
||||
fees,
|
||||
chainID,
|
||||
[]uint64{account.GetAccountNumber()},
|
||||
[]uint64{account.GetSequence()},
|
||||
simAccount.PrivKey,
|
||||
)
|
||||
|
||||
res := app.Deliver(tx)
|
||||
if !res.IsOK() {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, errors.New(res.Log)
|
||||
}
|
||||
|
||||
return simulation.NewOperationMsg(msg, true, ""), nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Pick a random deposit with a random denomination with a
|
||||
// deposit amount between (0, min(balance, minDepositAmount))
|
||||
// This is to simulate multiple users depositing to get the
|
||||
// proposal above the minimum deposit amount
|
||||
func randomDeposit(r *rand.Rand, ctx sdk.Context,
|
||||
ak types.AccountKeeper, k keeper.Keeper, addr sdk.AccAddress,
|
||||
) (deposit sdk.Coins, skip bool, err error) {
|
||||
account := ak.GetAccount(ctx, addr)
|
||||
coins := account.SpendableCoins(ctx.BlockHeader().Time)
|
||||
if coins.Empty() {
|
||||
return nil, true, nil // skip
|
||||
}
|
||||
|
||||
minDeposit := k.GetDepositParams(ctx).MinDeposit
|
||||
denomIndex := r.Intn(len(minDeposit))
|
||||
denom := minDeposit[denomIndex].Denom
|
||||
|
||||
depositCoins := coins.AmountOf(denom)
|
||||
if depositCoins.IsZero() {
|
||||
return nil, true, nil
|
||||
}
|
||||
|
||||
maxAmt := depositCoins
|
||||
if maxAmt.GT(minDeposit[denomIndex].Amount) {
|
||||
maxAmt = minDeposit[denomIndex].Amount
|
||||
}
|
||||
|
||||
amount, err := simulation.RandPositiveInt(r, maxAmt)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
return sdk.Coins{sdk.NewCoin(denom, amount)}, false, nil
|
||||
}
|
||||
|
||||
// Pick a random proposal ID between the initial proposal ID
|
||||
// (defined in gov GenesisState) and the latest proposal ID
|
||||
// that matches a given Status.
|
||||
// It does not provide a default ID.
|
||||
func randomProposalID(r *rand.Rand, k keeper.Keeper,
|
||||
ctx sdk.Context, status types.ProposalStatus) (proposalID uint64, found bool) {
|
||||
proposalID, _ = k.GetProposalID(ctx)
|
||||
|
||||
switch {
|
||||
case proposalID > initialProposalID:
|
||||
// select a random ID between [initialProposalID, proposalID]
|
||||
proposalID = uint64(simulation.RandIntBetween(r, int(initialProposalID), int(proposalID)))
|
||||
|
||||
default:
|
||||
// This is called on the first call to this funcion
|
||||
// in order to update the global variable
|
||||
initialProposalID = proposalID
|
||||
}
|
||||
|
||||
proposal, ok := k.GetProposal(ctx, proposalID)
|
||||
if !ok || proposal.Status != status {
|
||||
return proposalID, false
|
||||
}
|
||||
|
||||
return proposalID, true
|
||||
}
|
||||
|
||||
// Pick a random voting option
|
||||
func randomVotingOption(r *rand.Rand) types.VoteOption {
|
||||
switch r.Intn(4) {
|
||||
case 0:
|
||||
return types.OptionYes
|
||||
case 1:
|
||||
return types.OptionAbstain
|
||||
case 2:
|
||||
return types.OptionNo
|
||||
case 3:
|
||||
return types.OptionNoWithVeto
|
||||
default:
|
||||
panic("invalid vote option")
|
||||
}
|
||||
}
|
|
@ -1,222 +0,0 @@
|
|||
package operations
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov"
|
||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||
)
|
||||
|
||||
// ContentSimulator defines a function type alias for generating random proposal
|
||||
// content.
|
||||
type ContentSimulator func(r *rand.Rand, ctx sdk.Context, accs []simulation.Account) gov.Content
|
||||
|
||||
// SimulateSubmittingVotingAndSlashingForProposal simulates creating a msg Submit Proposal
|
||||
// voting on the proposal, and subsequently slashing the proposal. It is implemented using
|
||||
// future operations.
|
||||
// TODO: Vote more intelligently, so we can actually do some checks regarding votes passing or failing
|
||||
// TODO: Actually check that validator slashings happened
|
||||
func SimulateSubmittingVotingAndSlashingForProposal(k gov.Keeper, contentSim ContentSimulator) simulation.Operation {
|
||||
handler := gov.NewHandler(k)
|
||||
|
||||
// The states are:
|
||||
// column 1: All validators vote
|
||||
// column 2: 90% vote
|
||||
// column 3: 75% vote
|
||||
// column 4: 40% vote
|
||||
// column 5: 15% vote
|
||||
// column 6: noone votes
|
||||
// All columns sum to 100 for simplicity, values chosen by @valardragon semi-arbitrarily,
|
||||
// feel free to change.
|
||||
numVotesTransitionMatrix, _ := simulation.CreateTransitionMatrix([][]int{
|
||||
{20, 10, 0, 0, 0, 0},
|
||||
{55, 50, 20, 10, 0, 0},
|
||||
{25, 25, 30, 25, 30, 15},
|
||||
{0, 15, 30, 25, 30, 30},
|
||||
{0, 0, 20, 30, 30, 30},
|
||||
{0, 0, 0, 10, 10, 25},
|
||||
})
|
||||
|
||||
statePercentageArray := []float64{1, .9, .75, .4, .15, 0}
|
||||
curNumVotesState := 1
|
||||
|
||||
return func(
|
||||
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account,
|
||||
) (opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) {
|
||||
|
||||
// 1) submit proposal now
|
||||
sender := simulation.RandomAcc(r, accs)
|
||||
content := contentSim(r, ctx, accs)
|
||||
msg, err := simulationCreateMsgSubmitProposal(r, content, sender)
|
||||
if err != nil {
|
||||
return simulation.NoOpMsg(gov.ModuleName), nil, err
|
||||
}
|
||||
|
||||
ok := simulateHandleMsgSubmitProposal(msg, handler, ctx)
|
||||
opMsg = simulation.NewOperationMsg(msg, ok, content.ProposalType())
|
||||
// don't schedule votes if proposal failed
|
||||
if !ok {
|
||||
return opMsg, nil, nil
|
||||
}
|
||||
|
||||
proposalID, err := k.GetProposalID(ctx)
|
||||
if err != nil {
|
||||
return simulation.NoOpMsg(gov.ModuleName), nil, err
|
||||
}
|
||||
|
||||
proposalID = uint64(math.Max(float64(proposalID)-1, 0))
|
||||
|
||||
// 2) Schedule operations for votes
|
||||
// 2.1) first pick a number of people to vote.
|
||||
curNumVotesState = numVotesTransitionMatrix.NextState(r, curNumVotesState)
|
||||
numVotes := int(math.Ceil(float64(len(accs)) * statePercentageArray[curNumVotesState]))
|
||||
|
||||
// 2.2) select who votes and when
|
||||
whoVotes := r.Perm(len(accs))
|
||||
|
||||
// didntVote := whoVotes[numVotes:]
|
||||
whoVotes = whoVotes[:numVotes]
|
||||
votingPeriod := k.GetVotingParams(ctx).VotingPeriod
|
||||
|
||||
fops := make([]simulation.FutureOperation, numVotes+1)
|
||||
for i := 0; i < numVotes; i++ {
|
||||
whenVote := ctx.BlockHeader().Time.Add(time.Duration(r.Int63n(int64(votingPeriod.Seconds()))) * time.Second)
|
||||
fops[i] = simulation.FutureOperation{BlockTime: whenVote, Op: operationSimulateMsgVote(k, accs[whoVotes[i]], proposalID)}
|
||||
}
|
||||
|
||||
// 3) Make an operation to ensure slashes were done correctly. (Really should be a future invariant)
|
||||
// TODO: Find a way to check if a validator was slashed other than just checking their balance a block
|
||||
// before and after.
|
||||
|
||||
return opMsg, fops, nil
|
||||
}
|
||||
}
|
||||
|
||||
func simulateHandleMsgSubmitProposal(msg gov.MsgSubmitProposal, handler sdk.Handler, ctx sdk.Context) (ok bool) {
|
||||
ctx, write := ctx.CacheContext()
|
||||
ok = handler(ctx, msg).IsOK()
|
||||
if ok {
|
||||
write()
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
// SimulateTextProposalContent returns random text proposal content.
|
||||
func SimulateTextProposalContent(r *rand.Rand, _ sdk.Context, _ []simulation.Account) gov.Content {
|
||||
return gov.NewTextProposal(
|
||||
simulation.RandStringOfLength(r, 140),
|
||||
simulation.RandStringOfLength(r, 5000),
|
||||
)
|
||||
}
|
||||
|
||||
func simulationCreateMsgSubmitProposal(r *rand.Rand, c gov.Content, s simulation.Account) (msg gov.MsgSubmitProposal, err error) {
|
||||
msg = gov.NewMsgSubmitProposal(c, randomDeposit(r), s.Address)
|
||||
if msg.ValidateBasic() != nil {
|
||||
err = fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SimulateMsgDeposit generates a MsgDeposit with random values.
|
||||
func SimulateMsgDeposit(k gov.Keeper) simulation.Operation {
|
||||
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account) (
|
||||
opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) {
|
||||
|
||||
acc := simulation.RandomAcc(r, accs)
|
||||
proposalID, ok := randomProposalID(r, k, ctx)
|
||||
if !ok {
|
||||
return simulation.NoOpMsg(gov.ModuleName), nil, nil
|
||||
}
|
||||
deposit := randomDeposit(r)
|
||||
msg := gov.NewMsgDeposit(acc.Address, proposalID, deposit)
|
||||
if msg.ValidateBasic() != nil {
|
||||
return simulation.NoOpMsg(gov.ModuleName), nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
|
||||
}
|
||||
ctx, write := ctx.CacheContext()
|
||||
ok = gov.NewHandler(k)(ctx, msg).IsOK()
|
||||
if ok {
|
||||
write()
|
||||
}
|
||||
|
||||
opMsg = simulation.NewOperationMsg(msg, ok, "")
|
||||
return opMsg, nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// SimulateMsgVote generates a MsgVote with random values.
|
||||
func SimulateMsgVote(k gov.Keeper) simulation.Operation {
|
||||
return operationSimulateMsgVote(k, simulation.Account{}, 0)
|
||||
}
|
||||
|
||||
// nolint: unparam
|
||||
func operationSimulateMsgVote(k gov.Keeper, acc simulation.Account, proposalID uint64) simulation.Operation {
|
||||
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account) (
|
||||
opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) {
|
||||
|
||||
if acc.Equals(simulation.Account{}) {
|
||||
acc = simulation.RandomAcc(r, accs)
|
||||
}
|
||||
|
||||
if proposalID < uint64(0) {
|
||||
var ok bool
|
||||
proposalID, ok = randomProposalID(r, k, ctx)
|
||||
if !ok {
|
||||
return simulation.NoOpMsg(gov.ModuleName), nil, nil
|
||||
}
|
||||
}
|
||||
option := randomVotingOption(r)
|
||||
|
||||
msg := gov.NewMsgVote(acc.Address, proposalID, option)
|
||||
if msg.ValidateBasic() != nil {
|
||||
return simulation.NoOpMsg(gov.ModuleName), nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
|
||||
}
|
||||
|
||||
ctx, write := ctx.CacheContext()
|
||||
ok := gov.NewHandler(k)(ctx, msg).IsOK()
|
||||
if ok {
|
||||
write()
|
||||
}
|
||||
|
||||
opMsg = simulation.NewOperationMsg(msg, ok, "")
|
||||
return opMsg, nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Pick a random deposit
|
||||
func randomDeposit(r *rand.Rand) sdk.Coins {
|
||||
// TODO Choose based on account balance and min deposit
|
||||
amount := int64(r.Intn(20)) + 1
|
||||
return sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, amount)}
|
||||
}
|
||||
|
||||
// Pick a random proposal ID
|
||||
func randomProposalID(r *rand.Rand, k gov.Keeper, ctx sdk.Context) (proposalID uint64, ok bool) {
|
||||
lastProposalID, _ := k.GetProposalID(ctx)
|
||||
lastProposalID = uint64(math.Max(float64(lastProposalID)-1, 0))
|
||||
|
||||
if lastProposalID < 1 || lastProposalID == (2<<63-1) {
|
||||
return 0, false
|
||||
}
|
||||
proposalID = uint64(r.Intn(1+int(lastProposalID)) - 1)
|
||||
return proposalID, true
|
||||
}
|
||||
|
||||
// Pick a random voting option
|
||||
func randomVotingOption(r *rand.Rand) gov.VoteOption {
|
||||
switch r.Intn(4) {
|
||||
case 0:
|
||||
return gov.OptionYes
|
||||
case 1:
|
||||
return gov.OptionAbstain
|
||||
case 2:
|
||||
return gov.OptionNo
|
||||
case 3:
|
||||
return gov.OptionNoWithVeto
|
||||
}
|
||||
panic("should not happen")
|
||||
}
|
|
@ -2,6 +2,7 @@ package types
|
|||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
|
||||
stakingexported "github.com/cosmos/cosmos-sdk/x/staking/exported"
|
||||
supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported"
|
||||
)
|
||||
|
@ -36,3 +37,8 @@ type StakingKeeper interface {
|
|||
IterateDelegations(ctx sdk.Context, delegator sdk.AccAddress,
|
||||
fn func(index int64, delegation stakingexported.DelegationI) (stop bool))
|
||||
}
|
||||
|
||||
// AccountKeeper defines the expected account keeper (noalias)
|
||||
type AccountKeeper interface {
|
||||
GetAccount(ctx sdk.Context, addr sdk.AccAddress) authexported.Account
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package operations
|
||||
package simulation
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
govsimops "github.com/cosmos/cosmos-sdk/x/gov/simulation/operations"
|
||||
govsim "github.com/cosmos/cosmos-sdk/x/gov/simulation"
|
||||
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/params"
|
||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||
|
@ -13,7 +13,7 @@ import (
|
|||
// SimulateParamChangeProposalContent returns random parameter change content.
|
||||
// It will generate a ParameterChangeProposal object with anywhere between 1 and
|
||||
// the total amount of defined parameters changes, all of which have random valid values.
|
||||
func SimulateParamChangeProposalContent(paramChangePool []simulation.ParamChange) govsimops.ContentSimulator {
|
||||
func SimulateParamChangeProposalContent(paramChangePool []simulation.ParamChange) govsim.ContentSimulator {
|
||||
return func(r *rand.Rand, _ sdk.Context, _ []simulation.Account) govtypes.Content {
|
||||
|
||||
lenParamChange := len(paramChangePool)
|
|
@ -18,16 +18,16 @@ type Account struct {
|
|||
Address sdk.AccAddress
|
||||
}
|
||||
|
||||
// are two accounts equal
|
||||
// Equals returns true if two accounts are equal
|
||||
func (acc Account) Equals(acc2 Account) bool {
|
||||
return acc.Address.Equals(acc2.Address)
|
||||
}
|
||||
|
||||
// RandomAcc pick a random account from an array
|
||||
func RandomAcc(r *rand.Rand, accs []Account) Account {
|
||||
return accs[r.Intn(
|
||||
len(accs),
|
||||
)]
|
||||
// RandomAcc picks and returns a random account from an array and returs its
|
||||
// position in the array.
|
||||
func RandomAcc(r *rand.Rand, accs []Account) (Account, int) {
|
||||
idx := r.Intn(len(accs))
|
||||
return accs[idx], idx
|
||||
}
|
||||
|
||||
// RandomAccounts generates n random accounts
|
||||
|
@ -45,3 +45,41 @@ func RandomAccounts(r *rand.Rand, n int) []Account {
|
|||
|
||||
return accs
|
||||
}
|
||||
|
||||
// FindAccount iterates over all the simulation accounts to find the one that matches
|
||||
// the given address
|
||||
func FindAccount(accs []Account, address sdk.Address) (Account, bool) {
|
||||
for _, acc := range accs {
|
||||
if acc.Address.Equals(address) {
|
||||
return acc, true
|
||||
}
|
||||
}
|
||||
|
||||
return Account{}, false
|
||||
}
|
||||
|
||||
// RandomFees returns a random fee by selecting a random coin denomination and
|
||||
// amount from the account's available balance. If the user doesn't have enough
|
||||
// funds for paying fees, it returns empty coins.
|
||||
func RandomFees(r *rand.Rand, ctx sdk.Context, spendableCoins sdk.Coins) (sdk.Coins, error) {
|
||||
if spendableCoins.Empty() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
denomIndex := r.Intn(len(spendableCoins))
|
||||
randCoin := spendableCoins[denomIndex]
|
||||
|
||||
if randCoin.Amount.IsZero() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
amt, err := RandPositiveInt(r, randCoin.Amount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create a random fee and verify the fees are within the account's spendable
|
||||
// balance.
|
||||
fees := sdk.NewCoins(sdk.NewCoin(randCoin.Denom, amt))
|
||||
return fees, nil
|
||||
}
|
||||
|
|
|
@ -10,10 +10,11 @@ type Config struct {
|
|||
ExportStatePath string //custom file path to save the exported app state JSON
|
||||
ExportStatsPath string // custom file path to save the exported simulation statistics JSON
|
||||
|
||||
Seed int64 // simulation random seed
|
||||
InitialBlockHeight int // initial block to start the simulation
|
||||
NumBlocks int // number of new blocks to simulate from the initial block height
|
||||
BlockSize int // operations per block
|
||||
Seed int64 // simulation random seed
|
||||
InitialBlockHeight int // initial block to start the simulation
|
||||
NumBlocks int // number of new blocks to simulate from the initial block height
|
||||
BlockSize int // operations per block
|
||||
ChainID string // chain-id used on the simulation
|
||||
|
||||
Lean bool // lean simulation log output
|
||||
Commit bool // have the simulation commit
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
)
|
||||
|
||||
// Operation runs a state machine transition, and ensures the transition
|
||||
// happened as exported. The operation could be running and testing a fuzzed
|
||||
// happened as expected. The operation could be running and testing a fuzzed
|
||||
// transaction, or doing the same for a message.
|
||||
//
|
||||
// For ease of debugging, an operation returns a descriptive message "action",
|
||||
|
@ -20,7 +20,7 @@ import (
|
|||
// Operations can optionally provide a list of "FutureOperations" to run later
|
||||
// These will be ran at the beginning of the corresponding block.
|
||||
type Operation func(r *rand.Rand, app *baseapp.BaseApp,
|
||||
ctx sdk.Context, accounts []Account) (
|
||||
ctx sdk.Context, accounts []Account, chainID string) (
|
||||
OperationMsg OperationMsg, futureOps []FutureOperation, err error)
|
||||
|
||||
// entry kinds for use within OperationEntry
|
||||
|
@ -82,11 +82,11 @@ func (oe OperationEntry) MustMarshal() json.RawMessage {
|
|||
|
||||
// OperationMsg - structure for operation output
|
||||
type OperationMsg struct {
|
||||
Route string `json:"route" yaml:"route"`
|
||||
Name string `json:"name" yaml:"name"`
|
||||
Comment string `json:"comment" yaml:"comment"`
|
||||
OK bool `json:"ok" yaml:"ok"`
|
||||
Msg json.RawMessage `json:"msg" yaml:"msg"`
|
||||
Route string `json:"route" yaml:"route"` // msg route (i.e module name)
|
||||
Name string `json:"name" yaml:"name"` // operation name (msg Type or "no-operation")
|
||||
Comment string `json:"comment" yaml:"comment"` // additional comment
|
||||
OK bool `json:"ok" yaml:"ok"` // success
|
||||
Msg json.RawMessage `json:"msg" yaml:"msg"` // JSON encoded msg
|
||||
}
|
||||
|
||||
// NewOperationMsgBasic creates a new operation message from raw input.
|
||||
|
|
|
@ -69,7 +69,7 @@ type Params struct {
|
|||
func RandomParams(r *rand.Rand) Params {
|
||||
return Params{
|
||||
PastEvidenceFraction: r.Float64(),
|
||||
NumKeys: RandIntBetween(r, 2, 250),
|
||||
NumKeys: RandIntBetween(r, 2, 2500), // number of accounts created for the simulation
|
||||
EvidenceFraction: r.Float64(),
|
||||
InitialLivenessWeightings: []int{RandIntBetween(r, 1, 80), r.Intn(10), r.Intn(10)},
|
||||
LivenessTransitionMatrix: defaultLivenessTransitionMatrix,
|
||||
|
|
|
@ -39,7 +39,7 @@ func RandStringOfLength(r *rand.Rand, n int) string {
|
|||
|
||||
// RandPositiveInt get a rand positive sdk.Int
|
||||
func RandPositiveInt(r *rand.Rand, max sdk.Int) (sdk.Int, error) {
|
||||
if !max.GT(sdk.OneInt()) {
|
||||
if !max.GTE(sdk.OneInt()) {
|
||||
return sdk.Int{}, errors.New("max too small")
|
||||
}
|
||||
max = max.Sub(sdk.OneInt())
|
||||
|
@ -88,6 +88,43 @@ func RandIntBetween(r *rand.Rand, min, max int) int {
|
|||
return r.Intn(max-min) + min
|
||||
}
|
||||
|
||||
// returns random subset of the provided coins
|
||||
// will return at least one coin unless coins argument is empty or malformed
|
||||
// i.e. 0 amt in coins
|
||||
func RandSubsetCoins(r *rand.Rand, coins sdk.Coins) sdk.Coins {
|
||||
if len(coins) == 0 {
|
||||
return sdk.Coins{}
|
||||
}
|
||||
// make sure at least one coin added
|
||||
denomIdx := r.Intn(len(coins))
|
||||
coin := coins[denomIdx]
|
||||
amt, err := RandPositiveInt(r, coin.Amount)
|
||||
// malformed coin. 0 amt in coins
|
||||
if err != nil {
|
||||
return sdk.Coins{}
|
||||
}
|
||||
subset := sdk.Coins{sdk.NewCoin(coin.Denom, amt)}
|
||||
for i, c := range coins {
|
||||
// skip denom that we already chose earlier
|
||||
if i == denomIdx {
|
||||
continue
|
||||
}
|
||||
// coin flip if multiple coins
|
||||
// if there is single coin then return random amount of it
|
||||
if r.Intn(2) == 0 && len(coins) != 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
amt, err := RandPositiveInt(r, c.Amount)
|
||||
// ignore errors and try another denom
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
subset = append(subset, sdk.NewCoin(c.Denom, amt))
|
||||
}
|
||||
return subset
|
||||
}
|
||||
|
||||
// DeriveRand derives a new Rand deterministically from another random source.
|
||||
// Unlike rand.New(rand.NewSource(seed)), the result is "more random"
|
||||
// depending on the source and state of r.
|
||||
|
|
|
@ -17,7 +17,7 @@ import (
|
|||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// AppStateFn returns the app state json bytes, the genesis accounts, and the chain identifier
|
||||
// AppStateFn returns the app state json bytes and the genesis accounts
|
||||
type AppStateFn func(r *rand.Rand, accs []Account, config Config) (
|
||||
appState json.RawMessage, accounts []Account, chainId string, genesisTimestamp time.Time,
|
||||
)
|
||||
|
@ -26,7 +26,7 @@ type AppStateFn func(r *rand.Rand, accs []Account, config Config) (
|
|||
func initChain(
|
||||
r *rand.Rand, params Params, accounts []Account, app *baseapp.BaseApp,
|
||||
appStateFn AppStateFn, config Config,
|
||||
) (mockValidators, time.Time, []Account) {
|
||||
) (mockValidators, time.Time, []Account, string) {
|
||||
|
||||
appState, accounts, chainID, genesisTimestamp := appStateFn(r, accounts, config)
|
||||
|
||||
|
@ -37,7 +37,7 @@ func initChain(
|
|||
res := app.InitChain(req)
|
||||
validators := newMockValidators(r, res.Validators, params)
|
||||
|
||||
return validators, genesisTimestamp, accounts
|
||||
return validators, genesisTimestamp, accounts, chainID
|
||||
}
|
||||
|
||||
// SimulateFromSeed tests an application by running the provided
|
||||
|
@ -51,7 +51,7 @@ func SimulateFromSeed(
|
|||
|
||||
// in case we have to end early, don't os.Exit so that we can run cleanup code.
|
||||
testingMode, t, b := getTestingMode(tb)
|
||||
fmt.Fprintf(w, "Starting SimulateFromSeed with randomness created with config.Seed %d\n", int(config.Seed))
|
||||
fmt.Fprintf(w, "Starting SimulateFromSeed with randomness created with seed %d\n", int(config.Seed))
|
||||
|
||||
r := rand.New(rand.NewSource(config.Seed))
|
||||
params := RandomParams(r)
|
||||
|
@ -63,11 +63,13 @@ func SimulateFromSeed(
|
|||
|
||||
// Second variable to keep pending validator set (delayed one block since
|
||||
// TM 0.24) Initially this is the same as the initial validator set
|
||||
validators, genesisTimestamp, accs := initChain(r, params, accs, app, appStateFn, config)
|
||||
validators, genesisTimestamp, accs, chainID := initChain(r, params, accs, app, appStateFn, config)
|
||||
if len(accs) == 0 {
|
||||
return true, params, fmt.Errorf("must have greater than zero genesis accounts")
|
||||
}
|
||||
|
||||
config.ChainID = chainID
|
||||
|
||||
fmt.Printf(
|
||||
"Starting the simulation from time %v (unixtime %v)\n",
|
||||
genesisTimestamp.UTC().Format(time.UnixDate), genesisTimestamp.Unix(),
|
||||
|
@ -86,6 +88,7 @@ func SimulateFromSeed(
|
|||
nextValidators := validators
|
||||
|
||||
header := abci.Header{
|
||||
ChainID: config.ChainID,
|
||||
Height: 1,
|
||||
Time: genesisTimestamp,
|
||||
ProposerAddress: validators.randomProposer(r),
|
||||
|
@ -124,7 +127,7 @@ func SimulateFromSeed(
|
|||
// recover logs in case of panic
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
_, _ = fmt.Fprintf(w, "simulation halted due to panic on block %d; %v\n", header.Height, r)
|
||||
_, _ = fmt.Fprintf(w, "simulation halted due to panic on block %d\n", header.Height)
|
||||
logWriter.PrintLogs()
|
||||
panic(r)
|
||||
}
|
||||
|
@ -151,12 +154,15 @@ func SimulateFromSeed(
|
|||
|
||||
// Run queued operations. Ignores blocksize if blocksize is too small
|
||||
numQueuedOpsRan := runQueuedOperations(
|
||||
operationQueue, int(header.Height),
|
||||
tb, r, app, ctx, accs, logWriter, eventStats.Tally, config.Lean)
|
||||
operationQueue, int(header.Height), tb, r, app, ctx, accs, logWriter,
|
||||
eventStats.Tally, config.Lean, config.ChainID,
|
||||
)
|
||||
|
||||
numQueuedTimeOpsRan := runQueuedTimeOperations(
|
||||
timeOperationQueue, int(header.Height), header.Time,
|
||||
tb, r, app, ctx, accs, logWriter, eventStats.Tally, config.Lean)
|
||||
tb, r, app, ctx, accs, logWriter, eventStats.Tally,
|
||||
config.Lean, config.ChainID,
|
||||
)
|
||||
|
||||
// run standard operations
|
||||
operations := blockSimulator(r, app, ctx, accs, header)
|
||||
|
@ -271,7 +277,7 @@ func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, w io.Wr
|
|||
// NOTE: the Rand 'r' should not be used here.
|
||||
opAndR := opAndRz[i]
|
||||
op, r2 := opAndR.op, opAndR.rand
|
||||
opMsg, futureOps, err := op(r2, app, ctx, accounts)
|
||||
opMsg, futureOps, err := op(r2, app, ctx, accounts, config.ChainID)
|
||||
opMsg.LogEvent(event)
|
||||
|
||||
if !config.Lean || opMsg.OK {
|
||||
|
@ -280,8 +286,10 @@ func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, w io.Wr
|
|||
|
||||
if err != nil {
|
||||
logWriter.PrintLogs()
|
||||
tb.Fatalf("error on operation %d within block %d, %v",
|
||||
header.Height, opCount, err)
|
||||
tb.Fatalf(`error on block %d/%d, operation (%d/%d) from x/%s:
|
||||
%v
|
||||
Comment: %s`,
|
||||
header.Height, config.NumBlocks, opCount, blocksize, opMsg.Route, err, opMsg.Comment)
|
||||
}
|
||||
|
||||
queueOperations(operationQueue, timeOperationQueue, futureOps)
|
||||
|
@ -300,7 +308,8 @@ func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, w io.Wr
|
|||
// nolint: errcheck
|
||||
func runQueuedOperations(queueOps map[int][]Operation,
|
||||
height int, tb testing.TB, r *rand.Rand, app *baseapp.BaseApp,
|
||||
ctx sdk.Context, accounts []Account, logWriter LogWriter, event func(route, op, evResult string), lean bool) (numOpsRan int) {
|
||||
ctx sdk.Context, accounts []Account, logWriter LogWriter,
|
||||
event func(route, op, evResult string), lean bool, chainID string) (numOpsRan int) {
|
||||
|
||||
queuedOp, ok := queueOps[height]
|
||||
if !ok {
|
||||
|
@ -313,7 +322,7 @@ func runQueuedOperations(queueOps map[int][]Operation,
|
|||
// For now, queued operations cannot queue more operations.
|
||||
// If a need arises for us to support queued messages to queue more messages, this can
|
||||
// be changed.
|
||||
opMsg, _, err := queuedOp[i](r, app, ctx, accounts)
|
||||
opMsg, _, err := queuedOp[i](r, app, ctx, accounts, chainID)
|
||||
opMsg.LogEvent(event)
|
||||
if !lean || opMsg.OK {
|
||||
logWriter.AddEntry((QueuedMsgEntry(int64(height), opMsg)))
|
||||
|
@ -330,7 +339,8 @@ func runQueuedOperations(queueOps map[int][]Operation,
|
|||
func runQueuedTimeOperations(queueOps []FutureOperation,
|
||||
height int, currentTime time.Time, tb testing.TB, r *rand.Rand,
|
||||
app *baseapp.BaseApp, ctx sdk.Context, accounts []Account,
|
||||
logWriter LogWriter, event func(route, op, evResult string), lean bool) (numOpsRan int) {
|
||||
logWriter LogWriter, event func(route, op, evResult string),
|
||||
lean bool, chainID string) (numOpsRan int) {
|
||||
|
||||
numOpsRan = 0
|
||||
for len(queueOps) > 0 && currentTime.After(queueOps[0].BlockTime) {
|
||||
|
@ -338,7 +348,7 @@ func runQueuedTimeOperations(queueOps []FutureOperation,
|
|||
// For now, queued operations cannot queue more operations.
|
||||
// If a need arises for us to support queued messages to queue more messages, this can
|
||||
// be changed.
|
||||
opMsg, _, err := queueOps[0].Op(r, app, ctx, accounts)
|
||||
opMsg, _, err := queueOps[0].Op(r, app, ctx, accounts, chainID)
|
||||
opMsg.LogEvent(event)
|
||||
if !lean || opMsg.OK {
|
||||
logWriter.AddEntry(QueuedMsgEntry(int64(height), opMsg))
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
|
||||
// AccountKeeper expected account keeper
|
||||
type AccountKeeper interface {
|
||||
GetAccount(ctx sdk.Context, addr sdk.AccAddress) authexported.Account
|
||||
IterateAccounts(ctx sdk.Context, process func(authexported.Account) (stop bool))
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
package simulation
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/rand"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/simapp/helpers"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||
"github.com/cosmos/cosmos-sdk/x/slashing/internal/keeper"
|
||||
"github.com/cosmos/cosmos-sdk/x/slashing/internal/types"
|
||||
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
|
||||
)
|
||||
|
||||
// SimulateMsgUnjail generates a MsgUnjail with random values
|
||||
// nolint: funlen
|
||||
func SimulateMsgUnjail(ak types.AccountKeeper, k keeper.Keeper, sk stakingkeeper.Keeper) simulation.Operation {
|
||||
return func(
|
||||
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
|
||||
accs []simulation.Account, chainID string,
|
||||
) (simulation.OperationMsg, []simulation.FutureOperation, error) {
|
||||
|
||||
validator, ok := stakingkeeper.RandomValidator(r, sk, ctx)
|
||||
if !ok {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil // skip
|
||||
}
|
||||
|
||||
simAccount, found := simulation.FindAccount(accs, sdk.AccAddress(validator.GetOperator()))
|
||||
if !found {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil // skip
|
||||
}
|
||||
|
||||
if !validator.IsJailed() {
|
||||
// TODO: due to this condition this message is almost, if not always, skipped !
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
||||
}
|
||||
|
||||
consAddr := sdk.ConsAddress(validator.GetConsPubKey().Address())
|
||||
info, found := k.GetValidatorSigningInfo(ctx, consAddr)
|
||||
if !found {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil // skip
|
||||
}
|
||||
|
||||
selfDel := sk.Delegation(ctx, simAccount.Address, validator.GetOperator())
|
||||
if selfDel == nil {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil // skip
|
||||
}
|
||||
|
||||
account := ak.GetAccount(ctx, sdk.AccAddress(validator.GetOperator()))
|
||||
fees, err := simulation.RandomFees(r, ctx, account.SpendableCoins(ctx.BlockTime()))
|
||||
if err != nil {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, err
|
||||
}
|
||||
|
||||
msg := types.NewMsgUnjail(validator.GetOperator())
|
||||
|
||||
tx := helpers.GenTx(
|
||||
[]sdk.Msg{msg},
|
||||
fees,
|
||||
chainID,
|
||||
[]uint64{account.GetAccountNumber()},
|
||||
[]uint64{account.GetSequence()},
|
||||
simAccount.PrivKey,
|
||||
)
|
||||
|
||||
res := app.Deliver(tx)
|
||||
|
||||
// result should fail if:
|
||||
// - validator cannot be unjailed due to tombstone
|
||||
// - validator is still in jailed period
|
||||
// - self delegation too low
|
||||
if info.Tombstoned ||
|
||||
ctx.BlockHeader().Time.Before(info.JailedUntil) ||
|
||||
validator.TokensFromShares(selfDel.GetShares()).TruncateInt().LT(validator.GetMinSelfDelegation()) {
|
||||
if res.IsOK() {
|
||||
if info.Tombstoned {
|
||||
return simulation.NewOperationMsg(msg, true, ""), nil, errors.New("validator should not have been unjailed if validator tombstoned")
|
||||
}
|
||||
if ctx.BlockHeader().Time.Before(info.JailedUntil) {
|
||||
return simulation.NewOperationMsg(msg, true, ""), nil, errors.New("validator unjailed while validator still in jail period")
|
||||
}
|
||||
if validator.TokensFromShares(selfDel.GetShares()).TruncateInt().LT(validator.GetMinSelfDelegation()) {
|
||||
return simulation.NewOperationMsg(msg, true, ""), nil, errors.New("validator unjailed even though self-delegation too low")
|
||||
}
|
||||
}
|
||||
// msg failed as expected
|
||||
return simulation.NewOperationMsg(msg, false, ""), nil, nil
|
||||
}
|
||||
|
||||
if !res.IsOK() {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, errors.New(res.Log)
|
||||
}
|
||||
|
||||
return simulation.NewOperationMsg(msg, true, ""), nil, nil
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
package operations
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||
"github.com/cosmos/cosmos-sdk/x/slashing"
|
||||
)
|
||||
|
||||
// SimulateMsgUnjail generates a MsgUnjail with random values
|
||||
func SimulateMsgUnjail(k slashing.Keeper) simulation.Operation {
|
||||
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
|
||||
accs []simulation.Account) (opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) {
|
||||
|
||||
acc := simulation.RandomAcc(r, accs)
|
||||
address := sdk.ValAddress(acc.Address)
|
||||
msg := slashing.NewMsgUnjail(address)
|
||||
if msg.ValidateBasic() != nil {
|
||||
return simulation.NoOpMsg(slashing.ModuleName), nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
|
||||
}
|
||||
ctx, write := ctx.CacheContext()
|
||||
ok := slashing.NewHandler(k)(ctx, msg).IsOK()
|
||||
if ok {
|
||||
write()
|
||||
}
|
||||
opMsg = simulation.NewOperationMsg(msg, ok, "")
|
||||
return opMsg, nil, nil
|
||||
}
|
||||
}
|
|
@ -289,15 +289,12 @@ func validatorByPowerIndexExists(k Keeper, ctx sdk.Context, power []byte) bool {
|
|||
}
|
||||
|
||||
// RandomValidator returns a random validator given access to the keeper and ctx
|
||||
func RandomValidator(r *rand.Rand, keeper Keeper, ctx sdk.Context) types.Validator {
|
||||
func RandomValidator(r *rand.Rand, keeper Keeper, ctx sdk.Context) (val types.Validator, ok bool) {
|
||||
vals := keeper.GetAllValidators(ctx)
|
||||
i := r.Intn(len(vals))
|
||||
return vals[i]
|
||||
}
|
||||
if len(vals) == 0 {
|
||||
return types.Validator{}, false
|
||||
}
|
||||
|
||||
// RandomBondedValidator returns a random bonded validator given access to the keeper and ctx
|
||||
func RandomBondedValidator(r *rand.Rand, keeper Keeper, ctx sdk.Context) types.Validator {
|
||||
vals := keeper.GetBondedValidatorsByPower(ctx)
|
||||
i := r.Intn(len(vals))
|
||||
return vals[i]
|
||||
return vals[i], true
|
||||
}
|
||||
|
|
|
@ -63,9 +63,18 @@ func RandomizedGenState(simState *module.SimulationState) {
|
|||
valAddr := sdk.ValAddress(simState.Accounts[i].Address)
|
||||
valAddrs[i] = valAddr
|
||||
|
||||
maxCommission := sdk.NewDecWithPrec(int64(simulation.RandIntBetween(simState.Rand, 1, 100)), 2)
|
||||
commission := types.NewCommission(
|
||||
simulation.RandomDecAmount(simState.Rand, maxCommission),
|
||||
maxCommission,
|
||||
simulation.RandomDecAmount(simState.Rand, maxCommission),
|
||||
)
|
||||
|
||||
validator := types.NewValidator(valAddr, simState.Accounts[i].PubKey, types.Description{})
|
||||
validator.Tokens = sdk.NewInt(simState.InitialStake)
|
||||
validator.DelegatorShares = sdk.NewDec(simState.InitialStake)
|
||||
validator.Commission = commission
|
||||
|
||||
delegation := types.NewDelegation(simState.Accounts[i].Address, valAddr, sdk.NewDec(simState.InitialStake))
|
||||
validators = append(validators, validator)
|
||||
delegations = append(delegations, delegation)
|
||||
|
|
|
@ -0,0 +1,405 @@
|
|||
package simulation
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/simapp/helpers"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking/keeper"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
)
|
||||
|
||||
// SimulateMsgCreateValidator generates a MsgCreateValidator with random values
|
||||
// nolint: funlen
|
||||
func SimulateMsgCreateValidator(ak types.AccountKeeper, k keeper.Keeper) simulation.Operation {
|
||||
return func(
|
||||
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string,
|
||||
) (simulation.OperationMsg, []simulation.FutureOperation, error) {
|
||||
|
||||
simAccount, _ := simulation.RandomAcc(r, accs)
|
||||
address := sdk.ValAddress(simAccount.Address)
|
||||
|
||||
// ensure the validator doesn't exist already
|
||||
_, found := k.GetValidator(ctx, address)
|
||||
if found {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
||||
}
|
||||
|
||||
denom := k.GetParams(ctx).BondDenom
|
||||
amount := ak.GetAccount(ctx, simAccount.Address).GetCoins().AmountOf(denom)
|
||||
if !amount.IsPositive() {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
||||
}
|
||||
|
||||
amount, err := simulation.RandPositiveInt(r, amount)
|
||||
if err != nil {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, err
|
||||
}
|
||||
|
||||
selfDelegation := sdk.NewCoin(denom, amount)
|
||||
|
||||
account := ak.GetAccount(ctx, simAccount.Address)
|
||||
coins := account.SpendableCoins(ctx.BlockTime())
|
||||
|
||||
var fees sdk.Coins
|
||||
coins, hasNeg := coins.SafeSub(sdk.Coins{selfDelegation})
|
||||
if !hasNeg {
|
||||
fees, err = simulation.RandomFees(r, ctx, coins)
|
||||
if err != nil {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, err
|
||||
}
|
||||
}
|
||||
|
||||
description := types.NewDescription(
|
||||
simulation.RandStringOfLength(r, 10),
|
||||
simulation.RandStringOfLength(r, 10),
|
||||
simulation.RandStringOfLength(r, 10),
|
||||
simulation.RandStringOfLength(r, 10),
|
||||
simulation.RandStringOfLength(r, 10),
|
||||
)
|
||||
|
||||
maxCommission := sdk.NewDecWithPrec(int64(simulation.RandIntBetween(r, 0, 100)), 2)
|
||||
commission := types.NewCommissionRates(
|
||||
simulation.RandomDecAmount(r, maxCommission),
|
||||
maxCommission,
|
||||
simulation.RandomDecAmount(r, maxCommission),
|
||||
)
|
||||
|
||||
msg := types.NewMsgCreateValidator(address, simAccount.PubKey,
|
||||
selfDelegation, description, commission, sdk.OneInt())
|
||||
|
||||
tx := helpers.GenTx(
|
||||
[]sdk.Msg{msg},
|
||||
fees,
|
||||
chainID,
|
||||
[]uint64{account.GetAccountNumber()},
|
||||
[]uint64{account.GetSequence()},
|
||||
simAccount.PrivKey,
|
||||
)
|
||||
|
||||
res := app.Deliver(tx)
|
||||
if !res.IsOK() {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, errors.New(res.Log)
|
||||
}
|
||||
|
||||
return simulation.NewOperationMsg(msg, true, ""), nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// SimulateMsgEditValidator generates a MsgEditValidator with random values
|
||||
// nolint: funlen
|
||||
func SimulateMsgEditValidator(ak types.AccountKeeper, k keeper.Keeper) simulation.Operation {
|
||||
return func(
|
||||
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string,
|
||||
) (simulation.OperationMsg, []simulation.FutureOperation, error) {
|
||||
|
||||
if len(k.GetAllValidators(ctx)) == 0 {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
||||
}
|
||||
|
||||
val, ok := keeper.RandomValidator(r, k, ctx)
|
||||
if !ok {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
||||
}
|
||||
|
||||
address := val.GetOperator()
|
||||
|
||||
newCommissionRate := simulation.RandomDecAmount(r, val.Commission.MaxRate)
|
||||
|
||||
if err := val.Commission.ValidateNewRate(newCommissionRate, ctx.BlockHeader().Time); err != nil {
|
||||
// skip as the commission is invalid
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
||||
}
|
||||
|
||||
simAccount, found := simulation.FindAccount(accs, sdk.AccAddress(val.GetOperator()))
|
||||
if !found {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, fmt.Errorf("validator %s not found", val.GetOperator())
|
||||
}
|
||||
|
||||
account := ak.GetAccount(ctx, simAccount.Address)
|
||||
fees, err := simulation.RandomFees(r, ctx, account.SpendableCoins(ctx.BlockTime()))
|
||||
if err != nil {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, err
|
||||
}
|
||||
|
||||
description := types.NewDescription(
|
||||
simulation.RandStringOfLength(r, 10),
|
||||
simulation.RandStringOfLength(r, 10),
|
||||
simulation.RandStringOfLength(r, 10),
|
||||
simulation.RandStringOfLength(r, 10),
|
||||
simulation.RandStringOfLength(r, 10),
|
||||
)
|
||||
|
||||
msg := types.NewMsgEditValidator(address, description, &newCommissionRate, nil)
|
||||
|
||||
tx := helpers.GenTx(
|
||||
[]sdk.Msg{msg},
|
||||
fees,
|
||||
chainID,
|
||||
[]uint64{account.GetAccountNumber()},
|
||||
[]uint64{account.GetSequence()},
|
||||
simAccount.PrivKey,
|
||||
)
|
||||
|
||||
res := app.Deliver(tx)
|
||||
if !res.IsOK() {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, errors.New(res.Log)
|
||||
}
|
||||
|
||||
return simulation.NewOperationMsg(msg, true, ""), nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// SimulateMsgDelegate generates a MsgDelegate with random values
|
||||
// nolint: funlen
|
||||
func SimulateMsgDelegate(ak types.AccountKeeper, k keeper.Keeper) simulation.Operation {
|
||||
return func(
|
||||
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string,
|
||||
) (simulation.OperationMsg, []simulation.FutureOperation, error) {
|
||||
|
||||
denom := k.GetParams(ctx).BondDenom
|
||||
if len(k.GetAllValidators(ctx)) == 0 {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
||||
}
|
||||
|
||||
simAccount, _ := simulation.RandomAcc(r, accs)
|
||||
val, ok := keeper.RandomValidator(r, k, ctx)
|
||||
if !ok {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
||||
}
|
||||
|
||||
if val.InvalidExRate() {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
||||
}
|
||||
|
||||
amount := ak.GetAccount(ctx, simAccount.Address).GetCoins().AmountOf(denom)
|
||||
if !amount.IsPositive() {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
||||
}
|
||||
|
||||
amount, err := simulation.RandPositiveInt(r, amount)
|
||||
if err != nil {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, err
|
||||
}
|
||||
|
||||
bondAmt := sdk.NewCoin(denom, amount)
|
||||
|
||||
account := ak.GetAccount(ctx, simAccount.Address)
|
||||
coins := account.SpendableCoins(ctx.BlockTime())
|
||||
|
||||
var fees sdk.Coins
|
||||
coins, hasNeg := coins.SafeSub(sdk.Coins{bondAmt})
|
||||
if !hasNeg {
|
||||
fees, err = simulation.RandomFees(r, ctx, coins)
|
||||
if err != nil {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, err
|
||||
}
|
||||
}
|
||||
|
||||
msg := types.NewMsgDelegate(simAccount.Address, val.GetOperator(), bondAmt)
|
||||
|
||||
tx := helpers.GenTx(
|
||||
[]sdk.Msg{msg},
|
||||
fees,
|
||||
chainID,
|
||||
[]uint64{account.GetAccountNumber()},
|
||||
[]uint64{account.GetSequence()},
|
||||
simAccount.PrivKey,
|
||||
)
|
||||
|
||||
res := app.Deliver(tx)
|
||||
if !res.IsOK() {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, errors.New(res.Log)
|
||||
}
|
||||
|
||||
return simulation.NewOperationMsg(msg, true, ""), nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// SimulateMsgUndelegate generates a MsgUndelegate with random values
|
||||
// nolint: funlen
|
||||
func SimulateMsgUndelegate(ak types.AccountKeeper, k keeper.Keeper) simulation.Operation {
|
||||
return func(
|
||||
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string,
|
||||
) (simulation.OperationMsg, []simulation.FutureOperation, error) {
|
||||
|
||||
// get random validator
|
||||
validator, ok := keeper.RandomValidator(r, k, ctx)
|
||||
if !ok {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
||||
}
|
||||
valAddr := validator.GetOperator()
|
||||
|
||||
delegations := k.GetValidatorDelegations(ctx, validator.OperatorAddress)
|
||||
|
||||
// get random delegator from validator
|
||||
delegation := delegations[r.Intn(len(delegations))]
|
||||
delAddr := delegation.GetDelegatorAddr()
|
||||
|
||||
if k.HasMaxUnbondingDelegationEntries(ctx, delAddr, valAddr) {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
||||
}
|
||||
|
||||
totalBond := validator.TokensFromShares(delegation.GetShares()).TruncateInt()
|
||||
if !totalBond.IsPositive() {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
||||
}
|
||||
|
||||
unbondAmt, err := simulation.RandPositiveInt(r, totalBond)
|
||||
if err != nil {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, err
|
||||
}
|
||||
|
||||
if unbondAmt.IsZero() {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
||||
}
|
||||
|
||||
msg := types.NewMsgUndelegate(
|
||||
delAddr, valAddr, sdk.NewCoin(k.BondDenom(ctx), unbondAmt),
|
||||
)
|
||||
|
||||
// need to retrieve the simulation account associated with delegation to retrieve PrivKey
|
||||
var simAccount simulation.Account
|
||||
for _, simAcc := range accs {
|
||||
if simAcc.Address.Equals(delAddr) {
|
||||
simAccount = simAcc
|
||||
break
|
||||
}
|
||||
}
|
||||
// if simaccount.PrivKey == nil, delegation address does not exist in accs. Return error
|
||||
if simAccount.PrivKey == nil {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, fmt.Errorf("Delegation addr: %s does not exist in simulation accounts", delAddr)
|
||||
}
|
||||
|
||||
account := ak.GetAccount(ctx, delAddr)
|
||||
fees, err := simulation.RandomFees(r, ctx, account.SpendableCoins(ctx.BlockTime()))
|
||||
if err != nil {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, err
|
||||
}
|
||||
|
||||
tx := helpers.GenTx(
|
||||
[]sdk.Msg{msg},
|
||||
fees,
|
||||
chainID,
|
||||
[]uint64{account.GetAccountNumber()},
|
||||
[]uint64{account.GetSequence()},
|
||||
simAccount.PrivKey,
|
||||
)
|
||||
|
||||
res := app.Deliver(tx)
|
||||
if !res.IsOK() {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, errors.New(res.Log)
|
||||
}
|
||||
|
||||
return simulation.NewOperationMsg(msg, true, ""), nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// SimulateMsgBeginRedelegate generates a MsgBeginRedelegate with random values
|
||||
// nolint: funlen
|
||||
func SimulateMsgBeginRedelegate(ak types.AccountKeeper, k keeper.Keeper) simulation.Operation {
|
||||
return func(
|
||||
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string,
|
||||
) (simulation.OperationMsg, []simulation.FutureOperation, error) {
|
||||
|
||||
// get random source validator
|
||||
srcVal, ok := keeper.RandomValidator(r, k, ctx)
|
||||
if !ok {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
||||
}
|
||||
srcAddr := srcVal.GetOperator()
|
||||
|
||||
delegations := k.GetValidatorDelegations(ctx, srcAddr)
|
||||
|
||||
// get random delegator from src validator
|
||||
delegation := delegations[r.Intn(len(delegations))]
|
||||
delAddr := delegation.GetDelegatorAddr()
|
||||
|
||||
if k.HasReceivingRedelegation(ctx, delAddr, srcAddr) {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil // skip
|
||||
}
|
||||
|
||||
// get random destination validator
|
||||
destVal, ok := keeper.RandomValidator(r, k, ctx)
|
||||
if !ok {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
||||
}
|
||||
destAddr := destVal.GetOperator()
|
||||
|
||||
if srcAddr.Equals(destAddr) ||
|
||||
destVal.InvalidExRate() ||
|
||||
k.HasMaxRedelegationEntries(ctx, delAddr, srcAddr, destAddr) {
|
||||
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
||||
}
|
||||
|
||||
totalBond := srcVal.TokensFromShares(delegation.GetShares()).TruncateInt()
|
||||
if !totalBond.IsPositive() {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
||||
}
|
||||
|
||||
redAmt, err := simulation.RandPositiveInt(r, totalBond)
|
||||
if err != nil {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, err
|
||||
}
|
||||
|
||||
if redAmt.IsZero() {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
||||
}
|
||||
|
||||
// check if the shares truncate to zero
|
||||
shares, err := srcVal.SharesFromTokens(redAmt)
|
||||
if err != nil {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, err
|
||||
}
|
||||
|
||||
if srcVal.TokensFromShares(shares).TruncateInt().IsZero() {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, nil // skip
|
||||
}
|
||||
|
||||
// need to retrieve the simulation account associated with delegation to retrieve PrivKey
|
||||
var simAccount simulation.Account
|
||||
for _, simAcc := range accs {
|
||||
if simAcc.Address.Equals(delAddr) {
|
||||
simAccount = simAcc
|
||||
break
|
||||
}
|
||||
}
|
||||
// if simaccount.PrivKey == nil, delegation address does not exist in accs. Return error
|
||||
if simAccount.PrivKey == nil {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, fmt.Errorf("Delegation addr: %s does not exist in simulation accounts", delAddr)
|
||||
}
|
||||
|
||||
// get tx fees
|
||||
account := ak.GetAccount(ctx, delAddr)
|
||||
fees, err := simulation.RandomFees(r, ctx, account.SpendableCoins(ctx.BlockTime()))
|
||||
if err != nil {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, err
|
||||
}
|
||||
|
||||
msg := types.NewMsgBeginRedelegate(
|
||||
delAddr, srcAddr, destAddr,
|
||||
sdk.NewCoin(k.BondDenom(ctx), redAmt),
|
||||
)
|
||||
|
||||
tx := helpers.GenTx(
|
||||
[]sdk.Msg{msg},
|
||||
fees,
|
||||
chainID,
|
||||
[]uint64{account.GetAccountNumber()},
|
||||
[]uint64{account.GetSequence()},
|
||||
simAccount.PrivKey,
|
||||
)
|
||||
|
||||
res := app.Deliver(tx)
|
||||
if !res.IsOK() {
|
||||
return simulation.NoOpMsg(types.ModuleName), nil, errors.New(res.Log)
|
||||
}
|
||||
|
||||
return simulation.NewOperationMsg(msg, true, ""), nil, nil
|
||||
}
|
||||
}
|
|
@ -1,227 +0,0 @@
|
|||
package operations
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking/keeper"
|
||||
)
|
||||
|
||||
// SimulateMsgCreateValidator generates a MsgCreateValidator with random values
|
||||
func SimulateMsgCreateValidator(m auth.AccountKeeper, k staking.Keeper) simulation.Operation {
|
||||
handler := staking.NewHandler(k)
|
||||
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
|
||||
accs []simulation.Account) (
|
||||
opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) {
|
||||
|
||||
denom := k.GetParams(ctx).BondDenom
|
||||
description := staking.NewDescription(
|
||||
simulation.RandStringOfLength(r, 10),
|
||||
simulation.RandStringOfLength(r, 10),
|
||||
simulation.RandStringOfLength(r, 10),
|
||||
simulation.RandStringOfLength(r, 10),
|
||||
simulation.RandStringOfLength(r, 10),
|
||||
)
|
||||
|
||||
maxCommission := sdk.NewDecWithPrec(r.Int63n(1000), 3)
|
||||
commission := staking.NewCommissionRates(
|
||||
simulation.RandomDecAmount(r, maxCommission),
|
||||
maxCommission,
|
||||
simulation.RandomDecAmount(r, maxCommission),
|
||||
)
|
||||
|
||||
acc := simulation.RandomAcc(r, accs)
|
||||
address := sdk.ValAddress(acc.Address)
|
||||
amount := m.GetAccount(ctx, acc.Address).GetCoins().AmountOf(denom)
|
||||
if amount.IsPositive() {
|
||||
amount = simulation.RandomAmount(r, amount)
|
||||
}
|
||||
|
||||
if amount.Equal(sdk.ZeroInt()) {
|
||||
return simulation.NoOpMsg(staking.ModuleName), nil, nil
|
||||
}
|
||||
|
||||
selfDelegation := sdk.NewCoin(denom, amount)
|
||||
msg := staking.NewMsgCreateValidator(address, acc.PubKey,
|
||||
selfDelegation, description, commission, sdk.OneInt())
|
||||
|
||||
if msg.ValidateBasic() != nil {
|
||||
return simulation.NoOpMsg(staking.ModuleName), nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
|
||||
}
|
||||
|
||||
ctx, write := ctx.CacheContext()
|
||||
ok := handler(ctx, msg).IsOK()
|
||||
if ok {
|
||||
write()
|
||||
}
|
||||
|
||||
opMsg = simulation.NewOperationMsg(msg, ok, "")
|
||||
return opMsg, nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// SimulateMsgEditValidator generates a MsgEditValidator with random values
|
||||
func SimulateMsgEditValidator(k staking.Keeper) simulation.Operation {
|
||||
handler := staking.NewHandler(k)
|
||||
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
|
||||
accs []simulation.Account) (opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) {
|
||||
|
||||
description := staking.NewDescription(
|
||||
simulation.RandStringOfLength(r, 10),
|
||||
simulation.RandStringOfLength(r, 10),
|
||||
simulation.RandStringOfLength(r, 10),
|
||||
simulation.RandStringOfLength(r, 10),
|
||||
simulation.RandStringOfLength(r, 10),
|
||||
)
|
||||
|
||||
if len(k.GetAllValidators(ctx)) == 0 {
|
||||
return simulation.NoOpMsg(staking.ModuleName), nil, nil
|
||||
}
|
||||
val := keeper.RandomValidator(r, k, ctx)
|
||||
address := val.GetOperator()
|
||||
newCommissionRate := simulation.RandomDecAmount(r, val.Commission.MaxRate)
|
||||
|
||||
msg := staking.NewMsgEditValidator(address, description, &newCommissionRate, nil)
|
||||
|
||||
if msg.ValidateBasic() != nil {
|
||||
return simulation.NoOpMsg(staking.ModuleName), nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
|
||||
}
|
||||
ctx, write := ctx.CacheContext()
|
||||
ok := handler(ctx, msg).IsOK()
|
||||
if ok {
|
||||
write()
|
||||
}
|
||||
opMsg = simulation.NewOperationMsg(msg, ok, "")
|
||||
return opMsg, nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// SimulateMsgDelegate generates a MsgDelegate with random values
|
||||
func SimulateMsgDelegate(m auth.AccountKeeper, k staking.Keeper) simulation.Operation {
|
||||
handler := staking.NewHandler(k)
|
||||
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
|
||||
accs []simulation.Account) (opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) {
|
||||
|
||||
denom := k.GetParams(ctx).BondDenom
|
||||
if len(k.GetAllValidators(ctx)) == 0 {
|
||||
return simulation.NoOpMsg(staking.ModuleName), nil, nil
|
||||
}
|
||||
val := keeper.RandomValidator(r, k, ctx)
|
||||
validatorAddress := val.GetOperator()
|
||||
delegatorAcc := simulation.RandomAcc(r, accs)
|
||||
delegatorAddress := delegatorAcc.Address
|
||||
amount := m.GetAccount(ctx, delegatorAddress).GetCoins().AmountOf(denom)
|
||||
if amount.IsPositive() {
|
||||
amount = simulation.RandomAmount(r, amount)
|
||||
}
|
||||
if amount.Equal(sdk.ZeroInt()) {
|
||||
return simulation.NoOpMsg(staking.ModuleName), nil, nil
|
||||
}
|
||||
|
||||
msg := staking.NewMsgDelegate(
|
||||
delegatorAddress, validatorAddress, sdk.NewCoin(denom, amount))
|
||||
|
||||
if msg.ValidateBasic() != nil {
|
||||
return simulation.NoOpMsg(staking.ModuleName), nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
|
||||
}
|
||||
ctx, write := ctx.CacheContext()
|
||||
ok := handler(ctx, msg).IsOK()
|
||||
if ok {
|
||||
write()
|
||||
}
|
||||
opMsg = simulation.NewOperationMsg(msg, ok, "")
|
||||
return opMsg, nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// SimulateMsgUndelegate generates a MsgUndelegate with random values
|
||||
func SimulateMsgUndelegate(m auth.AccountKeeper, k staking.Keeper) simulation.Operation {
|
||||
handler := staking.NewHandler(k)
|
||||
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
|
||||
accs []simulation.Account) (opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) {
|
||||
|
||||
delegatorAcc := simulation.RandomAcc(r, accs)
|
||||
delegatorAddress := delegatorAcc.Address
|
||||
delegations := k.GetAllDelegatorDelegations(ctx, delegatorAddress)
|
||||
if len(delegations) == 0 {
|
||||
return simulation.NoOpMsg(staking.ModuleName), nil, nil
|
||||
}
|
||||
delegation := delegations[r.Intn(len(delegations))]
|
||||
|
||||
validator, found := k.GetValidator(ctx, delegation.GetValidatorAddr())
|
||||
if !found {
|
||||
return simulation.NoOpMsg(staking.ModuleName), nil, nil
|
||||
}
|
||||
|
||||
totalBond := validator.TokensFromShares(delegation.GetShares()).TruncateInt()
|
||||
unbondAmt := simulation.RandomAmount(r, totalBond)
|
||||
if unbondAmt.Equal(sdk.ZeroInt()) {
|
||||
return simulation.NoOpMsg(staking.ModuleName), nil, nil
|
||||
}
|
||||
|
||||
msg := staking.NewMsgUndelegate(
|
||||
delegatorAddress, delegation.ValidatorAddress, sdk.NewCoin(k.GetParams(ctx).BondDenom, unbondAmt),
|
||||
)
|
||||
if msg.ValidateBasic() != nil {
|
||||
return simulation.NoOpMsg(staking.ModuleName), nil, fmt.Errorf("expected msg to pass ValidateBasic: %s, got error %v",
|
||||
msg.GetSignBytes(), msg.ValidateBasic())
|
||||
}
|
||||
|
||||
ctx, write := ctx.CacheContext()
|
||||
ok := handler(ctx, msg).IsOK()
|
||||
if ok {
|
||||
write()
|
||||
}
|
||||
|
||||
opMsg = simulation.NewOperationMsg(msg, ok, "")
|
||||
return opMsg, nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// SimulateMsgBeginRedelegate generates a MsgBeginRedelegate with random values
|
||||
func SimulateMsgBeginRedelegate(m auth.AccountKeeper, k staking.Keeper) simulation.Operation {
|
||||
handler := staking.NewHandler(k)
|
||||
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
|
||||
accs []simulation.Account) (opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) {
|
||||
|
||||
denom := k.GetParams(ctx).BondDenom
|
||||
if len(k.GetAllValidators(ctx)) == 0 {
|
||||
return simulation.NoOpMsg(staking.ModuleName), nil, nil
|
||||
}
|
||||
srcVal := keeper.RandomValidator(r, k, ctx)
|
||||
srcValidatorAddress := srcVal.GetOperator()
|
||||
destVal := keeper.RandomValidator(r, k, ctx)
|
||||
destValidatorAddress := destVal.GetOperator()
|
||||
delegatorAcc := simulation.RandomAcc(r, accs)
|
||||
delegatorAddress := delegatorAcc.Address
|
||||
// TODO
|
||||
amount := m.GetAccount(ctx, delegatorAddress).GetCoins().AmountOf(denom)
|
||||
if amount.IsPositive() {
|
||||
amount = simulation.RandomAmount(r, amount)
|
||||
}
|
||||
if amount.Equal(sdk.ZeroInt()) {
|
||||
return simulation.NoOpMsg(staking.ModuleName), nil, nil
|
||||
}
|
||||
|
||||
msg := staking.NewMsgBeginRedelegate(
|
||||
delegatorAddress, srcValidatorAddress, destValidatorAddress, sdk.NewCoin(denom, amount),
|
||||
)
|
||||
if msg.ValidateBasic() != nil {
|
||||
return simulation.NoOpMsg(staking.ModuleName), nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
|
||||
}
|
||||
|
||||
ctx, write := ctx.CacheContext()
|
||||
ok := handler(ctx, msg).IsOK()
|
||||
if ok {
|
||||
write()
|
||||
}
|
||||
|
||||
opMsg = simulation.NewOperationMsg(msg, ok, "")
|
||||
return opMsg, nil, nil
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@ type DistributionKeeper interface {
|
|||
// AccountKeeper defines the expected account keeper (noalias)
|
||||
type AccountKeeper interface {
|
||||
IterateAccounts(ctx sdk.Context, process func(authexported.Account) (stop bool))
|
||||
GetAccount(ctx sdk.Context, addr sdk.AccAddress) authexported.Account // only used for simulation
|
||||
}
|
||||
|
||||
// SupplyKeeper defines the expected supply Keeper (noalias)
|
||||
|
|
Loading…
Reference in New Issue