main.go 15.8 KB
Newer Older
O
obscuren 已提交
1 2
/*
	This file is part of go-ethereum
F
Felix Lange 已提交
3

O
obscuren 已提交
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
	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
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.
*/
/**
 * @authors
 * 	Jeffrey Wilcke <i@jev.io>
 */
21 22 23
package main

import (
O
obscuren 已提交
24
	"fmt"
25
	"io"
26
	"io/ioutil"
27
	_ "net/http/pprof"
O
obscuren 已提交
28
	"os"
29
	"path/filepath"
O
obscuren 已提交
30
	"runtime"
31
	"strconv"
32
	"strings"
O
obscuren 已提交
33

34
	"github.com/codegangsta/cli"
35
	"github.com/ethereum/ethash"
Z
zelig 已提交
36
	"github.com/ethereum/go-ethereum/accounts"
O
obscuren 已提交
37
	"github.com/ethereum/go-ethereum/cmd/utils"
Z
zelig 已提交
38
	"github.com/ethereum/go-ethereum/common"
O
obscuren 已提交
39
	"github.com/ethereum/go-ethereum/eth"
O
obscuren 已提交
40
	"github.com/ethereum/go-ethereum/logger"
41 42
	"github.com/ethereum/go-ethereum/rpc/codec"
	"github.com/ethereum/go-ethereum/rpc/comms"
O
obscuren 已提交
43 44
	"github.com/mattn/go-colorable"
	"github.com/mattn/go-isatty"
45 46
)

Z
zelig 已提交
47
const (
O
obscuren 已提交
48
	ClientIdentifier = "Geth"
J
Jeffrey Wilcke 已提交
49
	Version          = "0.9.33"
Z
zelig 已提交
50 51
)

52 53 54 55 56
var (
	gitCommit       string // set via linker flag
	nodeNameVersion string
	app             *cli.App
)
57

