main.go 16.3 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 (
F
Felix Lange 已提交
24
	"bufio"
O
obscuren 已提交
25
	"fmt"
26
	"io/ioutil"
O
obscuren 已提交
27
	"os"
O
obscuren 已提交
28
	"runtime"
29
	"strconv"
O
obscuren 已提交
30
	"time"
O
obscuren 已提交
31

O
obscuren 已提交
32 33
	"path"

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"
39
	"github.com/ethereum/go-ethereum/core"
O
bump  
obscuren 已提交
40
	"github.com/ethereum/go-ethereum/core/state"
41
	"github.com/ethereum/go-ethereum/core/types"
O
obscuren 已提交
42
	"github.com/ethereum/go-ethereum/eth"
O
obscuren 已提交
43
	"github.com/ethereum/go-ethereum/logger"
F
Felix Lange 已提交
44
	"github.com/peterh/liner"
45
)
O
obscuren 已提交
46
import _ "net/http/pprof"
47

Z
zelig 已提交
48
const (
O
obscuren 已提交
49
	ClientIdentifier = "Geth"
O
obscuren 已提交
50
	Version          = "0.9.11"
Z
zelig 已提交
51 52
)

O
obscuren 已提交
53
var app = utils.NewApp(Version, "the go-ethereum command line interface")
54

