go-ethereum/cmd/ethereum/main.go
Felix Lange d7b5a87b3b miner: provide coinbase when starting the miner
This avoids having to query the coinbase when creating the miner, which
in turn eliminates the dreaded startup error when no accounts are set
up. Later, this will also allow us to simply restart the miner when the
user picks a different coinbase.

This causes a lot of changes in other packages. These are included in
this commit because they're impossible to separate.
2015-03-11 23:43:27 +01:00

339 lines
8.4 KiB
Go

/*
This file is part of go-ethereum
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>
*/
package main
import (
"bufio"
"fmt"
"os"
"runtime"
"strconv"
"strings"
"time"
"github.com/codegangsta/cli"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/state"
"github.com/peterh/liner"
)
const (
ClientIdentifier = "Ethereum(G)"
Version = "0.9.0"
)
var (
clilogger = logger.NewLogger("CLI")
app = utils.NewApp(Version, "the go-ethereum command line interface")
)
func init() {
app.Action = run
app.HideVersion = true // we have a command to print the version
app.Commands = []cli.Command{
{
Action: version,
Name: "version",
Usage: "print ethereum version numbers",
Description: `
The output of this command is supposed to be machine-readable.
`,
},
{
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",
},
},
},
{
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.
`,
},
{
Action: runjs,
Name: "js",
Usage: `interactive JavaScript console`,
Description: `
In the console, you can use the eth object to interact
with the running ethereum stack. The API does not match
ethereum.js.
A JavaScript file can be provided as the argument. The
runtime will execute the file and exit.
`,
},
{
Action: importchain,
Name: "import",
Usage: `import a blockchain file`,
},
{
Action: exportchain,
Name: "export",
Usage: `export blockchain into file`,
},
}
app.Flags = []cli.Flag{
utils.UnlockedAccountFlag,
utils.BootnodesFlag,
utils.DataDirFlag,
utils.ListenPortFlag,
utils.LogFileFlag,
utils.LogFormatFlag,
utils.LogLevelFlag,
utils.MaxPeersFlag,
utils.MinerThreadsFlag,
utils.MiningEnabledFlag,
utils.NATFlag,
utils.NodeKeyFileFlag,
utils.NodeKeyHexFlag,
utils.RPCEnabledFlag,
utils.RPCListenAddrFlag,
utils.RPCPortFlag,
utils.UnencryptedKeysFlag,
utils.VMDebugFlag,
//utils.VMTypeFlag,
}
// 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")
// 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")
}
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)
}
}
func run(ctx *cli.Context) {
fmt.Printf("Welcome to the FRONTIER\n")
utils.HandleInterrupt()
eth, err := utils.GetEthereum(ClientIdentifier, Version, ctx)
if err == accounts.ErrNoKeys {
utils.Fatalf(`No accounts configured.
Please run 'ethereum account new' to create a new account.`)
} else if err != nil {
utils.Fatalf("%v", err)
}
startEth(ctx, eth)
// this blocks the thread
eth.WaitForShutdown()
}
func runjs(ctx *cli.Context) {
eth, err := utils.GetEthereum(ClientIdentifier, Version, ctx)
if err == accounts.ErrNoKeys {
utils.Fatalf(`No accounts configured.
Please run 'ethereum account new' to create a new account.`)
} else if err != nil {
utils.Fatalf("%v", err)
}
startEth(ctx, eth)
repl := newJSRE(eth)
if len(ctx.Args()) == 0 {
repl.interactive()
} else {
for _, file := range ctx.Args() {
repl.exec(file)
}
}
eth.Stop()
eth.WaitForShutdown()
}
func startEth(ctx *cli.Context, eth *eth.Ethereum) {
utils.StartEthereum(eth)
// 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)")
}
am := eth.AccountManager()
// Attempt to unlock the account
err := am.Unlock(ethutil.Hex2Bytes(split[0]), split[1])
if err != nil {
utils.Fatalf("Unlock account failed '%v'", err)
}
}
// Start auxiliary services if enabled.
if ctx.GlobalBool(utils.RPCEnabledFlag.Name) {
utils.StartRPC(eth, ctx)
}
if ctx.GlobalBool(utils.MiningEnabledFlag.Name) {
eth.StartMining()
}
}
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 {
fmt.Printf("Address: %#x\n", acct)
}
}
func accountCreate(ctx *cli.Context) {
am := utils.GetAccountManager(ctx)
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
}
acct, err := am.NewAccount(passphrase)
if err != nil {
utils.Fatalf("Could not create the account: %v", err)
}
fmt.Printf("Address: %#x\n", acct.Address)
}
func importchain(ctx *cli.Context) {
if len(ctx.Args()) != 1 {
utils.Fatalf("This command requires an argument.")
}
chainmgr, _, _ := utils.GetChain(ctx)
start := time.Now()
err := utils.ImportChain(chainmgr, ctx.Args().First())
if err != nil {
utils.Fatalf("Import error: %v\n", err)
}
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))
return
}
func dump(ctx *cli.Context) {
chainmgr, _, stateDb := utils.GetChain(ctx)
for _, arg := range ctx.Args() {
var block *types.Block
if hashish(arg) {
block = chainmgr.GetBlock(ethutil.Hex2Bytes(arg))
} else {
num, _ := strconv.Atoi(arg)
block = chainmgr.GetBlockByNumber(uint64(num))
}
if block == nil {
fmt.Println("{}")
utils.Fatalf("block not found")
} else {
statedb := state.New(block.Root(), stateDb)
fmt.Printf("%s\n", statedb.Dump())
// fmt.Println(block)
}
}
}
func version(c *cli.Context) {
fmt.Printf(`%v
Version: %v
Protocol Version: %d
Network Id: %d
GO: %s
OS: %s
GOPATH=%s
GOROOT=%s
`, ClientIdentifier, Version, eth.ProtocolVersion, eth.NetworkId, runtime.Version(), runtime.GOOS, os.Getenv("GOPATH"), runtime.GOROOT())
}
// 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
}