58
func init() {
59 60 61 62 63 64 65
	if gitCommit == "" {
		nodeNameVersion = Version
	} else {
		nodeNameVersion = Version + "-" + gitCommit[:8]
	}

	app = utils.NewApp(Version, "the go-ethereum command line interface")
66 67 68
	app.Action = run
	app.HideVersion = true // we have a command to print the version
	app.Commands = []cli.Command{
69 70 71 72 73 74
		blocktestCommand,
		importCommand,
		exportCommand,
		upgradedbCommand,
		removedbCommand,
		dumpCommand,
75 76 77 78 79 80 81 82 83 84 85
		{
			Action: makedag,
			Name:   "makedag",
			Usage:  "generate ethash dag (for testing)",
			Description: `
The makedag command generates an ethash DAG in /tmp/dag.

This command exists to support the system testing project.
Regular users do not need to execute it.
`,
		},
86 87 88 89 90 91 92 93
		{
			Action: version,
			Name:   "version",
			Usage:  "print ethereum version numbers",
			Description: `
The output of this command is supposed to be machine-readable.
`,
		},
94 95

		{
96 97
			Name:  "wallet",
			Usage: "ethereum presale wallet",
98 99 100 101 102 103 104
			Subcommands: []cli.Command{
				{
					Action: importWallet,
					Name:   "import",
					Usage:  "import ethereum presale wallet",
				},
			},
Z
zelig 已提交
105 106 107 108 109 110 111 112 113
			Description: `

    get wallet import /path/to/my/presale.wallet

will prompt for your password and imports your ether presale account.
It can be used non-interactively with the --password option taking a
passwordfile as argument containing the wallet password in plaintext.

`},
F
Felix Lange 已提交
114 115 116 117
		{
			Action: accountList,
			Name:   "account",
			Usage:  "manage accounts",
118 119 120 121 122
			Description: `

Manage accounts lets you create new accounts, list all existing accounts,
import a private key into a new account.

123
'            help' shows a list of subcommands or help for one subcommand.
124

125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
It supports interactive mode, when you are prompted for password as well as
non-interactive mode where passwords are supplied via a given password file.
Non-interactive mode is only meant for scripted use on test networks or known
safe environments.

Make sure you remember the password you gave when creating a new account (with
either new or import). Without it you are not able to unlock your account.

Note that exporting your key in unencrypted format is NOT supported.

Keys are stored under <DATADIR>/keys.
It is safe to transfer the entire directory or the individual keys therein
between ethereum nodes.
Make sure you backup your keys regularly.

And finally. DO NOT FORGET YOUR PASSWORD.
`,
F
Felix Lange 已提交
142 143 144 145 146 147 148 149 150 151
			Subcommands: []cli.Command{
				{
					Action: accountList,
					Name:   "list",
					Usage:  "print account addresses",
				},
				{
					Action: accountCreate,
					Name:   "new",
					Usage:  "create a new account",
Z
zelig 已提交
152 153 154 155
					Description: `

    ethereum account new

156 157 158 159 160 161
Creates a new account. Prints the address.

The account is saved in encrypted format, you are prompted for a passphrase.

You must remember this passphrase to unlock your account in the future.

Z
zelig 已提交
162 163 164 165
For non-interactive use the passphrase can be specified with the --password flag:

    ethereum --password <passwordfile> account new

166 167
Note, this is meant to be used for testing only, it is a bad idea to save your
password to file or expose in any other way.
Z
zelig 已提交
168 169 170 171 172 173 174 175 176 177
					`,
				},
				{
					Action: accountImport,
					Name:   "import",
					Usage:  "import a private key into a new account",
					Description: `

    ethereum account import <keyfile>

178 179 180
Imports an unencrypted private key from <keyfile> and creates a new account.
Prints the address.

181
The keyfile is assumed to contain an unencrypted private key in hexadecimal format.
Z
zelig 已提交
182 183 184

The account is saved in encrypted format, you are prompted for a passphrase.

185
You must remember this passphrase to unlock your account in the future.
Z
zelig 已提交
186

187
For non-interactive use the passphrase can be specified with the -password flag:
Z
zelig 已提交
188

189
    ethereum --password <passwordfile> account import <keyfile>
Z
zelig 已提交
190 191

Note:
Z
zelig 已提交
192
As you can directly copy your encrypted accounts to another ethereum instance,
193
this import mechanism is not needed when you transfer an account between
Z
zelig 已提交
194
nodes.
Z
zelig 已提交
195
					`,
F
Felix Lange 已提交
196 197 198
				},
			},
		},
199
		{
Z
CLI:  
zelig 已提交
200 201
			Action: console,
			Name:   "console",
O
obscuren 已提交
202
			Usage:  `Geth Console: interactive JavaScript environment`,
Z
CLI:  
zelig 已提交
203
			Description: `
O
obscuren 已提交
204
The Geth console is an interactive shell for the JavaScript runtime environment
205 206
which exposes a node admin interface as well as the Ðapp JavaScript API.
See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console
B
Bas van Kervel 已提交
207 208 209 210
`},
		{
			Action: attach,
			Name:   "attach",
B
Bas van Kervel 已提交
211
			Usage:  `Geth Console: interactive JavaScript environment (connect to node)`,
B
Bas van Kervel 已提交
212 213 214 215 216
			Description: `
The Geth console is an interactive shell for the JavaScript runtime environment
which exposes a node admin interface as well as the Ðapp JavaScript API.
See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console.
This command allows to open a console on a running geth node.
217 218 219 220 221 222 223 224 225 226
`,
		},
		{
			Action: monitor,
			Name:   "monitor",
			Usage:  `Geth Monitor: node metrics monitoring and visualization`,
			Description: `
The Geth monitor is a tool to collect and visualize various internal metrics
gathered by the node, supporting different chart types as well as the capacity
to display multiple metrics simultaneously.
Z
CLI:  
zelig 已提交
227 228 229 230
`,
		},
		{
			Action: execJSFiles,
231
			Name:   "js",
O
obscuren 已提交
232
			Usage:  `executes the given JavaScript files in the Geth JavaScript VM`,
233
			Description: `
234
The JavaScript VM exposes a node admin interface as well as the Ðapp
Z
zelig 已提交
235
JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console
236 237 238 239
`,
		},
	}
	app.Flags = []cli.Flag{
240
		utils.IdentityFlag,
241
		utils.UnlockedAccountFlag,
Z
zelig 已提交
242
		utils.PasswordFileFlag,
O
obscuren 已提交
243
		utils.GenesisNonceFlag,
244 245
		utils.BootnodesFlag,
		utils.DataDirFlag,
246
		utils.BlockchainVersionFlag,
Z
CLI:  
zelig 已提交
247
		utils.JSpathFlag,
248 249
		utils.ListenPortFlag,
		utils.MaxPeersFlag,
250
		utils.MaxPendingPeersFlag,
Z
zelig 已提交
251
		utils.EtherbaseFlag,
252
		utils.GasPriceFlag,
253 254
		utils.MinerThreadsFlag,
		utils.MiningEnabledFlag,
255
		utils.AutoDAGFlag,
256
		utils.NATFlag,
257
		utils.NatspecEnabledFlag,
258
		utils.NoDiscoverFlag,
259 260 261 262 263
		utils.NodeKeyFileFlag,
		utils.NodeKeyHexFlag,
		utils.RPCEnabledFlag,
		utils.RPCListenAddrFlag,
		utils.RPCPortFlag,
264
		utils.RpcApiFlag,
B
Bas van Kervel 已提交
265 266 267
		utils.IPCDisabledFlag,
		utils.IPCApiFlag,
		utils.IPCPathFlag,
268
		utils.ExecFlag,
269
		utils.WhisperEnabledFlag,
270
		utils.VMDebugFlag,
Z
zelig 已提交
271 272
		utils.ProtocolVersionFlag,
		utils.NetworkIdFlag,
273
		utils.RPCCORSDomainFlag,
274
		utils.VerbosityFlag,
O
obscuren 已提交
275 276
		utils.BacktraceAtFlag,
		utils.LogToStdErrFlag,
O
obscuren 已提交
277 278 279
		utils.LogVModuleFlag,
		utils.LogFileFlag,
		utils.LogJSONFlag,
280
		utils.PProfEanbledFlag,
281
		utils.PProfPortFlag,
282
		utils.SolcPathFlag,
Z
zsfelfoldi 已提交
283 284 285 286 287 288
		utils.GpoMinGasPriceFlag,
		utils.GpoMaxGasPriceFlag,
		utils.GpoFullBlockRatioFlag,
		utils.GpobaseStepDownFlag,
		utils.GpobaseStepUpFlag,
		utils.GpobaseCorrectionFactorFlag,
289
	}
290
	app.Before = func(ctx *cli.Context) error {
291
		utils.SetupLogger(ctx)
292
		if ctx.GlobalBool(utils.PProfEanbledFlag.Name) {
293 294 295
			utils.StartPProf(ctx)
		}
		return nil
296 297
	}
}
O
obscuren 已提交
298