55 56 57 58
func init() {
	app.Action = run
	app.HideVersion = true // we have a command to print the version
	app.Commands = []cli.Command{
59
		blocktestCmd,
60 61 62 63 64 65 66 67 68 69 70
		{
			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.
`,
		},
71 72 73 74 75 76 77 78
		{
			Action: version,
			Name:   "version",
			Usage:  "print ethereum version numbers",
			Description: `
The output of this command is supposed to be machine-readable.
`,
		},
79 80

		{
81 82
			Name:  "wallet",
			Usage: "ethereum presale wallet",
83 84 85 86 87 88 89 90
			Subcommands: []cli.Command{
				{
					Action: importWallet,
					Name:   "import",
					Usage:  "import ethereum presale wallet",
				},
			},
		},
F
Felix Lange 已提交
91 92 93 94
		{
			Action: accountList,
			Name:   "account",
			Usage:  "manage accounts",
95 96 97 98 99
			Description: `

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

100 101
'account help' shows a list of subcommands or help for one subcommand.

102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
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 已提交
119 120 121 122 123 124 125 126 127 128
			Subcommands: []cli.Command{
				{
					Action: accountList,
					Name:   "list",
					Usage:  "print account addresses",
				},
				{
					Action: accountCreate,
					Name:   "new",
					Usage:  "create a new account",
Z
zelig 已提交
129 130 131 132
					Description: `

    ethereum account new

133 134 135 136 137 138
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 已提交
139 140 141 142
For non-interactive use the passphrase can be specified with the --password flag:

    ethereum --password <passwordfile> account new

143 144
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 已提交
145 146 147 148 149 150 151 152 153 154
					`,
				},
				{
					Action: accountImport,
					Name:   "import",
					Usage:  "import a private key into a new account",
					Description: `

    ethereum account import <keyfile>

155 156 157
Imports an unencrypted private key from <keyfile> and creates a new account.
Prints the address.

158
The keyfile is assumed to contain an unencrypted private key in hexadecimal format.
Z
zelig 已提交
159 160 161

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

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

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

166
    ethereum --password <passwordfile> account import <keyfile>
Z
zelig 已提交
167 168

Note:
Z
zelig 已提交
169
As you can directly copy your encrypted accounts to another ethereum instance,
170
this import mechanism is not needed when you transfer an account between
Z
zelig 已提交
171
nodes.
Z
zelig 已提交
172
					`,
F
Felix Lange 已提交
173 174 175
				},
			},
		},
176 177 178 179 180 181 182 183 184 185
		{
			Action: dump,
			Name:   "dump",
			Usage:  `dump a specific block from storage`,
			Description: `
The arguments are interpreted as block numbers or hashes.
Use "ethereum dump 0" to dump the genesis block.
`,
		},
		{
Z
CLI:  
zelig 已提交
186 187
			Action: console,
			Name:   "console",
O
obscuren 已提交
188
			Usage:  `Geth Console: interactive JavaScript environment`,
Z
CLI:  
zelig 已提交
189
			Description: `
O
obscuren 已提交
190
The Geth console is an interactive shell for the JavaScript runtime environment
191 192
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 已提交
193 194 195 196
`,
		},
		{
			Action: execJSFiles,
197
			Name:   "js",
O
obscuren 已提交
198
			Usage:  `executes the given JavaScript files in the Geth JavaScript VM`,
199
			Description: `
200
The JavaScript VM exposes a node admin interface as well as the Ðapp
Z
zelig 已提交
201
JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console
202 203 204 205 206 207 208
`,
		},
		{
			Action: importchain,
			Name:   "import",
			Usage:  `import a blockchain file`,
		},
209 210 211 212 213
		{
			Action: exportchain,
			Name:   "export",
			Usage:  `export blockchain into file`,
		},
214 215 216 217 218
		{
			Action: upgradeDb,
			Name:   "upgradedb",
			Usage:  "upgrade chainblock database",
		},
219 220
	}
	app.Flags = []cli.Flag{
221
		utils.IdentityFlag,
222
		utils.UnlockedAccountFlag,
Z
zelig 已提交
223
		utils.PasswordFileFlag,
224 225
		utils.BootnodesFlag,
		utils.DataDirFlag,
226
		utils.BlockchainVersionFlag,
Z
CLI:  
zelig 已提交
227
		utils.JSpathFlag,
228 229
		utils.ListenPortFlag,
		utils.MaxPeersFlag,
Z
zelig 已提交
230
		utils.EtherbaseFlag,
231 232 233
		utils.MinerThreadsFlag,
		utils.MiningEnabledFlag,
		utils.NATFlag,
234
		utils.NatspecEnabledFlag,
235 236 237 238 239
		utils.NodeKeyFileFlag,
		utils.NodeKeyHexFlag,
		utils.RPCEnabledFlag,
		utils.RPCListenAddrFlag,
		utils.RPCPortFlag,
240
		utils.WhisperEnabledFlag,
241
		utils.VMDebugFlag,
Z
zelig 已提交
242 243
		utils.ProtocolVersionFlag,
		utils.NetworkIdFlag,
244
		utils.RPCCORSDomainFlag,
O
obscuren 已提交
245
		utils.LogLevelFlag,
O
obscuren 已提交
246 247
		utils.BacktraceAtFlag,
		utils.LogToStdErrFlag,
O
obscuren 已提交
248 249 250
		utils.LogVModuleFlag,
		utils.LogFileFlag,
		utils.LogJSONFlag,
251
		utils.PProfEanbledFlag,
252
		utils.PProfPortFlag,
253
	}
254
	app.Before = func(ctx *cli.Context) error {
255
		if ctx.GlobalBool(utils.PProfEanbledFlag.Name) {
256 257 258
			utils.StartPProf(ctx)
		}
		return nil
259 260 261 262 263 264
	}

	// missing:
	// flag.StringVar(&ConfigFile, "conf", defaultConfigFile, "config file")
	// flag.BoolVar(&DiffTool, "difftool", false, "creates output for diff'ing. Sets LogLevel=0")
	// flag.StringVar(&DiffType, "diff", "all", "sets the level of diff output [vm, all]. Has no effect if difftool=false")
265

266
}
O
obscuren 已提交
267

268
func main() {
269
	fmt.Printf("\n\n█   █      █                       █           █  █           ███              █   █\n█ █ █ ███  █  ███ ███ ███ ███     ███ ███     ███ ███ ███     █   ███ ███ ██  ███     ███ ███ \n█ █ █ ██   █  █   █ █ ███ ██       █  █ █      █  █ █ ██      ██  █   █ █ █ █  █   █  ██  █   \n█████ ███  ██ ███ ███ █ █ ███      ██ ███      ██ █ █ ███     █   █   ███ █ █  ██  ██ ███ █   \n\n")
270 271 272 273 274 275 276
	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 281
	cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx)
	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
	cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx)
	ethereum, err := eth.New(cfg)
