simulation: Make governance simulation use future operations to schedule
votes. In a future PR, functionality to test that slashing occured properly should be added.
This commit is contained in:
parent
7f1b06a724
commit
753e58bac0
|
@ -93,9 +93,8 @@ func appStateFn(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json
|
||||||
func testAndRunTxs(app *GaiaApp) []simulation.Operation {
|
func testAndRunTxs(app *GaiaApp) []simulation.Operation {
|
||||||
return []simulation.Operation{
|
return []simulation.Operation{
|
||||||
banksim.SimulateSingleInputMsgSend(app.accountMapper),
|
banksim.SimulateSingleInputMsgSend(app.accountMapper),
|
||||||
govsim.SimulateMsgSubmitProposal(app.govKeeper, app.stakeKeeper),
|
govsim.SimulateSubmittingVotingAndSlashingForProposal(app.govKeeper, app.stakeKeeper),
|
||||||
govsim.SimulateMsgDeposit(app.govKeeper, app.stakeKeeper),
|
govsim.SimulateMsgDeposit(app.govKeeper, app.stakeKeeper),
|
||||||
govsim.SimulateMsgVote(app.govKeeper, app.stakeKeeper),
|
|
||||||
stakesim.SimulateMsgCreateValidator(app.accountMapper, app.stakeKeeper),
|
stakesim.SimulateMsgCreateValidator(app.accountMapper, app.stakeKeeper),
|
||||||
stakesim.SimulateMsgEditValidator(app.stakeKeeper),
|
stakesim.SimulateMsgEditValidator(app.stakeKeeper),
|
||||||
stakesim.SimulateMsgDelegate(app.accountMapper, app.stakeKeeper),
|
stakesim.SimulateMsgDelegate(app.accountMapper, app.stakeKeeper),
|
||||||
|
|
|
@ -2,6 +2,7 @@ package simulation
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -18,12 +19,73 @@ const (
|
||||||
denom = "steak"
|
denom = "steak"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 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, sk stake.Keeper) 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(tb testing.TB, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOps []simulation.FutureOperation, err sdk.Error) {
|
||||||
|
// 1) submit proposal now
|
||||||
|
sender := simulation.RandomKey(r, keys)
|
||||||
|
msg := simulationCreateMsgSubmitProposal(tb, r, sender, log)
|
||||||
|
action = simulateHandleMsgSubmitProposal(msg, sk, handler, ctx, event)
|
||||||
|
proposalID := k.GetLastProposalID(ctx)
|
||||||
|
// 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(keys)) * statePercentageArray[curNumVotesState]))
|
||||||
|
// 2.2) select who votes and when
|
||||||
|
whoVotes := r.Perm(len(keys))
|
||||||
|
// didntVote := whoVotes[numVotes:]
|
||||||
|
whoVotes = whoVotes[:numVotes]
|
||||||
|
votingPeriod := k.GetVotingProcedure(ctx).VotingPeriod
|
||||||
|
fops := make([]simulation.FutureOperation, numVotes+1)
|
||||||
|
for i := 0; i < numVotes; i++ {
|
||||||
|
whenVote := ctx.BlockHeight() + r.Int63n(votingPeriod)
|
||||||
|
fops[i] = simulation.FutureOperation{int(whenVote), operationSimulateMsgVote(k, sk, keys[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 action, fops, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SimulateMsgSubmitProposal simulates a msg Submit Proposal
|
// SimulateMsgSubmitProposal simulates a msg Submit Proposal
|
||||||
// Note: Currently doesn't ensure that the proposal txt is in JSON form
|
// Note: Currently doesn't ensure that the proposal txt is in JSON form
|
||||||
func SimulateMsgSubmitProposal(k gov.Keeper, sk stake.Keeper) simulation.Operation {
|
func SimulateMsgSubmitProposal(k gov.Keeper, sk stake.Keeper) simulation.Operation {
|
||||||
handler := gov.NewHandler(k)
|
handler := gov.NewHandler(k)
|
||||||
return func(tb testing.TB, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOps []simulation.FutureOperation, err sdk.Error) {
|
return func(tb testing.TB, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOps []simulation.FutureOperation, err sdk.Error) {
|
||||||
msg := simulationCreateMsgSubmitProposal(tb, r, keys, log)
|
sender := simulation.RandomKey(r, keys)
|
||||||
|
msg := simulationCreateMsgSubmitProposal(tb, r, sender, log)
|
||||||
|
action = simulateHandleMsgSubmitProposal(msg, sk, handler, ctx, event)
|
||||||
|
return action, nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func simulateHandleMsgSubmitProposal(msg gov.MsgSubmitProposal, sk stake.Keeper, handler sdk.Handler, ctx sdk.Context, event func(string)) (action string) {
|
||||||
ctx, write := ctx.CacheContext()
|
ctx, write := ctx.CacheContext()
|
||||||
result := handler(ctx, msg)
|
result := handler(ctx, msg)
|
||||||
if result.IsOK() {
|
if result.IsOK() {
|
||||||
|
@ -35,13 +97,11 @@ func SimulateMsgSubmitProposal(k gov.Keeper, sk stake.Keeper) simulation.Operati
|
||||||
}
|
}
|
||||||
event(fmt.Sprintf("gov/MsgSubmitProposal/%v", result.IsOK()))
|
event(fmt.Sprintf("gov/MsgSubmitProposal/%v", result.IsOK()))
|
||||||
action = fmt.Sprintf("TestMsgSubmitProposal: ok %v, msg %s", result.IsOK(), msg.GetSignBytes())
|
action = fmt.Sprintf("TestMsgSubmitProposal: ok %v, msg %s", result.IsOK(), msg.GetSignBytes())
|
||||||
return action, nil, nil
|
return action
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func simulationCreateMsgSubmitProposal(tb testing.TB, r *rand.Rand, keys []crypto.PrivKey, log string) gov.MsgSubmitProposal {
|
func simulationCreateMsgSubmitProposal(tb testing.TB, r *rand.Rand, sender crypto.PrivKey, log string) gov.MsgSubmitProposal {
|
||||||
key := simulation.RandomKey(r, keys)
|
addr := sdk.AccAddress(sender.PubKey().Address())
|
||||||
addr := sdk.AccAddress(key.PubKey().Address())
|
|
||||||
deposit := randomDeposit(r)
|
deposit := randomDeposit(r)
|
||||||
msg := gov.NewMsgSubmitProposal(
|
msg := gov.NewMsgSubmitProposal(
|
||||||
simulation.RandStringOfLength(r, 5),
|
simulation.RandStringOfLength(r, 5),
|
||||||
|
@ -87,13 +147,22 @@ func SimulateMsgDeposit(k gov.Keeper, sk stake.Keeper) simulation.Operation {
|
||||||
|
|
||||||
// SimulateMsgVote
|
// SimulateMsgVote
|
||||||
func SimulateMsgVote(k gov.Keeper, sk stake.Keeper) simulation.Operation {
|
func SimulateMsgVote(k gov.Keeper, sk stake.Keeper) simulation.Operation {
|
||||||
|
return operationSimulateMsgVote(k, sk, nil, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func operationSimulateMsgVote(k gov.Keeper, sk stake.Keeper, key crypto.PrivKey, proposalID int64) simulation.Operation {
|
||||||
return func(tb testing.TB, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
|
return func(tb testing.TB, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
|
||||||
key := simulation.RandomKey(r, keys)
|
if key == nil {
|
||||||
addr := sdk.AccAddress(key.PubKey().Address())
|
key = simulation.RandomKey(r, keys)
|
||||||
proposalID, ok := randomProposalID(r, k, ctx)
|
}
|
||||||
|
var ok bool
|
||||||
|
if proposalID < 0 {
|
||||||
|
proposalID, ok = randomProposalID(r, k, ctx)
|
||||||
if !ok {
|
if !ok {
|
||||||
return "no-operation", nil, nil
|
return "no-operation", nil, nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
addr := sdk.AccAddress(key.PubKey().Address())
|
||||||
option := randomVotingOption(r)
|
option := randomVotingOption(r)
|
||||||
msg := gov.NewMsgVote(addr, proposalID, option)
|
msg := gov.NewMsgVote(addr, proposalID, option)
|
||||||
if msg.ValidateBasic() != nil {
|
if msg.ValidateBasic() != nil {
|
||||||
|
|
|
@ -53,6 +53,7 @@ func TestGovWithRandomMessages(t *testing.T) {
|
||||||
gov.InitGenesis(ctx, govKeeper, gov.DefaultGenesisState())
|
gov.InitGenesis(ctx, govKeeper, gov.DefaultGenesisState())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test with unscheduled votes
|
||||||
simulation.Simulate(
|
simulation.Simulate(
|
||||||
t, mapp.BaseApp, appStateFn,
|
t, mapp.BaseApp, appStateFn,
|
||||||
[]simulation.Operation{
|
[]simulation.Operation{
|
||||||
|
@ -66,4 +67,18 @@ func TestGovWithRandomMessages(t *testing.T) {
|
||||||
}, 10, 100,
|
}, 10, 100,
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Test with scheduled votes
|
||||||
|
simulation.Simulate(
|
||||||
|
t, mapp.BaseApp, appStateFn,
|
||||||
|
[]simulation.Operation{
|
||||||
|
SimulateSubmittingVotingAndSlashingForProposal(govKeeper, stakeKeeper),
|
||||||
|
SimulateMsgDeposit(govKeeper, stakeKeeper),
|
||||||
|
}, []simulation.RandSetup{
|
||||||
|
setup,
|
||||||
|
}, []simulation.Invariant{
|
||||||
|
AllInvariants(),
|
||||||
|
}, 10, 100,
|
||||||
|
false,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue