Merge pull request #433 from fjl/newcli

Improved CLI
This commit is contained in:
Felix Lange
2015-03-06 14:02:16 +01:00
64 changed files with 6699 additions and 3161 deletions

View File

@ -1,40 +0,0 @@
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
//
// This library 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 2.1 of the License, or (at your option) any later version.
//
// This library 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 this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA
package main
import (
"io/ioutil"
"os"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/javascript"
"github.com/ethereum/go-ethereum/xeth"
)
func ExecJsFile(ethereum *eth.Ethereum, InputFile string) {
file, err := os.Open(InputFile)
if err != nil {
clilogger.Fatalln(err)
}
content, err := ioutil.ReadAll(file)
if err != nil {
clilogger.Fatalln(err)
}
re := javascript.NewJSRE(xeth.New(ethereum))
re.Run(string(content))
}

View File

@ -1,168 +0,0 @@
/*
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 (
"crypto/ecdsa"
"flag"
"fmt"
"log"
"os"
"path"
"runtime"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/vm"
)
var (
Identifier string
KeyRing string
DiffTool bool
DiffType string
KeyStore string
StartRpc bool
StartWebSockets bool
RpcListenAddress string
RpcPort int
OutboundPort string
ShowGenesis bool
AddPeer string
MaxPeer int
GenAddr bool
BootNodes string
NodeKey *ecdsa.PrivateKey
NAT nat.Interface
SecretFile string
ExportDir string
NonInteractive bool
Datadir string
LogFile string
ConfigFile string
DebugFile string
LogLevel int
LogFormat string
Dump bool
DumpHash string
DumpNumber int
VmType int
ImportChain string
SHH bool
Dial bool
PrintVersion bool
MinerThreads int
)
// flags specific to cli client
var (
StartMining bool
StartJsConsole bool
InputFile string
)
var defaultConfigFile = path.Join(ethutil.DefaultDataDir(), "conf.ini")
func Init() {
// TODO: move common flag processing to cmd/util
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "%s [options] [filename]:\noptions precedence: default < config file < environment variables < command line\n", os.Args[0])
flag.PrintDefaults()
}
flag.IntVar(&VmType, "vm", 0, "Virtual Machine type: 0-1: standard, debug")
flag.StringVar(&Identifier, "id", "", "Custom client identifier")
flag.StringVar(&KeyRing, "keyring", "", "identifier for keyring to use")
flag.StringVar(&KeyStore, "keystore", "db", "system to store keyrings: db|file")
flag.StringVar(&RpcListenAddress, "rpcaddr", "127.0.0.1", "address for json-rpc server to listen on")
flag.IntVar(&RpcPort, "rpcport", 8545, "port to start json-rpc server on")
flag.BoolVar(&StartRpc, "rpc", false, "start rpc server")
flag.BoolVar(&NonInteractive, "y", false, "non-interactive mode (say yes to confirmations)")
flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key")
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.StringVar(&LogFile, "logfile", "", "log file (defaults to standard output)")
flag.StringVar(&Datadir, "datadir", ethutil.DefaultDataDir(), "specifies the datadir to use")
flag.StringVar(&ConfigFile, "conf", defaultConfigFile, "config file")
flag.StringVar(&DebugFile, "debug", "", "debug file (no debugging if not set)")
flag.IntVar(&LogLevel, "loglevel", int(logger.InfoLevel), "loglevel: 0-5 (= silent,error,warn,info,debug,debug detail)")
flag.StringVar(&LogFormat, "logformat", "std", "logformat: std,raw")
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")
flag.BoolVar(&ShowGenesis, "genesis", false, "Dump the genesis block")
flag.StringVar(&ImportChain, "chain", "", "Imports given chain")
flag.BoolVar(&Dump, "dump", false, "output the ethereum state in JSON format. Sub args [number, hash]")
flag.StringVar(&DumpHash, "hash", "", "specify arg in hex")
flag.IntVar(&DumpNumber, "number", -1, "specify arg in number")
flag.BoolVar(&StartMining, "mine", false, "start mining")
flag.BoolVar(&StartJsConsole, "js", false, "launches javascript console")
flag.BoolVar(&PrintVersion, "version", false, "prints version number")
flag.IntVar(&MinerThreads, "minerthreads", runtime.NumCPU(), "number of miner threads")
// Network stuff
var (
nodeKeyFile = flag.String("nodekey", "", "network private key file")
nodeKeyHex = flag.String("nodekeyhex", "", "network private key (for testing)")
natstr = flag.String("nat", "any", "port mapping mechanism (any|none|upnp|pmp|extip:<IP>)")
)
flag.BoolVar(&Dial, "dial", true, "dial out connections (default on)")
//flag.BoolVar(&SHH, "shh", true, "run whisper protocol (default on)")
flag.StringVar(&OutboundPort, "port", "30303", "listening port")
flag.StringVar(&BootNodes, "bootnodes", "", "space-separated node URLs for discovery bootstrap")
flag.IntVar(&MaxPeer, "maxpeer", 30, "maximum desired peers")
flag.Parse()
// When the javascript console is started log to a file instead
// of stdout
if StartJsConsole {
LogFile = path.Join(Datadir, "ethereum.log")
}
var err error
if NAT, err = nat.Parse(*natstr); err != nil {
log.Fatalf("-nat: %v", err)
}
switch {
case *nodeKeyFile != "" && *nodeKeyHex != "":
log.Fatal("Options -nodekey and -nodekeyhex are mutually exclusive")
case *nodeKeyFile != "":
if NodeKey, err = crypto.LoadECDSA(*nodeKeyFile); err != nil {
log.Fatalf("-nodekey: %v", err)
}
case *nodeKeyHex != "":
if NodeKey, err = crypto.HexToECDSA(*nodeKeyHex); err != nil {
log.Fatalf("-nodekeyhex: %v", err)
}
}
if VmType >= int(vm.MaxVmTy) {
log.Fatal("Invalid VM type ", VmType)
}
InputFile = flag.Arg(0)
}

280
cmd/ethereum/js.go Normal file
View File

@ -0,0 +1,280 @@
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
//
// This library 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 2.1 of the License, or (at your option) any later version.
//
// This library 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 this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA
package main
import (
"bufio"
"fmt"
"io/ioutil"
"os"
"os/signal"
"path"
"strings"
"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/javascript"
"github.com/ethereum/go-ethereum/state"
"github.com/ethereum/go-ethereum/xeth"
"github.com/obscuren/otto"
"github.com/peterh/liner"
)
func execJsFile(ethereum *eth.Ethereum, filename string) {
file, err := os.Open(filename)
if err != nil {
utils.Fatalf("%v", err)
}
content, err := ioutil.ReadAll(file)
if err != nil {
utils.Fatalf("%v", err)
}
re := javascript.NewJSRE(xeth.New(ethereum))
if _, err := re.Run(string(content)); err != nil {
utils.Fatalf("Javascript Error: %v", err)
}
}
type repl struct {
re *javascript.JSRE
ethereum *eth.Ethereum
xeth *xeth.XEth
prompt string
lr *liner.State
}
func runREPL(ethereum *eth.Ethereum) {
xeth := xeth.New(ethereum)
repl := &repl{
re: javascript.NewJSRE(xeth),
xeth: xeth,
ethereum: ethereum,
prompt: "> ",
}
repl.initStdFuncs()
if !liner.TerminalSupported() {
repl.dumbRead()
} else {
lr := liner.NewLiner()
defer lr.Close()
lr.SetCtrlCAborts(true)
repl.withHistory(func(hist *os.File) { lr.ReadHistory(hist) })
repl.read(lr)
repl.withHistory(func(hist *os.File) { hist.Truncate(0); lr.WriteHistory(hist) })
}
}
func (self *repl) withHistory(op func(*os.File)) {
hist, err := os.OpenFile(path.Join(self.ethereum.DataDir, "history"), os.O_RDWR|os.O_CREATE, os.ModePerm)
if err != nil {
fmt.Printf("unable to open history file: %v\n", err)
return
}
op(hist)
hist.Close()
}
func (self *repl) parseInput(code string) {
defer func() {
if r := recover(); r != nil {
fmt.Println("[native] error", r)
}
}()
value, err := self.re.Run(code)
if err != nil {
fmt.Println(err)
return
}
self.printValue(value)
}
var indentCount = 0
var str = ""
func (self *repl) setIndent() {
open := strings.Count(str, "{")
open += strings.Count(str, "(")
closed := strings.Count(str, "}")
closed += strings.Count(str, ")")
indentCount = open - closed
if indentCount <= 0 {
self.prompt = "> "
} else {
self.prompt = strings.Join(make([]string, indentCount*2), "..")
self.prompt += " "
}
}
func (self *repl) read(lr *liner.State) {
for {
input, err := lr.Prompt(self.prompt)
if err != nil {
return
}
if input == "" {
continue
}
str += input + "\n"
self.setIndent()
if indentCount <= 0 {
if input == "exit" {
return
}
hist := str[:len(str)-1]
lr.AppendHistory(hist)
self.parseInput(str)
str = ""
}
}
}
func (self *repl) dumbRead() {
fmt.Println("Unsupported terminal, line editing will not work.")
// process lines
readDone := make(chan struct{})
go func() {
r := bufio.NewReader(os.Stdin)
loop:
for {
fmt.Print(self.prompt)
line, err := r.ReadString('\n')
switch {
case err != nil || line == "exit":
break loop
case line == "":
continue
default:
self.parseInput(line + "\n")
}
}
close(readDone)
}()
// wait for Ctrl-C
sigc := make(chan os.Signal, 1)
signal.Notify(sigc, os.Interrupt, os.Kill)
defer signal.Stop(sigc)
select {
case <-readDone:
case <-sigc:
os.Stdin.Close() // terminate read
}
}
func (self *repl) printValue(v interface{}) {
method, _ := self.re.Vm.Get("prettyPrint")
v, err := self.re.Vm.ToValue(v)
if err == nil {
val, err := method.Call(method, v)
if err == nil {
fmt.Printf("%v", val)
}
}
}
func (self *repl) initStdFuncs() {
t, _ := self.re.Vm.Get("eth")
eth := t.Object()
eth.Set("connect", self.connect)
eth.Set("stopMining", self.stopMining)
eth.Set("startMining", self.startMining)
eth.Set("dump", self.dump)
eth.Set("export", self.export)
}
/*
* The following methods are natively implemented javascript functions.
*/
func (self *repl) dump(call otto.FunctionCall) otto.Value {
var block *types.Block
if len(call.ArgumentList) > 0 {
if call.Argument(0).IsNumber() {
num, _ := call.Argument(0).ToInteger()
block = self.ethereum.ChainManager().GetBlockByNumber(uint64(num))
} else if call.Argument(0).IsString() {
hash, _ := call.Argument(0).ToString()
block = self.ethereum.ChainManager().GetBlock(ethutil.Hex2Bytes(hash))
} else {
fmt.Println("invalid argument for dump. Either hex string or number")
}
if block == nil {
fmt.Println("block not found")
return otto.UndefinedValue()
}
} else {
block = self.ethereum.ChainManager().CurrentBlock()
}
statedb := state.New(block.Root(), self.ethereum.Db())
v, _ := self.re.Vm.ToValue(statedb.RawDump())
return v
}
func (self *repl) stopMining(call otto.FunctionCall) otto.Value {
self.xeth.Miner().Stop()
return otto.TrueValue()
}
func (self *repl) startMining(call otto.FunctionCall) otto.Value {
self.xeth.Miner().Start()
return otto.TrueValue()
}
func (self *repl) connect(call otto.FunctionCall) otto.Value {
nodeURL, err := call.Argument(0).ToString()
if err != nil {
return otto.FalseValue()
}
if err := self.ethereum.SuggestPeer(nodeURL); err != nil {
return otto.FalseValue()
}
return otto.TrueValue()
}
func (self *repl) export(call otto.FunctionCall) otto.Value {
if len(call.ArgumentList) == 0 {
fmt.Println("err: require file name")
return otto.FalseValue()
}
fn, err := call.Argument(0).ToString()
if err != nil {
fmt.Println(err)
return otto.FalseValue()
}
data := self.ethereum.ChainManager().Export()
if err := ethutil.WriteFile(fn, data); err != nil {
fmt.Println(err)
return otto.FalseValue()
}
return otto.TrueValue()
}

