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