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.

315 lines
9.1 KiB

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