299 300 301 302 303 304 305 306
func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())
	defer logger.Flush()
	if err := app.Run(os.Args); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}
Z
zelig 已提交
307

308
func run(ctx *cli.Context) {
309
	utils.HandleInterrupt()
310
	cfg := utils.MakeEthConfig(ClientIdentifier, nodeNameVersion, ctx)
311
	ethereum, err := eth.New(cfg)
312
	if err != nil {
313 314 315
		utils.Fatalf("%v", err)
	}

316
	startEth(ctx, ethereum)
317
	// this blocks the thread
318
	ethereum.WaitForShutdown()
319
}
320

B
Bas van Kervel 已提交
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
func attach(ctx *cli.Context) {
	// Wrap the standard output with a colorified stream (windows)
	if isatty.IsTerminal(os.Stdout.Fd()) {
		if pr, pw, err := os.Pipe(); err == nil {
			go io.Copy(colorable.NewColorableStdout(), pr)
			os.Stdout = pw
		}
	}

	var client comms.EthereumClient
	var err error
	if ctx.Args().Present() {
		client, err = comms.ClientFromEndpoint(ctx.Args().First(), codec.JSON)
	} else {
		cfg := comms.IpcConfig{
			Endpoint: ctx.GlobalString(utils.IPCPathFlag.Name),
		}
		client, err = comms.NewIpcClient(cfg, codec.JSON)
	}

	if err != nil {
		utils.Fatalf("Unable to attach to geth node - %v", err)
	}

	repl := newLightweightJSRE(
346
		ctx.GlobalString(utils.JSpathFlag.Name),
B
Bas van Kervel 已提交
347 348 349 350
		client,
		true,
		nil)

351 352 353 354 355 356
	if ctx.GlobalString(utils.ExecFlag.Name) != "" {
		repl.batch(ctx.GlobalString(utils.ExecFlag.Name))
	} else {
		repl.welcome()
		repl.interactive()
	}
B
Bas van Kervel 已提交
357 358
}