294
	if err != nil {
295 296 297
		utils.Fatalf("%v", err)
	}

298
	startEth(ctx, ethereum)
299
	repl := newJSRE(ethereum, ctx.String(utils.JSpathFlag.Name), true)
Z
CLI:  
zelig 已提交
300 301 302 303 304 305 306 307 308 309 310
	repl.interactive()

	ethereum.Stop()
	ethereum.WaitForShutdown()
}

func execJSFiles(ctx *cli.Context) {
	cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx)
	ethereum, err := eth.New(cfg)
	if err != nil {
		utils.Fatalf("%v", err)
O
obscuren 已提交
311
	}
Z
CLI:  
zelig 已提交
312 313

	startEth(ctx, ethereum)
314
	repl := newJSRE(ethereum, ctx.String(utils.JSpathFlag.Name), false)
Z
CLI:  
zelig 已提交
315 316 317 318
	for _, file := range ctx.Args() {
		repl.exec(file)
	}

319 320
	ethereum.Stop()
	ethereum.WaitForShutdown()
321
}
O
obscuren 已提交
322

Z
zelig 已提交
323
func unlockAccount(ctx *cli.Context, am *accounts.Manager, account string) (passphrase string) {
324 325 326 327 328 329 330 331 332 333 334
	var err error
	// Load startup keys. XXX we are going to need a different format
	// Attempt to unlock the account
	passphrase = getPassPhrase(ctx, "", false)
	accbytes := common.FromHex(account)
	if len(accbytes) == 0 {
		utils.Fatalf("Invalid account address '%s'", account)
	}
	err = am.Unlock(accbytes, passphrase)
	if err != nil {
		utils.Fatalf("Unlock account failed '%v'", err)
335
	}
Z
zelig 已提交
336 337 338 339
	return
}

func startEth(ctx *cli.Context, eth *eth.Ethereum) {
340
	// Start Ethereum itself
Z
zelig 已提交
341 342 343 344 345
	utils.StartEthereum(eth)
	am := eth.AccountManager()

	account := ctx.GlobalString(utils.UnlockedAccountFlag.Name)
	if len(account) > 0 {
Z
zelig 已提交
346 347
		if account == "primary" {
			accbytes, err := am.Primary()
348
			if err != nil {
Z
zelig 已提交
349
				utils.Fatalf("no primary account: %v", err)
350 351
			}
			account = common.ToHex(accbytes)
Z
zelig 已提交
352
		}
Z
zelig 已提交
353 354
		unlockAccount(ctx, am, account)
	}
355
	// Start auxiliary services if enabled.
356
	if ctx.GlobalBool(utils.RPCEnabledFlag.Name) {
357
		utils.StartRPC(eth, ctx)
358 359
	}
	if ctx.GlobalBool(utils.MiningEnabledFlag.Name) {
360
		eth.StartMining()
361 362
	}
}
O
Merge  
obscuren 已提交
363

F
Felix Lange 已提交
364 365 366 367 368 369
func accountList(ctx *cli.Context) {
	am := utils.GetAccountManager(ctx)
	accts, err := am.Accounts()
	if err != nil {
		utils.Fatalf("Could not list accounts: %v", err)
	}
370 371 372 373
	name := "Primary"
	for i, acct := range accts {
		fmt.Printf("%s #%d: %x\n", name, i, acct)
		name = "Account"
F
Felix Lange 已提交
374 375 376
	}
}

