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.

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