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