377
func getPassPhrase(ctx *cli.Context, desc string, confirmation bool) (passphrase string) {
378 379 380 381 382 383 384 385 386
	passfile := ctx.GlobalString(utils.PasswordFileFlag.Name)
	if len(passfile) == 0 {
		fmt.Println(desc)
		auth, err := readPassword("Passphrase: ", true)
		if err != nil {
			utils.Fatalf("%v", err)
		}
		if confirmation {
			confirm, err := readPassword("Repeat Passphrase: ", false)
Z
zelig 已提交
387 388 389
			if err != nil {
				utils.Fatalf("%v", err)
			}
390 391
			if auth != confirm {
				utils.Fatalf("Passphrases did not match.")
Z
zelig 已提交
392
			}
393 394
		}
		passphrase = auth
Z
zelig 已提交
395

396 397 398 399
	} else {
		passbytes, err := ioutil.ReadFile(passfile)
		if err != nil {
			utils.Fatalf("Unable to read password file '%s': %v", passfile, err)
400
		}
401
		passphrase = string(passbytes)
F
Felix Lange 已提交
402
	}
Z
zelig 已提交
403 404 405 406 407
	return
}

func accountCreate(ctx *cli.Context) {
	am := utils.GetAccountManager(ctx)
408
	passphrase := getPassPhrase(ctx, "Your new account is locked with a password. Please give a password. Do not forget this password.", true)
409
	acct, err := am.NewAccount(passphrase)
F
Felix Lange 已提交
410 411 412
	if err != nil {
		utils.Fatalf("Could not create the account: %v", err)
	}
Z
zelig 已提交
413 414 415
	fmt.Printf("Address: %x\n", acct)
}

416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435
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)
	}

	am := utils.GetAccountManager(ctx)
	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 已提交
436 437 438 439 440 441
func accountImport(ctx *cli.Context) {
	keyfile := ctx.Args().First()
	if len(keyfile) == 0 {
		utils.Fatalf("keyfile must be given as argument")
	}
	am := utils.GetAccountManager(ctx)
442
	passphrase := getPassPhrase(ctx, "Your new account is locked with a password. Please give a password. Do not forget this password.", true)
Z
zelig 已提交
443 444 445 446 447 448 449
	acct, err := am.Import(keyfile, passphrase)
	if err != nil {
		utils.Fatalf("Could not create the account: %v", err)
	}
	fmt.Printf("Address: %x\n", acct)
}

450 451 452 453
func importchain(ctx *cli.Context) {
	if len(ctx.Args()) != 1 {
		utils.Fatalf("This command requires an argument.")
	}
454 455 456 457 458 459 460 461 462 463

	cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx)
	cfg.SkipBcVersionCheck = true

	ethereum, err := eth.New(cfg)
	if err != nil {
		utils.Fatalf("%v\n", err)
	}

	chainmgr := ethereum.ChainManager()
464
	start := time.Now()
465
	err = utils.ImportChain(chainmgr, ctx.Args().First())
O
obscuren 已提交
466
	if err != nil {
467
		utils.Fatalf("Import error: %v\n", err)
O
obscuren 已提交
468
	}
469 470 471 472 473 474

	// force database flush
	ethereum.BlockDb().Close()
	ethereum.StateDb().Close()
	ethereum.ExtraDb().Close()

475
	fmt.Printf("Import done in %v", time.Since(start))
476

477 478 479 480 481 482 483
	return
}

func exportchain(ctx *cli.Context) {
	if len(ctx.Args()) != 1 {
		utils.Fatalf("This command requires an argument.")
	}
484 485 486 487 488 489 490 491 492 493

	cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx)
	cfg.SkipBcVersionCheck = true

	ethereum, err := eth.New(cfg)
	if err != nil {
		utils.Fatalf("%v\n", err)
	}

	chainmgr := ethereum.ChainManager()
494
	start := time.Now()
495
	err = utils.ExportChain(chainmgr, ctx.Args().First())
496 497 498 499
	if err != nil {
		utils.Fatalf("Export error: %v\n", err)
	}
	fmt.Printf("Export done in %v", time.Since(start))
500 501
	return
}
O
Merge  
obscuren 已提交
502

