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