Simulation improvements (logging fix, random genesis parameters) (#2617)
* Print out initial update on every block * Randomize simulation parameters * Randomize initial liveness weightings * Randomize genesis parameters * fixed power store invariant * IterateValidatorsBonded -> IterateBondedValidatorsByPower * WriteValidators uses IterateLastValidators rather than IterateBondedValidatorsByPower * fixed democoin interface Closes #2556 Closes #2396 Via #2671: closes #2669 closes #2670 closes #2620 Offshoot issues: #2618 #2619 #2620 #2661
This commit is contained in:
parent
c20fcbfd8f
commit
256ec0f07b
|
@ -43,6 +43,10 @@ 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
|
||||
- #2669 [x/stake] Added invarant check to make sure validator's power aligns with its spot in the power store.
|
||||
- \#1924 [x/mock/simulation] Use a transition matrix for block size
|
||||
- \#2660 [x/mock/simulation] Staking transactions get tested far more frequently
|
||||
- #2610 [x/stake] Block redelegation to and from the same validator
|
||||
|
@ -58,6 +62,7 @@ BUG FIXES
|
|||
* Gaia CLI (`gaiacli`)
|
||||
|
||||
* Gaia
|
||||
- #2670 [x/stake] fixed incorrent `IterateBondedValidators` and split into two functions: `IterateBondedValidators` and `IterateLastBlockConsValidators`
|
||||
|
||||
* SDK
|
||||
- #2625 [x/gov] fix AppendTag function usage error
|
||||
|
|
|
@ -394,6 +394,7 @@ func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) (res
|
|||
|
||||
ctx := sdk.NewContext(app.cms.CacheMultiStore(), app.checkState.ctx.BlockHeader(), true, app.Logger).
|
||||
WithMinimumFees(app.minimumFees)
|
||||
|
||||
// Passes the rest of the path as an argument to the querier.
|
||||
// For example, in the path "custom/gov/proposal/test", the gov querier gets []string{"proposal", "test"} as the path
|
||||
resBytes, err := querier(ctx, path[2:], req)
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"math/rand"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
|
@ -50,42 +51,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 of steak per account: %v, initially bonded validators: %v}\n", amount, numInitiallyBonded)
|
||||
|
||||
// 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)),
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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,
|
||||
|
|
|
@ -82,8 +82,13 @@ func (vs *ValidatorSet) IterateValidators(ctx sdk.Context, fn func(index int64,
|
|||
}
|
||||
}
|
||||
|
||||
// IterateValidatorsBonded implements sdk.ValidatorSet
|
||||
func (vs *ValidatorSet) IterateValidatorsBonded(ctx sdk.Context, fn func(index int64, Validator sdk.Validator) bool) {
|
||||
// IterateBondedValidatorsByPower implements sdk.ValidatorSet
|
||||
func (vs *ValidatorSet) IterateBondedValidatorsByPower(ctx sdk.Context, fn func(index int64, Validator sdk.Validator) bool) {
|
||||
vs.IterateValidators(ctx, fn)
|
||||
}
|
||||
|
||||
// IterateLastValidators implements sdk.ValidatorSet
|
||||
func (vs *ValidatorSet) IterateLastValidators(ctx sdk.Context, fn func(index int64, Validator sdk.Validator) bool) {
|
||||
vs.IterateValidators(ctx, fn)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#!/bin/bash
|
||||
|
||||
seeds=(1 2 4 7 9 20 32 123 124 582 1893 2989 3012 4728 37827 981928 87821 891823782 989182 89182391)
|
||||
seeds=(1 2 4 7 9 20 32 123 124 582 1893 2989 3012 4728 37827 981928 87821 891823782 989182 89182391 \
|
||||
11 22 44 77 99 2020 3232 123123 124124 582582 18931893 29892989 30123012 47284728 37827)
|
||||
blocks=$1
|
||||
|
||||
echo "Running multi-seed simulation with seeds ${seeds[@]}"
|
||||
|
|
|
@ -64,7 +64,11 @@ type ValidatorSet interface {
|
|||
func(index int64, validator Validator) (stop bool))
|
||||
|
||||
// iterate through bonded validators by operator address, execute func for each validator
|
||||
IterateValidatorsBonded(Context,
|
||||
IterateBondedValidatorsByPower(Context,
|
||||
func(index int64, validator Validator) (stop bool))
|
||||
|
||||
// iterate through the consensus validator set of the last block by operator address, execute func for each validator
|
||||
IterateLastValidators(Context,
|
||||
func(index int64, validator Validator) (stop bool))
|
||||
|
||||
Validator(Context, ValAddress) Validator // get a particular validator by operator address
|
||||
|
|
|
@ -23,7 +23,7 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tall
|
|||
totalVotingPower := sdk.ZeroDec()
|
||||
currValidators := make(map[string]validatorGovInfo)
|
||||
|
||||
keeper.vs.IterateValidatorsBonded(ctx, func(index int64, validator sdk.Validator) (stop bool) {
|
||||
keeper.vs.IterateBondedValidatorsByPower(ctx, func(index int64, validator sdk.Validator) (stop bool) {
|
||||
currValidators[validator.GetOperator().String()] = validatorGovInfo{
|
||||
Address: validator.GetOperator(),
|
||||
Power: validator.GetPower(),
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
package simulation
|
||||
|
||||
const (
|
||||
// Fraction of double-signing evidence from a past height
|
||||
pastEvidenceFraction float64 = 0.5
|
||||
|
||||
// Minimum time per block
|
||||
minTimePerBlock int64 = 1000 / 2
|
||||
|
||||
// Maximum time per block
|
||||
maxTimePerBlock int64 = 1000
|
||||
|
||||
// Number of keys
|
||||
numKeys int = 250
|
||||
|
||||
// Chance that double-signing evidence is found on a given block
|
||||
evidenceFraction float64 = 0.5
|
||||
|
||||
// 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.
|
||||
initialLivenessWeightings = []int{40, 5, 5}
|
||||
livenessTransitionMatrix, _ = CreateTransitionMatrix([][]int{
|
||||
{90, 20, 1},
|
||||
{10, 50, 5},
|
||||
{0, 10, 1000},
|
||||
})
|
||||
// 3 states: rand in range [0, 4*provided blocksize], rand in range [0, 2 * provided blocksize], 0
|
||||
blockSizeTransitionMatrix, _ = CreateTransitionMatrix([][]int{
|
||||
{85, 5, 0},
|
||||
{15, 92, 1},
|
||||
{0, 3, 99},
|
||||
})
|
||||
)
|
|
@ -0,0 +1,66 @@
|
|||
package simulation
|
||||
|
||||
import (
|
||||
"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.
|
||||
defaultLivenessTransitionMatrix, _ = CreateTransitionMatrix([][]int{
|
||||
{90, 20, 1},
|
||||
{10, 50, 5},
|
||||
{0, 10, 1000},
|
||||
})
|
||||
|
||||
// 3 states: rand in range [0, 4*provided blocksize], rand in range [0, 2 * provided blocksize], 0
|
||||
defaultBlockSizeTransitionMatrix, _ = CreateTransitionMatrix([][]int{
|
||||
{85, 5, 0},
|
||||
{15, 92, 1},
|
||||
{0, 3, 99},
|
||||
})
|
||||
)
|
||||
|
||||
// Simulation parameters
|
||||
type Params struct {
|
||||
PastEvidenceFraction float64
|
||||
NumKeys int
|
||||
EvidenceFraction float64
|
||||
InitialLivenessWeightings []int
|
||||
LivenessTransitionMatrix TransitionMatrix
|
||||
BlockSizeTransitionMatrix TransitionMatrix
|
||||
}
|
||||
|
||||
// Return default simulation parameters
|
||||
func DefaultParams() Params {
|
||||
return Params{
|
||||
PastEvidenceFraction: 0.5,
|
||||
NumKeys: 250,
|
||||
EvidenceFraction: 0.5,
|
||||
InitialLivenessWeightings: []int{40, 5, 5},
|
||||
LivenessTransitionMatrix: defaultLivenessTransitionMatrix,
|
||||
BlockSizeTransitionMatrix: defaultBlockSizeTransitionMatrix,
|
||||
}
|
||||
}
|
||||
|
||||
// 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)},
|
||||
LivenessTransitionMatrix: defaultLivenessTransitionMatrix,
|
||||
BlockSizeTransitionMatrix: defaultBlockSizeTransitionMatrix,
|
||||
}
|
||||
}
|
|
@ -31,13 +31,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++ {
|
||||
|
@ -65,11 +65,13 @@ 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: %+v\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)
|
||||
|
@ -77,7 +79,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp,
|
|||
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
|
||||
|
@ -90,7 +92,7 @@ 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
|
||||
}()
|
||||
|
@ -98,7 +100,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp,
|
|||
var pastTimes []time.Time
|
||||
var pastVoteInfos [][]abci.VoteInfo
|
||||
|
||||
request := RandomRequestBeginBlock(r, validators, livenessTransitionMatrix, evidenceFraction, pastTimes, pastVoteInfos, event, header)
|
||||
request := RandomRequestBeginBlock(r, params, validators, pastTimes, pastVoteInfos, event, header)
|
||||
// These are operations which have been queued by previous operations
|
||||
operationQueue := make(map[int][]Operation)
|
||||
timeOperationQueue := []FutureOperation{}
|
||||
|
@ -108,7 +110,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp,
|
|||
blockLogBuilders = make([]*strings.Builder, numBlocks)
|
||||
}
|
||||
displayLogs := logPrinter(testingMode, blockLogBuilders)
|
||||
blockSimulator := createBlockSimulator(testingMode, tb, t, event, invariants, ops, operationQueue, timeOperationQueue, numBlocks, blockSize, displayLogs)
|
||||
blockSimulator := createBlockSimulator(testingMode, tb, t, params, event, invariants, ops, operationQueue, timeOperationQueue, numBlocks, blockSize, displayLogs)
|
||||
if !testingMode {
|
||||
b.ResetTimer()
|
||||
} else {
|
||||
|
@ -181,11 +183,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, 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)
|
||||
|
@ -203,7 +205,7 @@ type blockSimFn func(
|
|||
|
||||
// Returns a function to simulate blocks. Written like this to avoid constant parameters being passed everytime, to minimize
|
||||
// memory overhead
|
||||
func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T,
|
||||
func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, params Params,
|
||||
event func(string), invariants []Invariant,
|
||||
ops []WeightedOperation, operationQueue map[int][]Operation, timeOperationQueue []FutureOperation,
|
||||
totalNumBlocks int, avgBlockSize int, displayLogs func()) blockSimFn {
|
||||
|
@ -231,7 +233,8 @@ func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T,
|
|||
|
||||
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
|
||||
accounts []Account, header abci.Header, logWriter func(string)) (opCount int) {
|
||||
lastBlocksizeState, blocksize = getBlockSize(r, lastBlocksizeState, avgBlockSize)
|
||||
fmt.Printf("\rSimulating... block %d/%d, operation %d/%d. ", header.Height, totalNumBlocks, opCount, blocksize)
|
||||
lastBlocksizeState, blocksize = getBlockSize(r, params, lastBlocksizeState, avgBlockSize)
|
||||
for j := 0; j < blocksize; j++ {
|
||||
logUpdate, futureOps, err := selectOp(r)(r, app, ctx, accounts, event)
|
||||
logWriter(logUpdate)
|
||||
|
@ -272,11 +275,10 @@ func getTestingMode(tb testing.TB) (testingMode bool, t *testing.T, b *testing.B
|
|||
// "over stuffed" blocks with average size of 2 * avgblocksize,
|
||||
// normal sized blocks, hitting avgBlocksize on average,
|
||||
// and empty blocks, with no txs / only txs scheduled from the past.
|
||||
func getBlockSize(r *rand.Rand, lastBlockSizeState, avgBlockSize int) (state, blocksize int) {
|
||||
// TODO: Make blockSizeTransitionMatrix non-global
|
||||
func getBlockSize(r *rand.Rand, params Params, lastBlockSizeState, avgBlockSize int) (state, blocksize int) {
|
||||
// TODO: Make default blocksize transition matrix actually make the average
|
||||
// blocksize equal to avgBlockSize.
|
||||
state = blockSizeTransitionMatrix.NextState(r, lastBlockSizeState)
|
||||
state = params.BlockSizeTransitionMatrix.NextState(r, lastBlockSizeState)
|
||||
if state == 0 {
|
||||
blocksize = r.Intn(avgBlockSize * 4)
|
||||
} else if state == 1 {
|
||||
|
@ -379,7 +381,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,
|
||||
pastTimes []time.Time, pastVoteInfos [][]abci.VoteInfo, event func(string), header abci.Header) abci.RequestBeginBlock {
|
||||
if len(validators) == 0 {
|
||||
return abci.RequestBeginBlock{Header: header}
|
||||
|
@ -388,7 +390,7 @@ func RandomRequestBeginBlock(r *rand.Rand, validators map[string]mockValidator,
|
|||
i := 0
|
||||
for _, key := range getKeys(validators) {
|
||||
mVal := validators[key]
|
||||
mVal.livenessState = livenessTransitions.NextState(r, mVal.livenessState)
|
||||
mVal.livenessState = params.LivenessTransitionMatrix.NextState(r, mVal.livenessState)
|
||||
signed := true
|
||||
|
||||
if mVal.livenessState == 1 {
|
||||
|
@ -422,11 +424,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]
|
||||
|
@ -457,7 +459,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)
|
||||
|
@ -476,7 +478,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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ func WriteGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState {
|
|||
|
||||
// WriteValidators returns a slice of bonded genesis validators.
|
||||
func WriteValidators(ctx sdk.Context, keeper Keeper) (vals []tmtypes.GenesisValidator) {
|
||||
keeper.IterateValidatorsBonded(ctx, func(_ int64, validator sdk.Validator) (stop bool) {
|
||||
keeper.IterateLastValidators(ctx, func(_ int64, validator sdk.Validator) (stop bool) {
|
||||
vals = append(vals, tmtypes.GenesisValidator{
|
||||
PubKey: validator.GetConsPubKey(),
|
||||
Power: validator.GetPower().RoundInt64(),
|
||||
|
|
|
@ -28,9 +28,31 @@ func (k Keeper) IterateValidators(ctx sdk.Context, fn func(index int64, validato
|
|||
}
|
||||
|
||||
// iterate through the active validator set and perform the provided function
|
||||
func (k Keeper) IterateValidatorsBonded(ctx sdk.Context, fn func(index int64, validator sdk.Validator) (stop bool)) {
|
||||
func (k Keeper) IterateBondedValidatorsByPower(ctx sdk.Context, fn func(index int64, validator sdk.Validator) (stop bool)) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
iterator := sdk.KVStorePrefixIterator(store, LastValidatorPowerKey)
|
||||
maxValidators := k.MaxValidators(ctx)
|
||||
|
||||
iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey)
|
||||
defer iterator.Close()
|
||||
|
||||
i := int64(0)
|
||||
for ; iterator.Valid() && i < int64(maxValidators); iterator.Next() {
|
||||
address := iterator.Value()
|
||||
validator := k.mustGetValidator(ctx, address)
|
||||
|
||||
if validator.Status == sdk.Bonded {
|
||||
stop := fn(i, validator) // XXX is this safe will the validator unexposed fields be able to get written to?
|
||||
if stop {
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// iterate through the active validator set and perform the provided function
|
||||
func (k Keeper) IterateLastValidators(ctx sdk.Context, fn func(index int64, validator sdk.Validator) (stop bool)) {
|
||||
iterator := k.LastValidatorsIterator(ctx)
|
||||
i := int64(0)
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
address := AddressFromLastValidatorPowerKey(iterator.Key())
|
||||
|
|
|
@ -261,6 +261,13 @@ func (k Keeper) GetLastValidators(ctx sdk.Context) (validators []types.Validator
|
|||
return validators[:i] // trim
|
||||
}
|
||||
|
||||
// returns an iterator for the consensus validators in the last block
|
||||
func (k Keeper) LastValidatorsIterator(ctx sdk.Context) (iterator sdk.Iterator) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
iterator = sdk.KVStorePrefixIterator(store, LastValidatorPowerKey)
|
||||
return iterator
|
||||
}
|
||||
|
||||
// get the current group of bonded validators sorted by power-rank
|
||||
func (k Keeper) GetBondedValidatorsByPower(ctx sdk.Context) []types.Validator {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
|
@ -283,6 +290,13 @@ func (k Keeper) GetBondedValidatorsByPower(ctx sdk.Context) []types.Validator {
|
|||
return validators[:i] // trim
|
||||
}
|
||||
|
||||
// returns an iterator for the current validator power store
|
||||
func (k Keeper) ValidatorsPowerStoreIterator(ctx sdk.Context) (iterator sdk.Iterator) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
iterator = sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey)
|
||||
return iterator
|
||||
}
|
||||
|
||||
// gets a specific validator queue timeslice. A timeslice is a slice of ValAddresses corresponding to unbonding validators
|
||||
// that expire at a certain time.
|
||||
func (k Keeper) GetValidatorQueueTimeSlice(ctx sdk.Context, timestamp time.Time) (valAddrs []sdk.ValAddress) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package simulation
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
|
@ -10,6 +11,7 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/x/distribution"
|
||||
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/keeper"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
)
|
||||
|
||||
|
@ -24,10 +26,12 @@ func AllInvariants(ck bank.Keeper, k stake.Keeper,
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = PositivePowerInvariant(k)(app)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = ValidatorSetInvariant(k)(app)
|
||||
return err
|
||||
}
|
||||
|
@ -100,19 +104,29 @@ func SupplyInvariants(ck bank.Keeper, k stake.Keeper,
|
|||
}
|
||||
}
|
||||
|
||||
// PositivePowerInvariant checks that all stored validators have > 0 power
|
||||
// PositivePowerInvariant checks that all stored validators have > 0 power.
|
||||
func PositivePowerInvariant(k stake.Keeper) simulation.Invariant {
|
||||
return func(app *baseapp.BaseApp) error {
|
||||
ctx := app.NewContext(false, abci.Header{})
|
||||
var err error
|
||||
k.IterateValidatorsBonded(ctx, func(_ int64, validator sdk.Validator) bool {
|
||||
if !validator.GetPower().GT(sdk.ZeroDec()) {
|
||||
err = fmt.Errorf("validator with non-positive power stored. (pubkey %v)", validator.GetConsPubKey())
|
||||
return true
|
||||
|
||||
iterator := k.ValidatorsPowerStoreIterator(ctx)
|
||||
pool := k.GetPool(ctx)
|
||||
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
validator, found := k.GetValidator(ctx, iterator.Value())
|
||||
if !found {
|
||||
panic(fmt.Sprintf("validator record not found for address: %X\n", iterator.Value()))
|
||||
}
|
||||
return false
|
||||
})
|
||||
return err
|
||||
|
||||
powerKey := keeper.GetValidatorsByPowerIndexKey(validator, pool)
|
||||
|
||||
if !bytes.Equal(iterator.Key(), powerKey) {
|
||||
return fmt.Errorf("power store invariance:\n\tvalidator.Power: %v"+
|
||||
"\n\tkey should be: %v\n\tkey in store: %v", validator.GetPower(), powerKey, iterator.Key())
|
||||
}
|
||||
}
|
||||
iterator.Close()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue