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  }