github.com/Finschia/ostracon@v1.1.5/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/Finschia/ostracon/abci/server"
    16  	"github.com/Finschia/ostracon/config"
    17  	"github.com/Finschia/ostracon/crypto/ed25519"
    18  	"github.com/Finschia/ostracon/libs/log"
    19  	tmnet "github.com/Finschia/ostracon/libs/net"
    20  	"github.com/Finschia/ostracon/light"
    21  	lproxy "github.com/Finschia/ostracon/light/proxy"
    22  	lrpc "github.com/Finschia/ostracon/light/rpc"
    23  	dbs "github.com/Finschia/ostracon/light/store/db"
    24  	"github.com/Finschia/ostracon/node"
    25  	"github.com/Finschia/ostracon/p2p"
    26  	"github.com/Finschia/ostracon/privval"
    27  	"github.com/Finschia/ostracon/proxy"
    28  	rpcserver "github.com/Finschia/ostracon/rpc/jsonrpc/server"
    29  	"github.com/Finschia/ostracon/test/e2e/app"
    30  	e2e "github.com/Finschia/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.MaxBatchRequestNum = tmcfg.RPC.MaxBatchRequestNum
   178  	rpccfg.MaxHeaderBytes = tmcfg.RPC.MaxHeaderBytes
   179  	rpccfg.MaxOpenConnections = tmcfg.RPC.MaxOpenConnections
   180  	// If necessary adjust global WriteTimeout to ensure it's greater than
   181  	// TimeoutBroadcastTxCommit.
   182  	// See https://github.com/tendermint/tendermint/issues/3435
   183  	if rpccfg.WriteTimeout <= tmcfg.RPC.TimeoutBroadcastTxCommit {
   184  		rpccfg.WriteTimeout = tmcfg.RPC.TimeoutBroadcastTxCommit + 1*time.Second
   185  	}
   186  
   187  	p, err := lproxy.NewProxy(c, tmcfg.RPC.ListenAddress, providers[0], rpccfg, nodeLogger,
   188  		lrpc.KeyPathFn(lrpc.DefaultMerkleKeyPathFn()))
   189  	if err != nil {
   190  		return err
   191  	}
   192  
   193  	logger.Info("Starting proxy...", "laddr", tmcfg.RPC.ListenAddress)
   194  	if err := p.ListenAndServe(); err != http.ErrServerClosed {
   195  		// Error starting or closing listener:
   196  		logger.Error("proxy ListenAndServe", "err", err)
   197  	}
   198  
   199  	return nil
   200  }
   201  
   202  // startSigner starts a signer server connecting to the given endpoint.
   203  func startSigner(cfg *Config) error {
   204  	filePV := privval.LoadFilePV(cfg.PrivValKey, cfg.PrivValState)
   205  
   206  	protocol, address := tmnet.ProtocolAndAddress(cfg.PrivValServer)
   207  	var dialFn privval.SocketDialer
   208  	switch protocol {
   209  	case "tcp":
   210  		dialFn = privval.DialTCPFn(address, 3*time.Second, ed25519.GenPrivKey())
   211  	case "unix":
   212  		dialFn = privval.DialUnixFn(address)
   213  	default:
   214  		return fmt.Errorf("invalid privval protocol %q", protocol)
   215  	}
   216  
   217  	endpoint := privval.NewSignerDialerEndpoint(logger, dialFn,
   218  		privval.SignerDialerEndpointRetryWaitInterval(1*time.Second),
   219  		privval.SignerDialerEndpointConnRetries(100))
   220  	err := privval.NewSignerServer(endpoint, cfg.ChainID, filePV).Start()
   221  	if err != nil {
   222  		return err
   223  	}
   224  	logger.Info("start signer", "msg", log.NewLazySprintf("Remote signer connecting to %v", cfg.PrivValServer))
   225  	return nil
   226  }
   227  
   228  func setupNode() (*config.Config, log.Logger, *p2p.NodeKey, error) {
   229  	var tmcfg *config.Config
   230  
   231  	home := os.Getenv("OCHOME")
   232  	if home == "" {
   233  		return nil, nil, nil, errors.New("OCHOME not set")
   234  	}
   235  
   236  	viper.AddConfigPath(filepath.Join(home, "config"))
   237  	viper.SetConfigName("config")
   238  
   239  	if err := viper.ReadInConfig(); err != nil {
   240  		return nil, nil, nil, err
   241  	}
   242  
   243  	tmcfg = config.DefaultConfig()
   244  
   245  	if err := viper.Unmarshal(tmcfg); err != nil {
   246  		return nil, nil, nil, err
   247  	}
   248  
   249  	tmcfg.SetRoot(home)
   250  
   251  	if err := tmcfg.ValidateBasic(); err != nil {
   252  		return nil, nil, nil, fmt.Errorf("error in config file: %w", err)
   253  	}
   254  
   255  	if tmcfg.LogFormat == config.LogFormatJSON {
   256  		logger = log.NewOCJSONLogger(log.NewSyncWriter(os.Stdout))
   257  	}
   258  
   259  	nodeLogger, err := log.ParseLogLevel(tmcfg.LogLevel, logger, config.DefaultLogLevel)
   260  	if err != nil {
   261  		return nil, nil, nil, err
   262  	}
   263  
   264  	nodeLogger = nodeLogger.With("module", "main")
   265  
   266  	nodeKey, err := p2p.LoadOrGenNodeKey(tmcfg.NodeKeyFile())
   267  	if err != nil {
   268  		return nil, nil, nil, fmt.Errorf("failed to load or gen node key %s: %w", tmcfg.NodeKeyFile(), err)
   269  	}
   270  
   271  	return tmcfg, nodeLogger, nodeKey, nil
   272  }
   273  
   274  // rpcEndpoints takes a list of persistent peers and splits them into a list of rpc endpoints
   275  // using 26657 as the port number
   276  func rpcEndpoints(peers string) []string {
   277  	arr := strings.Split(peers, ",")
   278  	endpoints := make([]string, len(arr))
   279  	for i, v := range arr {
   280  		urlString := strings.SplitAfter(v, "@")[1]
   281  		hostName := strings.Split(urlString, ":26656")[0]
   282  		// use RPC port instead
   283  		port := 26657
   284  		rpcEndpoint := "http://" + hostName + ":" + fmt.Sprint(port)
   285  		endpoints[i] = rpcEndpoint
   286  	}
   287  	return endpoints
   288  }