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