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 }