github.com/Finschia/finschia-sdk@v0.48.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 abci "github.com/tendermint/tendermint/abci/types" 11 tmproto "github.com/tendermint/tendermint/proto/tendermint/types" 12 13 ocabci "github.com/Finschia/ostracon/abci/types" 14 cryptoenc "github.com/Finschia/ostracon/crypto/encoding" 15 ostbytes "github.com/Finschia/ostracon/libs/bytes" 16 ) 17 18 type mockValidator struct { 19 val abci.ValidatorUpdate 20 livenessState int 21 } 22 23 func (mv mockValidator) String() string { 24 return fmt.Sprintf("mockValidator{%s power:%v state:%v}", 25 mv.val.PubKey.String(), 26 mv.val.Power, 27 mv.livenessState) 28 } 29 30 type mockValidators map[string]mockValidator 31 32 // get mockValidators from abci validators 33 func newMockValidators(r *rand.Rand, abciVals []abci.ValidatorUpdate, params Params) mockValidators { 34 validators := make(mockValidators) 35 36 for _, validator := range abciVals { 37 str := fmt.Sprintf("%X", validator.PubKey.GetEd25519()) 38 liveliness := GetMemberOfInitialState(r, 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 54 for key := range vals { 55 keys[i] = key 56 i++ 57 } 58 59 sort.Strings(keys) 60 61 return keys 62 } 63 64 // randomProposer picks a random proposer from the current validator set 65 func (vals mockValidators) randomProposer(r *rand.Rand) ostbytes.HexBytes { 66 keys := vals.getKeys() 67 if len(keys) == 0 { 68 return nil 69 } 70 71 key := keys[r.Intn(len(keys))] 72 73 proposer := vals[key].val 74 pk, err := cryptoenc.PubKeyFromProto(&proposer.PubKey) 75 if err != nil { //nolint:wsl 76 panic(err) 77 } 78 79 return pk.Address() 80 } 81 82 // updateValidators mimics Ostracon's update logic. 83 func updateValidators( 84 tb testing.TB, 85 r *rand.Rand, 86 params Params, 87 current map[string]mockValidator, 88 updates []abci.ValidatorUpdate, 89 event func(route, op, evResult string), 90 ) map[string]mockValidator { 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 }