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.

320 lines
9.3 KiB

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