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  }