main.go 9.43 KB
Newer Older
obscuren's avatar
obscuren committed
1 2
/*
	This file is part of go-ethereum
Felix Lange's avatar
Felix Lange committed
3

obscuren's avatar
obscuren committed
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 (
24
	"bufio"
obscuren's avatar
obscuren committed
25 26
	"fmt"
	"os"
obscuren's avatar
obscuren committed
27
	"runtime"
28
	"strconv"
29
	"strings"
obscuren's avatar
obscuren committed
30
	"time"
obscuren's avatar
obscuren committed
31

32
	"github.com/codegangsta/cli"
33
	"github.com/ethereum/ethash"
obscuren's avatar
obscuren committed
34
	"github.com/ethereum/go-ethereum/cmd/utils"
35
	"github.com/ethereum/go-ethereum/common"
36
	"github.com/ethereum/go-ethereum/core/types"
obscuren's avatar
obscuren committed
37
	"github.com/ethereum/go-ethereum/eth"
obscuren's avatar
obscuren committed
38
	"github.com/ethereum/go-ethereum/logger"
obscuren's avatar
obscuren committed
39
	"github.com/ethereum/go-ethereum/core/state"
40
	"github.com/peterh/liner"
41 42
)

zelig's avatar
zelig committed
43 44
const (
	ClientIdentifier = "Ethereum(G)"
obscuren's avatar
obscuren committed
45
	Version          = "0.9.2"
zelig's avatar
zelig committed
46 47
)

48 49
var (
	clilogger = logger.NewLogger("CLI")
Felix Lange's avatar
Felix Lange committed
50
	app       = utils.NewApp(Version, "the go-ethereum command line interface")
51
)
52

53 54 55 56
func init() {
	app.Action = run
	app.HideVersion = true // we have a command to print the version
	app.Commands = []cli.Command{
57
		blocktestCmd,
58 59 60 61 62 63 64 65 66 67 68
		{
			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.
`,
		},
69 70 71 72 73 74 75 76
		{
			Action: version,
			Name:   "version",
			Usage:  "print ethereum version numbers",
			Description: `
The output of this command is supposed to be machine-readable.
`,
		},
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
		{
			Action: accountList,
			Name:   "account",
			Usage:  "manage accounts",
			Subcommands: []cli.Command{
				{
					Action: accountList,
					Name:   "list",
					Usage:  "print account addresses",
				},
				{
					Action: accountCreate,
					Name:   "new",
					Usage:  "create a new account",
				},
			},
		},
94 95 96 97 98 99 100 101 102 103
		{
			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.
`,
		},
		{
zelig's avatar
zelig committed
104 105
			Action: console,
			Name:   "console",
106
			Usage:  `Ethereum Console: interactive JavaScript environment`,
zelig's avatar
zelig committed
107
			Description: `
108
Console is an interactive shell for the Ethereum JavaScript runtime environment which exposes a node admin interface as well as the DAPP JavaScript API.
zelig's avatar
zelig committed
109 110 111 112 113
See https://github.com/ethereum/go-ethereum/wiki/Frontier-Console
`,
		},
		{
			Action: execJSFiles,
114
			Name:   "js",
zelig's avatar
zelig committed
115
			Usage:  `executes the given JavaScript files in the Ethereum Frontier JavaScript VM`,
116
			Description: `
117
The Ethereum JavaScript VM exposes a node admin interface as well as the DAPP JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Frontier-Console
118 119 120 121 122 123 124
`,
		},
		{
			Action: importchain,
			Name:   "import",
			Usage:  `import a blockchain file`,
		},
125 126 127 128 129
		{
			Action: exportchain,
			Name:   "export",
			Usage:  `export blockchain into file`,
		},
130 131
	}
	app.Flags = []cli.Flag{
132
		utils.UnlockedAccountFlag,
133 134
		utils.BootnodesFlag,
		utils.DataDirFlag,
zelig's avatar
zelig committed
135
		utils.JSpathFlag,
136 137
		utils.ListenPortFlag,
		utils.LogFileFlag,
138
		utils.LogJSONFlag,
139 140 141 142 143 144 145 146 147 148
		utils.LogLevelFlag,
		utils.MaxPeersFlag,
		utils.MinerThreadsFlag,
		utils.MiningEnabledFlag,
		utils.NATFlag,
		utils.NodeKeyFileFlag,
		utils.NodeKeyHexFlag,
		utils.RPCEnabledFlag,
		utils.RPCListenAddrFlag,
		utils.RPCPortFlag,
149
		utils.UnencryptedKeysFlag,
150
		utils.VMDebugFlag,
zelig's avatar
zelig committed
151 152
		utils.ProtocolVersionFlag,
		utils.NetworkIdFlag,
153 154 155 156 157 158
	}

	// 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")
159

160 161 162 163 164
	// potential subcommands:
	// flag.StringVar(&SecretFile, "import", "", "imports the file given (hex or mnemonic formats)")
	// flag.StringVar(&ExportDir, "export", "", "exports the session keyring to files in the directory given")
	// flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key")
}
obscuren's avatar
obscuren committed
165

166 167 168 169 170 171 172 173
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)
	}
}
zelig's avatar
zelig committed
174

175 176
func run(ctx *cli.Context) {
	fmt.Printf("Welcome to the FRONTIER\n")
177
	utils.HandleInterrupt()
178 179
	cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx)
	ethereum, err := eth.New(cfg)
180
	if err != nil {
181 182 183
		utils.Fatalf("%v", err)
	}

184
	startEth(ctx, ethereum)
185
	// this blocks the thread
186
	ethereum.WaitForShutdown()
187
}
188

zelig's avatar
zelig committed
189
func console(ctx *cli.Context) {
190 191
	cfg := utils.MakeEthConfig(ClientIdentifier, Version, ctx)
	ethereum, err := eth.New(cfg)
192
	if err != nil {
193 194 195
		utils.Fatalf("%v", err)
	}

196
	startEth(ctx, ethereum)
zelig's avatar
zelig committed
197 198 199 200 201 202 203 204 205 206 207 208
	repl := newJSRE(ethereum, ctx.String(utils.JSpathFlag.Name))
	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)
obscuren's avatar
obscuren committed
209
	}
zelig's avatar
zelig committed
210 211 212 213 214 215 216

	startEth(ctx, ethereum)
	repl := newJSRE(ethereum, ctx.String(utils.JSpathFlag.Name))
	for _, file := range ctx.Args() {
		repl.exec(file)
	}

217 218
	ethereum.Stop()
	ethereum.WaitForShutdown()
219
}
obscuren's avatar
obscuren committed
220

221 222
func startEth(ctx *cli.Context, eth *eth.Ethereum) {
	utils.StartEthereum(eth)
223 224 225 226 227 228 229 230

	// Load startup keys. XXX we are going to need a different format
	account := ctx.GlobalString(utils.UnlockedAccountFlag.Name)
	if len(account) > 0 {
		split := strings.Split(account, ":")
		if len(split) != 2 {
			utils.Fatalf("Illegal 'unlock' format (address:password)")
		}
obscuren's avatar
obscuren committed
231
		am := eth.AccountManager()
232
		// Attempt to unlock the account
obscuren's avatar
obscuren committed
233
		err := am.Unlock(common.FromHex(split[0]), split[1])
234 235 236 237
		if err != nil {
			utils.Fatalf("Unlock account failed '%v'", err)
		}
	}
238
	// Start auxiliary services if enabled.
239
	if ctx.GlobalBool(utils.RPCEnabledFlag.Name) {
240
		utils.StartRPC(eth, ctx)
241 242
	}
	if ctx.GlobalBool(utils.MiningEnabledFlag.Name) {
243
		eth.StartMining()
244 245
	}
}
obscuren's avatar
obscuren committed
246

247 248 249 250 251 252 253
func accountList(ctx *cli.Context) {
	am := utils.GetAccountManager(ctx)
	accts, err := am.Accounts()
	if err != nil {
		utils.Fatalf("Could not list accounts: %v", err)
	}
	for _, acct := range accts {
obscuren's avatar
obscuren committed
254
		fmt.Printf("Address: %x\n", acct)
255 256 257 258 259
	}
}

func accountCreate(ctx *cli.Context) {
	am := utils.GetAccountManager(ctx)
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
	passphrase := ""
	if !ctx.GlobalBool(utils.UnencryptedKeysFlag.Name) {
		fmt.Println("The new account will be encrypted with a passphrase.")
		fmt.Println("Please enter a passphrase now.")
		auth, err := readPassword("Passphrase: ", true)
		if err != nil {
			utils.Fatalf("%v", err)
		}
		confirm, err := readPassword("Repeat Passphrase: ", false)
		if err != nil {
			utils.Fatalf("%v", err)
		}
		if auth != confirm {
			utils.Fatalf("Passphrases did not match.")
		}
		passphrase = auth
276
	}
277
	acct, err := am.NewAccount(passphrase)
278 279 280
	if err != nil {
		utils.Fatalf("Could not create the account: %v", err)
	}
obscuren's avatar
obscuren committed
281
	fmt.Printf("Address: %x\n", acct.Address)
282 283
}

284 285 286 287
func importchain(ctx *cli.Context) {
	if len(ctx.Args()) != 1 {
		utils.Fatalf("This command requires an argument.")
	}
288
	chainmgr, _, _ := utils.GetChain(ctx)
289
	start := time.Now()
290
	err := utils.ImportChain(chainmgr, ctx.Args().First())
obscuren's avatar
obscuren committed
291
	if err != nil {
292
		utils.Fatalf("Import error: %v\n", err)
obscuren's avatar
obscuren committed
293
	}
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
	fmt.Printf("Import done in %v", time.Since(start))
	return
}

func exportchain(ctx *cli.Context) {
	if len(ctx.Args()) != 1 {
		utils.Fatalf("This command requires an argument.")
	}
	chainmgr, _, _ := utils.GetChain(ctx)
	start := time.Now()
	err := utils.ExportChain(chainmgr, ctx.Args().First())
	if err != nil {
		utils.Fatalf("Export error: %v\n", err)
	}
	fmt.Printf("Export done in %v", time.Since(start))
309 310
	return
}
obscuren's avatar
obscuren committed
311

312
func dump(ctx *cli.Context) {
313
	chainmgr, _, stateDb := utils.GetChain(ctx)
314
	for _, arg := range ctx.Args() {
obscuren's avatar
obscuren committed
315
		var block *types.Block
316
		if hashish(arg) {
obscuren's avatar
obscuren committed
317
			block = chainmgr.GetBlock(common.HexToHash(arg))
obscuren's avatar
obscuren committed
318
		} else {
319
			num, _ := strconv.Atoi(arg)
320
			block = chainmgr.GetBlockByNumber(uint64(num))
obscuren's avatar
obscuren committed
321 322 323
		}
		if block == nil {
			fmt.Println("{}")
324 325
			utils.Fatalf("block not found")
		} else {
326
			statedb := state.New(block.Root(), stateDb)
327 328
			fmt.Printf("%s\n", statedb.Dump())
			// fmt.Println(block)
obscuren's avatar
obscuren committed
329
		}
330
	}
331
}
332

333 334 335 336 337 338 339 340 341
func makedag(ctx *cli.Context) {
	chain, _, _ := utils.GetChain(ctx)
	pow := ethash.New(chain)
	fmt.Println("making cache")
	pow.UpdateCache(true)
	fmt.Println("making DAG")
	pow.UpdateDAG()
}

342
func version(c *cli.Context) {
zelig's avatar
zelig committed
343 344 345 346 347 348
	fmt.Printf(`%v
Version: %v
Protocol Version: %d
Network Id: %d
GO: %s
OS: %s
obscuren's avatar
obscuren committed
349 350
GOPATH=%s
GOROOT=%s
zelig's avatar
zelig committed
351
`, ClientIdentifier, Version, c.GlobalInt(utils.ProtocolVersionFlag.Name), c.GlobalInt(utils.NetworkIdFlag.Name), runtime.Version(), runtime.GOOS, os.Getenv("GOPATH"), runtime.GOROOT())
obscuren's avatar
obscuren committed
352
}
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373

// 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
}