Fix non-deterministic map iteration in fuzzer (#2069)

* This demonstrates that the state machine is non-deterministic if there
are more than two txs in a block.

* fix non-deterministic map iteration

* (squash this) fix build errors

* (squash this) iterate using range
This commit is contained in:
Dev Ojha 2018-08-16 14:45:07 -07:00 committed by Christopher Goes
parent 45bd414fc2
commit 8bb79d12ca
5 changed files with 48 additions and 20 deletions

View File

@ -130,17 +130,13 @@ func TestFullGaiaSimulation(t *testing.T) {
},
numBlocks,
blockSize,
false,
)
}
// TODO: Make this not depend on Gaia or any of the modules,
// and place it in random_simulation_test.go
//
// Test doesn't use `app.ExportAppStateAndValidators` as that panics with the following:
// panic: Stored pool should not have been nil [recovered]
// panic: Stored pool should not have been nil
// Change to `app.ExportAppStateAndValidators` once it is fixed
// TODO: Make another test for the fuzzer itself, which just has noOp txs
// and doesn't depend on gaia
func TestAppStateDeterminism(t *testing.T) {
numTimesToRun := 5
appHashList := make([]json.RawMessage, numTimesToRun)
@ -152,27 +148,39 @@ func TestAppStateDeterminism(t *testing.T) {
app := NewGaiaApp(logger, db, nil)
noOpInvariant := func(t *testing.T, baseapp *baseapp.BaseApp, log string) {}
noOpTestAndRunTx := func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
privKeys []crypto.PrivKey, log string, event func(string),
) (action string, err sdk.Error) {
return "", nil
}
// noOpTestAndRunTx := func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
// privKeys []crypto.PrivKey, log string, event func(string),
// ) (action string, err sdk.Error) {
// return "", nil
// }
// Run randomized simulation
simulation.SimulateFromSeed(
t, app.BaseApp, appStateFn, seed,
[]simulation.TestAndRunTx{
noOpTestAndRunTx,
banksim.TestAndRunSingleInputMsgSend(app.accountMapper),
govsim.SimulateMsgSubmitProposal(app.govKeeper, app.stakeKeeper),
govsim.SimulateMsgDeposit(app.govKeeper, app.stakeKeeper),
govsim.SimulateMsgVote(app.govKeeper, app.stakeKeeper),
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),
slashingsim.SimulateMsgUnrevoke(app.slashingKeeper),
},
[]simulation.RandSetup{},
[]simulation.Invariant{noOpInvariant},
0,
10,
20,
20,
true,
)
appHash := app.LastCommitID().Hash
appHashList[i] = appHash
}
for i := 1; i < numTimesToRun; i++ {
require.Equal(t, appHashList[0], appHashList[i])
require.Equal(t, appHashList[0], appHashList[i], "appHashes: %v", appHashList)
}
}

View File

@ -42,5 +42,6 @@ func TestBankWithRandomMessages(t *testing.T) {
TotalCoinsInvariant(mapper, func() sdk.Coins { return mapp.TotalCoinsSupply }),
},
30, 30,
false,
)
}

View File

@ -64,5 +64,6 @@ func TestGovWithRandomMessages(t *testing.T) {
}, []simulation.Invariant{
AllInvariants(),
}, 10, 100,
false,
)
}

View File

@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"math/rand"
"sort"
"testing"
"time"
@ -20,17 +21,17 @@ import (
// Simulate tests application by sending random messages.
func Simulate(
t *testing.T, app *baseapp.BaseApp, appStateFn func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage, ops []TestAndRunTx, setups []RandSetup,
invariants []Invariant, numBlocks int, blockSize int,
invariants []Invariant, numBlocks int, blockSize int, commit bool,
) {
time := time.Now().UnixNano()
SimulateFromSeed(t, app, appStateFn, time, ops, setups, invariants, numBlocks, blockSize)
SimulateFromSeed(t, app, appStateFn, time, ops, setups, invariants, numBlocks, blockSize, commit)
}
// SimulateFromSeed tests an application by running the provided
// operations, testing the provided invariants, but using the provided seed.
func SimulateFromSeed(
t *testing.T, app *baseapp.BaseApp, appStateFn func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage, seed int64, ops []TestAndRunTx, setups []RandSetup,
invariants []Invariant, numBlocks int, blockSize int,
invariants []Invariant, numBlocks int, blockSize int, commit bool,
) {
log := fmt.Sprintf("Starting SimulateFromSeed with randomness created with seed %d", int(seed))
fmt.Printf("%s\n", log)
@ -104,6 +105,9 @@ func SimulateFromSeed(
}
res := app.EndBlock(abci.RequestEndBlock{})
if commit {
app.Commit()
}
header.Height++
header.Time = header.Time.Add(time.Duration(minTimePerBlock) * time.Second).Add(time.Duration(int64(r.Intn(int(timeDiff)))) * time.Second)
@ -123,6 +127,17 @@ func SimulateFromSeed(
DisplayEvents(events)
}
func getKeys(validators map[string]mockValidator) []string {
keys := make([]string, len(validators))
i := 0
for key := range validators {
keys[i] = key
i++
}
sort.Strings(keys)
return keys
}
// RandomRequestBeginBlock generates a list of signing validators according to the provided list of validators, signing fraction, and evidence fraction
func RandomRequestBeginBlock(t *testing.T, r *rand.Rand, validators map[string]mockValidator, livenessTransitions TransitionMatrix, evidenceFraction float64,
pastTimes []time.Time, event func(string), header abci.Header, log string) abci.RequestBeginBlock {
@ -131,7 +146,9 @@ func RandomRequestBeginBlock(t *testing.T, r *rand.Rand, validators map[string]m
}
signingValidators := make([]abci.SigningValidator, len(validators))
i := 0
for _, mVal := range validators {
for _, key := range getKeys(validators) {
mVal := validators[key]
mVal.livenessState = livenessTransitions.NextState(r, mVal.livenessState)
signed := true

View File

@ -57,5 +57,6 @@ func TestStakeWithRandomMessages(t *testing.T) {
}, []simulation.Invariant{
AllInvariants(coinKeeper, stakeKeeper, mapp.AccountMapper),
}, 10, 100,
false,
)
}