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.

323 lines
9.2 KiB

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