main.go 12.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
// Copyright 2014 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 13 14
// GNU General Public License for more details.
//
// 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/>.
Felix Lange's avatar
Felix Lange committed
16

17
// geth is the official command-line client for Ethereum.
18 19 20
package main

import (
obscuren's avatar
obscuren committed
21
	"fmt"
22
	"math"
obscuren's avatar
obscuren committed
23
	"os"
24
	godebug "runtime/debug"
25
	"sort"
26
	"strconv"
27
	"strings"
28
	"time"
obscuren's avatar
obscuren committed
29

30
	"github.com/elastic/gosigar"
31
	"github.com/ethereum/go-ethereum/accounts"
32
	"github.com/ethereum/go-ethereum/accounts/keystore"
obscuren's avatar
obscuren committed
33
	"github.com/ethereum/go-ethereum/cmd/utils"
34
	"github.com/ethereum/go-ethereum/common"
35
	"github.com/ethereum/go-ethereum/console"
obscuren's avatar
obscuren committed
36
	"github.com/ethereum/go-ethereum/eth"
37
	"github.com/ethereum/go-ethereum/eth/downloader"
38
	"github.com/ethereum/go-ethereum/ethclient"
39
	"github.com/ethereum/go-ethereum/internal/debug"
40
	"github.com/ethereum/go-ethereum/log"
41
	"github.com/ethereum/go-ethereum/metrics"
42
	"github.com/ethereum/go-ethereum/node"
43
	cli "gopkg.in/urfave/cli.v1"
44 45
)

zelig's avatar
zelig committed
46
const (
47
	clientIdentifier = "geth" // Client identifier to advertise over the network
zelig's avatar
zelig committed
48 49
)

