main.go 13.7 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"
O
obscuren 已提交
41 42
	"github.com/mattn/go-colorable"
	"github.com/mattn/go-isatty"
43 44
)

Z
zelig 已提交
45
const (
O
obscuren 已提交
46
	ClientIdentifier = "Geth"
O
obscuren 已提交
47
	Version          = "0.9.29"
Z
zelig 已提交
48 49
)

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

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

	app = utils.NewApp(Version, "the go-ethereum command line interface")
64 65 66
	app.Action = run
	app.HideVersion = true // we have a command to print the version
	app.Commands = []cli.Command{
67 68 69 70 71 72
		blocktestCommand,
		importCommand,
		exportCommand,
		upgradedbCommand,
		removedbCommand,
		dumpCommand,
73 74 75 76 77 78 79 80 81 82 83
		{
			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.
`,
		},
84 85 86 87 88 89 90 91
		{
			Action: version,
			Name:   "version",
			Usage:  "print ethereum version numbers",
			Description: `
The output of this command is supposed to be machine-readable.
`,
		},
92 93

		{
94 95
			Name:  "wallet",
			Usage: "ethereum presale wallet",
96 97 98 99 100 101 102
			Subcommands: []cli.Command{
				{
					Action: importWallet,
					Name:   "import",
					Usage:  "import ethereum presale wallet",
				},
			},
Z
zelig 已提交
103 104 105 106 107 108 109 110 111
			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 已提交
112 113 114 115
		{
			Action: accountList,
			Name:   "account",
			Usage:  "manage accounts",
116 117 118 119 120
			Description: `

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

121
'            help' shows a list of subcommands or help for one subcommand.
122

123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
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 已提交
140 141 142 143 144 145 146 147 148 149
			Subcommands: []cli.Command{
				{
					Action: accountList,
					Name:   "list",
					Usage:  "print account addresses",
				},
				{
					Action: accountCreate,
					Name:   "new",
					Usage:  "create a new account",
Z
zelig 已提交
150 151 152 153
					Description: `

    ethereum account new

154 155 156 157 158 159
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 已提交
160 161 162 163
For non-interactive use the passphrase can be specified with the --password flag:

    ethereum --password <passwordfile> account new

164 165
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 已提交
166 167 168 169 170 171 172 173 174 175
					`,
				},
				{
					Action: accountImport,
					Name:   "import",
					Usage:  "import a private key into a new account",
					Description: `

    ethereum account import <keyfile>

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

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

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

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

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

187
    ethereum --password <passwordfile> account import <keyfile>
Z
zelig 已提交
188 189

Note:
Z
zelig 已提交
190
As you can directly copy your encrypted accounts to another ethereum instance,
191
this import mechanism is not needed when you transfer an account between
Z
zelig 已提交
192
nodes.
Z
zelig 已提交
193
					`,
F
Felix Lange 已提交
194 195 196
				},
			},
		},
197
		{
Z
CLI:  
zelig 已提交
198 199
			Action: console,
			Name:   "console",
O
obscuren 已提交
200
			Usage:  `Geth Console: interactive JavaScript environment`,
Z
CLI:  
zelig 已提交
201
			Description: `
O
obscuren 已提交
202
The Geth console is an interactive shell for the JavaScript runtime environment
203 204
which exposes a node admin interface as well as the Ðapp JavaScript API.
See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console
Z
CLI:  
zelig 已提交
205 206 207 208
`,
		},
		{
			Action: execJSFiles,
209
			Name:   "js",
O
obscuren 已提交
210
			Usage:  `executes the given JavaScript files in the Geth JavaScript VM`,
211
			Description: `
212
The JavaScript VM exposes a node admin interface as well as the Ðapp
Z
zelig 已提交
213
JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console
214 215 216 217
`,
		},
	}
	app.Flags = []cli.Flag{
218
		utils.IdentityFlag,
219
		utils.UnlockedAccountFlag,
Z
zelig 已提交
220
		utils.PasswordFileFlag,
O
obscuren 已提交
221
		utils.GenesisNonceFlag,
222 223
		utils.BootnodesFlag,
		utils.DataDirFlag,
224
		utils.BlockchainVersionFlag,
Z
CLI:  
zelig 已提交
225
		utils.JSpathFlag,
226 227
		utils.ListenPortFlag,
		utils.MaxPeersFlag,
228
		utils.MaxPendingPeersFlag,
Z
zelig 已提交
229
		utils.EtherbaseFlag,
230
		utils.GasPriceFlag,
231 232
		utils.MinerThreadsFlag,
		utils.MiningEnabledFlag,
233
		utils.AutoDAGFlag,
234
		utils.NATFlag,
235
		utils.NatspecEnabledFlag,
236
		utils.NoDiscoverFlag,
237 238 239 240 241
		utils.NodeKeyFileFlag,
		utils.NodeKeyHexFlag,
		utils.RPCEnabledFlag,
		utils.RPCListenAddrFlag,
		utils.RPCPortFlag,
B
Bas van Kervel 已提交
242 243 244
		utils.IPCDisabledFlag,
		utils.IPCApiFlag,
		utils.IPCPathFlag,
245
		utils.WhisperEnabledFlag,
246
		utils.VMDebugFlag,
Z
zelig 已提交
247 248
		utils.ProtocolVersionFlag,
		utils.NetworkIdFlag,
249
		utils.RPCCORSDomainFlag,
250
		utils.VerbosityFlag,
O
obscuren 已提交
251 252
		utils.BacktraceAtFlag,
		utils.LogToStdErrFlag,
O
obscuren 已提交
253 254 255
		utils.LogVModuleFlag,
		utils.LogFileFlag,
		utils.LogJSONFlag,
256
		utils.PProfEanbledFlag,
257
		utils.PProfPortFlag,
258
		utils.SolcPathFlag,
259
	}
