From fee0763b5ef4bc609bcec2a1eecbde3c57a0a4ff Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Thu, 8 Nov 2018 00:12:51 -0500 Subject: [PATCH] mock tendermint --- x/mock/simulation/mock_tendermint.go | 198 +++++++++++++++++++++++++++ x/mock/simulation/simulate.go | 108 ++------------- x/mock/simulation/validator.go | 96 ------------- 3 files changed, 206 insertions(+), 196 deletions(-) create mode 100644 x/mock/simulation/mock_tendermint.go delete mode 100644 x/mock/simulation/validator.go diff --git a/x/mock/simulation/mock_tendermint.go b/x/mock/simulation/mock_tendermint.go new file mode 100644 index 0000000000..ee381cbb61 --- /dev/null +++ b/x/mock/simulation/mock_tendermint.go @@ -0,0 +1,198 @@ +package simulation + +import ( + "fmt" + "math/rand" + "sort" + "testing" + "time" + + abci "github.com/tendermint/tendermint/abci/types" + cmn "github.com/tendermint/tendermint/libs/common" + tmtypes "github.com/tendermint/tendermint/types" +) + +type mockValidator struct { + val abci.ValidatorUpdate + livenessState int +} + +type mockValidators map[string]mockValidator + +// get mockValidators from abci validators +func newMockValidators(abciVals abci.ValidatorUpdate) mockValidators { + + validators = make(mockValidators) + for _, validator := range abciVals { + str := fmt.Sprintf("%v", validator.PubKey) + liveliness := GetMemberOfInitialState(r, + params.InitialLivenessWeightings) + + validators[str] = mockValidator{ + val: validator, + livenessState: liveliness, + } + } + + return validators +} + +// TODO describe usage +func (vals mockValidators) getKeys() []string { + keys := make([]string, len(validators)) + i := 0 + for key := range validators { + keys[i] = key + i++ + } + sort.Strings(keys) + return keys +} + +//_________________________________________________________________________________ + +// randomProposer picks a random proposer from the current validator set +func randomProposer(r *rand.Rand, validators map[string]mockValidator) cmn.HexBytes { + keys := validators.getKeys() + if len(keys) == 0 { + return nil + } + key := keys[r.Intn(len(keys))] + proposer := validators[key].val + pk, err := tmtypes.PB2TM.PubKey(proposer.PubKey) + if err != nil { + panic(err) + } + return pk.Address() +} + +// updateValidators mimicks Tendermint's update logic +// nolint: unparam +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) + if update.Power == 0 { + if _, ok := current[str]; !ok { + tb.Fatalf("tried to delete a nonexistent validator") + } + event("endblock/validatorupdates/kicked") + delete(current, str) + + } else if mVal, ok := current[str]; ok { + // validator already exists + mVal.val = update + event("endblock/validatorupdates/updated") + } else { + // Set this new validator + current[str] = mockValidator{ + update, + GetMemberOfInitialState(r, params.InitialLivenessWeightings), + } + event("endblock/validatorupdates/added") + } + } + + return current +} + +// RandomRequestBeginBlock generates a list of signing validators according to +// the provided list of validators, signing fraction, and evidence fraction +func RandomRequestBeginBlock(r *rand.Rand, params Params, + validators mockValidators, pastTimes []time.Time, + pastVoteInfos [][]abci.VoteInfo, + event func(string), header abci.Header) abci.RequestBeginBlock { + + if len(validators) == 0 { + return abci.RequestBeginBlock{ + Header: header, + } + } + + voteInfos := make([]abci.VoteInfo, len(validators)) + for i, key := range validators.getKeys() { + mVal := validators[key] + mVal.livenessState = params.LivenessTransitionMatrix.NextState(r, mVal.livenessState) + signed := true + + if mVal.livenessState == 1 { + // spotty connection, 50% probability of success + // See https://github.com/golang/go/issues/23804#issuecomment-365370418 + // for reasoning behind computing like this + signed = r.Int63()%2 == 0 + } else if mVal.livenessState == 2 { + // offline + signed = false + } + + if signed { + event("beginblock/signing/signed") + } else { + event("beginblock/signing/missed") + } + + pubkey, err := tmtypes.PB2TM.PubKey(mVal.val.PubKey) + if err != nil { + panic(err) + } + voteInfos[i] = abci.VoteInfo{ + Validator: abci.Validator{ + Address: pubkey.Address(), + Power: mVal.val.Power, + }, + SignedLastBlock: signed, + } + } + + // return if no past times + if len(pastTimes) <= 0 { + return abci.RequestBeginBlock{ + Header: header, + LastCommitInfo: abci.LastCommitInfo{ + Votes: voteInfos, + }, + } + } + + // TODO: Determine capacity before allocation + evidence := make([]abci.Evidence, 0) + for r.Float64() < params.EvidenceFraction { + + height := header.Height + time := header.Time + vals := voteInfos + + if r.Float64() < params.PastEvidenceFraction { + height = int64(r.Intn(int(header.Height) - 1)) + time = pastTimes[height] + vals = pastVoteInfos[height] + } + validator := vals[r.Intn(len(vals))].Validator + + var totalVotingPower int64 + for _, val := range vals { + totalVotingPower += val.Validator.Power + } + + evidence = append(evidence, + abci.Evidence{ + Type: tmtypes.ABCIEvidenceTypeDuplicateVote, + Validator: validator, + Height: height, + Time: time, + TotalVotingPower: totalVotingPower, + }, + ) + event("beginblock/evidence") + } + + return abci.RequestBeginBlock{ + Header: header, + LastCommitInfo: abci.LastCommitInfo{ + Votes: voteInfos, + }, + ByzantineValidators: evidence, + } +} diff --git a/x/mock/simulation/simulate.go b/x/mock/simulation/simulate.go index d9b5176f8c..dc7c12bfe9 100644 --- a/x/mock/simulation/simulate.go +++ b/x/mock/simulation/simulate.go @@ -13,7 +13,6 @@ import ( "time" abci "github.com/tendermint/tendermint/abci/types" - tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" @@ -25,7 +24,7 @@ type RandSetup func(r *rand.Rand, accounts []Account) // Simulate tests application by sending random messages. func Simulate(t *testing.T, app *baseapp.BaseApp, appStateFn func(r *rand.Rand, accs []Account) json.RawMessage, - ops []WeightedOperation, setups []RandSetup, + ops WeightedOperations, setups []RandSetup, invariants Invariants, numBlocks int, blockSize int, commit bool) error { time := time.Now().UnixNano() @@ -60,13 +59,17 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, // in case we have to end early, don't os.Exit so that we can run cleanup code. stopEarly := false testingMode, t, b := getTestingMode(tb) - fmt.Printf("Starting SimulateFromSeed with randomness created with seed %d\n", int(seed)) + fmt.Printf("Starting SimulateFromSeed with randomness "+ + "created with seed %d\n", int(seed)) + r := rand.New(rand.NewSource(seed)) params := RandomParams(r) // := DefaultParams() 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, params.NumKeys) @@ -232,10 +235,10 @@ type blockSimFn func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accounts []Account, header abci.Header, logWriter func(string)) (opCount int) // Returns a function to simulate blocks. Written like this to avoid constant -// parameters being passed everytime, to minimize memory overhead +// parameters being passed everytime, to minimize memory overhead. func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, params Params, event func(string), invariants Invariants, ops WeightedOperations, - operationQueue map[int][]Operation, timeOperationQueue []FutureOperation, + operationQueue OperationQueue, timeOperationQueue []FutureOperation, totalNumBlocks int, avgBlockSize int, displayLogs func()) blockSimFn { var lastBlocksizeState = 0 // state for [4 * uniform distribution] @@ -323,98 +326,3 @@ func runQueuedTimeOperations(queueOps []FutureOperation, } return numOpsRan } - -// RandomRequestBeginBlock generates a list of signing validators according to -// the provided list of validators, signing fraction, and evidence fraction -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, - } - } - voteInfos := make([]abci.VoteInfo, len(validators)) - - for i, key := range getKeys(validators) { - mVal := validators[key] - mVal.livenessState = params.LivenessTransitionMatrix.NextState(r, mVal.livenessState) - signed := true - - if mVal.livenessState == 1 { - // spotty connection, 50% probability of success - // See https://github.com/golang/go/issues/23804#issuecomment-365370418 - // for reasoning behind computing like this - signed = r.Int63()%2 == 0 - } else if mVal.livenessState == 2 { - // offline - signed = false - } - - if signed { - event("beginblock/signing/signed") - } else { - event("beginblock/signing/missed") - } - - pubkey, err := tmtypes.PB2TM.PubKey(mVal.val.PubKey) - if err != nil { - panic(err) - } - voteInfos[i] = abci.VoteInfo{ - Validator: abci.Validator{ - Address: pubkey.Address(), - Power: mVal.val.Power, - }, - SignedLastBlock: signed, - } - } - - // return if no past times - if len(pastTimes) <= 0 { - return abci.RequestBeginBlock{ - Header: header, - LastCommitInfo: abci.LastCommitInfo{ - Votes: voteInfos, - }, - } - } - - // TODO: Determine capacity before allocation - evidence := make([]abci.Evidence, 0) - for r.Float64() < params.EvidenceFraction { - height := header.Height - time := header.Time - vals := voteInfos - if r.Float64() < params.PastEvidenceFraction { - height = int64(r.Intn(int(header.Height) - 1)) - time = pastTimes[height] - vals = pastVoteInfos[height] - } - validator := vals[r.Intn(len(vals))].Validator - var totalVotingPower int64 - - for _, val := range vals { - totalVotingPower += val.Validator.Power - } - - evidence = append(evidence, abci.Evidence{ - Type: tmtypes.ABCIEvidenceTypeDuplicateVote, - Validator: validator, - Height: height, - Time: time, - TotalVotingPower: totalVotingPower, - }) - event("beginblock/evidence") - } - - return abci.RequestBeginBlock{ - Header: header, - LastCommitInfo: abci.LastCommitInfo{ - Votes: voteInfos, - }, - ByzantineValidators: evidence, - } -} diff --git a/x/mock/simulation/validator.go b/x/mock/simulation/validator.go deleted file mode 100644 index 7a2213bdb7..0000000000 --- a/x/mock/simulation/validator.go +++ /dev/null @@ -1,96 +0,0 @@ -package simulation - -import ( - "fmt" - "math/rand" - "sort" - "testing" - - abci "github.com/tendermint/tendermint/abci/types" - cmn "github.com/tendermint/tendermint/libs/common" - tmtypes "github.com/tendermint/tendermint/types" -) - -type mockValidator struct { - val abci.ValidatorUpdate - livenessState int -} - -type mockValidators map[string]mockValidator - -// get mockValidators from abci validators -func newMockValidators(abciVals abci.ValidatorUpdate) mockValidators { - - validators = make(mockValidators) - for _, validator := range abciVals { - str := fmt.Sprintf("%v", validator.PubKey) - liveliness := GetMemberOfInitialState(r, - params.InitialLivenessWeightings) - - validators[str] = mockValidator{ - val: validator, - livenessState: liveliness, - } - } - - return validators -} - -// TODO describe usage -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 -} - -// randomProposer picks a random proposer from the current validator set -func randomProposer(r *rand.Rand, validators map[string]mockValidator) cmn.HexBytes { - keys := getKeys(validators) - if len(keys) == 0 { - return nil - } - key := keys[r.Intn(len(keys))] - proposer := validators[key].val - pk, err := tmtypes.PB2TM.PubKey(proposer.PubKey) - if err != nil { - panic(err) - } - return pk.Address() -} - -// updateValidators mimicks Tendermint's update logic -// nolint: unparam -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) - if update.Power == 0 { - if _, ok := current[str]; !ok { - tb.Fatalf("tried to delete a nonexistent validator") - } - event("endblock/validatorupdates/kicked") - delete(current, str) - - } else if mVal, ok := current[str]; ok { - // validator already exists - mVal.val = update - event("endblock/validatorupdates/updated") - } else { - // Set this new validator - current[str] = mockValidator{ - update, - GetMemberOfInitialState(r, params.InitialLivenessWeightings), - } - event("endblock/validatorupdates/added") - } - } - - return current -}