github.com/soomindae/tendermint@v0.0.5-0.20210528140126-84a0c70c8162/test/e2e/app/main.go (about)

     1  package main
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"net/http"
     8  	"os"
     9  	"path/filepath"
    10  	"strconv"
    11  	"strings"
    12  	"time"
    13  
    14  	"github.com/spf13/viper"
    15  
    16  	"github.com/soomindae/tendermint/abci/server"
    17  	"github.com/soomindae/tendermint/config"
    18  	"github.com/soomindae/tendermint/crypto/ed25519"
    19  	tmflags "github.com/soomindae/tendermint/libs/cli/flags"
    20  	"github.com/soomindae/tendermint/libs/log"
    21  	tmnet "github.com/soomindae/tendermint/libs/net"
    22  	"github.com/soomindae/tendermint/light"
    23  	lproxy "github.com/soomindae/tendermint/light/proxy"
    24  	lrpc "github.com/soomindae/tendermint/light/rpc"
    25  	dbs "github.com/soomindae/tendermint/light/store/db"
    26  	"github.com/soomindae/tendermint/node"
    27  	"github.com/soomindae/tendermint/p2p"
    28  	"github.com/soomindae/tendermint/privval"
    29  	"github.com/soomindae/tendermint/proxy"
    30  	rpcserver "github.com/soomindae/tendermint/rpc/jsonrpc/server"
    31  	e2e "github.com/soomindae/tendermint/test/e2e/pkg"
    32  	mcs "github.com/soomindae/tendermint/test/maverick/consensus"
    33  	maverick "github.com/soomindae/tendermint/test/maverick/node"
    34  )
    35  
    36  var logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout))
    37  
    38  // main is the binary entrypoint.
    39  func main() {
    40  	if len(os.Args) != 2 {
    41  		fmt.Printf("Usage: %v <configfile>", os.Args[0])
    42  		return
    43  	}
    44  	configFile := ""
    45  	if len(os.Args) == 2 {
    46  		configFile = os.Args[1]
    47  	}
    48  
    49  	if err := run(configFile); err != nil {
    50  		logger.Error(err.Error())
    51  		os.Exit(1)
    52  	}
    53  }
    54  
    55  // run runs the application - basically like main() with error handling.
    56  func run(configFile string) error {
    57  	cfg, err := LoadConfig(configFile)
    58  	if err != nil {
    59  		return err
    60  	}
    61  
    62  	// Start remote signer (must start before node if running builtin).
    63  	if cfg.PrivValServer != "" {
    64  		if err = startSigner(cfg); err != nil {
    65  			return err
    66  		}
    67  		if cfg.Protocol == "builtin" {
    68  			time.Sleep(1 * time.Second)
    69  		}
    70  	}
    71  
    72  	// Start app server.
    73  	switch cfg.Protocol {
    74  	case "socket", "grpc":
    75  		err = startApp(cfg)
    76  	case "builtin":
    77  		if len(cfg.Misbehaviors) == 0 {
    78  			if cfg.Mode == string(e2e.ModeLight) {
    79  				err = startLightClient(cfg)
    80  			} else {
    81  				err = startNode(cfg)
    82  			}
    83  		} else {
    84  			err = startMaverick(cfg)
    85  		}
    86  	default:
    87  		err = fmt.Errorf("invalid protocol %q", cfg.Protocol)
    88  	}
    89  	if err != nil {
    90  		return err
    91  	}
    92  
    93  	// Apparently there's no way to wait for the server, so we just sleep
    94  	for {
    95  		time.Sleep(1 * time.Hour)
    96  	}
    97  }
    98  
    99  // startApp starts the application server, listening for connections from Tendermint.
   100  func startApp(cfg *Config) error {
   101  	app, err := NewApplication(cfg)
   102  	if err != nil {
   103  		return err
   104  	}
   105  	server, err := server.NewServer(cfg.Listen, cfg.Protocol, app)
   106  	if err != nil {
   107  		return err
   108  	}
   109  	err = server.Start()
   110  	if err != nil {
   111  		return err
   112  	}
   113  	logger.Info(fmt.Sprintf("Server listening on %v (%v protocol)", cfg.Listen, cfg.Protocol))
   114  	return nil
   115  }
   116  
   117  // startNode starts a Tendermint node running the application directly. It assumes the Tendermint
   118  // configuration is in $TMHOME/config/tendermint.toml.
   119  //
   120  // FIXME There is no way to simply load the configuration from a file, so we need to pull in Viper.
   121  func startNode(cfg *Config) error {
   122  	app, err := NewApplication(cfg)
   123  	if err != nil {
   124  		return err
   125  	}
   126  
   127  	tmcfg, nodeLogger, nodeKey, err := setupNode()
   128  	if err != nil {
   129  		return fmt.Errorf("failed to setup config: %w", err)
   130  	}
   131  
   132  	n, err := node.NewNode(tmcfg,
   133  		privval.LoadOrGenFilePV(tmcfg.PrivValidatorKeyFile(), tmcfg.PrivValidatorStateFile()),
   134  		nodeKey,
   135  		proxy.NewLocalClientCreator(app),
   136  		node.DefaultGenesisDocProviderFunc(tmcfg),
   137  		node.DefaultDBProvider,
   138  		node.DefaultMetricsProvider(tmcfg.Instrumentation),
   139  		nodeLogger,
   140  	)
   141  	if err != nil {
   142  		return err
   143  	}
   144  	return n.Start()
   145  }
   146  
   147  func startLightClient(cfg *Config) error {
   148  	tmcfg, nodeLogger, _, err := setupNode()
   149  	if err != nil {
   150  		return err
   151  	}
   152  
   153  	dbContext := &node.DBContext{ID: "light", Config: tmcfg}
   154  	lightDB, err := node.DefaultDBProvider(dbContext)
   155  	if err != nil {
   156  		return err
   157  	}
   158  
   159  	providers := rpcEndpoints(tmcfg.P2P.PersistentPeers)
   160  
   161  	c, err := light.NewHTTPClient(
   162  		context.Background(),
   163  		cfg.ChainID,
   164  		light.TrustOptions{
   165  			Period: tmcfg.StateSync.TrustPeriod,
   166  			Height: tmcfg.StateSync.TrustHeight,
   167  			Hash:   tmcfg.StateSync.TrustHashBytes(),
   168  		},
   169  		providers[0],
   170  		providers[1:],
   171  		dbs.New(lightDB, "light"),
   172  		light.Logger(nodeLogger),
   173  	)
   174  	if err != nil {
   175  		return err
   176  	}
   177  
   178  	rpccfg := rpcserver.DefaultConfig()
   179  	rpccfg.MaxBodyBytes = tmcfg.RPC.MaxBodyBytes
   180  	rpccfg.MaxHeaderBytes = tmcfg.RPC.MaxHeaderBytes
   181  	rpccfg.MaxOpenConnections = tmcfg.RPC.MaxOpenConnections
   182  	// If necessary adjust global WriteTimeout to ensure it's greater than
   183  	// TimeoutBroadcastTxCommit.
   184  	// See https://github.com/soomindae/tendermint/issues/3435
   185  	if rpccfg.WriteTimeout <= tmcfg.RPC.TimeoutBroadcastTxCommit {
   186  		rpccfg.WriteTimeout = tmcfg.RPC.TimeoutBroadcastTxCommit + 1*time.Second
   187  	}
   188  
   189  	p, err := lproxy.NewProxy(c, tmcfg.RPC.ListenAddress, providers[0], rpccfg, nodeLogger,
   190  		lrpc.KeyPathFn(lrpc.DefaultMerkleKeyPathFn()))
   191  	if err != nil {
   192  		return err
   193  	}
   194  
   195  	logger.Info("Starting proxy...", "laddr", tmcfg.RPC.ListenAddress)
   196  	if err := p.ListenAndServe(); err != http.ErrServerClosed {
   197  		// Error starting or closing listener:
   198  		logger.Error("proxy ListenAndServe", "err", err)
   199  	}
   200  
   201  	return nil
   202  }
   203  
   204  // FIXME: Temporarily disconnected maverick until it is redesigned
   205  // startMaverick starts a Maverick node that runs the application directly. It assumes the Tendermint
   206  // configuration is in $TMHOME/config/tendermint.toml.
   207  func startMaverick(cfg *Config) error {
   208  	app, err := NewApplication(cfg)
   209  	if err != nil {
   210  		return err
   211  	}
   212  
   213  	tmcfg, logger, nodeKey, err := setupNode()
   214  	if err != nil {
   215  		return fmt.Errorf("failed to setup config: %w", err)
   216  	}
   217  
   218  	misbehaviors := make(map[int64]mcs.Misbehavior, len(cfg.Misbehaviors))
   219  	for heightString, misbehaviorString := range cfg.Misbehaviors {
   220  		height, _ := strconv.ParseInt(heightString, 10, 64)
   221  		misbehaviors[height] = mcs.MisbehaviorList[misbehaviorString]
   222  	}
   223  
   224  	n, err := maverick.NewNode(tmcfg,
   225  		maverick.LoadOrGenFilePV(tmcfg.PrivValidatorKeyFile(), tmcfg.PrivValidatorStateFile()),
   226  		nodeKey,
   227  		proxy.NewLocalClientCreator(app),
   228  		maverick.DefaultGenesisDocProviderFunc(tmcfg),
   229  		maverick.DefaultDBProvider,
   230  		maverick.DefaultMetricsProvider(tmcfg.Instrumentation),
   231  		logger,
   232  		misbehaviors,
   233  	)
   234  	if err != nil {
   235  		return err
   236  	}
   237  
   238  	return n.Start()
   239  }
   240  
   241  // startSigner starts a signer server connecting to the given endpoint.
   242  func startSigner(cfg *Config) error {
   243  	filePV := privval.LoadFilePV(cfg.PrivValKey, cfg.PrivValState)
   244  
   245  	protocol, address := tmnet.ProtocolAndAddress(cfg.PrivValServer)
   246  	var dialFn privval.SocketDialer
   247  	switch protocol {
   248  	case "tcp":
   249  		dialFn = privval.DialTCPFn(address, 3*time.Second, ed25519.GenPrivKey())
   250  	case "unix":
   251  		dialFn = privval.DialUnixFn(address)
   252  	default:
   253  		return fmt.Errorf("invalid privval protocol %q", protocol)
   254  	}
   255  
   256  	endpoint := privval.NewSignerDialerEndpoint(logger, dialFn,
   257  		privval.SignerDialerEndpointRetryWaitInterval(1*time.Second),
   258  		privval.SignerDialerEndpointConnRetries(100))
   259  	err := privval.NewSignerServer(endpoint, cfg.ChainID, filePV).Start()
   260  	if err != nil {
   261  		return err
   262  	}
   263  	logger.Info(fmt.Sprintf("Remote signer connecting to %v", cfg.PrivValServer))
   264  	return nil
   265  }
   266  
   267  func setupNode() (*config.Config, log.Logger, *p2p.NodeKey, error) {
   268  	var tmcfg *config.Config
   269  
   270  	home := os.Getenv("TMHOME")
   271  	if home == "" {
   272  		return nil, nil, nil, errors.New("TMHOME not set")
   273  	}
   274  
   275  	viper.AddConfigPath(filepath.Join(home, "config"))
   276  	viper.SetConfigName("config")
   277  
   278  	if err := viper.ReadInConfig(); err != nil {
   279  		return nil, nil, nil, err
   280  	}
   281  
   282  	tmcfg = config.DefaultConfig()
   283  
   284  	if err := viper.Unmarshal(tmcfg); err != nil {
   285  		return nil, nil, nil, err
   286  	}
   287  
   288  	tmcfg.SetRoot(home)
   289  
   290  	if err := tmcfg.ValidateBasic(); err != nil {
   291  		return nil, nil, nil, fmt.Errorf("error in config file: %w", err)
   292  	}
   293  
   294  	if tmcfg.LogFormat == config.LogFormatJSON {
   295  		logger = log.NewTMJSONLogger(log.NewSyncWriter(os.Stdout))
   296  	}
   297  
   298  	nodeLogger, err := tmflags.ParseLogLevel(tmcfg.LogLevel, logger, config.DefaultLogLevel)
   299  	if err != nil {
   300  		return nil, nil, nil, err
   301  	}
   302  
   303  	nodeLogger = nodeLogger.With("module", "main")
   304  
   305  	nodeKey, err := p2p.LoadOrGenNodeKey(tmcfg.NodeKeyFile())
   306  	if err != nil {
   307  		return nil, nil, nil, fmt.Errorf("failed to load or gen node key %s: %w", tmcfg.NodeKeyFile(), err)
   308  	}
   309  
   310  	return tmcfg, nodeLogger, nodeKey, nil
   311  }
   312  
   313  // rpcEndpoints takes a list of persistent peers and splits them into a list of rpc endpoints
   314  // using 26657 as the port number
   315  func rpcEndpoints(peers string) []string {
   316  	arr := strings.Split(peers, ",")
   317  	endpoints := make([]string, len(arr))
   318  	for i, v := range arr {
   319  		urlString := strings.SplitAfter(v, "@")[1]
   320  		hostName := strings.Split(urlString, ":26656")[0]
   321  		// use RPC port instead
   322  		port := 26657
   323  		rpcEndpoint := "http://" + hostName + ":" + fmt.Sprint(port)
   324  		endpoints[i] = rpcEndpoint
   325  	}
   326  	return endpoints
   327  }