50
var (
51 52 53 54
	// Git SHA1 commit hash of the release (set via linker flags)
	gitCommit = ""
	// The app that holds all commands and flags.
	app = utils.NewApp(gitCommit, "the go-ethereum command line interface")
55 56
	// flags that configure the node
	nodeFlags = []cli.Flag{
57
		utils.IdentityFlag,
58
		utils.UnlockedAccountFlag,
zelig's avatar
zelig committed
59
		utils.PasswordFileFlag,
60
		utils.InsecureUnlockAllowedFlag,
61
		utils.BootnodesFlag,
62 63
		utils.BootnodesV4Flag,
		utils.BootnodesV5Flag,
64
		utils.DataDirFlag,
65
		utils.KeyStoreDirFlag,
66
		utils.ExternalSignerFlag,
67
		utils.NoUSBFlag,
68 69 70 71
		utils.DashboardEnabledFlag,
		utils.DashboardAddrFlag,
		utils.DashboardPortFlag,
		utils.DashboardRefreshFlag,
72 73 74 75
		utils.EthashCacheDirFlag,
		utils.EthashCachesInMemoryFlag,
		utils.EthashCachesOnDiskFlag,
		utils.EthashDatasetDirFlag,
76
		utils.EthashDatasetsInMemoryFlag,
77
		utils.EthashDatasetsOnDiskFlag,
78
		utils.TxPoolLocalsFlag,
79
		utils.TxPoolNoLocalsFlag,
80 81
		utils.TxPoolJournalFlag,
		utils.TxPoolRejournalFlag,
82 83 84 85 86 87 88
		utils.TxPoolPriceLimitFlag,
		utils.TxPoolPriceBumpFlag,
		utils.TxPoolAccountSlotsFlag,
		utils.TxPoolGlobalSlotsFlag,
		utils.TxPoolAccountQueueFlag,
		utils.TxPoolGlobalQueueFlag,
		utils.TxPoolLifetimeFlag,
89 90 91 92
		utils.ULCModeConfigFlag,
		utils.OnlyAnnounceModeFlag,
		utils.ULCTrustedNodesFlag,
		utils.ULCMinTrustedFractionFlag,
93
		utils.SyncModeFlag,
94
		utils.ExitWhenSyncedFlag,
95
		utils.GCModeFlag,
96
		utils.LightServFlag,
97 98
		utils.LightBandwidthInFlag,
		utils.LightBandwidthOutFlag,
99
		utils.LightPeersFlag,
100
		utils.LightKDFFlag,
101
		utils.WhitelistFlag,
102
		utils.CacheFlag,
103
		utils.CacheDatabaseFlag,
104
		utils.CacheTrieFlag,
105
		utils.CacheGCFlag,
106
		utils.CacheNoPrefetchFlag,
107 108
		utils.ListenPortFlag,
		utils.MaxPeersFlag,
109
		utils.MaxPendingPeersFlag,
110
		utils.MiningEnabledFlag,
111
		utils.MinerThreadsFlag,
112
		utils.MinerLegacyThreadsFlag,
113
		utils.MinerNotifyFlag,
114 115
		utils.MinerGasTargetFlag,
		utils.MinerLegacyGasTargetFlag,
116
		utils.MinerGasLimitFlag,
117 118 119 120 121 122
		utils.MinerGasPriceFlag,
		utils.MinerLegacyGasPriceFlag,
		utils.MinerEtherbaseFlag,
		utils.MinerLegacyEtherbaseFlag,
		utils.MinerExtraDataFlag,
		utils.MinerLegacyExtraDataFlag,
123
		utils.MinerRecommitIntervalFlag,
124
		utils.MinerNoVerfiyFlag,
125
		utils.NATFlag,
126
		utils.NoDiscoverFlag,
127
		utils.DiscoveryV5Flag,
128
		utils.NetrestrictFlag,
129 130
		utils.NodeKeyFileFlag,
		utils.NodeKeyHexFlag,
131 132
		utils.DeveloperFlag,
		utils.DeveloperPeriodFlag,
133 134
		utils.TestnetFlag,
		utils.RinkebyFlag,
135
		utils.GoerliFlag,
136
		utils.VMEnableDebugFlag,
zelig's avatar
zelig committed
137
		utils.NetworkIdFlag,
138
		utils.ConstantinopleOverrideFlag,
139
		utils.RPCCORSDomainFlag,
140
		utils.RPCVirtualHostsFlag,
141
		utils.EthStatsURLFlag,
142
		utils.FakePoWFlag,
143
		utils.NoCompactionFlag,
144 145
		utils.GpoBlocksFlag,
		utils.GpoPercentileFlag,
146 147
		utils.EWASMInterpreterFlag,
		utils.EVMInterpreterFlag,
148
		configFileFlag,
149
	}
150 151 152 153 154

	rpcFlags = []cli.Flag{
		utils.RPCEnabledFlag,
		utils.RPCListenAddrFlag,
		utils.RPCPortFlag,
155 156 157 158 159
		utils.GraphQLEnabledFlag,
		utils.GraphQLListenAddrFlag,
		utils.GraphQLPortFlag,
		utils.GraphQLCORSDomainFlag,
		utils.GraphQLVirtualHostsFlag,
160 161 162 163 164 165 166 167 168
		utils.RPCApiFlag,
		utils.WSEnabledFlag,
		utils.WSListenAddrFlag,
		utils.WSPortFlag,
		utils.WSApiFlag,
		utils.WSAllowedOriginsFlag,
		utils.IPCDisabledFlag,
		utils.IPCPathFlag,
	}
169 170 171 172 173

	whisperFlags = []cli.Flag{
		utils.WhisperEnabledFlag,
		utils.WhisperMaxMessageSizeFlag,
		utils.WhisperMinPOWFlag,
174
		utils.WhisperRestrictConnectionBetweenLightClientsFlag,
175
	}
176 177

	metricsFlags = []cli.Flag{
178 179
		utils.MetricsEnabledFlag,
		utils.MetricsEnabledExpensiveFlag,
180 181 182 183 184
		utils.MetricsEnableInfluxDBFlag,
		utils.MetricsInfluxDBEndpointFlag,
		utils.MetricsInfluxDBDatabaseFlag,
		utils.MetricsInfluxDBUsernameFlag,
		utils.MetricsInfluxDBPasswordFlag,
185
		utils.MetricsInfluxDBTagsFlag,
186
	}
187 188 189 190 191 192
)

