github.com/snowblossomcoin/go-ethereum@v1.9.25/cmd/geth/main.go (about)

     1  // Copyright 2014 The go-ethereum Authors
     2  // This file is part of go-ethereum.
     3  //
     4  // go-ethereum is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // go-ethereum is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU General Public License
    15  // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  // geth is the official command-line client for Ethereum.
    18  package main
    19  
    20  import (
    21  	"fmt"
    22  	"math"
    23  	"os"
    24  	godebug "runtime/debug"
    25  	"sort"
    26  	"strconv"
    27  	"strings"
    28  	"time"
    29  
    30  	"github.com/ethereum/go-ethereum/accounts"
    31  	"github.com/ethereum/go-ethereum/accounts/keystore"
    32  	"github.com/ethereum/go-ethereum/cmd/utils"
    33  	"github.com/ethereum/go-ethereum/common"
    34  	"github.com/ethereum/go-ethereum/console/prompt"
    35  	"github.com/ethereum/go-ethereum/eth"
    36  	"github.com/ethereum/go-ethereum/eth/downloader"
    37  	"github.com/ethereum/go-ethereum/ethclient"
    38  	"github.com/ethereum/go-ethereum/internal/debug"
    39  	"github.com/ethereum/go-ethereum/internal/ethapi"
    40  	"github.com/ethereum/go-ethereum/internal/flags"
    41  	"github.com/ethereum/go-ethereum/log"
    42  	"github.com/ethereum/go-ethereum/metrics"
    43  	"github.com/ethereum/go-ethereum/node"
    44  	gopsutil "github.com/shirou/gopsutil/mem"
    45  	cli "gopkg.in/urfave/cli.v1"
    46  )
    47  
    48  const (
    49  	clientIdentifier = "geth" // Client identifier to advertise over the network
    50  )
    51  
    52  var (
    53  	// Git SHA1 commit hash of the release (set via linker flags)
    54  	gitCommit = ""
    55  	gitDate   = ""
    56  	// The app that holds all commands and flags.
    57  	app = flags.NewApp(gitCommit, gitDate, "the go-ethereum command line interface")
    58  	// flags that configure the node
    59  	nodeFlags = []cli.Flag{
    60  		utils.IdentityFlag,
    61  		utils.UnlockedAccountFlag,
    62  		utils.PasswordFileFlag,
    63  		utils.BootnodesFlag,
    64  		utils.LegacyBootnodesV4Flag,
    65  		utils.LegacyBootnodesV5Flag,
    66  		utils.DataDirFlag,
    67  		utils.AncientFlag,
    68  		utils.KeyStoreDirFlag,
    69  		utils.ExternalSignerFlag,
    70  		utils.NoUSBFlag,
    71  		utils.SmartCardDaemonPathFlag,
    72  		utils.EthashCacheDirFlag,
    73  		utils.EthashCachesInMemoryFlag,
    74  		utils.EthashCachesOnDiskFlag,
    75  		utils.EthashCachesLockMmapFlag,
    76  		utils.EthashDatasetDirFlag,
    77  		utils.EthashDatasetsInMemoryFlag,
    78  		utils.EthashDatasetsOnDiskFlag,
    79  		utils.EthashDatasetsLockMmapFlag,
    80  		utils.TxPoolLocalsFlag,
    81  		utils.TxPoolNoLocalsFlag,
    82  		utils.TxPoolJournalFlag,
    83  		utils.TxPoolRejournalFlag,
    84  		utils.TxPoolPriceLimitFlag,
    85  		utils.TxPoolPriceBumpFlag,
    86  		utils.TxPoolAccountSlotsFlag,
    87  		utils.TxPoolGlobalSlotsFlag,
    88  		utils.TxPoolAccountQueueFlag,
    89  		utils.TxPoolGlobalQueueFlag,
    90  		utils.TxPoolLifetimeFlag,
    91  		utils.SyncModeFlag,
    92  		utils.ExitWhenSyncedFlag,
    93  		utils.GCModeFlag,
    94  		utils.SnapshotFlag,
    95  		utils.TxLookupLimitFlag,
    96  		utils.LightServeFlag,
    97  		utils.LegacyLightServFlag,
    98  		utils.LightIngressFlag,
    99  		utils.LightEgressFlag,
   100  		utils.LightMaxPeersFlag,
   101  		utils.LegacyLightPeersFlag,
   102  		utils.LightNoPruneFlag,
   103  		utils.LightKDFFlag,
   104  		utils.UltraLightServersFlag,
   105  		utils.UltraLightFractionFlag,
   106  		utils.UltraLightOnlyAnnounceFlag,
   107  		utils.WhitelistFlag,
   108  		utils.CacheFlag,
   109  		utils.CacheDatabaseFlag,
   110  		utils.CacheTrieFlag,
   111  		utils.CacheTrieJournalFlag,
   112  		utils.CacheTrieRejournalFlag,
   113  		utils.CacheGCFlag,
   114  		utils.CacheSnapshotFlag,
   115  		utils.CacheNoPrefetchFlag,
   116  		utils.ListenPortFlag,
   117  		utils.MaxPeersFlag,
   118  		utils.MaxPendingPeersFlag,
   119  		utils.MiningEnabledFlag,
   120  		utils.MinerThreadsFlag,
   121  		utils.LegacyMinerThreadsFlag,
   122  		utils.MinerNotifyFlag,
   123  		utils.MinerGasTargetFlag,
   124  		utils.LegacyMinerGasTargetFlag,
   125  		utils.MinerGasLimitFlag,
   126  		utils.MinerGasPriceFlag,
   127  		utils.LegacyMinerGasPriceFlag,
   128  		utils.MinerEtherbaseFlag,
   129  		utils.LegacyMinerEtherbaseFlag,
   130  		utils.MinerExtraDataFlag,
   131  		utils.LegacyMinerExtraDataFlag,
   132  		utils.MinerRecommitIntervalFlag,
   133  		utils.MinerNoVerfiyFlag,
   134  		utils.NATFlag,
   135  		utils.NoDiscoverFlag,
   136  		utils.DiscoveryV5Flag,
   137  		utils.NetrestrictFlag,
   138  		utils.NodeKeyFileFlag,
   139  		utils.NodeKeyHexFlag,
   140  		utils.DNSDiscoveryFlag,
   141  		utils.DeveloperFlag,
   142  		utils.DeveloperPeriodFlag,
   143  		utils.LegacyTestnetFlag,
   144  		utils.RopstenFlag,
   145  		utils.RinkebyFlag,
   146  		utils.GoerliFlag,
   147  		utils.YoloV2Flag,
   148  		utils.VMEnableDebugFlag,
   149  		utils.NetworkIdFlag,
   150  		utils.EthStatsURLFlag,
   151  		utils.FakePoWFlag,
   152  		utils.NoCompactionFlag,
   153  		utils.GpoBlocksFlag,
   154  		utils.LegacyGpoBlocksFlag,
   155  		utils.GpoPercentileFlag,
   156  		utils.LegacyGpoPercentileFlag,
   157  		utils.GpoMaxGasPriceFlag,
   158  		utils.EWASMInterpreterFlag,
   159  		utils.EVMInterpreterFlag,
   160  		configFileFlag,
   161  	}
   162  
   163  	rpcFlags = []cli.Flag{
   164  		utils.HTTPEnabledFlag,
   165  		utils.HTTPListenAddrFlag,
   166  		utils.HTTPPortFlag,
   167  		utils.HTTPCORSDomainFlag,
   168  		utils.HTTPVirtualHostsFlag,
   169  		utils.LegacyRPCEnabledFlag,
   170  		utils.LegacyRPCListenAddrFlag,
   171  		utils.LegacyRPCPortFlag,
   172  		utils.LegacyRPCCORSDomainFlag,
   173  		utils.LegacyRPCVirtualHostsFlag,
   174  		utils.GraphQLEnabledFlag,
   175  		utils.GraphQLCORSDomainFlag,
   176  		utils.GraphQLVirtualHostsFlag,
   177  		utils.HTTPApiFlag,
   178  		utils.LegacyRPCApiFlag,
   179  		utils.WSEnabledFlag,
   180  		utils.WSListenAddrFlag,
   181  		utils.LegacyWSListenAddrFlag,
   182  		utils.WSPortFlag,
   183  		utils.LegacyWSPortFlag,
   184  		utils.WSApiFlag,
   185  		utils.LegacyWSApiFlag,
   186  		utils.WSAllowedOriginsFlag,
   187  		utils.LegacyWSAllowedOriginsFlag,
   188  		utils.IPCDisabledFlag,
   189  		utils.IPCPathFlag,
   190  		utils.InsecureUnlockAllowedFlag,
   191  		utils.RPCGlobalGasCapFlag,
   192  		utils.RPCGlobalTxFeeCapFlag,
   193  	}
   194  
   195  	whisperFlags = []cli.Flag{
   196  		utils.WhisperEnabledFlag,
   197  		utils.WhisperMaxMessageSizeFlag,
   198  		utils.WhisperMinPOWFlag,
   199  		utils.WhisperRestrictConnectionBetweenLightClientsFlag,
   200  	}
   201  
   202  	metricsFlags = []cli.Flag{
   203  		utils.MetricsEnabledFlag,
   204  		utils.MetricsEnabledExpensiveFlag,
   205  		utils.MetricsHTTPFlag,
   206  		utils.MetricsPortFlag,
   207  		utils.MetricsEnableInfluxDBFlag,
   208  		utils.MetricsInfluxDBEndpointFlag,
   209  		utils.MetricsInfluxDBDatabaseFlag,
   210  		utils.MetricsInfluxDBUsernameFlag,
   211  		utils.MetricsInfluxDBPasswordFlag,
   212  		utils.MetricsInfluxDBTagsFlag,
   213  	}
   214  )
   215  
   216  func init() {
   217  	// Initialize the CLI app and start Geth
   218  	app.Action = geth
   219  	app.HideVersion = true // we have a command to print the version
   220  	app.Copyright = "Copyright 2013-2020 The go-ethereum Authors"
   221  	app.Commands = []cli.Command{
   222  		// See chaincmd.go:
   223  		initCommand,
   224  		importCommand,
   225  		exportCommand,
   226  		importPreimagesCommand,
   227  		exportPreimagesCommand,
   228  		copydbCommand,
   229  		removedbCommand,
   230  		dumpCommand,
   231  		dumpGenesisCommand,
   232  		inspectCommand,
   233  		// See accountcmd.go:
   234  		accountCommand,
   235  		walletCommand,
   236  		// See consolecmd.go:
   237  		consoleCommand,
   238  		attachCommand,
   239  		javascriptCommand,
   240  		// See misccmd.go:
   241  		makecacheCommand,
   242  		makedagCommand,
   243  		versionCommand,
   244  		licenseCommand,
   245  		// See config.go
   246  		dumpConfigCommand,
   247  		// See retesteth.go
   248  		retestethCommand,
   249  		// See cmd/utils/flags_legacy.go
   250  		utils.ShowDeprecated,
   251  	}
   252  	sort.Sort(cli.CommandsByName(app.Commands))
   253  
   254  	app.Flags = append(app.Flags, nodeFlags...)
   255  	app.Flags = append(app.Flags, rpcFlags...)
   256  	app.Flags = append(app.Flags, consoleFlags...)
   257  	app.Flags = append(app.Flags, debug.Flags...)
   258  	app.Flags = append(app.Flags, debug.DeprecatedFlags...)
   259  	app.Flags = append(app.Flags, whisperFlags...)
   260  	app.Flags = append(app.Flags, metricsFlags...)
   261  
   262  	app.Before = func(ctx *cli.Context) error {
   263  		return debug.Setup(ctx)
   264  	}
   265  	app.After = func(ctx *cli.Context) error {
   266  		debug.Exit()
   267  		prompt.Stdin.Close() // Resets terminal mode.
   268  		return nil
   269  	}
   270  }
   271  
   272  func main() {
   273  	if err := app.Run(os.Args); err != nil {
   274  		fmt.Fprintln(os.Stderr, err)
   275  		os.Exit(1)
   276  	}
   277  }
   278  
   279  // prepare manipulates memory cache allowance and setups metric system.
   280  // This function should be called before launching devp2p stack.
   281  func prepare(ctx *cli.Context) {
   282  	// If we're running a known preset, log it for convenience.
   283  	switch {
   284  	case ctx.GlobalIsSet(utils.LegacyTestnetFlag.Name):
   285  		log.Info("Starting Geth on Ropsten testnet...")
   286  		log.Warn("The --testnet flag is ambiguous! Please specify one of --goerli, --rinkeby, or --ropsten.")
   287  		log.Warn("The generic --testnet flag is deprecated and will be removed in the future!")
   288  
   289  	case ctx.GlobalIsSet(utils.RopstenFlag.Name):
   290  		log.Info("Starting Geth on Ropsten testnet...")
   291  
   292  	case ctx.GlobalIsSet(utils.RinkebyFlag.Name):
   293  		log.Info("Starting Geth on Rinkeby testnet...")
   294  
   295  	case ctx.GlobalIsSet(utils.GoerliFlag.Name):
   296  		log.Info("Starting Geth on Görli testnet...")
   297  
   298  	case ctx.GlobalIsSet(utils.DeveloperFlag.Name):
   299  		log.Info("Starting Geth in ephemeral dev mode...")
   300  
   301  	case !ctx.GlobalIsSet(utils.NetworkIdFlag.Name):
   302  		log.Info("Starting Geth on Ethereum mainnet...")
   303  	}
   304  	// If we're a full node on mainnet without --cache specified, bump default cache allowance
   305  	if ctx.GlobalString(utils.SyncModeFlag.Name) != "light" && !ctx.GlobalIsSet(utils.CacheFlag.Name) && !ctx.GlobalIsSet(utils.NetworkIdFlag.Name) {
   306  		// Make sure we're not on any supported preconfigured testnet either
   307  		if !ctx.GlobalIsSet(utils.LegacyTestnetFlag.Name) && !ctx.GlobalIsSet(utils.RopstenFlag.Name) && !ctx.GlobalIsSet(utils.RinkebyFlag.Name) && !ctx.GlobalIsSet(utils.GoerliFlag.Name) && !ctx.GlobalIsSet(utils.DeveloperFlag.Name) {
   308  			// Nope, we're really on mainnet. Bump that cache up!
   309  			log.Info("Bumping default cache on mainnet", "provided", ctx.GlobalInt(utils.CacheFlag.Name), "updated", 4096)
   310  			ctx.GlobalSet(utils.CacheFlag.Name, strconv.Itoa(4096))
   311  		}
   312  	}
   313  	// If we're running a light client on any network, drop the cache to some meaningfully low amount
   314  	if ctx.GlobalString(utils.SyncModeFlag.Name) == "light" && !ctx.GlobalIsSet(utils.CacheFlag.Name) {
   315  		log.Info("Dropping default light client cache", "provided", ctx.GlobalInt(utils.CacheFlag.Name), "updated", 128)
   316  		ctx.GlobalSet(utils.CacheFlag.Name, strconv.Itoa(128))
   317  	}
   318  	// Cap the cache allowance and tune the garbage collector
   319  	mem, err := gopsutil.VirtualMemory()
   320  	if err == nil {
   321  		if 32<<(^uintptr(0)>>63) == 32 && mem.Total > 2*1024*1024*1024 {
   322  			log.Warn("Lowering memory allowance on 32bit arch", "available", mem.Total/1024/1024, "addressable", 2*1024)
   323  			mem.Total = 2 * 1024 * 1024 * 1024
   324  		}
   325  		allowance := int(mem.Total / 1024 / 1024 / 3)
   326  		if cache := ctx.GlobalInt(utils.CacheFlag.Name); cache > allowance {
   327  			log.Warn("Sanitizing cache to Go's GC limits", "provided", cache, "updated", allowance)
   328  			ctx.GlobalSet(utils.CacheFlag.Name, strconv.Itoa(allowance))
   329  		}
   330  	}
   331  	// Ensure Go's GC ignores the database cache for trigger percentage
   332  	cache := ctx.GlobalInt(utils.CacheFlag.Name)
   333  	gogc := math.Max(20, math.Min(100, 100/(float64(cache)/1024)))
   334  
   335  	log.Debug("Sanitizing Go's GC trigger", "percent", int(gogc))
   336  	godebug.SetGCPercent(int(gogc))
   337  
   338  	// Start metrics export if enabled
   339  	utils.SetupMetrics(ctx)
   340  
   341  	// Start system runtime metrics collection
   342  	go metrics.CollectProcessMetrics(3 * time.Second)
   343  }
   344  
   345  // geth is the main entry point into the system if no special subcommand is ran.
   346  // It creates a default node based on the command line arguments and runs it in
   347  // blocking mode, waiting for it to be shut down.
   348  func geth(ctx *cli.Context) error {
   349  	if args := ctx.Args(); len(args) > 0 {
   350  		return fmt.Errorf("invalid command: %q", args[0])
   351  	}
   352  
   353  	prepare(ctx)
   354  	stack, backend := makeFullNode(ctx)
   355  	defer stack.Close()
   356  
   357  	startNode(ctx, stack, backend)
   358  	stack.Wait()
   359  	return nil
   360  }
   361  
   362  // startNode boots up the system node and all registered protocols, after which
   363  // it unlocks any requested accounts, and starts the RPC/IPC interfaces and the
   364  // miner.
   365  func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend) {
   366  	debug.Memsize.Add("node", stack)
   367  
   368  	// Start up the node itself
   369  	utils.StartNode(stack)
   370  
   371  	// Unlock any account specifically requested
   372  	unlockAccounts(ctx, stack)
   373  
   374  	// Register wallet event handlers to open and auto-derive wallets
   375  	events := make(chan accounts.WalletEvent, 16)
   376  	stack.AccountManager().Subscribe(events)
   377  
   378  	// Create a client to interact with local geth node.
   379  	rpcClient, err := stack.Attach()
   380  	if err != nil {
   381  		utils.Fatalf("Failed to attach to self: %v", err)
   382  	}
   383  	ethClient := ethclient.NewClient(rpcClient)
   384  
   385  	go func() {
   386  		// Open any wallets already attached
   387  		for _, wallet := range stack.AccountManager().Wallets() {
   388  			if err := wallet.Open(""); err != nil {
   389  				log.Warn("Failed to open wallet", "url", wallet.URL(), "err", err)
   390  			}
   391  		}
   392  		// Listen for wallet event till termination
   393  		for event := range events {
   394  			switch event.Kind {
   395  			case accounts.WalletArrived:
   396  				if err := event.Wallet.Open(""); err != nil {
   397  					log.Warn("New wallet appeared, failed to open", "url", event.Wallet.URL(), "err", err)
   398  				}
   399  			case accounts.WalletOpened:
   400  				status, _ := event.Wallet.Status()
   401  				log.Info("New wallet appeared", "url", event.Wallet.URL(), "status", status)
   402  
   403  				var derivationPaths []accounts.DerivationPath
   404  				if event.Wallet.URL().Scheme == "ledger" {
   405  					derivationPaths = append(derivationPaths, accounts.LegacyLedgerBaseDerivationPath)
   406  				}
   407  				derivationPaths = append(derivationPaths, accounts.DefaultBaseDerivationPath)
   408  
   409  				event.Wallet.SelfDerive(derivationPaths, ethClient)
   410  
   411  			case accounts.WalletDropped:
   412  				log.Info("Old wallet dropped", "url", event.Wallet.URL())
   413  				event.Wallet.Close()
   414  			}
   415  		}
   416  	}()
   417  
   418  	// Spawn a standalone goroutine for status synchronization monitoring,
   419  	// close the node when synchronization is complete if user required.
   420  	if ctx.GlobalBool(utils.ExitWhenSyncedFlag.Name) {
   421  		go func() {
   422  			sub := stack.EventMux().Subscribe(downloader.DoneEvent{})
   423  			defer sub.Unsubscribe()
   424  			for {
   425  				event := <-sub.Chan()
   426  				if event == nil {
   427  					continue
   428  				}
   429  				done, ok := event.Data.(downloader.DoneEvent)
   430  				if !ok {
   431  					continue
   432  				}
   433  				if timestamp := time.Unix(int64(done.Latest.Time), 0); time.Since(timestamp) < 10*time.Minute {
   434  					log.Info("Synchronisation completed", "latestnum", done.Latest.Number, "latesthash", done.Latest.Hash(),
   435  						"age", common.PrettyAge(timestamp))
   436  					stack.Close()
   437  				}
   438  			}
   439  		}()
   440  	}
   441  
   442  	// Start auxiliary services if enabled
   443  	if ctx.GlobalBool(utils.MiningEnabledFlag.Name) || ctx.GlobalBool(utils.DeveloperFlag.Name) {
   444  		// Mining only makes sense if a full Ethereum node is running
   445  		if ctx.GlobalString(utils.SyncModeFlag.Name) == "light" {
   446  			utils.Fatalf("Light clients do not support mining")
   447  		}
   448  		ethBackend, ok := backend.(*eth.EthAPIBackend)
   449  		if !ok {
   450  			utils.Fatalf("Ethereum service not running: %v", err)
   451  		}
   452  
   453  		// Set the gas price to the limits from the CLI and start mining
   454  		gasprice := utils.GlobalBig(ctx, utils.MinerGasPriceFlag.Name)
   455  		if ctx.GlobalIsSet(utils.LegacyMinerGasPriceFlag.Name) && !ctx.GlobalIsSet(utils.MinerGasPriceFlag.Name) {
   456  			gasprice = utils.GlobalBig(ctx, utils.LegacyMinerGasPriceFlag.Name)
   457  		}
   458  		ethBackend.TxPool().SetGasPrice(gasprice)
   459  		// start mining
   460  		threads := ctx.GlobalInt(utils.MinerThreadsFlag.Name)
   461  		if ctx.GlobalIsSet(utils.LegacyMinerThreadsFlag.Name) && !ctx.GlobalIsSet(utils.MinerThreadsFlag.Name) {
   462  			threads = ctx.GlobalInt(utils.LegacyMinerThreadsFlag.Name)
   463  			log.Warn("The flag --minerthreads is deprecated and will be removed in the future, please use --miner.threads")
   464  		}
   465  		if err := ethBackend.StartMining(threads); err != nil {
   466  			utils.Fatalf("Failed to start mining: %v", err)
   467  		}
   468  	}
   469  }
   470  
   471  // unlockAccounts unlocks any account specifically requested.
   472  func unlockAccounts(ctx *cli.Context, stack *node.Node) {
   473  	var unlocks []string
   474  	inputs := strings.Split(ctx.GlobalString(utils.UnlockedAccountFlag.Name), ",")
   475  	for _, input := range inputs {
   476  		if trimmed := strings.TrimSpace(input); trimmed != "" {
   477  			unlocks = append(unlocks, trimmed)
   478  		}
   479  	}
   480  	// Short circuit if there is no account to unlock.
   481  	if len(unlocks) == 0 {
   482  		return
   483  	}
   484  	// If insecure account unlocking is not allowed if node's APIs are exposed to external.
   485  	// Print warning log to user and skip unlocking.
   486  	if !stack.Config().InsecureUnlockAllowed && stack.Config().ExtRPCEnabled() {
   487  		utils.Fatalf("Account unlock with HTTP access is forbidden!")
   488  	}
   489  	ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
   490  	passwords := utils.MakePasswordList(ctx)
   491  	for i, account := range unlocks {
   492  		unlockAccount(ks, account, i, passwords)
   493  	}
   494  }