github.com/unicornultrafoundation/go-u2u@v1.0.0-rc1.0.20240205080301-e74a83d3fadc/cmd/u2u/launcher/launcher.go (about) 1 package launcher 2 3 import ( 4 "fmt" 5 "path" 6 "sort" 7 "strings" 8 "time" 9 10 "github.com/unicornultrafoundation/go-helios/native/idx" 11 "gopkg.in/urfave/cli.v1" 12 13 "github.com/unicornultrafoundation/go-u2u/accounts" 14 "github.com/unicornultrafoundation/go-u2u/accounts/keystore" 15 "github.com/unicornultrafoundation/go-u2u/cmd/u2u/launcher/monitoring" 16 "github.com/unicornultrafoundation/go-u2u/cmd/u2u/launcher/tracing" 17 "github.com/unicornultrafoundation/go-u2u/cmd/utils" 18 "github.com/unicornultrafoundation/go-u2u/console/prompt" 19 "github.com/unicornultrafoundation/go-u2u/debug" 20 "github.com/unicornultrafoundation/go-u2u/ethclient" 21 "github.com/unicornultrafoundation/go-u2u/evmcore" 22 "github.com/unicornultrafoundation/go-u2u/flags" 23 "github.com/unicornultrafoundation/go-u2u/gossip" 24 "github.com/unicornultrafoundation/go-u2u/gossip/emitter" 25 "github.com/unicornultrafoundation/go-u2u/integration" 26 "github.com/unicornultrafoundation/go-u2u/log" 27 evmetrics "github.com/unicornultrafoundation/go-u2u/metrics" 28 "github.com/unicornultrafoundation/go-u2u/node" 29 "github.com/unicornultrafoundation/go-u2u/p2p/discover/discfilter" 30 "github.com/unicornultrafoundation/go-u2u/params" 31 "github.com/unicornultrafoundation/go-u2u/u2u/genesis" 32 "github.com/unicornultrafoundation/go-u2u/u2u/genesisstore" 33 "github.com/unicornultrafoundation/go-u2u/utils/errlock" 34 "github.com/unicornultrafoundation/go-u2u/valkeystore" 35 _ "github.com/unicornultrafoundation/go-u2u/version" 36 ) 37 38 const ( 39 // clientIdentifier to advertise over the network. 40 clientIdentifier = "go-u2u" 41 ) 42 43 var ( 44 // Git SHA1 commit hash of the release (set via linker flags). 45 gitCommit = "" 46 gitDate = "" 47 // The app that holds all commands and flags. 48 app = flags.NewApp(gitCommit, gitDate, "the go-u2u command line interface") 49 50 nodeFlags []cli.Flag 51 testFlags []cli.Flag 52 gpoFlags []cli.Flag 53 accountFlags []cli.Flag 54 performanceFlags []cli.Flag 55 networkingFlags []cli.Flag 56 txpoolFlags []cli.Flag 57 u2uFlags []cli.Flag 58 rpcFlags []cli.Flag 59 metricsFlags []cli.Flag 60 ) 61 62 func initFlags() { 63 // Flags for testing purpose. 64 testFlags = []cli.Flag{ 65 FakeNetFlag, 66 } 67 68 // Flags that configure the node. 69 gpoFlags = []cli.Flag{} 70 accountFlags = []cli.Flag{ 71 utils.UnlockedAccountFlag, 72 utils.PasswordFileFlag, 73 utils.ExternalSignerFlag, 74 utils.InsecureUnlockAllowedFlag, 75 } 76 performanceFlags = []cli.Flag{ 77 CacheFlag, 78 } 79 networkingFlags = []cli.Flag{ 80 utils.BootnodesFlag, 81 utils.ListenPortFlag, 82 utils.MaxPeersFlag, 83 utils.MaxPendingPeersFlag, 84 utils.NATFlag, 85 utils.NoDiscoverFlag, 86 utils.DiscoveryV5Flag, 87 utils.NetrestrictFlag, 88 utils.IPrestrictFlag, 89 utils.PrivateNodeFlag, 90 utils.NodeKeyFileFlag, 91 utils.NodeKeyHexFlag, 92 } 93 txpoolFlags = []cli.Flag{ 94 utils.TxPoolLocalsFlag, 95 utils.TxPoolNoLocalsFlag, 96 utils.TxPoolJournalFlag, 97 utils.TxPoolRejournalFlag, 98 utils.TxPoolPriceLimitFlag, 99 utils.TxPoolPriceBumpFlag, 100 utils.TxPoolAccountSlotsFlag, 101 utils.TxPoolGlobalSlotsFlag, 102 utils.TxPoolAccountQueueFlag, 103 utils.TxPoolGlobalQueueFlag, 104 utils.TxPoolLifetimeFlag, 105 } 106 u2uFlags = []cli.Flag{ 107 GenesisFlag, 108 ExperimentalGenesisFlag, 109 utils.IdentityFlag, 110 DataDirFlag, 111 utils.MinFreeDiskSpaceFlag, 112 utils.KeyStoreDirFlag, 113 utils.USBFlag, 114 utils.SmartCardDaemonPathFlag, 115 ExitWhenAgeFlag, 116 ExitWhenEpochFlag, 117 utils.LightKDFFlag, 118 configFileFlag, 119 validatorIDFlag, 120 validatorPubkeyFlag, 121 validatorPasswordFlag, 122 SyncModeFlag, 123 GCModeFlag, 124 DBPresetFlag, 125 DBMigrationModeFlag, 126 EnableTxTracerFlag, 127 EnableMonitorFlag, 128 PrometheusMonitoringPortFlag, 129 } 130 131 rpcFlags = []cli.Flag{ 132 utils.HTTPEnabledFlag, 133 utils.HTTPListenAddrFlag, 134 utils.HTTPPortFlag, 135 utils.HTTPCORSDomainFlag, 136 utils.HTTPVirtualHostsFlag, 137 utils.HTTPApiFlag, 138 utils.HTTPPathPrefixFlag, 139 utils.WSEnabledFlag, 140 utils.WSListenAddrFlag, 141 utils.WSPortFlag, 142 utils.WSApiFlag, 143 utils.WSAllowedOriginsFlag, 144 utils.WSPathPrefixFlag, 145 utils.IPCDisabledFlag, 146 utils.IPCPathFlag, 147 utils.AllowUnprotectedTxs, 148 RPCGlobalGasCapFlag, 149 RPCGlobalTxFeeCapFlag, 150 RPCGlobalTimeoutFlag, 151 } 152 153 metricsFlags = []cli.Flag{ 154 utils.MetricsEnabledFlag, 155 utils.MetricsEnabledExpensiveFlag, 156 utils.MetricsHTTPFlag, 157 utils.MetricsPortFlag, 158 utils.MetricsEnableInfluxDBFlag, 159 utils.MetricsInfluxDBEndpointFlag, 160 utils.MetricsInfluxDBDatabaseFlag, 161 utils.MetricsInfluxDBUsernameFlag, 162 utils.MetricsInfluxDBPasswordFlag, 163 utils.MetricsInfluxDBTagsFlag, 164 utils.MetricsEnableInfluxDBV2Flag, 165 utils.MetricsInfluxDBTokenFlag, 166 utils.MetricsInfluxDBBucketFlag, 167 utils.MetricsInfluxDBOrganizationFlag, 168 tracing.EnableFlag, 169 } 170 171 nodeFlags = []cli.Flag{} 172 nodeFlags = append(nodeFlags, gpoFlags...) 173 nodeFlags = append(nodeFlags, accountFlags...) 174 nodeFlags = append(nodeFlags, performanceFlags...) 175 nodeFlags = append(nodeFlags, networkingFlags...) 176 nodeFlags = append(nodeFlags, txpoolFlags...) 177 nodeFlags = append(nodeFlags, u2uFlags...) 178 } 179 180 // init the CLI app. 181 func init() { 182 discfilter.Enable() 183 overrideFlags() 184 overrideParams() 185 186 initFlags() 187 188 // App. 189 190 app.Action = hashgraphMain 191 app.Version = params.VersionWithCommit(gitCommit, gitDate) 192 app.HideVersion = true // we have a command to print the version 193 app.Commands = []cli.Command{ 194 // See accountcmd.go: 195 accountCommand, 196 walletCommand, 197 // see validatorcmd.go: 198 validatorCommand, 199 // See consolecmd.go: 200 consoleCommand, 201 attachCommand, 202 javascriptCommand, 203 // See config.go: 204 dumpConfigCommand, 205 checkConfigCommand, 206 // See misccmd.go: 207 versionCommand, 208 licenseCommand, 209 // See chaincmd.go 210 importCommand, 211 exportCommand, 212 checkCommand, 213 // See snapshot.go 214 snapshotCommand, 215 // See dbcmd.go 216 dbCommand, 217 deleteCommand, 218 } 219 sort.Sort(cli.CommandsByName(app.Commands)) 220 221 app.Flags = append(app.Flags, testFlags...) 222 app.Flags = append(app.Flags, nodeFlags...) 223 app.Flags = append(app.Flags, rpcFlags...) 224 app.Flags = append(app.Flags, consoleFlags...) 225 app.Flags = append(app.Flags, debug.Flags...) 226 app.Flags = append(app.Flags, metricsFlags...) 227 228 app.Before = func(ctx *cli.Context) error { 229 if err := debug.Setup(ctx); err != nil { 230 return err 231 } 232 233 // Start metrics export if enabled 234 utils.SetupMetrics(ctx) 235 // Start system runtime metrics collection 236 go evmetrics.CollectProcessMetrics(3 * time.Second) 237 return nil 238 } 239 240 app.After = func(ctx *cli.Context) error { 241 debug.Exit() 242 prompt.Stdin.Close() // Resets terminal mode. 243 244 return nil 245 } 246 } 247 248 func Launch(args []string) error { 249 return app.Run(args) 250 } 251 252 // u2u is the main entry point into the system if no special subcommand is ran. 253 // It creates a default node based on the command line arguments and runs it in 254 // blocking mode, waiting for it to be shut down. 255 func hashgraphMain(ctx *cli.Context) error { 256 if args := ctx.Args(); len(args) > 0 { 257 return fmt.Errorf("invalid command: %q", args[0]) 258 } 259 260 // TODO: tracing flags 261 //tracingStop, err := tracing.Start(ctx) 262 //if err != nil { 263 // return err 264 //} 265 //defer tracingStop() 266 267 cfg := makeAllConfigs(ctx) 268 genesisStore := mayGetGenesisStore(ctx) 269 node, _, nodeClose := makeNode(ctx, cfg, genesisStore) 270 defer nodeClose() 271 startNode(ctx, node) 272 node.Wait() 273 return nil 274 } 275 276 func makeNode(ctx *cli.Context, cfg *config, genesisStore *genesisstore.Store) (*node.Node, *gossip.Service, func()) { 277 // check errlock file 278 errlock.SetDefaultDatadir(cfg.Node.DataDir) 279 errlock.Check() 280 281 var g *genesis.Genesis 282 if genesisStore != nil { 283 gv := genesisStore.Genesis() 284 g = &gv 285 } 286 287 engine, dagIndex, gdb, cdb, blockProc, closeDBs := integration.MakeEngine(path.Join(cfg.Node.DataDir, "chaindata"), g, cfg.AppConfigs()) 288 if genesisStore != nil { 289 _ = genesisStore.Close() 290 } 291 292 monitoring.SetupPrometheus(fmt.Sprintf(":%d", cfg.Monitoring.Port)) 293 monitoring.SetDataDir(cfg.Node.DataDir) 294 295 memorizeDBPreset(cfg) 296 297 // substitute default bootnodes if requested 298 networkName := "" 299 if gdb.HasBlockEpochState() { 300 networkName = gdb.GetRules().Name 301 } 302 if len(networkName) == 0 && genesisStore != nil { 303 networkName = genesisStore.Header().NetworkName 304 } 305 if needDefaultBootnodes(cfg.Node.P2P.BootstrapNodes) { 306 bootnodes := Bootnodes[networkName] 307 if bootnodes == nil { 308 bootnodes = []string{} 309 } 310 setBootnodes(ctx, bootnodes, &cfg.Node) 311 } 312 313 stack := makeConfigNode(ctx, &cfg.Node) 314 315 valKeystore := valkeystore.NewDefaultFileKeystore(path.Join(getValKeystoreDir(cfg.Node), "validator")) 316 valPubkey := cfg.Emitter.Validator.PubKey 317 if key := getFakeValidatorKey(ctx); key != nil && cfg.Emitter.Validator.ID != 0 { 318 addFakeValidatorKey(ctx, key, valPubkey, valKeystore) 319 coinbase := integration.SetAccountKey(stack.AccountManager(), key, "fakepassword") 320 log.Info("Unlocked fake validator account", "address", coinbase.Address.Hex()) 321 } 322 323 // unlock validator key 324 if !valPubkey.Empty() { 325 err := unlockValidatorKey(ctx, valPubkey, valKeystore) 326 if err != nil { 327 utils.Fatalf("Failed to unlock validator key: %v", err) 328 } 329 } 330 signer := valkeystore.NewSigner(valKeystore) 331 332 // Create and register a gossip network service. 333 newTxPool := func(reader evmcore.StateReader) gossip.TxPool { 334 if cfg.TxPool.Journal != "" { 335 cfg.TxPool.Journal = stack.ResolvePath(cfg.TxPool.Journal) 336 } 337 return evmcore.NewTxPool(cfg.TxPool, reader.Config(), reader) 338 } 339 haltCheck := func(oldEpoch, newEpoch idx.Epoch, age time.Time) bool { 340 stop := ctx.GlobalIsSet(ExitWhenAgeFlag.Name) && ctx.GlobalDuration(ExitWhenAgeFlag.Name) >= time.Since(age) 341 stop = stop || ctx.GlobalIsSet(ExitWhenEpochFlag.Name) && idx.Epoch(ctx.GlobalUint64(ExitWhenEpochFlag.Name)) <= newEpoch 342 if stop { 343 go func() { 344 // do it in a separate thread to avoid deadlock 345 _ = stack.Close() 346 }() 347 return true 348 } 349 return false 350 } 351 svc, err := gossip.NewService(stack, cfg.U2U, gdb, blockProc, engine, dagIndex, newTxPool, haltCheck) 352 if err != nil { 353 utils.Fatalf("Failed to create the service: %v", err) 354 } 355 err = engine.StartFrom(svc.GetConsensusCallbacks(), gdb.GetEpoch(), gdb.GetValidators()) 356 if err != nil { 357 utils.Fatalf("Failed to bootstrap the engine: %v", err) 358 } 359 svc.ReprocessEpochEvents() 360 if cfg.Emitter.Validator.ID != 0 { 361 svc.RegisterEmitter(emitter.NewEmitter(cfg.Emitter, svc.EmitterWorld(signer))) 362 } 363 364 stack.RegisterAPIs(svc.APIs()) 365 stack.RegisterProtocols(svc.Protocols()) 366 stack.RegisterLifecycle(svc) 367 368 return stack, svc, func() { 369 _ = stack.Close() 370 gdb.Close() 371 _ = cdb.Close() 372 if closeDBs != nil { 373 _ = closeDBs() 374 } 375 } 376 } 377 378 func makeConfigNode(ctx *cli.Context, cfg *node.Config) *node.Node { 379 stack, err := node.New(cfg) 380 if err != nil { 381 utils.Fatalf("Failed to create the protocol stack: %v", err) 382 } 383 384 return stack 385 } 386 387 // startNode boots up the system node and all registered protocols, after which 388 // it unlocks any requested accounts, and starts the RPC/IPC interfaces. 389 func startNode(ctx *cli.Context, stack *node.Node) { 390 debug.Memsize.Add("node", stack) 391 392 // Start up the node itself 393 utils.StartNode(ctx, stack) 394 395 // Unlock any account specifically requested 396 unlockAccounts(ctx, stack) 397 398 // Register wallet event handlers to open and auto-derive wallets 399 events := make(chan accounts.WalletEvent, 16) 400 stack.AccountManager().Subscribe(events) 401 402 // Create a client to interact with local u2u node. 403 rpcClient, err := stack.Attach() 404 if err != nil { 405 utils.Fatalf("Failed to attach to self: %v", err) 406 } 407 ethClient := ethclient.NewClient(rpcClient) 408 go func() { 409 // Open any wallets already attached 410 for _, wallet := range stack.AccountManager().Wallets() { 411 if err := wallet.Open(""); err != nil { 412 log.Warn("Failed to open wallet", "url", wallet.URL(), "err", err) 413 } 414 } 415 // Listen for wallet event till termination 416 for event := range events { 417 switch event.Kind { 418 case accounts.WalletArrived: 419 if err := event.Wallet.Open(""); err != nil { 420 log.Warn("New wallet appeared, failed to open", "url", event.Wallet.URL(), "err", err) 421 } 422 case accounts.WalletOpened: 423 status, _ := event.Wallet.Status() 424 log.Info("New wallet appeared", "url", event.Wallet.URL(), "status", status) 425 426 var derivationPaths []accounts.DerivationPath 427 if event.Wallet.URL().Scheme == "ledger" { 428 derivationPaths = append(derivationPaths, accounts.LegacyLedgerBaseDerivationPath) 429 } 430 derivationPaths = append(derivationPaths, accounts.DefaultBaseDerivationPath) 431 432 event.Wallet.SelfDerive(derivationPaths, ethClient) 433 434 case accounts.WalletDropped: 435 log.Info("Old wallet dropped", "url", event.Wallet.URL()) 436 event.Wallet.Close() 437 } 438 } 439 }() 440 } 441 442 // unlockAccounts unlocks any account specifically requested. 443 func unlockAccounts(ctx *cli.Context, stack *node.Node) { 444 var unlocks []string 445 inputs := strings.Split(ctx.GlobalString(utils.UnlockedAccountFlag.Name), ",") 446 for _, input := range inputs { 447 if trimmed := strings.TrimSpace(input); trimmed != "" { 448 unlocks = append(unlocks, trimmed) 449 } 450 } 451 // Short circuit if there is no account to unlock. 452 if len(unlocks) == 0 { 453 return 454 } 455 // If insecure account unlocking is not allowed if node's APIs are exposed to external. 456 // Print warning log to user and skip unlocking. 457 if !stack.Config().InsecureUnlockAllowed && stack.Config().ExtRPCEnabled() { 458 utils.Fatalf("Account unlock with HTTP access is forbidden!") 459 } 460 ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) 461 passwords := utils.MakePasswordList(ctx) 462 for i, account := range unlocks { 463 unlockAccount(ks, account, i, passwords) 464 } 465 }