You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

285 lines
8.4 KiB

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