func init() {
	// Initialize the CLI app and start Geth
	app.Action = geth
	app.HideVersion = true // we have a command to print the version
193
	app.Copyright = "Copyright 2013-2019 The go-ethereum Authors"
194 195 196 197 198
	app.Commands = []cli.Command{
		// See chaincmd.go:
		initCommand,
		importCommand,
		exportCommand,
199 200
		importPreimagesCommand,
		exportPreimagesCommand,
201
		copydbCommand,
202 203 204 205 206 207 208 209 210 211
		removedbCommand,
		dumpCommand,
		// See accountcmd.go:
		accountCommand,
		walletCommand,
		// See consolecmd.go:
		consoleCommand,
		attachCommand,
		javascriptCommand,
		// See misccmd.go:
212
		makecacheCommand,
213 214 215 216 217 218
		makedagCommand,
		versionCommand,
		licenseCommand,
		// See config.go
		dumpConfigCommand,
	}
219
	sort.Sort(cli.CommandsByName(app.Commands))
220 221 222 223

	app.Flags = append(app.Flags, nodeFlags...)
	app.Flags = append(app.Flags, rpcFlags...)
	app.Flags = append(app.Flags, consoleFlags...)
224
	app.Flags = append(app.Flags, debug.Flags...)
225
	app.Flags = append(app.Flags, whisperFlags...)
226
	app.Flags = append(app.Flags, metricsFlags...)
227

228
	app.Before = func(ctx *cli.Context) error {
229 230 231 232 233
		logdir := ""
		if ctx.GlobalBool(utils.DashboardEnabledFlag.Name) {
			logdir = (&node.Config{DataDir: utils.MakeDataDir(ctx)}).ResolvePath("logs")
		}
		if err := debug.Setup(ctx, logdir); err != nil {
234 235
			return err
		}
236
		// Cap the cache allowance and tune the garbage collector
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
		var mem gosigar.Mem
		if err := mem.Get(); err == nil {
			allowance := int(mem.Total / 1024 / 1024 / 3)
			if cache := ctx.GlobalInt(utils.CacheFlag.Name); cache > allowance {
				log.Warn("Sanitizing cache to Go's GC limits", "provided", cache, "updated", allowance)
				ctx.GlobalSet(utils.CacheFlag.Name, strconv.Itoa(allowance))
			}
		}
		// Ensure Go's GC ignores the database cache for trigger percentage
		cache := ctx.GlobalInt(utils.CacheFlag.Name)
		gogc := math.Max(20, math.Min(100, 100/(float64(cache)/1024)))

		log.Debug("Sanitizing Go's GC trigger", "percent", int(gogc))
		godebug.SetGCPercent(int(gogc))

252 253 254
		// Start metrics export if enabled
		utils.SetupMetrics(ctx)

255 256
		// Start system runtime metrics collection
		go metrics.CollectProcessMetrics(3 * time.Second)
257

258
		return nil
259
	}
260 261 262

	app.After = func(ctx *cli.Context) error {
		debug.Exit()
263
		console.Stdin.Close() // Resets terminal mode.
264 265
		return nil
	}
266
}
obscuren's avatar
obscuren committed
267

268 269 270 271 272 273
func main() {
	if err := app.Run(os.Args); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}
zelig's avatar
zelig committed
274

275 276 277
// geth is the main entry point into the system if no special subcommand is ran.
// It creates a default node based on the command line arguments and runs it in
// blocking mode, waiting for it to be shut down.
278
func geth(ctx *cli.Context) error {
279 280 281
	if args := ctx.Args(); len(args) > 0 {
		return fmt.Errorf("invalid command: %q", args[0])
	}
282
	node := makeFullNode(ctx)
283
	defer node.Close()
284 285
	startNode(ctx, node)
	node.Wait()
286
	return nil
287
}
288

