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  }