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.

265 lines
9.2 KiB

  1. package e2e
  2. import (
  3. "fmt"
  4. "os"
  5. "sort"
  6. "github.com/BurntSushi/toml"
  7. )
  8. // Manifest represents a TOML testnet manifest.
  9. type Manifest struct {
  10. // IPv6 uses IPv6 networking instead of IPv4. Defaults to IPv4.
  11. IPv6 bool `toml:"ipv6"`
  12. // InitialHeight specifies the initial block height, set in genesis. Defaults to 1.
  13. InitialHeight int64 `toml:"initial_height"`
  14. // InitialState is an initial set of key/value pairs for the application,
  15. // set in genesis. Defaults to nothing.
  16. InitialState map[string]string `toml:"initial_state"`
  17. // Validators is the initial validator set in genesis, given as node names
  18. // and power:
  19. //
  20. // validators = { validator01 = 10; validator02 = 20; validator03 = 30 }
  21. //
  22. // Defaults to all nodes that have mode=validator at power 100. Explicitly
  23. // specifying an empty set will start with no validators in genesis, and
  24. // the application must return the validator set in InitChain via the
  25. // setting validator_update.0 (see below).
  26. Validators *map[string]int64 `toml:"validators"`
  27. // ValidatorUpdates is a map of heights to validator names and their power,
  28. // and will be returned by the ABCI application. For example, the following
  29. // changes the power of validator01 and validator02 at height 1000:
  30. //
  31. // [validator_update.1000]
  32. // validator01 = 20
  33. // validator02 = 10
  34. //
  35. // Specifying height 0 returns the validator update during InitChain. The
  36. // application returns the validator updates as-is, i.e. removing a
  37. // validator must be done by returning it with power 0, and any validators
  38. // not specified are not changed.
  39. ValidatorUpdates map[string]map[string]int64 `toml:"validator_update"`
  40. // Nodes specifies the network nodes. At least one node must be given.
  41. Nodes map[string]*ManifestNode `toml:"node"`
  42. // KeyType sets the curve that will be used by validators.
  43. // Options are ed25519 & secp256k1
  44. KeyType string `toml:"key_type"`
  45. // Evidence indicates the amount of evidence that will be injected into the
  46. // testnet via the RPC endpoint of a random node. Default is 0
  47. Evidence int `toml:"evidence"`
  48. // LogLevel sets the log level of the entire testnet. This can be overridden
  49. // by individual nodes.
  50. LogLevel string `toml:"log_level"`
  51. // QueueType describes the type of queue that the system uses internally
  52. QueueType string `toml:"queue_type"`
  53. // Number of bytes per tx. Default is 1kb (1024)
  54. TxSize int64
  55. // ABCIProtocol specifies the protocol used to communicate with the ABCI
  56. // application: "unix", "tcp", "grpc", or "builtin". Defaults to builtin.
  57. // builtin will build a complete Tendermint node into the application and
  58. // launch it instead of launching a separate Tendermint process.
  59. ABCIProtocol string `toml:"abci_protocol"`
  60. }
  61. // ManifestNode represents a node in a testnet manifest.
  62. type ManifestNode struct {
  63. // Mode specifies the type of node: "validator", "full", "light" or "seed".
  64. // Defaults to "validator". Full nodes do not get a signing key (a dummy key
  65. // is generated), and seed nodes run in seed mode with the PEX reactor enabled.
  66. Mode string `toml:"mode"`
  67. // Seeds is the list of node names to use as P2P seed nodes. Defaults to none.
  68. Seeds []string `toml:"seeds"`
  69. // PersistentPeers is a list of node names to maintain persistent P2P
  70. // connections to. If neither seeds nor persistent peers are specified,
  71. // this defaults to all other nodes in the network. For light clients,
  72. // this relates to the providers the light client is connected to.
  73. PersistentPeers []string `toml:"persistent_peers"`
  74. // Database specifies the database backend: "goleveldb", "cleveldb",
  75. // "rocksdb", "boltdb", or "badgerdb". Defaults to goleveldb.
  76. Database string `toml:"database"`
  77. // PrivvalProtocol specifies the protocol used to sign consensus messages:
  78. // "file", "unix", "tcp", or "grpc". Defaults to "file". For tcp and unix, the ABCI
  79. // application will launch a remote signer client in a separate goroutine.
  80. // For grpc the ABCI application will launch a remote signer server.
  81. // Only nodes with mode=validator will actually make use of this.
  82. PrivvalProtocol string `toml:"privval_protocol"`
  83. // StartAt specifies the block height at which the node will be started. The
  84. // runner will wait for the network to reach at least this block height.
  85. StartAt int64 `toml:"start_at"`
  86. // BlockSync specifies the block sync mode: "" (disable), "v0" or "v2".
  87. // Defaults to disabled.
  88. BlockSync string `toml:"block_sync"`
  89. // Mempool specifies which version of mempool to use. Either "v0" or "v1"
  90. Mempool string `toml:"mempool_version"`
  91. // StateSync enables state sync. The runner automatically configures trusted
  92. // block hashes and RPC servers. At least one node in the network must have
  93. // SnapshotInterval set to non-zero, and the state syncing node must have
  94. // StartAt set to an appropriate height where a snapshot is available.
  95. // StateSync can either be "p2p" or "rpc" or an empty string to disable
  96. StateSync string `toml:"state_sync"`
  97. // PersistInterval specifies the height interval at which the application
  98. // will persist state to disk. Defaults to 1 (every height), setting this to
  99. // 0 disables state persistence.
  100. PersistInterval *uint64 `toml:"persist_interval"`
  101. // SnapshotInterval specifies the height interval at which the application
  102. // will take state sync snapshots. Defaults to 0 (disabled).
  103. SnapshotInterval uint64 `toml:"snapshot_interval"`
  104. // RetainBlocks specifies the number of recent blocks to retain. Defaults to
  105. // 0, which retains all blocks. Must be greater that PersistInterval,
  106. // SnapshotInterval and EvidenceAgeHeight.
  107. RetainBlocks uint64 `toml:"retain_blocks"`
  108. // Perturb lists perturbations to apply to the node after it has been
  109. // started and synced with the network:
  110. //
  111. // disconnect: temporarily disconnects the node from the network
  112. // kill: kills the node with SIGKILL then restarts it
  113. // pause: temporarily pauses (freezes) the node
  114. // restart: restarts the node, shutting it down with SIGTERM
  115. Perturb []string `toml:"perturb"`
  116. // Log level sets the log level of the specific node i.e. "info".
  117. // This is helpful when debugging a specific problem. This overrides the network
  118. // level.
  119. LogLevel string `toml:"log_level"`
  120. // UseLegacyP2P enables use of the legacy p2p layer for this node.
  121. UseLegacyP2P bool `toml:"use_legacy_p2p"`
  122. }
  123. // Stateless reports whether m is a node that does not own state, including light and seed nodes.
  124. func (m ManifestNode) Stateless() bool {
  125. return m.Mode == string(ModeLight) || m.Mode == string(ModeSeed)
  126. }
  127. // Save saves the testnet manifest to a file.
  128. func (m Manifest) Save(file string) error {
  129. f, err := os.Create(file)
  130. if err != nil {
  131. return fmt.Errorf("failed to create manifest file %q: %w", file, err)
  132. }
  133. return toml.NewEncoder(f).Encode(m)
  134. }
  135. // LoadManifest loads a testnet manifest from a file.
  136. func LoadManifest(file string) (Manifest, error) {
  137. manifest := Manifest{}
  138. _, err := toml.DecodeFile(file, &manifest)
  139. if err != nil {
  140. return manifest, fmt.Errorf("failed to load testnet manifest %q: %w", file, err)
  141. }
  142. return manifest, nil
  143. }
  144. // SortManifests orders (in-place) a list of manifests such that the
  145. // manifests will be ordered in terms of complexity (or expected
  146. // runtime). Complexity is determined first by the number of nodes,
  147. // and then by the total number of perturbations in the network.
  148. //
  149. // If reverse is true, then the manifests are ordered with the most
  150. // complex networks before the less complex networks.
  151. func SortManifests(manifests []Manifest, reverse bool) {
  152. sort.SliceStable(manifests, func(i, j int) bool {
  153. // sort based on a point-based comparison between two
  154. // manifests.
  155. var (
  156. left = manifests[i]
  157. right = manifests[j]
  158. )
  159. // scores start with 100 points for each node. The
  160. // number of nodes in a network is the most important
  161. // factor in the complexity of the test.
  162. leftScore := len(left.Nodes) * 100
  163. rightScore := len(right.Nodes) * 100
  164. // add two points for every node perturbation, and one
  165. // point for every node that starts after genesis.
  166. for _, n := range left.Nodes {
  167. leftScore += (len(n.Perturb) * 2)
  168. if n.StartAt > 0 {
  169. leftScore += 3
  170. }
  171. }
  172. for _, n := range right.Nodes {
  173. rightScore += (len(n.Perturb) * 2)
  174. if n.StartAt > 0 {
  175. rightScore += 3
  176. }
  177. }
  178. // add one point if the network has evidence.
  179. if left.Evidence > 0 {
  180. leftScore += 2
  181. }
  182. if right.Evidence > 0 {
  183. rightScore += 2
  184. }
  185. if left.TxSize > right.TxSize {
  186. leftScore++
  187. }
  188. if right.TxSize > left.TxSize {
  189. rightScore++
  190. }
  191. if reverse {
  192. return leftScore >= rightScore
  193. }
  194. return leftScore < rightScore
  195. })
  196. }
  197. // SplitGroups divides a list of manifests into n groups of
  198. // manifests.
  199. func SplitGroups(groups int, manifests []Manifest) [][]Manifest {
  200. groupSize := (len(manifests) + groups - 1) / groups
  201. splitManifests := make([][]Manifest, 0, groups)
  202. for i := 0; i < len(manifests); i += groupSize {
  203. grp := make([]Manifest, groupSize)
  204. n := copy(grp, manifests[i:])
  205. splitManifests = append(splitManifests, grp[:n])
  206. }
  207. return splitManifests
  208. }
  209. // WriteManifests writes a collection of manifests into files with the
  210. // specified path prefix.
  211. func WriteManifests(prefix string, manifests []Manifest) error {
  212. for i, manifest := range manifests {
  213. if err := manifest.Save(fmt.Sprintf("%s-%04d.toml", prefix, i)); err != nil {
  214. return err
  215. }
  216. }
  217. return nil
  218. }