Skip to content
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 improvements (logging fix, random genesis parameters) #2617

Merged
merged 23 commits into from
Nov 5, 2018
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
4e44ddf
Print out initial update on every block
cwgoes Oct 28, 2018
38ceea1
Randomize simulation parameters
cwgoes Oct 28, 2018
4e48217
Randomize initial liveness weightings
cwgoes Oct 28, 2018
46e6b40
Randomize genesis parameters
cwgoes Oct 28, 2018
3884b13
Update PENDING.md
cwgoes Oct 28, 2018
991573e
Avoid dividing-by-zero
cwgoes Oct 28, 2018
f5918db
More simulation seeds, more blocks in CI version
cwgoes Oct 29, 2018
e2aa3e9
Update cmd/gaia/app/sim_test.go
zmanian Oct 29, 2018
01c235d
Update cmd/gaia/app/sim_test.go
zmanian Oct 29, 2018
f13f602
Update cmd/gaia/app/sim_test.go
zmanian Oct 29, 2018
1868ee3
Merge remote-tracking branch 'origin/develop' into cwgoes/simulation-…
rigelrozanski Oct 31, 2018
a84cf4f
Merge tag 'v0.25.0' into develop
cwgoes Nov 1, 2018
fb0f4a4
Merge branch 'develop' into cwgoes/simulation-transubstantiated
cwgoes Nov 1, 2018
d9907cc
Address comments
cwgoes Nov 1, 2018
f695a66
Minimize variable passing
cwgoes Nov 1, 2018
39165c9
fixed power store invariant
sunnya97 Nov 2, 2018
3cf6687
PENDING
sunnya97 Nov 2, 2018
42eb4e2
IterateValidatorsBonded -> IterateBondedValidatorsByPower
sunnya97 Nov 2, 2018
888c061
WriteValidators uses IterateLastValidators rather than IterateBondedV…
sunnya97 Nov 2, 2018
13d933f
fixed democoin interface
sunnya97 Nov 2, 2018
191a732
fixed comment
sunnya97 Nov 3, 2018
559ebb7
Merge PR #2671: Power Store Invariant
rigelrozanski Nov 3, 2018
7b10ac4
Merge branch 'develop' into cwgoes/simulation-transubstantiated
Nov 3, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions PENDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ IMPROVEMENTS

* SDK
- #2573 [x/distribution] add accum invariance
- #2556 [x/mock/simulation] Fix debugging output
- #2396 [x/mock/simulation] Change parameters to get more slashes
- #2617 [x/mock/simulation] Randomize all genesis parameters

* Tendermint

Expand Down
78 changes: 65 additions & 13 deletions cmd/gaia/app/sim_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"math/rand"
"os"
"testing"
"time"

"github.com/stretchr/testify/require"

