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