github.com/number571/tendermint@v0.34.11-gost/test/e2e/runner/evidence.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "io/ioutil" 8 "math/rand" 9 "path/filepath" 10 "time" 11 12 "github.com/number571/tendermint/crypto" 13 "github.com/number571/tendermint/crypto/tmhash" 14 "github.com/number571/tendermint/internal/test/factory" 15 tmjson "github.com/number571/tendermint/libs/json" 16 "github.com/number571/tendermint/privval" 17 tmproto "github.com/number571/tendermint/proto/tendermint/types" 18 e2e "github.com/number571/tendermint/test/e2e/pkg" 19 "github.com/number571/tendermint/types" 20 "github.com/number571/tendermint/version" 21 ) 22 23 // 1 in 4 evidence is light client evidence, the rest is duplicate vote evidence 24 const lightClientEvidenceRatio = 4 25 26 // InjectEvidence takes a running testnet and generates an amount of valid 27 // evidence and broadcasts it to a random node through the rpc endpoint `/broadcast_evidence`. 28 // Evidence is random and can be a mixture of LightClientAttackEvidence and 29 // DuplicateVoteEvidence. 30 func InjectEvidence(testnet *e2e.Testnet, amount int) error { 31 // select a random node 32 targetNode := testnet.RandomNode() 33 34 logger.Info(fmt.Sprintf("Injecting evidence through %v (amount: %d)...", targetNode.Name, amount)) 35 36 client, err := targetNode.Client() 37 if err != nil { 38 return err 39 } 40 41 // request the latest block and validator set from the node 42 blockRes, err := client.Block(context.Background(), nil) 43 if err != nil { 44 return err 45 } 46 evidenceHeight := blockRes.Block.Height 47 waitHeight := blockRes.Block.Height + 3 48 49 nValidators := 100 50 valRes, err := client.Validators(context.Background(), &evidenceHeight, nil, &nValidators) 51 if err != nil { 52 return err 53 } 54 55 valSet, err := types.ValidatorSetFromExistingValidators(valRes.Validators) 56 if err != nil { 57 return err 58 } 59 60 // get the private keys of all the validators in the network 61 privVals, err := getPrivateValidatorKeys(testnet) 62 if err != nil { 63 return err 64 } 65 66 // wait for the node to reach the height above the forged height so that 67 // it is able to validate the evidence 68 _, err = waitForNode(targetNode, waitHeight, 30*time.Second) 69 if err != nil { 70 return err 71 } 72 73 var ev types.Evidence 74 for i := 1; i <= amount; i++ { 75 if i%lightClientEvidenceRatio == 0 { 76 ev, err = generateLightClientAttackEvidence( 77 privVals, evidenceHeight, valSet, testnet.Name, blockRes.Block.Time, 78 ) 79 } else { 80 ev, err = generateDuplicateVoteEvidence( 81 privVals, evidenceHeight, valSet, testnet.Name, blockRes.Block.Time, 82 ) 83 } 84 if err != nil { 85 return err 86 } 87 88 _, err := client.BroadcastEvidence(context.Background(), ev) 89 if err != nil { 90 return err 91 } 92 } 93 94 // wait for the node to reach the height above the forged height so that 95 // it is able to validate the evidence 96 _, err = waitForNode(targetNode, blockRes.Block.Height+2, 10*time.Second) 97 if err != nil { 98 return err 99 } 100 101 logger.Info(fmt.Sprintf("Finished sending evidence (height %d)", blockRes.Block.Height+2)) 102 103 return nil 104 } 105 106 func getPrivateValidatorKeys(testnet *e2e.Testnet) ([]types.MockPV, error) { 107 privVals := []types.MockPV{} 108 109 for _, node := range testnet.Nodes { 110 if node.Mode == e2e.ModeValidator { 111 privKeyPath := filepath.Join(testnet.Dir, node.Name, PrivvalKeyFile) 112 privKey, err := readPrivKey(privKeyPath) 113 if err != nil { 114 return nil, err 115 } 116 // Create mock private validators from the validators private key. MockPV is 117 // stateless which means we can double vote and do other funky stuff 118 privVals = append(privVals, types.NewMockPVWithParams(privKey, false, false)) 119 } 120 } 121 122 return privVals, nil 123 } 124 125 // creates evidence of a lunatic attack. The height provided is the common height. 126 // The forged height happens 2 blocks later. 127 func generateLightClientAttackEvidence( 128 privVals []types.MockPV, 129 height int64, 130 vals *types.ValidatorSet, 131 chainID string, 132 evTime time.Time, 133 ) (*types.LightClientAttackEvidence, error) { 134 // forge a random header 135 forgedHeight := height + 2 136 forgedTime := evTime.Add(1 * time.Second) 137 header := makeHeaderRandom(chainID, forgedHeight) 138 header.Time = forgedTime 139 140 // add a new bogus validator and remove an existing one to 141 // vary the validator set slightly 142 pv, conflictingVals, err := mutateValidatorSet(privVals, vals) 143 if err != nil { 144 return nil, err 145 } 146 147 header.ValidatorsHash = conflictingVals.Hash() 148 149 // create a commit for the forged header 150 blockID := makeBlockID(header.Hash(), 1000, []byte("partshash")) 151 voteSet := types.NewVoteSet(chainID, forgedHeight, 0, tmproto.SignedMsgType(2), conflictingVals) 152 commit, err := factory.MakeCommit(blockID, forgedHeight, 0, voteSet, pv, forgedTime) 153 if err != nil { 154 return nil, err 155 } 156 157 ev := &types.LightClientAttackEvidence{ 158 ConflictingBlock: &types.LightBlock{ 159 SignedHeader: &types.SignedHeader{ 160 Header: header, 161 Commit: commit, 162 }, 163 ValidatorSet: conflictingVals, 164 }, 165 CommonHeight: height, 166 TotalVotingPower: vals.TotalVotingPower(), 167 Timestamp: evTime, 168 } 169 ev.ByzantineValidators = ev.GetByzantineValidators(vals, &types.SignedHeader{ 170 Header: makeHeaderRandom(chainID, forgedHeight), 171 }) 172 return ev, nil 173 } 174 175 // generateDuplicateVoteEvidence picks a random validator from the val set and 176 // returns duplicate vote evidence against the validator 177 func generateDuplicateVoteEvidence( 178 privVals []types.MockPV, 179 height int64, 180 vals *types.ValidatorSet, 181 chainID string, 182 time time.Time, 183 ) (*types.DuplicateVoteEvidence, error) { 184 // nolint:gosec // G404: Use of weak random number generator 185 privVal := privVals[rand.Intn(len(privVals))] 186 187 valIdx, _ := vals.GetByAddress(privVal.PrivKey.PubKey().Address()) 188 voteA, err := factory.MakeVote(privVal, chainID, valIdx, height, 0, 2, makeRandomBlockID(), time) 189 if err != nil { 190 return nil, err 191 } 192 voteB, err := factory.MakeVote(privVal, chainID, valIdx, height, 0, 2, makeRandomBlockID(), time) 193 if err != nil { 194 return nil, err 195 } 196 ev := types.NewDuplicateVoteEvidence(voteA, voteB, time, vals) 197 if ev == nil { 198 return nil, fmt.Errorf("could not generate evidence a=%v b=%v vals=%v", voteA, voteB, vals) 199 } 200 201 return ev, nil 202 } 203 204 func readPrivKey(keyFilePath string) (crypto.PrivKey, error) { 205 keyJSONBytes, err := ioutil.ReadFile(keyFilePath) 206 if err != nil { 207 return nil, err 208 } 209 pvKey := privval.FilePVKey{} 210 err = tmjson.Unmarshal(keyJSONBytes, &pvKey) 211 if err != nil { 212 return nil, fmt.Errorf("error reading PrivValidator key from %v: %w", keyFilePath, err) 213 } 214 215 return pvKey.PrivKey, nil 216 } 217 218 func makeHeaderRandom(chainID string, height int64) *types.Header { 219 return &types.Header{ 220 Version: version.Consensus{Block: version.BlockProtocol, App: 1}, 221 ChainID: chainID, 222 Height: height, 223 Time: time.Now(), 224 LastBlockID: makeBlockID([]byte("headerhash"), 1000, []byte("partshash")), 225 LastCommitHash: crypto.CRandBytes(tmhash.Size), 226 DataHash: crypto.CRandBytes(tmhash.Size), 227 ValidatorsHash: crypto.CRandBytes(tmhash.Size), 228 NextValidatorsHash: crypto.CRandBytes(tmhash.Size), 229 ConsensusHash: crypto.CRandBytes(tmhash.Size), 230 AppHash: crypto.CRandBytes(tmhash.Size), 231 LastResultsHash: crypto.CRandBytes(tmhash.Size), 232 EvidenceHash: crypto.CRandBytes(tmhash.Size), 233 ProposerAddress: crypto.CRandBytes(crypto.AddressSize), 234 } 235 } 236 237 func makeRandomBlockID() types.BlockID { 238 return makeBlockID(crypto.CRandBytes(tmhash.Size), 100, crypto.CRandBytes(tmhash.Size)) 239 } 240 241 func makeBlockID(hash []byte, partSetSize uint32, partSetHash []byte) types.BlockID { 242 var ( 243 h = make([]byte, tmhash.Size) 244 psH = make([]byte, tmhash.Size) 245 ) 246 copy(h, hash) 247 copy(psH, partSetHash) 248 return types.BlockID{ 249 Hash: h, 250 PartSetHeader: types.PartSetHeader{ 251 Total: partSetSize, 252 Hash: psH, 253 }, 254 } 255 } 256 257 func mutateValidatorSet(privVals []types.MockPV, vals *types.ValidatorSet, 258 ) ([]types.PrivValidator, *types.ValidatorSet, error) { 259 newVal, newPrivVal := factory.RandValidator(false, 10) 260 261 var newVals *types.ValidatorSet 262 if vals.Size() > 2 { 263 newVals = types.NewValidatorSet(append(vals.Copy().Validators[:vals.Size()-1], newVal)) 264 } else { 265 newVals = types.NewValidatorSet(append(vals.Copy().Validators, newVal)) 266 } 267 268 // we need to sort the priv validators with the same index as the validator set 269 pv := make([]types.PrivValidator, newVals.Size()) 270 for idx, val := range newVals.Validators { 271 found := false 272 for _, p := range append(privVals, newPrivVal.(types.MockPV)) { 273 if bytes.Equal(p.PrivKey.PubKey().Address(), val.Address) { 274 pv[idx] = p 275 found = true 276 break 277 } 278 } 279 if !found { 280 return nil, nil, fmt.Errorf("missing priv validator for %v", val.Address) 281 } 282 } 283 284 return pv, newVals, nil 285 }