260
	app.Before = func(ctx *cli.Context) error {
261
		utils.SetupLogger(ctx)
262
		if ctx.GlobalBool(utils.PProfEanbledFlag.Name) {
263 264 265
			utils.StartPProf(ctx)
		}
		return nil
266 267
	}
}
O
obscuren 已提交
268

269 270 271 272 273 274 275 276
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 已提交
277

278
func run(ctx *cli.Context) {
279
	utils.HandleInterrupt()
280
	cfg := utils.MakeEthConfig(ClientIdentifier, nodeNameVersion, ctx)
281
	ethereum, err := eth.New(cfg)
282
	if err != nil {
283 284 285
		utils.Fatalf("%v", err)
	}

286
	startEth(ctx, ethereum)
287
	// this blocks the thread
288
	ethereum.WaitForShutdown()
289
}
290

Z
CLI:  
zelig 已提交
291
func console(ctx *cli.Context) {
292 293 294 295 296 297 298 299
	// 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
		}
	}

300
	cfg := utils.MakeEthConfig(ClientIdentifier, nodeNameVersion, ctx)
301
	ethereum, err := eth.New(cfg)
302
	if err != nil {
303 304 305
		utils.Fatalf("%v", err)
	}

306
	startEth(ctx, ethereum)
307 308 309 310
	repl := newJSRE(
		ethereum,
		ctx.String(utils.JSpathFlag.Name),
		ctx.GlobalString(utils.RPCCORSDomainFlag.Name),
B
Bas van Kervel 已提交
311
		utils.IpcSocketPath(ctx),
312 313 314
		true,
		nil,
	)
Z
CLI:  
zelig 已提交
315 316 317 318 319 320 321
	repl.interactive()

	ethereum.Stop()
	ethereum.WaitForShutdown()
}

func execJSFiles(ctx *cli.Context) {
322
	cfg := utils.MakeEthConfig(ClientIdentifier, nodeNameVersion, ctx)
Z
CLI:  
zelig 已提交
323 324 325
	ethereum, err := eth.New(cfg)
	if err != nil {
		utils.Fatalf("%v", err)
O
obscuren 已提交
326
	}
Z
CLI:  
zelig 已提交
327 328

	startEth(ctx, ethereum)
329 330 331 332
	repl := newJSRE(
		ethereum,
		ctx.String(utils.JSpathFlag.Name),
		ctx.GlobalString(utils.RPCCORSDomainFlag.Name),
B
Bas van Kervel 已提交
333
		utils.IpcSocketPath(ctx),
334 335 336
		false,
		nil,
	)
Z
CLI:  
zelig 已提交
337 338 339 340
	for _, file := range ctx.Args() {
		repl.exec(file)
	}

341 342
	ethereum.Stop()
	ethereum.WaitForShutdown()
343
}
O
obscuren 已提交
344

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

349
	if !((len(account) == 40) || (len(account) == 42)) { // with or without 0x
350 351
		utils.Fatalf("Invalid account address '%s'", account)
	}
352 353 354
	// Attempt to unlock the account 3 times
	attempts := 3
	for tries := 0; tries < attempts; tries++ {
355
		msg := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", account, tries+1, attempts)
356 357 358 359 360 361
		passphrase = getPassPhrase(ctx, msg, false)
		err = am.Unlock(common.HexToAddress(account), passphrase)
		if err == nil {
			break
		}
	}
362 363
	if err != nil {
		utils.Fatalf("Unlock account failed '%v'", err)
364
	}
365
	fmt.Printf("Account '%s' unlocked.\n", account)
