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  }