503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556
func upgradeDb(ctx *cli.Context) {
	fmt.Println("Upgrade blockchain DB")

	cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx)
	cfg.SkipBcVersionCheck = true

	ethereum, err := eth.New(cfg)
	if err != nil {
		utils.Fatalf("%v\n", err)
	}

	v, _ := ethereum.BlockDb().Get([]byte("BlockchainVersion"))
	bcVersion := int(common.NewValue(v).Uint())

	if bcVersion == 0 {
		bcVersion = core.BlockChainVersion
	}

	filename := fmt.Sprintf("blockchain_%d_%s.chain", bcVersion, time.Now().Format("2006-01-02_15:04:05"))
	exportFile := path.Join(ctx.GlobalString(utils.DataDirFlag.Name), filename)

	err = utils.ExportChain(ethereum.ChainManager(), exportFile)
	if err != nil {
		utils.Fatalf("Unable to export chain for reimport %s\n", err)
	}

	ethereum.BlockDb().Close()
	ethereum.StateDb().Close()
	ethereum.ExtraDb().Close()

	os.RemoveAll(path.Join(ctx.GlobalString(utils.DataDirFlag.Name), "blockchain"))

	ethereum, err = eth.New(cfg)
	if err != nil {
		utils.Fatalf("%v\n", err)
	}

	ethereum.BlockDb().Put([]byte("BlockchainVersion"), common.NewValue(core.BlockChainVersion).Bytes())

	err = utils.ImportChain(ethereum.ChainManager(), exportFile)
	if err != nil {
		utils.Fatalf("Import error %v (a backup is made in %s, use the import command to import it)\n", err, exportFile)
	}

	// force database flush
	ethereum.BlockDb().Close()
	ethereum.StateDb().Close()
	ethereum.ExtraDb().Close()

	os.Remove(exportFile)

	fmt.Println("Import finished")
}

557
func dump(ctx *cli.Context) {
558
	chainmgr, _, stateDb := utils.GetChain(ctx)
559
	for _, arg := range ctx.Args() {
O
obscuren 已提交
560
		var block *types.Block
561
		if hashish(arg) {
O
obscuren 已提交
562
			block = chainmgr.GetBlock(common.HexToHash(arg))
O
obscuren 已提交
563
		} else {
564
			num, _ := strconv.Atoi(arg)
565
			block = chainmgr.GetBlockByNumber(uint64(num))
O
obscuren 已提交
566 567 568
		}
		if block == nil {
			fmt.Println("{}")
569 570
			utils.Fatalf("block not found")
		} else {
571
			statedb := state.New(block.Root(), stateDb)
572
			fmt.Printf("%s\n", statedb.Dump())
O
obscuren 已提交
573
		}
O
obscuren 已提交
574
	}
575
}
O
obscuren 已提交
576

577 578 579 580
func makedag(ctx *cli.Context) {
	chain, _, _ := utils.GetChain(ctx)
	pow := ethash.New(chain)
	fmt.Println("making cache")
O
obscuren 已提交
581
	pow.UpdateCache(0, true)
582 583 584 585
	fmt.Println("making DAG")
	pow.UpdateDAG()
}

586
func version(c *cli.Context) {
Z
zelig 已提交
587 588 589 590 591 592
	fmt.Printf(`%v
Version: %v
Protocol Version: %d
Network Id: %d
GO: %s
OS: %s
O
obscuren 已提交
593 594
GOPATH=%s
GOROOT=%s
Z
zelig 已提交
595
`, ClientIdentifier, Version, c.GlobalInt(utils.ProtocolVersionFlag.Name), c.GlobalInt(utils.NetworkIdFlag.Name), runtime.Version(), runtime.GOOS, os.Getenv("GOPATH"), runtime.GOROOT())
O
obscuren 已提交
596
}
F
Felix Lange 已提交
597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617

// hashish returns true for strings that look like hashes.
func hashish(x string) bool {
	_, err := strconv.Atoi(x)
	return err != nil
}

func readPassword(prompt string, warnTerm bool) (string, error) {
	if liner.TerminalSupported() {
		lr := liner.NewLiner()
		defer lr.Close()
		return lr.PasswordPrompt(prompt)
	}
	if warnTerm {
		fmt.Println("!! Unsupported terminal, password will be echoed.")
	}
	fmt.Print(prompt)
	input, err := bufio.NewReader(os.Stdin).ReadString('\n')
	fmt.Println()
	return input, err
}