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.

227 lines
7.4 KiB

  1. package main
  2. import (
  3. "fmt"
  4. "math/rand"
  5. "sort"
  6. "strings"
  7. e2e "github.com/tendermint/tendermint/test/e2e/pkg"
  8. )
  9. var (
  10. // testnetCombinations defines global testnet options, where we generate a
  11. // separate testnet for each combination (Cartesian product) of options.
  12. testnetCombinations = map[string][]interface{}{
  13. "topology": {"single", "quad", "large"},
  14. "ipv6": {false, true},
  15. "initialHeight": {0, 1000},
  16. "initialState": {
  17. map[string]string{},
  18. map[string]string{"initial01": "a", "initial02": "b", "initial03": "c"},
  19. },
  20. "validators": {"genesis", "initchain"},
  21. }
  22. // The following specify randomly chosen values for testnet nodes.
  23. nodeDatabases = uniformChoice{"goleveldb", "cleveldb", "rocksdb", "boltdb", "badgerdb"}
  24. // FIXME disabled grpc due to https://github.com/tendermint/tendermint/issues/5439
  25. nodeABCIProtocols = uniformChoice{"unix", "tcp", "builtin"} // "grpc"
  26. nodePrivvalProtocols = uniformChoice{"file", "unix", "tcp"}
  27. // FIXME disabled v1 due to https://github.com/tendermint/tendermint/issues/5444
  28. nodeFastSyncs = uniformChoice{"", "v0", "v2"} // "v1"
  29. nodeStateSyncs = uniformChoice{false, true}
  30. nodePersistIntervals = uniformChoice{0, 1, 5}
  31. nodeSnapshotIntervals = uniformChoice{0, 3}
  32. nodeRetainBlocks = uniformChoice{0, 1, 5}
  33. nodePerturbations = probSetChoice{
  34. "disconnect": 0.1,
  35. "pause": 0.1,
  36. // FIXME disabled due to https://github.com/tendermint/tendermint/issues/5422
  37. // "kill": 0.1,
  38. // "restart": 0.1,
  39. }
  40. )
  41. // Generate generates random testnets using the given RNG.
  42. func Generate(r *rand.Rand) ([]e2e.Manifest, error) {
  43. manifests := []e2e.Manifest{}
  44. for _, opt := range combinations(testnetCombinations) {
  45. manifest, err := generateTestnet(r, opt)
  46. if err != nil {
  47. return nil, err
  48. }
  49. manifests = append(manifests, manifest)
  50. }
  51. return manifests, nil
  52. }
  53. // generateTestnet generates a single testnet with the given options.
  54. func generateTestnet(r *rand.Rand, opt map[string]interface{}) (e2e.Manifest, error) {
  55. manifest := e2e.Manifest{
  56. IPv6: opt["ipv6"].(bool),
  57. InitialHeight: int64(opt["initialHeight"].(int)),
  58. InitialState: opt["initialState"].(map[string]string),
  59. Validators: &map[string]int64{},
  60. ValidatorUpdates: map[string]map[string]int64{},
  61. Nodes: map[string]*e2e.ManifestNode{},
  62. }
  63. var numSeeds, numValidators, numFulls int
  64. switch opt["topology"].(string) {
  65. case "single":
  66. numValidators = 1
  67. case "quad":
  68. numValidators = 4
  69. case "large":
  70. // FIXME Networks are kept small since large ones use too much CPU.
  71. numSeeds = r.Intn(4)
  72. numValidators = 4 + r.Intn(7)
  73. numFulls = r.Intn(5)
  74. default:
  75. return manifest, fmt.Errorf("unknown topology %q", opt["topology"])
  76. }
  77. // First we generate seed nodes, starting at the initial height.
  78. for i := 1; i <= numSeeds; i++ {
  79. manifest.Nodes[fmt.Sprintf("seed%02d", i)] = generateNode(r, e2e.ModeSeed, 0, false)
  80. }
  81. // Next, we generate validators. We make sure a BFT quorum of validators start
  82. // at the initial height, and that we have two archive nodes. We also set up
  83. // the initial validator set, and validator set updates for delayed nodes.
  84. nextStartAt := manifest.InitialHeight + 5
  85. quorum := numValidators*2/3 + 1
  86. for i := 1; i <= numValidators; i++ {
  87. startAt := int64(0)
  88. if i > quorum {
  89. startAt = nextStartAt
  90. nextStartAt += 5
  91. }
  92. name := fmt.Sprintf("validator%02d", i)
  93. manifest.Nodes[name] = generateNode(r, e2e.ModeValidator, startAt, i <= 2)
  94. if startAt == 0 {
  95. (*manifest.Validators)[name] = int64(30 + r.Intn(71))
  96. } else {
  97. manifest.ValidatorUpdates[fmt.Sprint(startAt+5)] = map[string]int64{
  98. name: int64(30 + r.Intn(71)),
  99. }
  100. }
  101. }
  102. // Move validators to InitChain if specified.
  103. switch opt["validators"].(string) {
  104. case "genesis":
  105. case "initchain":
  106. manifest.ValidatorUpdates["0"] = *manifest.Validators
  107. manifest.Validators = &map[string]int64{}
  108. default:
  109. return manifest, fmt.Errorf("invalid validators option %q", opt["validators"])
  110. }
  111. // Finally, we generate random full nodes.
  112. for i := 1; i <= numFulls; i++ {
  113. startAt := int64(0)
  114. if r.Float64() >= 0.5 {
  115. startAt = nextStartAt
  116. nextStartAt += 5
  117. }
  118. manifest.Nodes[fmt.Sprintf("full%02d", i)] = generateNode(r, e2e.ModeFull, startAt, false)
  119. }
  120. // We now set up peer discovery for nodes. Seed nodes are fully meshed with
  121. // each other, while non-seed nodes either use a set of random seeds or a
  122. // set of random peers that start before themselves.
  123. var seedNames, peerNames []string
  124. for name, node := range manifest.Nodes {
  125. if node.Mode == string(e2e.ModeSeed) {
  126. seedNames = append(seedNames, name)
  127. } else {
  128. peerNames = append(peerNames, name)
  129. }
  130. }
  131. for _, name := range seedNames {
  132. for _, otherName := range seedNames {
  133. if name != otherName {
  134. manifest.Nodes[name].Seeds = append(manifest.Nodes[name].Seeds, otherName)
  135. }
  136. }
  137. }
  138. sort.Slice(peerNames, func(i, j int) bool {
  139. iName, jName := peerNames[i], peerNames[j]
  140. switch {
  141. case manifest.Nodes[iName].StartAt < manifest.Nodes[jName].StartAt:
  142. return true
  143. case manifest.Nodes[iName].StartAt > manifest.Nodes[jName].StartAt:
  144. return false
  145. default:
  146. return strings.Compare(iName, jName) == -1
  147. }
  148. })
  149. for i, name := range peerNames {
  150. if len(seedNames) > 0 && (i == 0 || r.Float64() >= 0.5) {
  151. manifest.Nodes[name].Seeds = uniformSetChoice(seedNames).Choose(r)
  152. } else if i > 0 {
  153. manifest.Nodes[name].PersistentPeers = uniformSetChoice(peerNames[:i]).Choose(r)
  154. }
  155. }
  156. return manifest, nil
  157. }
  158. // generateNode randomly generates a node, with some constraints to avoid
  159. // generating invalid configurations. We do not set Seeds or PersistentPeers
  160. // here, since we need to know the overall network topology and startup
  161. // sequencing.
  162. func generateNode(r *rand.Rand, mode e2e.Mode, startAt int64, forceArchive bool) *e2e.ManifestNode {
  163. node := e2e.ManifestNode{
  164. Mode: string(mode),
  165. StartAt: startAt,
  166. Database: nodeDatabases.Choose(r).(string),
  167. ABCIProtocol: nodeABCIProtocols.Choose(r).(string),
  168. PrivvalProtocol: nodePrivvalProtocols.Choose(r).(string),
  169. FastSync: nodeFastSyncs.Choose(r).(string),
  170. StateSync: nodeStateSyncs.Choose(r).(bool) && startAt > 0,
  171. PersistInterval: ptrUint64(uint64(nodePersistIntervals.Choose(r).(int))),
  172. SnapshotInterval: uint64(nodeSnapshotIntervals.Choose(r).(int)),
  173. RetainBlocks: uint64(nodeRetainBlocks.Choose(r).(int)),
  174. Perturb: nodePerturbations.Choose(r),
  175. }
  176. // If this node is forced to be an archive node, retain all blocks and
  177. // enable state sync snapshotting.
  178. if forceArchive {
  179. node.RetainBlocks = 0
  180. node.SnapshotInterval = 3
  181. }
  182. // If a node which does not persist state also does not retain blocks, randomly
  183. // choose to either persist state or retain all blocks.
  184. if node.PersistInterval != nil && *node.PersistInterval == 0 && node.RetainBlocks > 0 {
  185. if r.Float64() > 0.5 {
  186. node.RetainBlocks = 0
  187. } else {
  188. node.PersistInterval = ptrUint64(node.RetainBlocks)
  189. }
  190. }
  191. // If either PersistInterval or SnapshotInterval are greater than RetainBlocks,
  192. // expand the block retention time.
  193. if node.RetainBlocks > 0 {
  194. if node.PersistInterval != nil && node.RetainBlocks < *node.PersistInterval {
  195. node.RetainBlocks = *node.PersistInterval
  196. }
  197. if node.RetainBlocks < node.SnapshotInterval {
  198. node.RetainBlocks = node.SnapshotInterval
  199. }
  200. }
  201. return &node
  202. }
  203. func ptrUint64(i uint64) *uint64 {
  204. return &i
  205. }