View File

@ -24,15 +24,15 @@ import (
"fmt"
"os"
"runtime"
"strconv"
"time"
"github.com/ethereum/go-ethereum/cmd/ethereum/repl"
"github.com/codegangsta/cli"
"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/p2p"
"github.com/ethereum/go-ethereum/state"
)
@ -41,117 +41,173 @@ const (
Version = "0.8.6"
)
var clilogger = logger.NewLogger("CLI")
var (
clilogger = logger.NewLogger("CLI")
app = cli.NewApp()
)
func init() {
app.Version = Version
app.Usage = "the go-ethereum command-line client"
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: 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`,
},
}
app.Author = ""
app.Email = ""
app.Flags = []cli.Flag{
utils.BootnodesFlag,
utils.DataDirFlag,
utils.KeyRingFlag,
utils.KeyStoreFlag,
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.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 func() {
logger.Flush()
}()
utils.HandleInterrupt()
// precedence: code-internal flag default < config file < environment variables < command line
Init() // parsing command line
if PrintVersion {
printVersion()
return
defer logger.Flush()
if err := app.Run(os.Args); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
utils.InitConfig(VmType, ConfigFile, Datadir, "ETH")
ethereum, err := eth.New(&eth.Config{
Name: p2p.MakeName(ClientIdentifier, Version),
KeyStore: KeyStore,
DataDir: Datadir,
LogFile: LogFile,
LogLevel: LogLevel,
LogFormat: LogFormat,
MaxPeers: MaxPeer,
Port: OutboundPort,
NAT: NAT,
KeyRing: KeyRing,
Shh: true,
Dial: Dial,
BootNodes: BootNodes,
NodeKey: NodeKey,
MinerThreads: MinerThreads,
})
if err != nil {
clilogger.Fatalln(err)
}
utils.KeyTasks(ethereum.KeyManager(), KeyRing, GenAddr, SecretFile, ExportDir, NonInteractive)
if Dump {
var block *types.Block
if len(DumpHash) == 0 && DumpNumber == -1 {
block = ethereum.ChainManager().CurrentBlock()
} else if len(DumpHash) > 0 {
block = ethereum.ChainManager().GetBlock(ethutil.Hex2Bytes(DumpHash))
} else {
block = ethereum.ChainManager().GetBlockByNumber(uint64(DumpNumber))
}
if block == nil {
fmt.Fprintln(os.Stderr, "block not found")
// We want to output valid JSON
fmt.Println("{}")
os.Exit(1)
}
// Leave the Println. This needs clean output for piping
statedb := state.New(block.Root(), ethereum.Db())
fmt.Printf("%s\n", statedb.Dump())
fmt.Println(block)
return
}
if len(ImportChain) > 0 {
start := time.Now()
err := utils.ImportChain(ethereum, ImportChain)
if err != nil {
clilogger.Infoln(err)
}
clilogger.Infoln("import done in", time.Since(start))
return
}
if StartRpc {
utils.StartRpc(ethereum, RpcListenAddress, RpcPort)
}
utils.StartEthereum(ethereum)
fmt.Printf("Welcome to the FRONTIER\n")
if StartMining {
ethereum.Miner().Start()
}
if StartJsConsole {
repl := ethrepl.NewJSRepl(ethereum)
repl.Start()
utils.RegisterInterrupt(func(os.Signal) {
repl.Stop()
})
} else if len(InputFile) > 0 {
ExecJsFile(ethereum, InputFile)
}
// this blocks the thread
ethereum.WaitForShutdown()
}
func printVersion() {
func run(ctx *cli.Context) {
fmt.Printf("Welcome to the FRONTIER\n")
utils.HandleInterrupt()
eth := utils.GetEthereum(ClientIdentifier, Version, ctx)
startEth(ctx, eth)
// this blocks the thread
eth.WaitForShutdown()
}
func runjs(ctx *cli.Context) {
eth := utils.GetEthereum(ClientIdentifier, Version, ctx)
startEth(ctx, eth)
if len(ctx.Args()) == 0 {
runREPL(eth)
eth.Stop()
eth.WaitForShutdown()
} else if len(ctx.Args()) == 1 {
execJsFile(eth, ctx.Args()[0])
} else {
utils.Fatalf("This command can handle at most one argument.")
}
}
func startEth(ctx *cli.Context, eth *eth.Ethereum) {
utils.StartEthereum(eth)
if ctx.GlobalBool(utils.RPCEnabledFlag.Name) {
addr := ctx.GlobalString(utils.RPCListenAddrFlag.Name)
port := ctx.GlobalInt(utils.RPCPortFlag.Name)
utils.StartRpc(eth, addr, port)
}
if ctx.GlobalBool(utils.MiningEnabledFlag.Name) {
eth.Miner().Start()
}
}
func importchain(ctx *cli.Context) {
if len(ctx.Args()) != 1 {
utils.Fatalf("This command requires an argument.")
}
chain, _ := utils.GetChain(ctx)
start := time.Now()
err := utils.ImportChain(chain, ctx.Args().First())
if err != nil {
utils.Fatalf("Import error: %v\n", err)
}
fmt.Printf("Import done in", time.Since(start))
return
}
func dump(ctx *cli.Context) {
chain, db := utils.GetChain(ctx)
for _, arg := range ctx.Args() {
var block *types.Block
if hashish(arg) {
block = chain.GetBlock(ethutil.Hex2Bytes(arg))
} else {
num, _ := strconv.Atoi(arg)
block = chain.GetBlockByNumber(uint64(num))
}
if block == nil {
fmt.Println("{}")
utils.Fatalf("block not found")
} else {
statedb := state.New(block.Root(), db)
fmt.Printf("%s\n", statedb.Dump())
// fmt.Println(block)
}
}
}
// hashish returns true for strings that look like hashes.
func hashish(x string) bool {
_, err := strconv.Atoi(x)
return err != nil
}
func version(c *cli.Context) {
fmt.Printf(`%v %v
PV=%d
GOOS=%s

View File

@ -1,97 +0,0 @@
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
//
// This library 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 2.1 of the License, or (at your option) any later version.
//
// This library 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 this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA
/* Inspired by https://github.com/xuyu/logging/blob/master/colorful_win.go */
package ethrepl
import (
"syscall"
"unsafe"
)
type color uint16
const (
green = color(0x0002)
red = color(0x0004)
yellow = color(0x000E)
)
const (
mask = uint16(yellow | green | red)
)
var (
kernel32 = syscall.NewLazyDLL("kernel32.dll")
procGetStdHandle = kernel32.NewProc("GetStdHandle")
procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute")
procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
hStdout uintptr
initScreenInfo *consoleScreenBufferInfo
)
func setConsoleTextAttribute(hConsoleOutput uintptr, wAttributes uint16) bool {
ret, _, _ := procSetConsoleTextAttribute.Call(hConsoleOutput, uintptr(wAttributes))
return ret != 0
}
type coord struct {
X, Y int16
}
type smallRect struct {
Left, Top, Right, Bottom int16
}
type consoleScreenBufferInfo struct {
DwSize coord
DwCursorPosition coord
WAttributes uint16
SrWindow smallRect
DwMaximumWindowSize coord
}
func getConsoleScreenBufferInfo(hConsoleOutput uintptr) *consoleScreenBufferInfo {
var csbi consoleScreenBufferInfo
ret, _, _ := procGetConsoleScreenBufferInfo.Call(hConsoleOutput, uintptr(unsafe.Pointer(&csbi)))
if ret == 0 {
return nil
}
return &csbi
}
const (
stdOutputHandle = uint32(-11 & 0xFFFFFFFF)
)
func init() {
hStdout, _, _ = procGetStdHandle.Call(uintptr(stdOutputHandle))
initScreenInfo = getConsoleScreenBufferInfo(hStdout)
}
func resetColorful() {
if initScreenInfo == nil {
return
}
setConsoleTextAttribute(hStdout, initScreenInfo.WAttributes)
}
func changeColor(c color) {
attr := uint16(0) & ^mask | uint16(c)
setConsoleTextAttribute(hStdout, attr)
}

View File

@ -1,201 +0,0 @@
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
//
// This library 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 2.1 of the License, or (at your option) any later version.
//
// This library 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 this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA
package ethrepl
import (
"bufio"
"fmt"
"io"
"os"
"path"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/javascript"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/state"
"github.com/ethereum/go-ethereum/xeth"
"github.com/obscuren/otto"
)
var repllogger = logger.NewLogger("REPL")
type Repl interface {
Start()
Stop()
}
type JSRepl struct {
re *javascript.JSRE
ethereum *eth.Ethereum
xeth *xeth.XEth
prompt string
history *os.File
running bool
}
func NewJSRepl(ethereum *eth.Ethereum) *JSRepl {
hist, err := os.OpenFile(path.Join(ethutil.Config.ExecPath, "history"), os.O_RDWR|os.O_CREATE, os.ModePerm)
if err != nil {
panic(err)
}
xeth := xeth.New(ethereum)
repl := &JSRepl{re: javascript.NewJSRE(xeth), xeth: xeth, ethereum: ethereum, prompt: "> ", history: hist}
repl.initStdFuncs()
return repl
}
func (self *JSRepl) Start() {
if !self.running {
self.running = true
repllogger.Infoln("init JS Console")
reader := bufio.NewReader(self.history)
for {
line, err := reader.ReadString('\n')
if err != nil && err == io.EOF {
break
} else if err != nil {
fmt.Println("error reading history", err)
break
}
addHistory(line[:len(line)-1])
}
self.read()
}
}
func (self *JSRepl) Stop() {
if self.running {
self.running = false
repllogger.Infoln("exit JS Console")
self.history.Close()
}
}
func (self *JSRepl) parseInput(code string) {
defer func() {
if r := recover(); r != nil {
fmt.Println("[native] error", r)
}
}()
value, err := self.re.Run(code)
if err != nil {
fmt.Println(err)
return
}
self.PrintValue(value)
}
func (self *JSRepl) initStdFuncs() {
t, _ := self.re.Vm.Get("eth")
eth := t.Object()
eth.Set("connect", self.connect)
eth.Set("stopMining", self.stopMining)
eth.Set("startMining", self.startMining)
eth.Set("dump", self.dump)
eth.Set("export", self.export)
}
/*
* The following methods are natively implemented javascript functions
*/
func (self *JSRepl) dump(call otto.FunctionCall) otto.Value {
var block *types.Block
if len(call.ArgumentList) > 0 {
if call.Argument(0).IsNumber() {
num, _ := call.Argument(0).ToInteger()
block = self.ethereum.ChainManager().GetBlockByNumber(uint64(num))
} else if call.Argument(0).IsString() {
hash, _ := call.Argument(0).ToString()
block = self.ethereum.ChainManager().GetBlock(ethutil.Hex2Bytes(hash))
} else {
fmt.Println("invalid argument for dump. Either hex string or number")
}
if block == nil {
fmt.Println("block not found")
return otto.UndefinedValue()
}
} else {
block = self.ethereum.ChainManager().CurrentBlock()
}
statedb := state.New(block.Root(), self.ethereum.Db())
v, _ := self.re.Vm.ToValue(statedb.RawDump())
return v
}
func (self *JSRepl) stopMining(call otto.FunctionCall) otto.Value {
self.xeth.Miner().Stop()
return otto.TrueValue()
}
func (self *JSRepl) startMining(call otto.FunctionCall) otto.Value {
self.xeth.Miner().Start()
return otto.TrueValue()
}
func (self *JSRepl) connect(call otto.FunctionCall) otto.Value {
nodeURL, err := call.Argument(0).ToString()
if err != nil {
return otto.FalseValue()
}
if err := self.ethereum.SuggestPeer(nodeURL); err != nil {
return otto.FalseValue()
}
return otto.TrueValue()
}
func (self *JSRepl) export(call otto.FunctionCall) otto.Value {
if len(call.ArgumentList) == 0 {
fmt.Println("err: require file name")
return otto.FalseValue()
}
fn, err := call.Argument(0).ToString()
if err != nil {
fmt.Println(err)
return otto.FalseValue()
}
data := self.ethereum.ChainManager().Export()
if err := ethutil.WriteFile(fn, data); err != nil {
fmt.Println(err)
return otto.FalseValue()
}
return otto.TrueValue()
}

View File

@ -1,144 +0,0 @@
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
//
// This library 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 2.1 of the License, or (at your option) any later version.
//
// This library 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 this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA
package ethrepl
// #cgo darwin CFLAGS: -I/usr/local/opt/readline/include
// #cgo darwin LDFLAGS: -L/usr/local/opt/readline/lib
// #cgo LDFLAGS: -lreadline
// #include <stdio.h>
// #include <stdlib.h>
// #include <readline/readline.h>
// #include <readline/history.h>
import "C"
import (
"fmt"
"os"
"os/signal"
"strings"
"syscall"
"unsafe"
)
func initReadLine() {
C.rl_catch_sigwinch = 0
C.rl_catch_signals = 0
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGWINCH)
signal.Notify(c, os.Interrupt)
go func() {
for sig := range c {
switch sig {
case syscall.SIGWINCH:
C.rl_resize_terminal()
case os.Interrupt:
C.rl_cleanup_after_signal()
default:
}
}
}()
}
func readLine(prompt *string) *string {
var p *C.char
//readline allows an empty prompt(NULL)
if prompt != nil {
p = C.CString(*prompt)
}
ret := C.readline(p)
if p != nil {
C.free(unsafe.Pointer(p))
}
if ret == nil {
return nil
} //EOF
s := C.GoString(ret)
C.free(unsafe.Pointer(ret))
return &s
}
func addHistory(s string) {
p := C.CString(s)
C.add_history(p)
C.free(unsafe.Pointer(p))
}
var indentCount = 0
var str = ""
func (self *JSRepl) setIndent() {
open := strings.Count(str, "{")
open += strings.Count(str, "(")
closed := strings.Count(str, "}")
closed += strings.Count(str, ")")
indentCount = open - closed
if indentCount <= 0 {
self.prompt = "> "
} else {
self.prompt = strings.Join(make([]string, indentCount*2), "..")
self.prompt += " "
}
}
func (self *JSRepl) read() {
initReadLine()
L:
for {
switch result := readLine(&self.prompt); true {
case result == nil:
break L
case *result != "":
str += *result + "\n"
self.setIndent()
if indentCount <= 0 {
if *result == "exit" {
self.Stop()
break L
}
hist := str[:len(str)-1]
addHistory(hist) //allow user to recall this line
self.history.WriteString(str)
self.parseInput(str)
str = ""
}
}
}
}
func (self *JSRepl) PrintValue(v interface{}) {
method, _ := self.re.Vm.Get("prettyPrint")
v, err := self.re.Vm.ToValue(v)
if err == nil {
val, err := method.Call(method, v)
if err == nil {
fmt.Printf("%v", val)
}
}
}

