mock tendermint
This commit is contained in:
parent
ff327049ee
commit
fee0763b5e
|
@ -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,
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue