bitbucket.org/number571/tendermint@v0.8.14/test/e2e/pkg/testnet.go (about) 1 //nolint: gosec 2 package e2e 3 4 import ( 5 "errors" 6 "fmt" 7 "io" 8 "math/rand" 9 "net" 10 "path/filepath" 11 "sort" 12 "strconv" 13 "strings" 14 "time" 15 16 "bitbucket.org/number571/tendermint/crypto" 17 "bitbucket.org/number571/tendermint/crypto/gost256" 18 "bitbucket.org/number571/tendermint/crypto/gost512" 19 rpchttp "bitbucket.org/number571/tendermint/rpc/client/http" 20 "bitbucket.org/number571/tendermint/types" 21 ) 22 23 const ( 24 randomSeed int64 = 2308084734268 25 proxyPortFirst uint32 = 5701 26 networkIPv4 = "10.186.73.0/24" 27 networkIPv6 = "fd80:b10c::/48" 28 ) 29 30 type Mode string 31 type Protocol string 32 type Perturbation string 33 34 const ( 35 ModeValidator Mode = "validator" 36 ModeFull Mode = "full" 37 ModeLight Mode = "light" 38 ModeSeed Mode = "seed" 39 40 ProtocolBuiltin Protocol = "builtin" 41 ProtocolFile Protocol = "file" 42 ProtocolGRPC Protocol = "grpc" 43 ProtocolTCP Protocol = "tcp" 44 ProtocolUNIX Protocol = "unix" 45 46 PerturbationDisconnect Perturbation = "disconnect" 47 PerturbationKill Perturbation = "kill" 48 PerturbationPause Perturbation = "pause" 49 PerturbationRestart Perturbation = "restart" 50 51 EvidenceAgeHeight int64 = 7 52 EvidenceAgeTime time.Duration = 500 * time.Millisecond 53 ) 54 55 // Testnet represents a single testnet. 56 type Testnet struct { 57 Name string 58 File string 59 Dir string 60 IP *net.IPNet 61 InitialHeight int64 62 InitialState map[string]string 63 Validators map[*Node]int64 64 ValidatorUpdates map[int64]map[*Node]int64 65 Nodes []*Node 66 KeyType string 67 Evidence int 68 LogLevel string 69 TxSize int64 70 } 71 72 // Node represents a Tendermint node in a testnet. 73 type Node struct { 74 Name string 75 Testnet *Testnet 76 Mode Mode 77 PrivvalKey crypto.PrivKey 78 NodeKey crypto.PrivKey 79 IP net.IP 80 ProxyPort uint32 81 StartAt int64 82 FastSync string 83 Mempool string 84 StateSync bool 85 Database string 86 ABCIProtocol Protocol 87 PrivvalProtocol Protocol 88 PersistInterval uint64 89 SnapshotInterval uint64 90 RetainBlocks uint64 91 Seeds []*Node 92 PersistentPeers []*Node 93 Perturbations []Perturbation 94 LogLevel string 95 DisableLegacyP2P bool 96 QueueType string 97 } 98 99 // LoadTestnet loads a testnet from a manifest file, using the filename to 100 // determine the testnet name and directory (from the basename of the file). 101 // The testnet generation must be deterministic, since it is generated 102 // separately by the runner and the test cases. For this reason, testnets use a 103 // random seed to generate e.g. keys. 104 func LoadTestnet(file string) (*Testnet, error) { 105 manifest, err := LoadManifest(file) 106 if err != nil { 107 return nil, err 108 } 109 dir := strings.TrimSuffix(file, filepath.Ext(file)) 110 111 // Set up resource generators. These must be deterministic. 112 netAddress := networkIPv4 113 if manifest.IPv6 { 114 netAddress = networkIPv6 115 } 116 _, ipNet, err := net.ParseCIDR(netAddress) 117 if err != nil { 118 return nil, fmt.Errorf("invalid IP network address %q: %w", netAddress, err) 119 } 120 121 ipGen := newIPGenerator(ipNet) 122 keyGen := newKeyGenerator(randomSeed) 123 proxyPortGen := newPortGenerator(proxyPortFirst) 124 125 testnet := &Testnet{ 126 Name: filepath.Base(dir), 127 File: file, 128 Dir: dir, 129 IP: ipGen.Network(), 130 InitialHeight: 1, 131 InitialState: manifest.InitialState, 132 Validators: map[*Node]int64{}, 133 ValidatorUpdates: map[int64]map[*Node]int64{}, 134 Nodes: []*Node{}, 135 Evidence: manifest.Evidence, 136 KeyType: "gost512", 137 LogLevel: manifest.LogLevel, 138 TxSize: manifest.TxSize, 139 } 140 if len(manifest.KeyType) != 0 { 141 testnet.KeyType = manifest.KeyType 142 } 143 if testnet.TxSize <= 0 { 144 testnet.TxSize = 1024 145 } 146 if manifest.InitialHeight > 0 { 147 testnet.InitialHeight = manifest.InitialHeight 148 } 149 150 // Set up nodes, in alphabetical order (IPs and ports get same order). 151 nodeNames := []string{} 152 for name := range manifest.Nodes { 153 nodeNames = append(nodeNames, name) 154 } 155 sort.Strings(nodeNames) 156 157 for _, name := range nodeNames { 158 nodeManifest := manifest.Nodes[name] 159 node := &Node{ 160 Name: name, 161 Testnet: testnet, 162 PrivvalKey: keyGen.Generate(manifest.KeyType), 163 NodeKey: keyGen.Generate("gost512"), 164 IP: ipGen.Next(), 165 ProxyPort: proxyPortGen.Next(), 166 Mode: ModeValidator, 167 Database: "goleveldb", 168 ABCIProtocol: ProtocolBuiltin, 169 PrivvalProtocol: ProtocolFile, 170 StartAt: nodeManifest.StartAt, 171 FastSync: nodeManifest.FastSync, 172 Mempool: nodeManifest.Mempool, 173 StateSync: nodeManifest.StateSync, 174 PersistInterval: 1, 175 SnapshotInterval: nodeManifest.SnapshotInterval, 176 RetainBlocks: nodeManifest.RetainBlocks, 177 Perturbations: []Perturbation{}, 178 LogLevel: manifest.LogLevel, 179 QueueType: manifest.QueueType, 180 DisableLegacyP2P: manifest.DisableLegacyP2P || nodeManifest.DisableLegacyP2P, 181 } 182 183 if node.StartAt == testnet.InitialHeight { 184 node.StartAt = 0 // normalize to 0 for initial nodes, since code expects this 185 } 186 if nodeManifest.Mode != "" { 187 node.Mode = Mode(nodeManifest.Mode) 188 } 189 if nodeManifest.Database != "" { 190 node.Database = nodeManifest.Database 191 } 192 if nodeManifest.ABCIProtocol != "" { 193 node.ABCIProtocol = Protocol(nodeManifest.ABCIProtocol) 194 } 195 if nodeManifest.PrivvalProtocol != "" { 196 node.PrivvalProtocol = Protocol(nodeManifest.PrivvalProtocol) 197 } 198 if nodeManifest.PersistInterval != nil { 199 node.PersistInterval = *nodeManifest.PersistInterval 200 } 201 for _, p := range nodeManifest.Perturb { 202 node.Perturbations = append(node.Perturbations, Perturbation(p)) 203 } 204 if nodeManifest.LogLevel != "" { 205 node.LogLevel = nodeManifest.LogLevel 206 } 207 testnet.Nodes = append(testnet.Nodes, node) 208 } 209 210 // We do a second pass to set up seeds and persistent peers, which allows graph cycles. 211 for _, node := range testnet.Nodes { 212 nodeManifest, ok := manifest.Nodes[node.Name] 213 if !ok { 214 return nil, fmt.Errorf("failed to look up manifest for node %q", node.Name) 215 } 216 for _, seedName := range nodeManifest.Seeds { 217 seed := testnet.LookupNode(seedName) 218 if seed == nil { 219 return nil, fmt.Errorf("unknown seed %q for node %q", seedName, node.Name) 220 } 221 node.Seeds = append(node.Seeds, seed) 222 } 223 for _, peerName := range nodeManifest.PersistentPeers { 224 peer := testnet.LookupNode(peerName) 225 if peer == nil { 226 return nil, fmt.Errorf("unknown persistent peer %q for node %q", peerName, node.Name) 227 } 228 if peer.Mode == ModeLight { 229 return nil, fmt.Errorf("can not have a light client as a persistent peer (for %q)", node.Name) 230 } 231 node.PersistentPeers = append(node.PersistentPeers, peer) 232 } 233 234 // If there are no seeds or persistent peers specified, default to persistent 235 // connections to all other full nodes. 236 if len(node.PersistentPeers) == 0 && len(node.Seeds) == 0 { 237 for _, peer := range testnet.Nodes { 238 if peer.Name == node.Name { 239 continue 240 } 241 if peer.Mode == ModeLight { 242 continue 243 } 244 node.PersistentPeers = append(node.PersistentPeers, peer) 245 } 246 } 247 } 248 249 // Set up genesis validators. If not specified explicitly, use all validator nodes. 250 if manifest.Validators != nil { 251 for validatorName, power := range *manifest.Validators { 252 validator := testnet.LookupNode(validatorName) 253 if validator == nil { 254 return nil, fmt.Errorf("unknown validator %q", validatorName) 255 } 256 testnet.Validators[validator] = power 257 } 258 } else { 259 for _, node := range testnet.Nodes { 260 if node.Mode == ModeValidator { 261 testnet.Validators[node] = 100 262 } 263 } 264 } 265 266 // Set up validator updates. 267 for heightStr, validators := range manifest.ValidatorUpdates { 268 height, err := strconv.Atoi(heightStr) 269 if err != nil { 270 return nil, fmt.Errorf("invalid validator update height %q: %w", height, err) 271 } 272 valUpdate := map[*Node]int64{} 273 for name, power := range validators { 274 node := testnet.LookupNode(name) 275 if node == nil { 276 return nil, fmt.Errorf("unknown validator %q for update at height %v", name, height) 277 } 278 valUpdate[node] = power 279 } 280 testnet.ValidatorUpdates[int64(height)] = valUpdate 281 } 282 283 return testnet, testnet.Validate() 284 } 285 286 // Validate validates a testnet. 287 func (t Testnet) Validate() error { 288 if t.Name == "" { 289 return errors.New("network has no name") 290 } 291 if t.IP == nil { 292 return errors.New("network has no IP") 293 } 294 if len(t.Nodes) == 0 { 295 return errors.New("network has no nodes") 296 } 297 switch t.KeyType { 298 case "", types.ABCIPubKeyTypeGost512, types.ABCIPubKeyTypeGost256: 299 default: 300 return errors.New("unsupported KeyType") 301 } 302 for _, node := range t.Nodes { 303 if err := node.Validate(t); err != nil { 304 return fmt.Errorf("invalid node %q: %w", node.Name, err) 305 } 306 } 307 return nil 308 } 309 310 // Validate validates a node. 311 func (n Node) Validate(testnet Testnet) error { 312 if n.Name == "" { 313 return errors.New("node has no name") 314 } 315 if n.IP == nil { 316 return errors.New("node has no IP address") 317 } 318 if !testnet.IP.Contains(n.IP) { 319 return fmt.Errorf("node IP %v is not in testnet network %v", n.IP, testnet.IP) 320 } 321 if n.ProxyPort > 0 { 322 if n.ProxyPort <= 1024 { 323 return fmt.Errorf("local port %v must be >1024", n.ProxyPort) 324 } 325 for _, peer := range testnet.Nodes { 326 if peer.Name != n.Name && peer.ProxyPort == n.ProxyPort { 327 return fmt.Errorf("peer %q also has local port %v", peer.Name, n.ProxyPort) 328 } 329 } 330 } 331 switch n.FastSync { 332 case "", "v0", "v2": 333 default: 334 return fmt.Errorf("invalid fast sync setting %q", n.FastSync) 335 } 336 switch n.Mempool { 337 case "", "v0", "v1": 338 default: 339 return fmt.Errorf("invalid mempool version %q", n.Mempool) 340 } 341 switch n.QueueType { 342 case "", "priority", "wdrr", "fifo": 343 default: 344 return fmt.Errorf("unsupported p2p queue type: %s", n.QueueType) 345 } 346 switch n.Database { 347 case "goleveldb", "cleveldb", "boltdb", "rocksdb", "badgerdb": 348 default: 349 return fmt.Errorf("invalid database setting %q", n.Database) 350 } 351 switch n.ABCIProtocol { 352 case ProtocolBuiltin, ProtocolUNIX, ProtocolTCP, ProtocolGRPC: 353 default: 354 return fmt.Errorf("invalid ABCI protocol setting %q", n.ABCIProtocol) 355 } 356 if n.Mode == ModeLight && n.ABCIProtocol != ProtocolBuiltin { 357 return errors.New("light client must use builtin protocol") 358 } 359 switch n.PrivvalProtocol { 360 case ProtocolFile, ProtocolTCP, ProtocolGRPC, ProtocolUNIX: 361 default: 362 return fmt.Errorf("invalid privval protocol setting %q", n.PrivvalProtocol) 363 } 364 365 if n.StartAt > 0 && n.StartAt < n.Testnet.InitialHeight { 366 return fmt.Errorf("cannot start at height %v lower than initial height %v", 367 n.StartAt, n.Testnet.InitialHeight) 368 } 369 if n.StateSync && n.StartAt == 0 { 370 return errors.New("state synced nodes cannot start at the initial height") 371 } 372 if n.RetainBlocks != 0 && n.RetainBlocks < uint64(EvidenceAgeHeight) { 373 return fmt.Errorf("retain_blocks must be greater or equal to max evidence age (%d)", 374 EvidenceAgeHeight) 375 } 376 if n.PersistInterval == 0 && n.RetainBlocks > 0 { 377 return errors.New("persist_interval=0 requires retain_blocks=0") 378 } 379 if n.PersistInterval > 1 && n.RetainBlocks > 0 && n.RetainBlocks < n.PersistInterval { 380 return errors.New("persist_interval must be less than or equal to retain_blocks") 381 } 382 if n.SnapshotInterval > 0 && n.RetainBlocks > 0 && n.RetainBlocks < n.SnapshotInterval { 383 return errors.New("snapshot_interval must be less than er equal to retain_blocks") 384 } 385 386 for _, perturbation := range n.Perturbations { 387 switch perturbation { 388 case PerturbationDisconnect, PerturbationKill, PerturbationPause, PerturbationRestart: 389 default: 390 return fmt.Errorf("invalid perturbation %q", perturbation) 391 } 392 } 393 394 return nil 395 } 396 397 // LookupNode looks up a node by name. For now, simply do a linear search. 398 func (t Testnet) LookupNode(name string) *Node { 399 for _, node := range t.Nodes { 400 if node.Name == name { 401 return node 402 } 403 } 404 return nil 405 } 406 407 // ArchiveNodes returns a list of archive nodes that start at the initial height 408 // and contain the entire blockchain history. They are used e.g. as light client 409 // RPC servers. 410 func (t Testnet) ArchiveNodes() []*Node { 411 nodes := []*Node{} 412 for _, node := range t.Nodes { 413 if !node.Stateless() && node.StartAt == 0 && node.RetainBlocks == 0 { 414 nodes = append(nodes, node) 415 } 416 } 417 return nodes 418 } 419 420 // RandomNode returns a random non-seed node. 421 func (t Testnet) RandomNode() *Node { 422 for { 423 node := t.Nodes[rand.Intn(len(t.Nodes))] 424 if node.Mode != ModeSeed { 425 return node 426 } 427 } 428 } 429 430 // IPv6 returns true if the testnet is an IPv6 network. 431 func (t Testnet) IPv6() bool { 432 return t.IP.IP.To4() == nil 433 } 434 435 // HasPerturbations returns whether the network has any perturbations. 436 func (t Testnet) HasPerturbations() bool { 437 for _, node := range t.Nodes { 438 if len(node.Perturbations) > 0 { 439 return true 440 } 441 } 442 return false 443 } 444 445 // Address returns a P2P endpoint address for the node. 446 func (n Node) AddressP2P(withID bool) string { 447 ip := n.IP.String() 448 if n.IP.To4() == nil { 449 // IPv6 addresses must be wrapped in [] to avoid conflict with : port separator 450 ip = fmt.Sprintf("[%v]", ip) 451 } 452 addr := fmt.Sprintf("%v:26656", ip) 453 if withID { 454 addr = fmt.Sprintf("%x@%v", n.NodeKey.PubKey().Address().Bytes(), addr) 455 } 456 return addr 457 } 458 459 // Address returns an RPC endpoint address for the node. 460 func (n Node) AddressRPC() string { 461 ip := n.IP.String() 462 if n.IP.To4() == nil { 463 // IPv6 addresses must be wrapped in [] to avoid conflict with : port separator 464 ip = fmt.Sprintf("[%v]", ip) 465 } 466 return fmt.Sprintf("%v:26657", ip) 467 } 468 469 // Client returns an RPC client for a node. 470 func (n Node) Client() (*rpchttp.HTTP, error) { 471 return rpchttp.New(fmt.Sprintf("http://127.0.0.1:%v", n.ProxyPort)) 472 } 473 474 // Stateless returns true if the node is either a seed node or a light node 475 func (n Node) Stateless() bool { 476 return n.Mode == ModeLight || n.Mode == ModeSeed 477 } 478 479 // keyGenerator generates pseudorandom gost512 keys based on a seed. 480 type keyGenerator struct { 481 random *rand.Rand 482 } 483 484 func newKeyGenerator(seed int64) *keyGenerator { 485 return &keyGenerator{ 486 random: rand.New(rand.NewSource(seed)), 487 } 488 } 489 490 func (g *keyGenerator) Generate(keyType string) crypto.PrivKey { 491 seed := make([]byte, gost512.SeedSize) 492 493 _, err := io.ReadFull(g.random, seed) 494 if err != nil { 495 panic(err) // this shouldn't happen 496 } 497 switch keyType { 498 case gost256.KeyType: 499 return gost256.GenPrivKeyGost256(seed) 500 case "", gost512.KeyType: 501 return gost512.GenPrivKeyFromSecret(seed) 502 default: 503 panic("KeyType not supported") // should not make it this far 504 } 505 } 506 507 // portGenerator generates local Docker proxy ports for each node. 508 type portGenerator struct { 509 nextPort uint32 510 } 511 512 func newPortGenerator(firstPort uint32) *portGenerator { 513 return &portGenerator{nextPort: firstPort} 514 } 515 516 func (g *portGenerator) Next() uint32 { 517 port := g.nextPort 518 g.nextPort++ 519 if g.nextPort == 0 { 520 panic("port overflow") 521 } 522 return port 523 } 524 525 // ipGenerator generates sequential IP addresses for each node, using a random 526 // network address. 527 type ipGenerator struct { 528 network *net.IPNet 529 nextIP net.IP 530 } 531 532 func newIPGenerator(network *net.IPNet) *ipGenerator { 533 nextIP := make([]byte, len(network.IP)) 534 copy(nextIP, network.IP) 535 gen := &ipGenerator{network: network, nextIP: nextIP} 536 // Skip network and gateway addresses 537 gen.Next() 538 gen.Next() 539 return gen 540 } 541 542 func (g *ipGenerator) Network() *net.IPNet { 543 n := &net.IPNet{ 544 IP: make([]byte, len(g.network.IP)), 545 Mask: make([]byte, len(g.network.Mask)), 546 } 547 copy(n.IP, g.network.IP) 548 copy(n.Mask, g.network.Mask) 549 return n 550 } 551 552 func (g *ipGenerator) Next() net.IP { 553 ip := make([]byte, len(g.nextIP)) 554 copy(ip, g.nextIP) 555 for i := len(g.nextIP) - 1; i >= 0; i-- { 556 g.nextIP[i]++ 557 if g.nextIP[i] != 0 { 558 break 559 } 560 } 561 return ip 562 }