View File

@ -1 +0,0 @@
repl_darwin.go

View File

@ -1,92 +0,0 @@
// Copyright (c) 2013-2014, Jeffrey Wilcke. All rights reserved.
//
// This library 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 2.1 of the License, or (at your option) any later version.
//
// This library 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 this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301 USA
package ethrepl
import (
"bufio"
"fmt"
"os"
"strings"
)
func (self *JSRepl) read() {
reader := bufio.NewReader(os.Stdin)
for {
fmt.Printf(self.prompt)
str, _, err := reader.ReadLine()
if err != nil {
fmt.Println("Error reading input", err)
} else {
if string(str) == "exit" {
self.Stop()
break
} else {
self.parseInput(string(str))
}
}
}
}
func addHistory(s string) {
}
func printColored(outputVal string) {
for outputVal != "" {
codePart := ""
if strings.HasPrefix(outputVal, "\033[32m") {
codePart = "\033[32m"
changeColor(2)
}
if strings.HasPrefix(outputVal, "\033[1m\033[30m") {
codePart = "\033[1m\033[30m"
changeColor(8)
}
if strings.HasPrefix(outputVal, "\033[31m") {
codePart = "\033[31m"
changeColor(red)
}
if strings.HasPrefix(outputVal, "\033[35m") {
codePart = "\033[35m"
changeColor(5)
}
if strings.HasPrefix(outputVal, "\033[0m") {
codePart = "\033[0m"
resetColorful()
}
textPart := outputVal[len(codePart):len(outputVal)]
index := strings.Index(textPart, "\033")
if index == -1 {
outputVal = ""
} else {
outputVal = textPart[index:len(textPart)]
textPart = textPart[0:index]
}
fmt.Printf("%v", textPart)
}
}
func (self *JSRepl) PrintValue(v interface{}) {
method, _ := self.re.Vm.Get("prettyPrint")
v, err := self.re.Vm.ToValue(v)
if err == nil {
val, err := method.Call(method, v)
if err == nil {
printColored(fmt.Sprintf("%v", val))
}
}
}