Z
CLI:  
zelig 已提交
359
func console(ctx *cli.Context) {
360 361 362 363 364 365 366 367
	// Wrap the standard output with a colorified stream (windows)
	if isatty.IsTerminal(os.Stdout.Fd()) {
		if pr, pw, err := os.Pipe(); err == nil {
			go io.Copy(colorable.NewColorableStdout(), pr)
			os.Stdout = pw
		}
	}

368
	cfg := utils.MakeEthConfig(ClientIdentifier, nodeNameVersion, ctx)
369
	ethereum, err := eth.New(cfg)
370
	if err != nil {
371 372 373
		utils.Fatalf("%v", err)
	}

374 375
	client := comms.NewInProcClient(codec.JSON)

376
	startEth(ctx, ethereum)
377 378
	repl := newJSRE(
		ethereum,
379
		ctx.GlobalString(utils.JSpathFlag.Name),
380
		ctx.GlobalString(utils.RPCCORSDomainFlag.Name),
381
		client,
382 383 384
		true,
		nil,
	)
B
Bas van Kervel 已提交
385

386 387 388 389 390 391
	if ctx.GlobalString(utils.ExecFlag.Name) != "" {
		repl.batch(ctx.GlobalString(utils.ExecFlag.Name))
	} else {
		repl.welcome()
		repl.interactive()
	}
Z
CLI:  
zelig 已提交
392 393 394 395 396 397

	ethereum.Stop()
	ethereum.WaitForShutdown()
}

func execJSFiles(ctx *cli.Context) {
398
	cfg := utils.MakeEthConfig(ClientIdentifier, nodeNameVersion, ctx)
Z
CLI:  
zelig 已提交
399 400 401
	ethereum, err := eth.New(cfg)
	if err != nil {
		utils.Fatalf("%v", err)
O
obscuren 已提交
402
	}
Z
CLI:  
zelig 已提交
403

404
	client := comms.NewInProcClient(codec.JSON)
Z
CLI:  
zelig 已提交
405
	startEth(ctx, ethereum)
406 407
	repl := newJSRE(
		ethereum,
408
		ctx.GlobalString(utils.JSpathFlag.Name),
409
		ctx.GlobalString(utils.RPCCORSDomainFlag.Name),
410
		client,
411 412 413
		false,
		nil,
	)
Z
CLI:  
zelig 已提交
414 415 416 417
	for _, file := range ctx.Args() {
		repl.exec(file)
	}

418 419
	ethereum.Stop()
	ethereum.WaitForShutdown()
420
}
O
obscuren 已提交
421

