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