View File

@ -79,14 +79,14 @@ func (self *Gui) AddPlugin(pluginPath string) {
self.plugins[pluginPath] = plugin{Name: pluginPath, Path: pluginPath}
json, _ := json.MarshalIndent(self.plugins, "", " ")
ethutil.WriteFile(ethutil.Config.ExecPath+"/plugins.json", json)
ethutil.WriteFile(self.eth.DataDir+"/plugins.json", json)
}
func (self *Gui) RemovePlugin(pluginPath string) {
delete(self.plugins, pluginPath)
json, _ := json.MarshalIndent(self.plugins, "", " ")
ethutil.WriteFile(ethutil.Config.ExecPath+"/plugins.json", json)
ethutil.WriteFile(self.eth.DataDir+"/plugins.json", json)
}
// this extra function needed to give int typecast value to gui widget

View File

@ -99,7 +99,7 @@ func NewWindow(ethereum *eth.Ethereum, config *ethutil.ConfigManager, session st
plugins: make(map[string]plugin),
serviceEvents: make(chan ServEv, 1),
}
data, _ := ethutil.ReadAllFile(path.Join(ethutil.Config.ExecPath, "plugins.json"))
data, _ := ethutil.ReadAllFile(path.Join(ethereum.DataDir, "plugins.json"))
json.Unmarshal([]byte(data), &gui.plugins)
return gui