Z
zelig 已提交
422
func unlockAccount(ctx *cli.Context, am *accounts.Manager, account string) (passphrase string) {
423 424
	var err error
	// Load startup keys. XXX we are going to need a different format
425

426
	if !((len(account) == 40) || (len(account) == 42)) { // with or without 0x
427 428
		utils.Fatalf("Invalid account address '%s'", account)
	}
429 430 431
	// Attempt to unlock the account 3 times
	attempts := 3
	for tries := 0; tries < attempts; tries++ {
432
		msg := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", account, tries+1, attempts)
433 434 435 436 437 438
		passphrase = getPassPhrase(ctx, msg, false)
		err = am.Unlock(common.HexToAddress(account), passphrase)
		if err == nil {
			break
		}
	}
439 440
	if err != nil {
		utils.Fatalf("Unlock account failed '%v'", err)
441
	}
442
	fmt.Printf("Account '%s' unlocked.\n", account)
Z
zelig 已提交
443 444 445 446
	return
}

func startEth(ctx *cli.Context, eth *eth.Ethereum) {
447
	// Start Ethereum itself
448

Z
zelig 已提交
449 450 451 452
	utils.StartEthereum(eth)
	am := eth.AccountManager()

	account := ctx.GlobalString(utils.UnlockedAccountFlag.Name)
453 454 455 456 457 458 459 460 461
	accounts := strings.Split(account, " ")
	for _, account := range accounts {
		if len(account) > 0 {
			if account == "primary" {
				primaryAcc, err := am.Primary()
				if err != nil {
					utils.Fatalf("no primary account: %v", err)
				}
				account = primaryAcc.Hex()
462
			}
463
			unlockAccount(ctx, am, account)
Z
zelig 已提交
464
		}
Z
zelig 已提交
465
	}
466
	// Start auxiliary services if enabled.
B
Bas van Kervel 已提交
467 468 469 470 471
	if !ctx.GlobalBool(utils.IPCDisabledFlag.Name) {
		if err := utils.StartIPC(eth, ctx); err != nil {
			utils.Fatalf("Error string IPC: %v", err)
		}
	}
472
	if ctx.GlobalBool(utils.RPCEnabledFlag.Name) {
473 474 475
		if err := utils.StartRPC(eth, ctx); err != nil {
			utils.Fatalf("Error starting RPC: %v", err)
		}
476 477
	}
	if ctx.GlobalBool(utils.MiningEnabledFlag.Name) {
478
		if err := eth.StartMining(ctx.GlobalInt(utils.MinerThreadsFlag.Name)); err != nil {
479 480
			utils.Fatalf("%v", err)
		}
481 482
	}
}
O
Merge  
obscuren 已提交
483

F
Felix Lange 已提交
484
func accountList(ctx *cli.Context) {
485
	am := utils.MakeAccountManager(ctx)
F
Felix Lange 已提交
486 487 488 489
	accts, err := am.Accounts()
	if err != nil {
		utils.Fatalf("Could not list accounts: %v", err)
	}
490 491 492 493
	name := "Primary"
	for i, acct := range accts {
		fmt.Printf("%s #%d: %x\n", name, i, acct)
		name = "Account"
F
Felix Lange 已提交
494 495 496
	}
}

497
func getPassPhrase(ctx *cli.Context, desc string, confirmation bool) (passphrase string) {
498 499 500
	passfile := ctx.GlobalString(utils.PasswordFileFlag.Name)
	if len(passfile) == 0 {
		fmt.Println(desc)
501
		auth, err := utils.PromptPassword("Passphrase: ", true)
502 503 504 505
		if err != nil {
			utils.Fatalf("%v", err)
		}
		if confirmation {
506
			confirm, err := utils.PromptPassword("Repeat Passphrase: ", false)
Z
zelig 已提交
507 508 509
			if err != nil {
				utils.Fatalf("%v", err)
			}
510 511
			if auth != confirm {
				utils.Fatalf("Passphrases did not match.")
Z
zelig 已提交
512
			}
513 514
		}
		passphrase = auth
Z
zelig 已提交
515

516 517 518 519
	} else {
		passbytes, err := ioutil.ReadFile(passfile)
		if err != nil {
			utils.Fatalf("Unable to read password file '%s': %v", passfile, err)
520
		}
521
		passphrase = string(passbytes)
F
Felix Lange 已提交
522
	}
Z
zelig 已提交
523 524 525 526
	return
}