289 290 291 292
// startNode boots up the system node and all registered protocols, after which
// it unlocks any requested accounts, and starts the RPC/IPC interfaces and the
// miner.
func startNode(ctx *cli.Context, stack *node.Node) {
293 294
	debug.Memsize.Add("node", stack)

295 296
	// Start up the node itself
	utils.StartNode(stack)
297

298
	// Unlock any account specifically requested
299 300
	unlockAccounts(ctx, stack)

301 302 303 304 305
	// Register wallet event handlers to open and auto-derive wallets
	events := make(chan accounts.WalletEvent, 16)
	stack.AccountManager().Subscribe(events)

	go func() {
306
		// Create a chain state reader for self-derivation
307 308
		rpcClient, err := stack.Attach()
		if err != nil {
309
			utils.Fatalf("Failed to attach to self: %v", err)
310 311 312
		}
		stateReader := ethclient.NewClient(rpcClient)

313
		// Open any wallets already attached
314 315
		for _, wallet := range stack.AccountManager().Wallets() {
			if err := wallet.Open(""); err != nil {
316
				log.Warn("Failed to open wallet", "url", wallet.URL(), "err", err)
317 318
			}
		}
319 320
		// Listen for wallet event till termination
		for event := range events {
321 322
			switch event.Kind {
			case accounts.WalletArrived:
323
				if err := event.Wallet.Open(""); err != nil {
324
					log.Warn("New wallet appeared, failed to open", "url", event.Wallet.URL(), "err", err)
325 326
				}
			case accounts.WalletOpened:
327 328 329
				status, _ := event.Wallet.Status()
				log.Info("New wallet appeared", "url", event.Wallet.URL(), "status", status)

330
				derivationPath := accounts.DefaultBaseDerivationPath
331
				if event.Wallet.URL().Scheme == "ledger" {
332
					derivationPath = accounts.DefaultLedgerBaseDerivationPath
333
				}
334
				event.Wallet.SelfDerive(derivationPath, stateReader)
335 336

			case accounts.WalletDropped:
337
				log.Info("Old wallet dropped", "url", event.Wallet.URL())
338
				event.Wallet.Close()
339 340 341
			}
		}
	}()
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357

	// Spawn a standalone goroutine for status synchronization monitoring,
	// close the node when synchronization is complete if user required.
	if ctx.GlobalBool(utils.ExitWhenSyncedFlag.Name) {
		go func() {
			sub := stack.EventMux().Subscribe(downloader.DoneEvent{})
			defer sub.Unsubscribe()
			for {
				event := <-sub.Chan()
				if event == nil {
					continue
				}
				done, ok := event.Data.(downloader.DoneEvent)
				if !ok {
					continue
				}
358
				if timestamp := time.Unix(int64(done.Latest.Time), 0); time.Since(timestamp) < 10*time.Minute {
359 360 361 362 363 364 365 366 367
					log.Info("Synchronisation completed", "latestnum", done.Latest.Number, "latesthash", done.Latest.Hash(),
						"age", common.PrettyAge(timestamp))
					stack.Stop()
				}

			}
		}()
	}

368
	// Start auxiliary services if enabled
369
	if ctx.GlobalBool(utils.MiningEnabledFlag.Name) || ctx.GlobalBool(utils.DeveloperFlag.Name) {
370
		// Mining only makes sense if a full Ethereum node is running
371
		if ctx.GlobalString(utils.SyncModeFlag.Name) == "light" {
372 373
			utils.Fatalf("Light clients do not support mining")
		}
374
		var ethereum *eth.Ethereum
375
		if err := stack.Service(&ethereum); err != nil {
376
			utils.Fatalf("Ethereum service not running: %v", err)
377
		}
378
		// Set the gas price to the limits from the CLI and start mining
379 380 381 382 383
		gasprice := utils.GlobalBig(ctx, utils.MinerLegacyGasPriceFlag.Name)
		if ctx.IsSet(utils.MinerGasPriceFlag.Name) {
			gasprice = utils.GlobalBig(ctx, utils.MinerGasPriceFlag.Name)
		}
		ethereum.TxPool().SetGasPrice(gasprice)
384 385 386 387 388 389

		threads := ctx.GlobalInt(utils.MinerLegacyThreadsFlag.Name)
		if ctx.GlobalIsSet(utils.MinerThreadsFlag.Name) {
			threads = ctx.GlobalInt(utils.MinerThreadsFlag.Name)
		}
		if err := ethereum.StartMining(threads); err != nil {
390
			utils.Fatalf("Failed to start mining: %v", err)
391
		}
392 393
	}
}
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418

// unlockAccounts unlocks any account specifically requested.
func unlockAccounts(ctx *cli.Context, stack *node.Node) {
	var unlocks []string
	inputs := strings.Split(ctx.GlobalString(utils.UnlockedAccountFlag.Name), ",")
	for _, input := range inputs {
		if trimmed := strings.TrimSpace(input); trimmed != "" {
			unlocks = append(unlocks, trimmed)
		}
	}
	// Short circuit if there is no account to unlock.
	if len(unlocks) == 0 {
		return
	}
	// If insecure account unlocking is not allowed if node's APIs are exposed to external.
	// Print warning log to user and skip unlocking.
	if !stack.Config().InsecureUnlockAllowed && stack.Config().ExtRPCEnabled() {
		utils.Fatalf("Account unlock with HTTP access is forbidden!")
	}
	ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
	passwords := utils.MakePasswordList(ctx)
	for i, account := range unlocks {
		unlockAccount(ks, account, i, passwords)
	}
}