Expand Down Expand Up @@ -49,42 +50,93 @@ func init() {
func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage {
var genesisAccounts []GenesisAccount

amt := int64(10000)
amount := int64(r.Intn(1e6))
numInitiallyBonded := int64(r.Intn(250))
numAccs := int64(len(accs))
if numInitiallyBonded > numAccs {
numInitiallyBonded = numAccs
}
fmt.Printf("Selected randomly generated parameters for simulated genesis: {amount: %v, numInitiallyBonded: %v}\n", amount, numInitiallyBonded)
cwgoes marked this conversation as resolved.
Show resolved Hide resolved

// Randomly generate some genesis accounts
for _, acc := range accs {
coins := sdk.Coins{sdk.Coin{"steak", sdk.NewInt(amt)}}
coins := sdk.Coins{sdk.Coin{"steak", sdk.NewInt(amount)}}
genesisAccounts = append(genesisAccounts, GenesisAccount{
Address: acc.Address,
Coins: coins,
})
}

// Default genesis state
govGenesis := gov.DefaultGenesisState()
stakeGenesis := stake.DefaultGenesisState()
slashingGenesis := slashing.DefaultGenesisState()
// Random genesis states
govGenesis := gov.GenesisState{
StartingProposalID: int64(r.Intn(100)),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense for randomized genesis states to be defined within the module itself?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good question. I think they may need to be dependent (e.g. the max evidence age for slashing should be the unbonding period), although in that case I wonder if we should just share the parameters themselves...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we have that sort of requirement, they definitely should be dependent when being set or be the same variable

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets keep things here for a bit... we lose agility by organizing things too far, and simulation is an edge application, so I think it's fine as is for now.

DepositProcedure: gov.DepositProcedure{
MinDeposit: sdk.Coins{sdk.NewInt64Coin("steak", int64(r.Intn(1e3)))},
MaxDepositPeriod: time.Duration(r.Intn(2*172800)) * time.Second,
},
VotingProcedure: gov.VotingProcedure{
VotingPeriod: time.Duration(r.Intn(2*172800)) * time.Second,
},
TallyingProcedure: gov.TallyingProcedure{
Threshold: sdk.NewDecWithPrec(5, 1),
Veto: sdk.NewDecWithPrec(334, 3),
GovernancePenalty: sdk.NewDecWithPrec(1, 2),
},
}
fmt.Printf("Selected randomly generated governance parameters: %v\n", govGenesis)
cwgoes marked this conversation as resolved.
Show resolved Hide resolved
stakeGenesis := stake.GenesisState{
Pool: stake.InitialPool(),
Params: stake.Params{
UnbondingTime: time.Duration(r.Intn(60*60*24*3*2)) * time.Second,
MaxValidators: uint16(r.Intn(250)),
BondDenom: "steak",
},
}
fmt.Printf("Selected randomly generated staking parameters: %v\n", stakeGenesis)
cwgoes marked this conversation as resolved.
Show resolved Hide resolved
slashingGenesis := slashing.GenesisState{
Params: slashing.Params{
MaxEvidenceAge: stakeGenesis.Params.UnbondingTime,
DoubleSignUnbondDuration: time.Duration(r.Intn(60*60*24)) * time.Second,
SignedBlocksWindow: int64(r.Intn(1000)),
DowntimeUnbondDuration: time.Duration(r.Intn(86400)) * time.Second,
MinSignedPerWindow: sdk.NewDecWithPrec(int64(r.Intn(10)), 1),
SlashFractionDoubleSign: sdk.NewDec(1).Quo(sdk.NewDec(int64(r.Intn(50) + 1))),
SlashFractionDowntime: sdk.NewDec(1).Quo(sdk.NewDec(int64(r.Intn(200) + 1))),
},
}
fmt.Printf("Selected randomly generated slashing parameters: %v\n", slashingGenesis)
cwgoes marked this conversation as resolved.
Show resolved Hide resolved
mintGenesis := mint.GenesisState{
Minter: mint.Minter{
InflationLastTime: time.Unix(0, 0),
Inflation: sdk.NewDecWithPrec(int64(r.Intn(99)), 2),
},
Params: mint.Params{
MintDenom: "steak",
InflationRateChange: sdk.NewDecWithPrec(int64(r.Intn(99)), 2),
InflationMax: sdk.NewDecWithPrec(20, 2),
InflationMin: sdk.NewDecWithPrec(7, 2),
GoalBonded: sdk.NewDecWithPrec(67, 2),
},
}
fmt.Printf("Selected randomly generated minting parameters: %v\n", mintGenesis)
var validators []stake.Validator
var delegations []stake.Delegation

// XXX Try different numbers of initially bonded validators
numInitiallyBonded := int64(50)
valAddrs := make([]sdk.ValAddress, numInitiallyBonded)
for i := 0; i < int(numInitiallyBonded); i++ {
valAddr := sdk.ValAddress(accs[i].Address)
valAddrs[i] = valAddr

validator := stake.NewValidator(valAddr, accs[i].PubKey, stake.Description{})
validator.Tokens = sdk.NewDec(amt)
validator.DelegatorShares = sdk.NewDec(amt)
delegation := stake.Delegation{accs[i].Address, valAddr, sdk.NewDec(amt), 0}
validator.Tokens = sdk.NewDec(amount)
validator.DelegatorShares = sdk.NewDec(amount)
delegation := stake.Delegation{accs[i].Address, valAddr, sdk.NewDec(amount), 0}
validators = append(validators, validator)
delegations = append(delegations, delegation)
}
stakeGenesis.Pool.LooseTokens = sdk.NewDec(amt*250 + (numInitiallyBonded * amt))
stakeGenesis.Pool.LooseTokens = sdk.NewDec((amount * numAccs) + (numInitiallyBonded * amount))
stakeGenesis.Validators = validators
stakeGenesis.Bonds = delegations
mintGenesis := mint.DefaultGenesisState()

genesis := GenesisState{
Accounts: genesisAccounts,
Expand Down
31 changes: 0 additions & 31 deletions x/mock/simulation/constants.go

This file was deleted.

59 changes: 59 additions & 0 deletions x/mock/simulation/params.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package simulation

import (
"fmt"
"math/rand"
)

const (
// Minimum time per block
minTimePerBlock int64 = 10000 / 2

// Maximum time per block
maxTimePerBlock int64 = 10000

// TODO Remove in favor of binary search for invariant violation
onOperation bool = false
)

var (
// Currently there are 3 different liveness types, fully online, spotty connection, offline.
livenessTransitionMatrix, _ = CreateTransitionMatrix([][]int{
{90, 20, 1},
{10, 50, 5},
{0, 10, 1000},
})
)

// Simulation parameters
type Params struct {
PastEvidenceFraction float64
NumKeys int
EvidenceFraction float64
InitialLivenessWeightings []int
}

// Return default simulation parameters
func DefaultParams() Params {
return Params{
PastEvidenceFraction: 0.5,
NumKeys: 250,
EvidenceFraction: 0.5,
InitialLivenessWeightings: []int{40, 5, 5},
}
}

// Return random simulation parameters
func RandomParams(r *rand.Rand) Params {
return Params{
PastEvidenceFraction: r.Float64(),
NumKeys: r.Intn(250),
EvidenceFraction: r.Float64(),
InitialLivenessWeightings: []int{r.Intn(80), r.Intn(10), r.Intn(10)},
}
}

func (params Params) String() string {
return fmt.Sprintf("{pastEvidenceFraction: %v, numKeys: %v, evidenceFraction: %v, initialLivenessWeightings: %v}",
cwgoes marked this conversation as resolved.
Show resolved Hide resolved
params.PastEvidenceFraction, params.NumKeys, params.EvidenceFraction, params.InitialLivenessWeightings)
}
29 changes: 16 additions & 13 deletions x/mock/simulation/random_simulate_blocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ func Simulate(t *testing.T, app *baseapp.BaseApp,
return SimulateFromSeed(t, app, appStateFn, time, ops, setups, invariants, numBlocks, blockSize, commit)
}

func initChain(r *rand.Rand, accounts []Account, setups []RandSetup, app *baseapp.BaseApp,
func initChain(r *rand.Rand, params Params, accounts []Account, setups []RandSetup, app *baseapp.BaseApp,
appStateFn func(r *rand.Rand, accounts []Account) json.RawMessage) (validators map[string]mockValidator) {
res := app.InitChain(abci.RequestInitChain{AppStateBytes: appStateFn(r, accounts)})
validators = make(map[string]mockValidator)
for _, validator := range res.Validators {
str := fmt.Sprintf("%v", validator.PubKey)
validators[str] = mockValidator{validator, GetMemberOfInitialState(r, initialLivenessWeightings)}
validators[str] = mockValidator{validator, GetMemberOfInitialState(r, params.InitialLivenessWeightings)}
}

for i := 0; i < len(setups); i++ {
Expand All @@ -65,19 +65,21 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp,
testingMode, t, b := getTestingMode(tb)
fmt.Printf("Starting SimulateFromSeed with randomness created with seed %d\n", int(seed))
r := rand.New(rand.NewSource(seed))
params := RandomParams(r)
fmt.Printf("Randomized simulation params: %s\n", params)
timestamp := randTimestamp(r)
fmt.Printf("Starting the simulation from time %v, unixtime %v\n", timestamp.UTC().Format(time.UnixDate), timestamp.Unix())
timeDiff := maxTimePerBlock - minTimePerBlock

accs := RandomAccounts(r, numKeys)
accs := RandomAccounts(r, params.NumKeys)

// Setup event stats
events := make(map[string]uint)
event := func(what string) {
events[what]++
}

validators := initChain(r, accs, setups, app, appStateFn)
validators := initChain(r, params, accs, setups, app, appStateFn)
// Second variable to keep pending validator set (delayed one block since TM 0.24)
// Initially this is the same as the initial validator set
nextValidators := validators
Expand All @@ -90,15 +92,15 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp,
signal.Notify(c, os.Interrupt, syscall.SIGTERM, syscall.SIGINT)
go func() {
receivedSignal := <-c
fmt.Printf("Exiting early due to %s, on block %d, operation %d\n", receivedSignal, header.Height, opCount)
fmt.Printf("\nExiting early due to %s, on block %d, operation %d\n", receivedSignal, header.Height, opCount)
simError = fmt.Errorf("Exited due to %s", receivedSignal)
stopEarly = true
}()

var pastTimes []time.Time
var pastVoteInfos [][]abci.VoteInfo

request := RandomRequestBeginBlock(r, validators, livenessTransitionMatrix, evidenceFraction, pastTimes, pastVoteInfos, event, header)
request := RandomRequestBeginBlock(r, params, validators, livenessTransitionMatrix, pastTimes, pastVoteInfos, event, header)
// These are operations which have been queued by previous operations
operationQueue := make(map[int][]Operation)
timeOperationQueue := []FutureOperation{}
Expand Down Expand Up @@ -183,11 +185,11 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp,
}

// Generate a random RequestBeginBlock with the current validator set for the next block
request = RandomRequestBeginBlock(r, validators, livenessTransitionMatrix, evidenceFraction, pastTimes, pastVoteInfos, event, header)
request = RandomRequestBeginBlock(r, params, validators, livenessTransitionMatrix, pastTimes, pastVoteInfos, event, header)

// Update the validator set, which will be reflected in the application on the next block
validators = nextValidators
nextValidators = updateValidators(tb, r, validators, res.ValidatorUpdates, event)
nextValidators = updateValidators(tb, r, params, validators, res.ValidatorUpdates, event)
}
if stopEarly {
DisplayEvents(events)
Expand Down Expand Up @@ -219,6 +221,7 @@ func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, event f
}
return func(blocksize int, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
accounts []Account, header abci.Header, logWriter func(string)) (opCount int) {
fmt.Printf("\rSimulating... block %d/%d, operation %d/%d. ", header.Height, totalNumBlocks, opCount, blocksize)
for j := 0; j < blocksize; j++ {
logUpdate, futureOps, err := selectOp(r)(r, app, ctx, accounts, event)
if err != nil {
Expand Down Expand Up @@ -357,7 +360,7 @@ func randomProposer(r *rand.Rand, validators map[string]mockValidator) common.He

// RandomRequestBeginBlock generates a list of signing validators according to the provided list of validators, signing fraction, and evidence fraction
// nolint: unparam
func RandomRequestBeginBlock(r *rand.Rand, validators map[string]mockValidator, livenessTransitions TransitionMatrix, evidenceFraction float64,
func RandomRequestBeginBlock(r *rand.Rand, params Params, validators map[string]mockValidator, livenessTransitions TransitionMatrix,
cwgoes marked this conversation as resolved.
Show resolved Hide resolved
pastTimes []time.Time, pastVoteInfos [][]abci.VoteInfo, event func(string), header abci.Header) abci.RequestBeginBlock {
if len(validators) == 0 {
return abci.RequestBeginBlock{Header: header}
Expand Down Expand Up @@ -400,11 +403,11 @@ func RandomRequestBeginBlock(r *rand.Rand, validators map[string]mockValidator,
evidence := make([]abci.Evidence, 0)
// Anything but the first block
if len(pastTimes) > 0 {
for r.Float64() < evidenceFraction {
for r.Float64() < params.EvidenceFraction {
height := header.Height
time := header.Time
vals := voteInfos
if r.Float64() < pastEvidenceFraction {
if r.Float64() < params.PastEvidenceFraction {
height = int64(r.Intn(int(header.Height) - 1))
time = pastTimes[height]
vals = pastVoteInfos[height]
Expand Down Expand Up @@ -435,7 +438,7 @@ func RandomRequestBeginBlock(r *rand.Rand, validators map[string]mockValidator,

// updateValidators mimicks Tendermint's update logic
// nolint: unparam
func updateValidators(tb testing.TB, r *rand.Rand, current map[string]mockValidator, updates []abci.ValidatorUpdate, event func(string)) map[string]mockValidator {
func updateValidators(tb testing.TB, r *rand.Rand, params Params, current map[string]mockValidator, updates []abci.ValidatorUpdate, event func(string)) map[string]mockValidator {

for _, update := range updates {
str := fmt.Sprintf("%v", update.PubKey)
Expand All @@ -454,7 +457,7 @@ func updateValidators(tb testing.TB, r *rand.Rand, current map[string]mockValida
event("endblock/validatorupdates/updated")
} else {
// Set this new validator
current[str] = mockValidator{update, GetMemberOfInitialState(r, initialLivenessWeightings)}
current[str] = mockValidator{update, GetMemberOfInitialState(r, params.InitialLivenessWeightings)}
event("endblock/validatorupdates/added")
}
}
Expand Down