Z
zelig 已提交
366 367 368 369
	return
}

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

Z
zelig 已提交
372 373 374 375
	utils.StartEthereum(eth)
	am := eth.AccountManager()

	account := ctx.GlobalString(utils.UnlockedAccountFlag.Name)
376 377 378 379 380 381 382 383 384
	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()
385
			}
386
			unlockAccount(ctx, am, account)
Z
zelig 已提交
387
		}
Z
zelig 已提交
388
	}
389
	// Start auxiliary services if enabled.
B
Bas van Kervel 已提交
390 391 392 393 394
	if !ctx.GlobalBool(utils.IPCDisabledFlag.Name) {
		if err := utils.StartIPC(eth, ctx); err != nil {
			utils.Fatalf("Error string IPC: %v", err)
		}
	}
395
	if ctx.GlobalBool(utils.RPCEnabledFlag.Name) {
396 397 398
		if err := utils.StartRPC(eth, ctx); err != nil {
			utils.Fatalf("Error starting RPC: %v", err)
		}
399 400
	}
	if ctx.GlobalBool(utils.MiningEnabledFlag.Name) {
401
		if err := eth.StartMining(ctx.GlobalInt(utils.MinerThreadsFlag.Name)); err != nil {
402 403
			utils.Fatalf("%v", err)
		}
404 405
	}
}
O
Merge  
obscuren 已提交
406

F
Felix Lange 已提交
407
func accountList(ctx *cli.Context) {
408
	am := utils.MakeAccountManager(ctx)
F
Felix Lange 已提交
409 410 411 412
	accts, err := am.Accounts()
	if err != nil {
		utils.Fatalf("Could not list accounts: %v", err)
	}
413 414 415 416
	name := "Primary"
	for i, acct := range accts {
		fmt.Printf("%s #%d: %x\n", name, i, acct)
		name = "Account"
F
Felix Lange 已提交
417 418 419
	}
}

420
func getPassPhrase(ctx *cli.Context, desc string, confirmation bool) (passphrase string) {
421 422 423
	passfile := ctx.GlobalString(utils.PasswordFileFlag.Name)
	if len(passfile) == 0 {
		fmt.Println(desc)
424
		auth, err := utils.PromptPassword("Passphrase: ", true)
425 426 427 428
		if err != nil {
			utils.Fatalf("%v", err)
		}
		if confirmation {
429
			confirm, err := utils.PromptPassword("Repeat Passphrase: ", false)
Z
zelig 已提交
430 431 432
			if err != nil {
				utils.Fatalf("%v", err)
			}
433 434
			if auth != confirm {
				utils.Fatalf("Passphrases did not match.")
Z
zelig 已提交
435
			}
436 437
		}
		passphrase = auth
Z
zelig 已提交
438

439 440 441 442
	} else {
		passbytes, err := ioutil.ReadFile(passfile)
		if err != nil {
			utils.Fatalf("Unable to read password file '%s': %v", passfile, err)
443
		}
444
		passphrase = string(passbytes)
F
Felix Lange 已提交
445
	}
Z
zelig 已提交
446 447 448 449
	return
}

func accountCreate(ctx *cli.Context) {
450
	am := utils.MakeAccountManager(ctx)
451
	passphrase := getPassPhrase(ctx, "Your new account is locked with a password. Please give a password. Do not forget this password.", true)
452
	acct, err := am.NewAccount(passphrase)
F
Felix Lange 已提交
453 454 455
	if err != nil {
		utils.Fatalf("Could not create the account: %v", err)
	}
Z
zelig 已提交
456 457 458
	fmt.Printf("Address: %x\n", acct)
}

459 460 461 462 463 464 465 466 467 468
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)
	}

469
	am := utils.MakeAccountManager(ctx)
470 471 472 473 474 475 476 477 478
	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 已提交
479 480 481 482 483
func accountImport(ctx *cli.Context) {
	keyfile := ctx.Args().First()
	if len(keyfile) == 0 {
		utils.Fatalf("keyfile must be given as argument")
	}
484
	am := utils.MakeAccountManager(ctx)
485
	passphrase := getPassPhrase(ctx, "Your new account is locked with a password. Please give a password. Do not forget this password.", true)
Z
zelig 已提交
486 487 488 489 490 491 492
	acct, err := am.Import(keyfile, passphrase)
	if err != nil {
		utils.Fatalf("Could not create the account: %v", err)
	}
	fmt.Printf("Address: %x\n", acct)
}

493
func makedag(ctx *cli.Context) {
494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519
	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()
	}
520 521
}

522
func version(c *cli.Context) {
523 524 525 526 527 528 529 530 531 532 533
	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 已提交
534
}