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.

321 lines
9.2 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(ctx,
  73. privVals, evidenceHeight, valSet, testnet.Name, blockRes.Block.Time,
  74. )
  75. } else {
  76. ev, err = generateDuplicateVoteEvidence(ctx,
  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. ctx context.Context,
  123. privVals []types.MockPV,
  124. height int64,
  125. vals *types.ValidatorSet,
  126. chainID string,
  127. evTime time.Time,
  128. ) (*types.LightClientAttackEvidence, error) {
  129. // forge a random header
  130. forgedHeight := height + 2
  131. forgedTime := evTime.Add(1 * time.Second)
  132. header := makeHeaderRandom(chainID, forgedHeight)
  133. header.Time = forgedTime
  134. // add a new bogus validator and remove an existing one to
  135. // vary the validator set slightly
  136. pv, conflictingVals, err := mutateValidatorSet(ctx, privVals, vals)
  137. if err != nil {
  138. return nil, err
  139. }
  140. header.ValidatorsHash = conflictingVals.Hash()
  141. // create a commit for the forged header
  142. blockID := makeBlockID(header.Hash(), 1000, []byte("partshash"))
  143. voteSet := types.NewVoteSet(chainID, forgedHeight, 0, tmproto.SignedMsgType(2), conflictingVals)
  144. commit, err := factory.MakeCommit(ctx, blockID, forgedHeight, 0, voteSet, pv, forgedTime)
  145. if err != nil {
  146. return nil, err
  147. }
  148. ev := &types.LightClientAttackEvidence{
  149. ConflictingBlock: &types.LightBlock{
  150. SignedHeader: &types.SignedHeader{
  151. Header: header,
  152. Commit: commit,
  153. },
  154. ValidatorSet: conflictingVals,
  155. },
  156. CommonHeight: height,
  157. TotalVotingPower: vals.TotalVotingPower(),
  158. Timestamp: evTime,
  159. }
  160. ev.ByzantineValidators = ev.GetByzantineValidators(vals, &types.SignedHeader{
  161. Header: makeHeaderRandom(chainID, forgedHeight),
  162. })
  163. return ev, nil
  164. }
  165. // generateDuplicateVoteEvidence picks a random validator from the val set and
  166. // returns duplicate vote evidence against the validator
  167. func generateDuplicateVoteEvidence(
  168. ctx context.Context,
  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(ctx, privVal, chainID, valIdx, height, 0, 2, makeRandomBlockID(), time)
  180. if err != nil {
  181. return nil, err
  182. }
  183. voteB, err := factory.MakeVote(ctx, 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 := os.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(ctx context.Context, privVals []types.MockPV, vals *types.ValidatorSet) ([]types.PrivValidator, *types.ValidatorSet, error) {
  254. newVal, newPrivVal, err := factory.RandValidator(ctx, false, 10)
  255. if err != nil {
  256. return nil, nil, err
  257. }
  258. var newVals *types.ValidatorSet
  259. if vals.Size() > 2 {
  260. newVals = types.NewValidatorSet(append(vals.Copy().Validators[:vals.Size()-1], newVal))
  261. } else {
  262. newVals = types.NewValidatorSet(append(vals.Copy().Validators, newVal))
  263. }
  264. // we need to sort the priv validators with the same index as the validator set
  265. pv := make([]types.PrivValidator, newVals.Size())
  266. for idx, val := range newVals.Validators {
  267. found := false
  268. for _, p := range append(privVals, newPrivVal.(types.MockPV)) {
  269. if bytes.Equal(p.PrivKey.PubKey().Address(), val.Address) {
  270. pv[idx] = p
  271. found = true
  272. break
  273. }
  274. }
  275. if !found {
  276. return nil, nil, fmt.Errorf("missing priv validator for %v", val.Address)
  277. }
  278. }
  279. return pv, newVals, nil
  280. }