-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
simulation: Make governance simulation use future operations to schedule votes #2226
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ package simulation | |
|
||
import ( | ||
"fmt" | ||
"math" | ||
"math/rand" | ||
"testing" | ||
|
||
|
@@ -18,30 +19,89 @@ const ( | |
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 | ||
// Note: Currently doesn't ensure that the proposal txt is in JSON form | ||
func SimulateMsgSubmitProposal(k gov.Keeper, sk stake.Keeper) simulation.Operation { | ||
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) { | ||
msg := simulationCreateMsgSubmitProposal(tb, r, keys, log) | ||
ctx, write := ctx.CacheContext() | ||
result := handler(ctx, msg) | ||
if result.IsOK() { | ||
// Update pool to keep invariants | ||
pool := sk.GetPool(ctx) | ||
pool.LooseTokens = pool.LooseTokens.Sub(sdk.NewDecFromInt(msg.InitialDeposit.AmountOf(denom))) | ||
sk.SetPool(ctx, pool) | ||
write() | ||
} | ||
event(fmt.Sprintf("gov/MsgSubmitProposal/%v", result.IsOK())) | ||
action = fmt.Sprintf("TestMsgSubmitProposal: ok %v, msg %s", result.IsOK(), msg.GetSignBytes()) | ||
sender := simulation.RandomKey(r, keys) | ||
msg := simulationCreateMsgSubmitProposal(tb, r, sender, log) | ||
action = simulateHandleMsgSubmitProposal(msg, sk, handler, ctx, event) | ||
return action, nil, nil | ||
} | ||
} | ||
|
||
func simulationCreateMsgSubmitProposal(tb testing.TB, r *rand.Rand, keys []crypto.PrivKey, log string) gov.MsgSubmitProposal { | ||
key := simulation.RandomKey(r, keys) | ||
addr := sdk.AccAddress(key.PubKey().Address()) | ||
func simulateHandleMsgSubmitProposal(msg gov.MsgSubmitProposal, sk stake.Keeper, handler sdk.Handler, ctx sdk.Context, event func(string)) (action string) { | ||
ctx, write := ctx.CacheContext() | ||
result := handler(ctx, msg) | ||
if result.IsOK() { | ||
// Update pool to keep invariants | ||
pool := sk.GetPool(ctx) | ||
pool.LooseTokens = pool.LooseTokens.Sub(sdk.NewDecFromInt(msg.InitialDeposit.AmountOf(denom))) | ||
sk.SetPool(ctx, pool) | ||
write() | ||
} | ||
event(fmt.Sprintf("gov/MsgSubmitProposal/%v", result.IsOK())) | ||
action = fmt.Sprintf("TestMsgSubmitProposal: ok %v, msg %s", result.IsOK(), msg.GetSignBytes()) | ||
return action | ||
} | ||
|
||
func simulationCreateMsgSubmitProposal(tb testing.TB, r *rand.Rand, sender crypto.PrivKey, log string) gov.MsgSubmitProposal { | ||
addr := sdk.AccAddress(sender.PubKey().Address()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In a future PR, we should make a simulation key object which has the privkey, pubkey, and address. The simulator spends ~3% of its time computing pubkeys. Improving this will allow us to simulate greater block sizes. (After we reduce how often governance proposals are. The amount of slashes they induce is what causes the simulator to slow down at blocks > 200) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good idea |
||
deposit := randomDeposit(r) | ||
msg := gov.NewMsgSubmitProposal( | ||
simulation.RandStringOfLength(r, 5), | ||
|
@@ -87,13 +147,22 @@ func SimulateMsgDeposit(k gov.Keeper, sk stake.Keeper) simulation.Operation { | |
|
||
// SimulateMsgVote | ||
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) { | ||
key := simulation.RandomKey(r, keys) | ||
addr := sdk.AccAddress(key.PubKey().Address()) | ||
proposalID, ok := randomProposalID(r, k, ctx) | ||
if !ok { | ||
return "no-operation", nil, nil | ||
if key == nil { | ||
key = simulation.RandomKey(r, keys) | ||
} | ||
var ok bool | ||
if proposalID < 0 { | ||
proposalID, ok = randomProposalID(r, k, ctx) | ||
if !ok { | ||
return "no-operation", nil, nil | ||
} | ||
} | ||
addr := sdk.AccAddress(key.PubKey().Address()) | ||
option := randomVotingOption(r) | ||
msg := gov.NewMsgVote(addr, proposalID, option) | ||
if msg.ValidateBasic() != nil { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of this code has been moved to
simulateHandleMsgSubmitProposal
, which just is a verbatim copy of this.