github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/x/simulation/mock_tendermint.go (about) 1 package simulation 2 3 import ( 4 "fmt" 5 "math/rand" 6 "sort" 7 "testing" 8 "time" 9 10 abci "github.com/fibonacci-chain/fbc/libs/tendermint/abci/types" 11 tmbytes "github.com/fibonacci-chain/fbc/libs/tendermint/libs/bytes" 12 tmtypes "github.com/fibonacci-chain/fbc/libs/tendermint/types" 13 ) 14 15 type mockValidator struct { 16 val abci.ValidatorUpdate 17 livenessState int 18 } 19 20 func (mv mockValidator) String() string { 21 return fmt.Sprintf("mockValidator{%s:%X power:%v state:%v}", 22 mv.val.PubKey.Type, 23 mv.val.PubKey.Data, 24 mv.val.Power, 25 mv.livenessState) 26 } 27 28 type mockValidators map[string]mockValidator 29 30 // get mockValidators from abci validators 31 func newMockValidators(r *rand.Rand, abciVals []abci.ValidatorUpdate, 32 params Params) mockValidators { 33 34 validators := make(mockValidators) 35 for _, validator := range abciVals { 36 str := fmt.Sprintf("%v", validator.PubKey) 37 liveliness := GetMemberOfInitialState(r, 38 params.InitialLivenessWeightings) 39 40 validators[str] = mockValidator{ 41 val: validator, 42 livenessState: liveliness, 43 } 44 } 45 46 return validators 47 } 48 49 // TODO describe usage 50 func (vals mockValidators) getKeys() []string { 51 keys := make([]string, len(vals)) 52 i := 0 53 for key := range vals { 54 keys[i] = key 55 i++ 56 } 57 sort.Strings(keys) 58 return keys 59 } 60 61 //_________________________________________________________________________________ 62 63 // randomProposer picks a random proposer from the current validator set 64 func (vals mockValidators) randomProposer(r *rand.Rand) tmbytes.HexBytes { 65 keys := vals.getKeys() 66 if len(keys) == 0 { 67 return nil 68 } 69 key := keys[r.Intn(len(keys))] 70 proposer := vals[key].val 71 pk, err := tmtypes.PB2TM.PubKey(proposer.PubKey) 72 if err != nil { 73 panic(err) 74 } 75 return pk.Address() 76 } 77 78 // updateValidators mimicks Tendermint's update logic 79 // nolint: unparam 80 func updateValidators(tb testing.TB, r *rand.Rand, params Params, 81 current map[string]mockValidator, updates []abci.ValidatorUpdate, 82 event func(route, op, evResult string)) map[string]mockValidator { 83 84 for _, update := range updates { 85 str := fmt.Sprintf("%v", update.PubKey) 86 87 if update.Power == 0 { 88 if _, ok := current[str]; !ok { 89 tb.Fatalf("tried to delete a nonexistent validator") 90 } 91 event("end_block", "validator_updates", "kicked") 92 delete(current, str) 93 94 } else if mVal, ok := current[str]; ok { 95 // validator already exists 96 mVal.val = update 97 event("end_block", "validator_updates", "updated") 98 99 } else { 100 // Set this new validator 101 current[str] = mockValidator{ 102 update, 103 GetMemberOfInitialState(r, params.InitialLivenessWeightings), 104 } 105 event("end_block", "validator_updates", "added") 106 } 107 } 108 109 return current 110 } 111 112 // RandomRequestBeginBlock generates a list of signing validators according to 113 // the provided list of validators, signing fraction, and evidence fraction 114 func RandomRequestBeginBlock(r *rand.Rand, params Params, 115 validators mockValidators, pastTimes []time.Time, 116 pastVoteInfos [][]abci.VoteInfo, 117 event func(route, op, evResult string), header abci.Header) abci.RequestBeginBlock { 118 119 if len(validators) == 0 { 120 return abci.RequestBeginBlock{ 121 Header: header, 122 } 123 } 124 125 voteInfos := make([]abci.VoteInfo, len(validators)) 126 for i, key := range validators.getKeys() { 127 mVal := validators[key] 128 mVal.livenessState = params.LivenessTransitionMatrix.NextState(r, mVal.livenessState) 129 signed := true 130 131 if mVal.livenessState == 1 { 132 // spotty connection, 50% probability of success 133 // See https://github.com/golang/go/issues/23804#issuecomment-365370418 134 // for reasoning behind computing like this 135 signed = r.Int63()%2 == 0 136 } else if mVal.livenessState == 2 { 137 // offline 138 signed = false 139 } 140 141 if signed { 142 event("begin_block", "signing", "signed") 143 } else { 144 event("begin_block", "signing", "missed") 145 } 146 147 pubkey, err := tmtypes.PB2TM.PubKey(mVal.val.PubKey) 148 if err != nil { 149 panic(err) 150 } 151 voteInfos[i] = abci.VoteInfo{ 152 Validator: abci.Validator{ 153 Address: pubkey.Address(), 154 Power: mVal.val.Power, 155 }, 156 SignedLastBlock: signed, 157 } 158 } 159 160 // return if no past times 161 if len(pastTimes) == 0 { 162 return abci.RequestBeginBlock{ 163 Header: header, 164 LastCommitInfo: abci.LastCommitInfo{ 165 Votes: voteInfos, 166 }, 167 } 168 } 169 170 // TODO: Determine capacity before allocation 171 evidence := make([]abci.Evidence, 0) 172 for r.Float64() < params.EvidenceFraction { 173 174 height := header.Height 175 time := header.Time 176 vals := voteInfos 177 178 if r.Float64() < params.PastEvidenceFraction && header.Height > 1 { 179 height = int64(r.Intn(int(header.Height)-1)) + 1 // Tendermint starts at height 1 180 // array indices offset by one 181 time = pastTimes[height-1] 182 vals = pastVoteInfos[height-1] 183 } 184 validator := vals[r.Intn(len(vals))].Validator 185 186 var totalVotingPower int64 187 for _, val := range vals { 188 totalVotingPower += val.Validator.Power 189 } 190 191 evidence = append(evidence, 192 abci.Evidence{ 193 Type: tmtypes.ABCIEvidenceTypeDuplicateVote, 194 Validator: validator, 195 Height: height, 196 Time: time, 197 TotalVotingPower: totalVotingPower, 198 }, 199 ) 200 event("begin_block", "evidence", "ok") 201 } 202 203 return abci.RequestBeginBlock{ 204 Header: header, 205 LastCommitInfo: abci.LastCommitInfo{ 206 Votes: voteInfos, 207 }, 208 ByzantineValidators: evidence, 209 } 210 }