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