github.com/jpmorganchase/quorum@v21.1.0+incompatible/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  	"runtime"
    25  	godebug "runtime/debug"
    26  	"sort"
    27  	"strconv"
    28  	"strings"
    29  	"time"
    30  
    31  	"github.com/elastic/gosigar"
    32  	"github.com/ethereum/go-ethereum/accounts"
    33  	"github.com/ethereum/go-ethereum/accounts/keystore"
    34  	"github.com/ethereum/go-ethereum/accounts/pluggable"
    35  	"github.com/ethereum/go-ethereum/cmd/utils"
    36  	"github.com/ethereum/go-ethereum/common"
    37  	"github.com/ethereum/go-ethereum/console"
    38  	"github.com/ethereum/go-ethereum/eth"
    39  	"github.com/ethereum/go-ethereum/eth/downloader"
    40  	"github.com/ethereum/go-ethereum/ethclient"
    41  	"github.com/ethereum/go-ethereum/internal/debug"
    42  	"github.com/ethereum/go-ethereum/les"
    43  	"github.com/ethereum/go-ethereum/log"
    44  	"github.com/ethereum/go-ethereum/metrics"
    45  	"github.com/ethereum/go-ethereum/multitenancy"
    46  	"github.com/ethereum/go-ethereum/node"
    47  	"github.com/ethereum/go-ethereum/permission"
    48  	"github.com/ethereum/go-ethereum/plugin"
    49  	"gopkg.in/urfave/cli.v1"
    50  )
    51  
    52  const (
    53  	clientIdentifier = "geth" // Client identifier to advertise over the network
    54  )
    55  
    56  var (
    57  	// Git SHA1 commit hash of the release (set via linker flags)
    58  	gitCommit = ""
    59  	gitDate   = ""
    60  	// The app that holds all commands and flags.
    61  	app = utils.NewApp(gitCommit, gitDate, "the go-ethereum command line interface")
    62  	// flags that configure the node
    63  	nodeFlags = []cli.Flag{
    64  		utils.IdentityFlag,
    65  		utils.UnlockedAccountFlag,
    66  		utils.PasswordFileFlag,
    67  		utils.BootnodesFlag,
    68  		utils.BootnodesV4Flag,
    69  		utils.BootnodesV5Flag,
    70  		utils.DataDirFlag,
    71  		utils.AncientFlag,
    72  		utils.KeyStoreDirFlag,
    73  		utils.ExternalSignerFlag,
    74  		utils.NoUSBFlag,
    75  		utils.SmartCardDaemonPathFlag,
    76  		utils.OverrideIstanbulFlag,
    77  		utils.DashboardEnabledFlag,
    78  		utils.DashboardAddrFlag,
    79  		utils.DashboardPortFlag,
    80  		utils.DashboardRefreshFlag,
    81  		utils.EthashCacheDirFlag,
    82  		utils.EthashCachesInMemoryFlag,
    83  		utils.EthashCachesOnDiskFlag,
    84  		utils.EthashDatasetDirFlag,
    85  		utils.EthashDatasetsInMemoryFlag,
    86  		utils.EthashDatasetsOnDiskFlag,
    87  		utils.TxPoolLocalsFlag,
    88  		utils.TxPoolNoLocalsFlag,
    89  		utils.TxPoolJournalFlag,
    90  		utils.TxPoolRejournalFlag,
    91  		utils.TxPoolPriceLimitFlag,
    92  		utils.TxPoolPriceBumpFlag,
    93  		utils.TxPoolAccountSlotsFlag,
    94  		utils.TxPoolGlobalSlotsFlag,
    95  		utils.TxPoolAccountQueueFlag,
    96  		utils.TxPoolGlobalQueueFlag,
    97  		utils.TxPoolLifetimeFlag,
    98  		utils.SyncModeFlag,
    99  		utils.ExitWhenSyncedFlag,
   100  		utils.GCModeFlag,
   101  		utils.LightServeFlag,
   102  		utils.LightLegacyServFlag,
   103  		utils.LightIngressFlag,
   104  		utils.LightEgressFlag,
   105  		utils.LightMaxPeersFlag,
   106  		utils.LightLegacyPeersFlag,
   107  		utils.LightKDFFlag,
   108  		utils.UltraLightServersFlag,
   109  		utils.UltraLightFractionFlag,
   110  		utils.UltraLightOnlyAnnounceFlag,
   111  		utils.WhitelistFlag,
   112  		utils.CacheFlag,
   113  		utils.CacheDatabaseFlag,
   114  		utils.CacheTrieFlag,
   115  		utils.CacheGCFlag,
   116  		utils.CacheNoPrefetchFlag,
   117  		utils.ListenPortFlag,
   118  		utils.MaxPeersFlag,
   119  		utils.MaxPendingPeersFlag,
   120  		utils.MiningEnabledFlag,
   121  		utils.MinerThreadsFlag,
   122  		utils.MinerLegacyThreadsFlag,
   123  		utils.MinerNotifyFlag,
   124  		utils.MinerGasTargetFlag,
   125  		utils.MinerLegacyGasTargetFlag,
   126  		utils.MinerGasLimitFlag,
   127  		utils.MinerGasPriceFlag,
   128  		utils.MinerLegacyGasPriceFlag,
   129  		utils.MinerEtherbaseFlag,
   130  		utils.MinerLegacyEtherbaseFlag,
   131  		utils.MinerExtraDataFlag,
   132  		utils.MinerLegacyExtraDataFlag,
   133  		utils.MinerRecommitIntervalFlag,
   134  		utils.MinerNoVerfiyFlag,
   135  		utils.NATFlag,
   136  		utils.NoDiscoverFlag,
   137  		utils.DiscoveryV5Flag,
   138  		utils.NetrestrictFlag,
   139  		utils.NodeKeyFileFlag,
   140  		utils.NodeKeyHexFlag,
   141  		utils.DeveloperFlag,
   142  		utils.DeveloperPeriodFlag,
   143  		utils.TestnetFlag,
   144  		utils.RinkebyFlag,
   145  		utils.GoerliFlag,
   146  		utils.VMEnableDebugFlag,
   147  		utils.NetworkIdFlag,
   148  		utils.EthStatsURLFlag,
   149  		utils.FakePoWFlag,
   150  		utils.NoCompactionFlag,
   151  		utils.GpoBlocksFlag,
   152  		utils.GpoPercentileFlag,
   153  		utils.EWASMInterpreterFlag,
   154  		utils.EVMInterpreterFlag,
   155  		configFileFlag,
   156  		// Quorum
   157  		utils.QuorumImmutabilityThreshold,
   158  		utils.EnableNodePermissionFlag,
   159  		utils.RaftModeFlag,
   160  		utils.RaftBlockTimeFlag,
   161  		utils.RaftJoinExistingFlag,
   162  		utils.RaftPortFlag,
   163  		utils.RaftDNSEnabledFlag,
   164  		utils.EmitCheckpointsFlag,
   165  		utils.IstanbulRequestTimeoutFlag,
   166  		utils.IstanbulBlockPeriodFlag,
   167  		utils.PluginSettingsFlag,
   168  		utils.PluginSkipVerifyFlag,
   169  		utils.PluginLocalVerifyFlag,
   170  		utils.PluginPublicKeyFlag,
   171  		utils.AllowedFutureBlockTimeFlag,
   172  		utils.EVMCallTimeOutFlag,
   173  		utils.MultitenancyFlag,
   174  		// End-Quorum
   175  	}
   176  
   177  	rpcFlags = []cli.Flag{
   178  		utils.RPCEnabledFlag,
   179  		utils.RPCListenAddrFlag,
   180  		utils.RPCPortFlag,
   181  		utils.RPCCORSDomainFlag,
   182  		utils.RPCVirtualHostsFlag,
   183  		utils.GraphQLEnabledFlag,
   184  		utils.GraphQLListenAddrFlag,
   185  		utils.GraphQLPortFlag,
   186  		utils.GraphQLCORSDomainFlag,
   187  		utils.GraphQLVirtualHostsFlag,
   188  		utils.RPCApiFlag,
   189  		utils.WSEnabledFlag,
   190  		utils.WSListenAddrFlag,
   191  		utils.WSPortFlag,
   192  		utils.WSApiFlag,
   193  		utils.WSAllowedOriginsFlag,
   194  		utils.IPCDisabledFlag,
   195  		utils.IPCPathFlag,
   196  		utils.InsecureUnlockAllowedFlag,
   197  		utils.RPCGlobalGasCap,
   198  	}
   199  
   200  	whisperFlags = []cli.Flag{
   201  		utils.WhisperEnabledFlag,
   202  		utils.WhisperMaxMessageSizeFlag,
   203  		utils.WhisperMinPOWFlag,
   204  		utils.WhisperRestrictConnectionBetweenLightClientsFlag,
   205  	}
   206  
   207  	metricsFlags = []cli.Flag{
   208  		utils.MetricsEnabledFlag,
   209  		utils.MetricsEnabledExpensiveFlag,
   210  		utils.MetricsEnableInfluxDBFlag,
   211  		utils.MetricsInfluxDBEndpointFlag,
   212  		utils.MetricsInfluxDBDatabaseFlag,
   213  		utils.MetricsInfluxDBUsernameFlag,
   214  		utils.MetricsInfluxDBPasswordFlag,
   215  		utils.MetricsInfluxDBTagsFlag,
   216  	}
   217  )
   218  
   219  func init() {
   220  	// Initialize the CLI app and start Geth
   221  	app.Action = geth
   222  	app.HideVersion = true // we have a command to print the version
   223  	app.Copyright = "Copyright 2013-2019 The go-ethereum Authors"
   224  	app.Commands = []cli.Command{
   225  		// See chaincmd.go:
   226  		initCommand,
   227  		importCommand,
   228  		exportCommand,
   229  		importPreimagesCommand,
   230  		exportPreimagesCommand,
   231  		copydbCommand,
   232  		removedbCommand,
   233  		dumpCommand,
   234  		inspectCommand,
   235  		// See accountcmd.go:
   236  		accountCommand,
   237  		walletCommand,
   238  		// See consolecmd.go:
   239  		consoleCommand,
   240  		attachCommand,
   241  		javascriptCommand,
   242  		// See misccmd.go:
   243  		makecacheCommand,
   244  		makedagCommand,
   245  		versionCommand,
   246  		licenseCommand,
   247  		// See config.go
   248  		dumpConfigCommand,
   249  		// See retesteth.go
   250  		retestethCommand,
   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, whisperFlags...)
   259  	app.Flags = append(app.Flags, metricsFlags...)
   260  
   261  	app.Before = func(ctx *cli.Context) error {
   262  		logdir := ""
   263  		if ctx.GlobalBool(utils.DashboardEnabledFlag.Name) {
   264  			logdir = (&node.Config{DataDir: utils.MakeDataDir(ctx)}).ResolvePath("logs")
   265  		}
   266  		if err := debug.Setup(ctx, logdir); err != nil {
   267  			return err
   268  		}
   269  		return nil
   270  	}
   271  
   272  	app.After = func(ctx *cli.Context) error {
   273  		debug.Exit()
   274  		console.Stdin.Close() // Resets terminal mode.
   275  		return nil
   276  	}
   277  }
   278  
   279  func main() {
   280  	if err := app.Run(os.Args); err != nil {
   281  		fmt.Fprintln(os.Stderr, err)
   282  		os.Exit(1)
   283  	}
   284  }
   285  
   286  // prepare manipulates memory cache allowance and setups metric system.
   287  // This function should be called before launching devp2p stack.
   288  func prepare(ctx *cli.Context) {
   289  	// If we're a full node on mainnet without --cache specified, bump default cache allowance
   290  	if ctx.GlobalString(utils.SyncModeFlag.Name) != "light" && !ctx.GlobalIsSet(utils.CacheFlag.Name) && !ctx.GlobalIsSet(utils.NetworkIdFlag.Name) {
   291  		// Make sure we're not on any supported preconfigured testnet either
   292  		if !ctx.GlobalIsSet(utils.TestnetFlag.Name) && !ctx.GlobalIsSet(utils.RinkebyFlag.Name) && !ctx.GlobalIsSet(utils.GoerliFlag.Name) && !ctx.GlobalIsSet(utils.DeveloperFlag.Name) {
   293  			// Nope, we're really on mainnet. Bump that cache up!
   294  			log.Info("Bumping default cache on mainnet", "provided", ctx.GlobalInt(utils.CacheFlag.Name), "updated", 4096)
   295  			ctx.GlobalSet(utils.CacheFlag.Name, strconv.Itoa(4096))
   296  		}
   297  	}
   298  	// If we're running a light client on any network, drop the cache to some meaningfully low amount
   299  	if ctx.GlobalString(utils.SyncModeFlag.Name) == "light" && !ctx.GlobalIsSet(utils.CacheFlag.Name) {
   300  		log.Info("Dropping default light client cache", "provided", ctx.GlobalInt(utils.CacheFlag.Name), "updated", 128)
   301  		ctx.GlobalSet(utils.CacheFlag.Name, strconv.Itoa(128))
   302  	}
   303  	// Cap the cache allowance and tune the garbage collector
   304  	var mem gosigar.Mem
   305  	// Workaround until OpenBSD support lands into gosigar
   306  	// Check https://github.com/elastic/gosigar#supported-platforms
   307  	if runtime.GOOS != "openbsd" {
   308  		if err := mem.Get(); err == nil {
   309  			allowance := int(mem.Total / 1024 / 1024 / 3)
   310  			if cache := ctx.GlobalInt(utils.CacheFlag.Name); cache > allowance {
   311  				log.Warn("Sanitizing cache to Go's GC limits", "provided", cache, "updated", allowance)
   312  				ctx.GlobalSet(utils.CacheFlag.Name, strconv.Itoa(allowance))
   313  			}
   314  		}
   315  	}
   316  	// Ensure Go's GC ignores the database cache for trigger percentage
   317  	cache := ctx.GlobalInt(utils.CacheFlag.Name)
   318  	gogc := math.Max(20, math.Min(100, 100/(float64(cache)/1024)))
   319  
   320  	log.Debug("Sanitizing Go's GC trigger", "percent", int(gogc))
   321  	godebug.SetGCPercent(int(gogc))
   322  
   323  	// Start metrics export if enabled
   324  	utils.SetupMetrics(ctx)
   325  
   326  	// Start system runtime metrics collection
   327  	go metrics.CollectProcessMetrics(3 * time.Second)
   328  }
   329  
   330  // geth is the main entry point into the system if no special subcommand is ran.
   331  // It creates a default node based on the command line arguments and runs it in
   332  // blocking mode, waiting for it to be shut down.
   333  func geth(ctx *cli.Context) error {
   334  	if args := ctx.Args(); len(args) > 0 {
   335  		return fmt.Errorf("invalid command: %q", args[0])
   336  	}
   337  	prepare(ctx)
   338  
   339  	node := makeFullNode(ctx)
   340  	defer node.Close()
   341  	startNode(ctx, node)
   342  
   343  	node.Wait()
   344  	return nil
   345  }
   346  
   347  // startNode boots up the system node and all registered protocols, after which
   348  // it unlocks any requested accounts, and starts the RPC/IPC interfaces and the
   349  // miner.
   350  // Quorum
   351  // - Enrich eth/les service with ContractAuthorizationProvider for multitenancy support if prequisites are met
   352  func startNode(ctx *cli.Context, stack *node.Node) {
   353  	log.DoEmitCheckpoints = ctx.GlobalBool(utils.EmitCheckpointsFlag.Name)
   354  	debug.Memsize.Add("node", stack)
   355  
   356  	if !quorumValidatePrivateTransactionManager() {
   357  		utils.Fatalf("the PRIVATE_CONFIG environment variable must be specified for Quorum")
   358  	}
   359  
   360  	// raft mode does not support --exitwhensynced
   361  	if ctx.GlobalBool(utils.ExitWhenSyncedFlag.Name) && ctx.GlobalBool(utils.RaftModeFlag.Name) {
   362  		utils.Fatalf("raft consensus does not support --exitwhensynced")
   363  	}
   364  
   365  	// Start up the node itself
   366  	utils.StartNode(stack)
   367  
   368  	// Now that the plugin manager has been started we register the account plugin with the corresponding account backend.  All other account management is disabled when using External Signer
   369  	if !ctx.IsSet(utils.ExternalSignerFlag.Name) && stack.PluginManager().IsEnabled(plugin.AccountPluginInterfaceName) {
   370  		b := stack.AccountManager().Backends(pluggable.BackendType)[0].(*pluggable.Backend)
   371  		if err := stack.PluginManager().AddAccountPluginToBackend(b); err != nil {
   372  			log.Error("failed to setup account plugin", "err", err)
   373  		}
   374  	}
   375  
   376  	// Unlock any account specifically requested
   377  	unlockAccounts(ctx, stack)
   378  
   379  	// Register wallet event handlers to open and auto-derive wallets
   380  	events := make(chan accounts.WalletEvent, 16)
   381  	stack.AccountManager().Subscribe(events)
   382  
   383  	// Create a client to interact with local geth node.
   384  	rpcClient, err := stack.Attach()
   385  	if err != nil {
   386  		utils.Fatalf("Failed to attach to self: %v", err)
   387  	}
   388  	ethClient := ethclient.NewClient(rpcClient)
   389  
   390  	var ethService *eth.Ethereum
   391  	if err := stack.Service(&ethService); err != nil {
   392  		utils.Fatalf("Failed to retrieve ethereum service: %v", err)
   393  	}
   394  	setContractAuthzProviderFunc := ethService.SetContractAuthorizationProvider
   395  	// Set contract backend for ethereum service if local node
   396  	// is serving LES requests.
   397  	if ctx.GlobalInt(utils.LightLegacyServFlag.Name) > 0 || ctx.GlobalInt(utils.LightServeFlag.Name) > 0 {
   398  		var ethService *eth.Ethereum
   399  		if err := stack.Service(&ethService); err != nil {
   400  			utils.Fatalf("Failed to retrieve ethereum service: %v", err)
   401  		}
   402  		ethService.SetContractBackend(ethClient)
   403  	}
   404  	// Set contract backend for les service if local node is
   405  	// running as a light client.
   406  	if ctx.GlobalString(utils.SyncModeFlag.Name) == "light" {
   407  		var lesService *les.LightEthereum
   408  		if err := stack.Service(&lesService); err != nil {
   409  			utils.Fatalf("Failed to retrieve light ethereum service: %v", err)
   410  		}
   411  		lesService.SetContractBackend(ethClient)
   412  		setContractAuthzProviderFunc = lesService.SetContractAuthorizationManager
   413  	}
   414  
   415  	// Set ContractAuthorizationProvider if multitenancy flag is on AND plugin security is configured
   416  	if ctx.GlobalBool(utils.MultitenancyFlag.Name) {
   417  		if stack.PluginManager().IsEnabled(plugin.SecurityPluginInterfaceName) {
   418  			log.Info("Node supports multitenancy")
   419  			setContractAuthzProviderFunc(&multitenancy.DefaultContractAuthorizationProvider{})
   420  		} else {
   421  			utils.Fatalf("multitenancy requires RPC Security Plugin to be configured")
   422  		}
   423  	}
   424  
   425  	go func() {
   426  		// Open any wallets already attached
   427  		for _, wallet := range stack.AccountManager().Wallets() {
   428  			if err := wallet.Open(""); err != nil {
   429  				log.Warn("Failed to open wallet", "url", wallet.URL(), "err", err)
   430  			}
   431  		}
   432  		// Listen for wallet event till termination
   433  		for event := range events {
   434  			switch event.Kind {
   435  			case accounts.WalletArrived:
   436  				if err := event.Wallet.Open(""); err != nil {
   437  					log.Warn("New wallet appeared, failed to open", "url", event.Wallet.URL(), "err", err)
   438  				}
   439  			case accounts.WalletOpened:
   440  				status, _ := event.Wallet.Status()
   441  				log.Info("New wallet appeared", "url", event.Wallet.URL(), "status", status)
   442  
   443  				var derivationPaths []accounts.DerivationPath
   444  				if event.Wallet.URL().Scheme == "ledger" {
   445  					derivationPaths = append(derivationPaths, accounts.LegacyLedgerBaseDerivationPath)
   446  				}
   447  				derivationPaths = append(derivationPaths, accounts.DefaultBaseDerivationPath)
   448  
   449  				event.Wallet.SelfDerive(derivationPaths, ethClient)
   450  
   451  			case accounts.WalletDropped:
   452  				log.Info("Old wallet dropped", "url", event.Wallet.URL())
   453  				event.Wallet.Close()
   454  			}
   455  		}
   456  	}()
   457  
   458  	// Spawn a standalone goroutine for status synchronization monitoring,
   459  	// close the node when synchronization is complete if user required.
   460  	if ctx.GlobalBool(utils.ExitWhenSyncedFlag.Name) {
   461  		go func() {
   462  			sub := stack.EventMux().Subscribe(downloader.DoneEvent{})
   463  			defer sub.Unsubscribe()
   464  			for {
   465  				event := <-sub.Chan()
   466  				if event == nil {
   467  					continue
   468  				}
   469  				done, ok := event.Data.(downloader.DoneEvent)
   470  				if !ok {
   471  					continue
   472  				}
   473  				if timestamp := time.Unix(int64(done.Latest.Time), 0); time.Since(timestamp) < 10*time.Minute {
   474  					log.Info("Synchronisation completed", "latestnum", done.Latest.Number, "latesthash", done.Latest.Hash(),
   475  						"age", common.PrettyAge(timestamp))
   476  					stack.Stop()
   477  				}
   478  			}
   479  		}()
   480  	}
   481  
   482  	// Quorum
   483  	//
   484  	// checking if permissions is enabled and staring the permissions service
   485  	if stack.Config().EnableNodePermission {
   486  		stack.Server().SetIsNodePermissioned(permission.IsNodePermissioned)
   487  		if stack.IsPermissionEnabled() {
   488  			var permissionService *permission.PermissionCtrl
   489  			if err := stack.Service(&permissionService); err != nil {
   490  				utils.Fatalf("Permission service not runnning: %v", err)
   491  			}
   492  			if err := permissionService.AfterStart(); err != nil {
   493  				utils.Fatalf("Permission service post construct failure: %v", err)
   494  			}
   495  		}
   496  	}
   497  
   498  	// Start auxiliary services if enabled
   499  	if ctx.GlobalBool(utils.MiningEnabledFlag.Name) || ctx.GlobalBool(utils.DeveloperFlag.Name) {
   500  		// Mining only makes sense if a full Ethereum node is running
   501  		if ctx.GlobalString(utils.SyncModeFlag.Name) == "light" {
   502  			utils.Fatalf("Light clients do not support mining")
   503  		}
   504  		var ethereum *eth.Ethereum
   505  		if err := stack.Service(&ethereum); err != nil {
   506  			utils.Fatalf("Ethereum service not running: %v", err)
   507  		}
   508  		// Set the gas price to the limits from the CLI and start mining
   509  		gasprice := utils.GlobalBig(ctx, utils.MinerLegacyGasPriceFlag.Name)
   510  		if ctx.IsSet(utils.MinerGasPriceFlag.Name) {
   511  			gasprice = utils.GlobalBig(ctx, utils.MinerGasPriceFlag.Name)
   512  		}
   513  		ethereum.TxPool().SetGasPrice(gasprice)
   514  
   515  		threads := ctx.GlobalInt(utils.MinerLegacyThreadsFlag.Name)
   516  		if ctx.GlobalIsSet(utils.MinerThreadsFlag.Name) {
   517  			threads = ctx.GlobalInt(utils.MinerThreadsFlag.Name)
   518  		}
   519  		if err := ethereum.StartMining(threads); err != nil {
   520  			utils.Fatalf("Failed to start mining: %v", err)
   521  		}
   522  	}
   523  
   524  	// checks quorum features that depend on the ethereum service
   525  	quorumValidateEthService(stack, ctx.GlobalBool(utils.RaftModeFlag.Name))
   526  }
   527  
   528  // unlockAccounts unlocks any account specifically requested.
   529  func unlockAccounts(ctx *cli.Context, stack *node.Node) {
   530  	var unlocks []string
   531  	inputs := strings.Split(ctx.GlobalString(utils.UnlockedAccountFlag.Name), ",")
   532  	for _, input := range inputs {
   533  		if trimmed := strings.TrimSpace(input); trimmed != "" {
   534  			unlocks = append(unlocks, trimmed)
   535  		}
   536  	}
   537  	// Short circuit if there is no account to unlock.
   538  	if len(unlocks) == 0 {
   539  		return
   540  	}
   541  	// If insecure account unlocking is not allowed if node's APIs are exposed to external.
   542  	// Print warning log to user and skip unlocking.
   543  	if !stack.Config().InsecureUnlockAllowed && stack.Config().ExtRPCEnabled() {
   544  		utils.Fatalf("Account unlock with HTTP access is forbidden!")
   545  	}
   546  	ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
   547  	passwords := utils.MakePasswordList(ctx)
   548  	for i, account := range unlocks {
   549  		unlockAccount(ks, account, i, passwords)
   550  	}
   551  }