func accountCreate(ctx *cli.Context) {
527
	am := utils.MakeAccountManager(ctx)
528
	passphrase := getPassPhrase(ctx, "Your new account is locked with a password. Please give a password. Do not forget this password.", true)
529
	acct, err := am.NewAccount(passphrase)
F
Felix Lange 已提交
530 531 532
	if err != nil {
		utils.Fatalf("Could not create the account: %v", err)
	}
Z
zelig 已提交
533 534 535
	fmt.Printf("Address: %x\n", acct)
}

536 537 538 539 540 541 542 543 544 545
func importWallet(ctx *cli.Context) {
	keyfile := ctx.Args().First()
	if len(keyfile) == 0 {
		utils.Fatalf("keyfile must be given as argument")
	}
	keyJson, err := ioutil.ReadFile(keyfile)
	if err != nil {
		utils.Fatalf("Could not read wallet file: %v", err)
	}

546
	am := utils.MakeAccountManager(ctx)
547 548 549 550 551 552 553 554 555
	passphrase := getPassPhrase(ctx, "", false)

	acct, err := am.ImportPreSaleKey(keyJson, passphrase)
	if err != nil {
		utils.Fatalf("Could not create the account: %v", err)
	}
	fmt.Printf("Address: %x\n", acct)
}

Z
zelig 已提交
556 557 558 559 560
func accountImport(ctx *cli.Context) {
	keyfile := ctx.Args().First()
	if len(keyfile) == 0 {
		utils.Fatalf("keyfile must be given as argument")
	}
561
	am := utils.MakeAccountManager(ctx)
562
	passphrase := getPassPhrase(ctx, "Your new account is locked with a password. Please give a password. Do not forget this password.", true)
Z
zelig 已提交
563 564 565 566 567 568 569
	acct, err := am.Import(keyfile, passphrase)
	if err != nil {
		utils.Fatalf("Could not create the account: %v", err)
	}
	fmt.Printf("Address: %x\n", acct)
}

570
func makedag(ctx *cli.Context) {
571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596
	args := ctx.Args()
	wrongArgs := func() {
		utils.Fatalf(`Usage: geth makedag <block number> <outputdir>`)
	}
	switch {
	case len(args) == 2:
		blockNum, err := strconv.ParseUint(args[0], 0, 64)
		dir := args[1]
		if err != nil {
			wrongArgs()
		} else {
			dir = filepath.Clean(dir)
			// seems to require a trailing slash
			if !strings.HasSuffix(dir, "/") {
				dir = dir + "/"
			}
			_, err = ioutil.ReadDir(dir)
			if err != nil {
				utils.Fatalf("Can't find dir")
			}
			fmt.Println("making DAG, this could take awhile...")
			ethash.MakeDAG(blockNum, dir)
		}
	default:
		wrongArgs()
	}
597 598
}

599
func version(c *cli.Context) {
600 601 602 603 604 605 606 607 608 609 610
	fmt.Println(ClientIdentifier)
	fmt.Println("Version:", Version)
	if gitCommit != "" {
		fmt.Println("Git Commit:", gitCommit)
	}
	fmt.Println("Protocol Version:", c.GlobalInt(utils.ProtocolVersionFlag.Name))
	fmt.Println("Network Id:", c.GlobalInt(utils.NetworkIdFlag.Name))
	fmt.Println("Go Version:", runtime.Version())
	fmt.Println("OS:", runtime.GOOS)
	fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH"))
	fmt.Printf("GOROOT=%s\n", runtime.GOROOT())
O
obscuren 已提交
611
}