View File

@ -27,6 +27,7 @@ import (
"os/signal"
"regexp"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
@ -108,13 +109,20 @@ func InitConfig(vmType int, ConfigFile string, Datadir string, EnvPrefix string)
func exit(err error) {
status := 0
if err != nil {
clilogger.Errorln("Fatal: ", err)
fmt.Fprintln(os.Stderr, "Fatal: ", err)
status = 1
}
logger.Flush()
os.Exit(status)
}
// Fatalf formats a message to standard output and exits the program.
func Fatalf(format string, args ...interface{}) {
fmt.Fprintf(os.Stderr, "Fatal: "+format+"\n", args...)
logger.Flush()
os.Exit(1)
}
func StartEthereum(ethereum *eth.Ethereum) {
clilogger.Infoln("Starting ", ethereum.Name())
if err := ethereum.Start(); err != nil {
@ -127,7 +135,6 @@ func StartEthereum(ethereum *eth.Ethereum) {
}
func KeyTasks(keyManager *crypto.KeyManager, KeyRing string, GenAddr bool, SecretFile string, ExportDir string, NonInteractive bool) {
var err error
switch {
case GenAddr:
@ -200,24 +207,24 @@ func BlockDo(ethereum *eth.Ethereum, hash []byte) error {
}
func ImportChain(ethereum *eth.Ethereum, fn string) error {
clilogger.Infof("importing chain '%s'\n", fn)
func ImportChain(chain *core.ChainManager, fn string) error {
fmt.Printf("importing chain '%s'\n", fn)
fh, err := os.OpenFile(fn, os.O_RDONLY, os.ModePerm)
if err != nil {
return err
}
defer fh.Close()
var chain types.Blocks
if err := rlp.Decode(fh, &chain); err != nil {
var blocks types.Blocks
if err := rlp.Decode(fh, &blocks); err != nil {
return err
}
ethereum.ChainManager().Reset()
if err := ethereum.ChainManager().InsertChain(chain); err != nil {
chain.Reset()
if err := chain.InsertChain(blocks); err != nil {
return err
}
clilogger.Infof("imported %d blocks\n", len(chain))
fmt.Printf("imported %d blocks\n", len(blocks))
return nil
}

178
cmd/utils/flags.go Normal file
View File

@ -0,0 +1,178 @@
package utils
import (
"crypto/ecdsa"
"path"
"runtime"
"github.com/codegangsta/cli"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/nat"
)
// These are all the command line flags we support.
// If you add to this list, please remember to include the
// flag in the appropriate command definition.
//
// The flags are defined here so their names and help texts
// are the same for all commands.
var (
// General settings
VMTypeFlag = cli.IntFlag{
Name: "vm",
Usage: "Virtual Machine type: 0 is standard VM, 1 is debug VM",
}
KeyRingFlag = cli.StringFlag{
Name: "keyring",
Usage: "Name of keyring to be used",
Value: "",
}
KeyStoreFlag = cli.StringFlag{
Name: "keystore",
Usage: `Where to store keyrings: "db" or "file"`,
Value: "db",
}
DataDirFlag = cli.StringFlag{
Name: "datadir",
Usage: "Data directory to be used",
Value: ethutil.DefaultDataDir(),
}
MinerThreadsFlag = cli.IntFlag{
Name: "minerthreads",
Usage: "Number of miner threads",
Value: runtime.NumCPU(),
}
MiningEnabledFlag = cli.BoolFlag{
Name: "mine",
Usage: "Enable mining",
}
LogFileFlag = cli.StringFlag{
Name: "logfile",
Usage: "Send log output to a file",
}
LogLevelFlag = cli.IntFlag{
Name: "loglevel",
Usage: "0-5 (silent, error, warn, info, debug, debug detail)",
Value: int(logger.InfoLevel),
}
LogFormatFlag = cli.StringFlag{
Name: "logformat",
Usage: `"std" or "raw"`,
Value: "std",
}
// RPC settings
RPCEnabledFlag = cli.BoolFlag{
Name: "rpc",
Usage: "Whether RPC server is enabled",
}
RPCListenAddrFlag = cli.StringFlag{
Name: "rpcaddr",
Usage: "Listening address for the JSON-RPC server",
Value: "127.0.0.1",
}
RPCPortFlag = cli.IntFlag{
Name: "rpcport",
Usage: "Port on which the JSON-RPC server should listen",
Value: 8545,
}
// Network Settings
MaxPeersFlag = cli.IntFlag{
Name: "maxpeers",
Usage: "Maximum number of network peers",
Value: 16,
}
ListenPortFlag = cli.IntFlag{
Name: "port",
Usage: "Network listening port",
Value: 30303,
}
BootnodesFlag = cli.StringFlag{
Name: "bootnodes",
Usage: "Space-separated enode URLs for discovery bootstrap",
Value: "",
}
NodeKeyFileFlag = cli.StringFlag{
Name: "nodekey",
Usage: "P2P node key file",
}
NodeKeyHexFlag = cli.StringFlag{
Name: "nodekeyhex",
Usage: "P2P node key as hex (for testing)",
}
NATFlag = cli.StringFlag{
Name: "nat",
Usage: "Port mapping mechanism (any|none|upnp|pmp|extip:<IP>)",
Value: "any",
}
)
func GetNAT(ctx *cli.Context) nat.Interface {
natif, err := nat.Parse(ctx.GlobalString(NATFlag.Name))
if err != nil {
Fatalf("Option %s: %v", NATFlag.Name, err)
}
return natif
}
func GetNodeKey(ctx *cli.Context) (key *ecdsa.PrivateKey) {
hex, file := ctx.GlobalString(NodeKeyHexFlag.Name), ctx.GlobalString(NodeKeyFileFlag.Name)
var err error
switch {
case file != "" && hex != "":
Fatalf("Options %q and %q are mutually exclusive", NodeKeyFileFlag.Name, NodeKeyHexFlag.Name)
case file != "":
if key, err = crypto.LoadECDSA(file); err != nil {
Fatalf("Option %q: %v", NodeKeyFileFlag.Name, err)
}
case hex != "":
if key, err = crypto.HexToECDSA(hex); err != nil {
Fatalf("Option %q: %v", NodeKeyHexFlag.Name, err)
}
}
return key
}
func GetEthereum(clientID, version string, ctx *cli.Context) *eth.Ethereum {
ethereum, err := eth.New(&eth.Config{
Name: p2p.MakeName(clientID, version),
KeyStore: ctx.GlobalString(KeyStoreFlag.Name),
DataDir: ctx.GlobalString(DataDirFlag.Name),
LogFile: ctx.GlobalString(LogFileFlag.Name),
LogLevel: ctx.GlobalInt(LogLevelFlag.Name),
LogFormat: ctx.GlobalString(LogFormatFlag.Name),
MinerThreads: ctx.GlobalInt(MinerThreadsFlag.Name),
MaxPeers: ctx.GlobalInt(MaxPeersFlag.Name),
Port: ctx.GlobalString(ListenPortFlag.Name),
NAT: GetNAT(ctx),
NodeKey: GetNodeKey(ctx),
KeyRing: ctx.GlobalString(KeyRingFlag.Name),
Shh: true,
Dial: true,
BootNodes: ctx.GlobalString(BootnodesFlag.Name),
})
if err != nil {
exit(err)
}
return ethereum
}
func GetChain(ctx *cli.Context) (*core.ChainManager, ethutil.Database) {
dataDir := ctx.GlobalString(DataDirFlag.Name)
db, err := ethdb.NewLDBDatabase(path.Join(dataDir, "blockchain"))
if err != nil {
Fatalf("Could not open database: %v", err)
}
return core.NewChainManager(db, new(event.TypeMux)), db
}