-
Notifications
You must be signed in to change notification settings - Fork 3.7k
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
R4R: Simulation framework, including staking simulation #1620
Changes from all commits
6a119f6
a332293
940cfa9
a3d8b38
27f157a
5e3cd77
0572a27
53138fb
88364c8
4623923
f9f326c
50d384a
601251d
bb217b9
d8ec44e
9bd09e6
c272db0
405bb53
a6dd96d
a4e7216
9ad3d62
a49f9d6
cbcd0f0
d171dbf
cbc9d7d
af206bd
eda7eb4
253b82f
5918ab1
c61b1aa
6c61577
966f26d
bf83385
ad410c1
8bd54f0
05ceff5
cea2be6
dc14eef
dcbd13c
2c0cd73
16d4e07
423d0c4
1e5a799
ee29e10
7e88a50
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 |
---|---|---|
@@ -0,0 +1,100 @@ | ||
package app | ||
|
||
import ( | ||
"encoding/json" | ||
"flag" | ||
"math/rand" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
|
||
dbm "github.com/tendermint/tendermint/libs/db" | ||
"github.com/tendermint/tendermint/libs/log" | ||
|
||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
banksim "github.com/cosmos/cosmos-sdk/x/bank/simulation" | ||
"github.com/cosmos/cosmos-sdk/x/mock/simulation" | ||
stake "github.com/cosmos/cosmos-sdk/x/stake" | ||
stakesim "github.com/cosmos/cosmos-sdk/x/stake/simulation" | ||
) | ||
|
||
var ( | ||
seed int64 | ||
numKeys int | ||
numBlocks int | ||
blockSize int | ||
enabled bool | ||
) | ||
|
||
func init() { | ||
flag.Int64Var(&seed, "SimulationSeed", 42, "Simulation random seed") | ||
flag.IntVar(&numKeys, "SimulationNumKeys", 10, "Number of keys (accounts)") | ||
flag.IntVar(&numBlocks, "SimulationNumBlocks", 100, "Number of blocks") | ||
flag.IntVar(&blockSize, "SimulationBlockSize", 100, "Operations per block") | ||
flag.BoolVar(&enabled, "SimulationEnabled", false, "Enable the simulation") | ||
} | ||
|
||
func appStateFn(r *rand.Rand, accs []sdk.AccAddress) json.RawMessage { | ||
var genesisAccounts []GenesisAccount | ||
|
||
// Randomly generate some genesis accounts | ||
for _, addr := range accs { | ||
coins := sdk.Coins{sdk.Coin{"steak", sdk.NewInt(100)}} | ||
genesisAccounts = append(genesisAccounts, GenesisAccount{ | ||
Address: addr, | ||
Coins: coins, | ||
}) | ||
} | ||
|
||
// Default genesis state | ||
stakeGenesis := stake.DefaultGenesisState() | ||
stakeGenesis.Pool.LooseTokens = sdk.NewRat(1000) | ||
genesis := GenesisState{ | ||
Accounts: genesisAccounts, | ||
StakeData: stakeGenesis, | ||
} | ||
|
||
// Marshal genesis | ||
appState, err := MakeCodec().MarshalJSON(genesis) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
return appState | ||
} | ||
|
||
func TestFullGaiaSimulation(t *testing.T) { | ||
if !enabled { | ||
t.Skip("Skipping Gaia simulation") | ||
} | ||
|
||
// Setup Gaia application | ||
logger := log.NewNopLogger() | ||
db := dbm.NewMemDB() | ||
app := NewGaiaApp(logger, db, nil) | ||
require.Equal(t, "GaiaApp", app.Name()) | ||
|
||
// Run randomized simulation | ||
simulation.SimulateFromSeed( | ||
t, app.BaseApp, appStateFn, seed, | ||
[]simulation.TestAndRunTx{ | ||
banksim.TestAndRunSingleInputMsgSend(app.accountMapper), | ||
stakesim.SimulateMsgCreateValidator(app.accountMapper, app.stakeKeeper), | ||
stakesim.SimulateMsgEditValidator(app.stakeKeeper), | ||
stakesim.SimulateMsgDelegate(app.accountMapper, app.stakeKeeper), | ||
stakesim.SimulateMsgBeginUnbonding(app.accountMapper, app.stakeKeeper), | ||
stakesim.SimulateMsgCompleteUnbonding(app.stakeKeeper), | ||
stakesim.SimulateMsgBeginRedelegate(app.accountMapper, app.stakeKeeper), | ||
stakesim.SimulateMsgCompleteRedelegate(app.stakeKeeper), | ||
}, | ||
[]simulation.RandSetup{}, | ||
[]simulation.Invariant{ | ||
banksim.NonnegativeBalanceInvariant(app.accountMapper), | ||
stakesim.AllInvariants(app.coinKeeper, app.stakeKeeper, app.accountMapper), | ||
}, | ||
numKeys, | ||
numBlocks, | ||
blockSize, | ||
) | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,9 +15,13 @@ type Coin struct { | |
} | ||
|
||
func NewCoin(denom string, amount int64) Coin { | ||
return NewIntCoin(denom, NewInt(amount)) | ||
} | ||
|
||
func NewIntCoin(denom string, amount Int) Coin { | ||
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. I suggest flipping this API. Have NewCoin take in an Int, and have a 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. I agree, but that should be done separately since it requires changing hundreds of calls - #1723 |
||
return Coin{ | ||
Denom: denom, | ||
Amount: NewInt(amount), | ||
Amount: amount, | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package simulation | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
|
||
"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/mock" | ||
"github.com/cosmos/cosmos-sdk/x/mock/simulation" | ||
abci "github.com/tendermint/tendermint/abci/types" | ||
) | ||
|
||
// NonnegativeBalanceInvariant checks that all accounts in the application have non-negative balances | ||
func NonnegativeBalanceInvariant(mapper auth.AccountMapper) simulation.Invariant { | ||
return func(t *testing.T, app *baseapp.BaseApp, log string) { | ||
ctx := app.NewContext(false, abci.Header{}) | ||
accts := mock.GetAllAccounts(mapper, ctx) | ||
for _, acc := range accts { | ||
coins := acc.GetCoins() | ||
require.True(t, coins.IsNotNegative(), | ||
fmt.Sprintf("%s has a negative denomination of %s\n%s", | ||
acc.GetAddress().String(), | ||
coins.String(), | ||
log), | ||
) | ||
} | ||
} | ||
} | ||
|
||
// TotalCoinsInvariant checks that the sum of the coins across all accounts | ||
// is what is expected | ||
func TotalCoinsInvariant(mapper auth.AccountMapper, totalSupplyFn func() sdk.Coins) simulation.Invariant { | ||
return func(t *testing.T, app *baseapp.BaseApp, log string) { | ||
ctx := app.NewContext(false, abci.Header{}) | ||
totalCoins := sdk.Coins{} | ||
|
||
chkAccount := func(acc auth.Account) bool { | ||
coins := acc.GetCoins() | ||
totalCoins = totalCoins.Plus(coins) | ||
return false | ||
} | ||
|
||
mapper.IterateAccounts(ctx, chkAccount) | ||
require.Equal(t, totalSupplyFn(), totalCoins, log) | ||
} | ||
} |
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.
COOL!