diff --git a/cmd/statusd-cli/main.go b/cmd/statusd-cli/main.go deleted file mode 100644 index 920c377ca..000000000 --- a/cmd/statusd-cli/main.go +++ /dev/null @@ -1,44 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "os" -) - -const ( - // Addr is the default statusd address to connect to. - Addr = "localhost:51515" -) - -var ( - addr = flag.String("addr", Addr, "set statusd address (default localhost:51515)") -) - -// main is the entrypoint for the statusd command line interface. -func main() { - flag.Usage = printUsage - flag.Parse() - - fmt.Printf("statusd-cli connecting statusd on '%s'\n", *addr) - - // Running REPL. - repl := NewREPL(*addr) - err := repl.Run() - if err != nil { - fmt.Printf("stopped with error: %v\n", err) - os.Exit(-1) - } -} - -// printUsage prints a little help for statusd-cli. -func printUsage() { - fmt.Fprintln(os.Stderr, "Usage: statusd-cli [options]") - fmt.Fprintf(os.Stderr, ` -Examples: - statusd-cli -addr=
# connect statusd on
- -Options: -`) - flag.PrintDefaults() -} diff --git a/cmd/statusd-cli/repl.go b/cmd/statusd-cli/repl.go deleted file mode 100644 index ed04e12fb..000000000 --- a/cmd/statusd-cli/repl.go +++ /dev/null @@ -1,95 +0,0 @@ -package main - -import ( - "bufio" - "fmt" - "net" - "os" - "strconv" - "strings" -) - -// REPL implements the read-eval-print loop for the commands -// to be sent to statusd. -type REPL struct { - addr string -} - -// NewREPL creates a REPL instance communicating with the -// addressed statusd. -func NewREPL(addr string) *REPL { - return &REPL{ - addr: addr, - } -} - -// Run operates the loop to read a command and its arguments, -// execute it via the client, and print the result. -func (r *REPL) Run() error { - var ( - conn net.Conn - reader *bufio.Reader - writer *bufio.Writer - err error - ) - input := bufio.NewReader(os.Stdin) - connect := true - for { - // Connect first time and after connection errors. - if connect { - conn, err = net.Dial("tcp", r.addr) - if err != nil { - return fmt.Errorf("error connecting to statusd: %v", err) - } - connect = false - reader = bufio.NewReader(conn) - writer = bufio.NewWriter(conn) - } - // Read command line. - fmt.Print(">>> ") - command, err := input.ReadString('\n') - if err != nil { - fmt.Printf("ERR %v\n", err) - continue - } - // Check for possible end. - if strings.ToLower(command) == "quit\n" { - return nil - } - // Execute on statusd. - _, err = writer.WriteString(command) - if err != nil { - fmt.Printf("ERR %v\n", err) - connect = true - continue - } - err = writer.Flush() - if err != nil { - fmt.Printf("ERR %v\n", err) - connect = true - continue - } - // Read number of expected result lines. - countStr, err := reader.ReadString('\n') - if err != nil { - fmt.Printf("ERR %v\n", err) - connect = true - continue - } - count, err := strconv.Atoi(strings.TrimSuffix(countStr, "\n")) - if err != nil { - fmt.Printf("ERR %v\n", err) - continue - } - // Read and print result lines. - for i := 0; i < count; i++ { - reply, err := reader.ReadString('\n') - if err != nil { - fmt.Printf("ERR %v\n", err) - continue - } - fmt.Print("<<< ") - fmt.Print(reply) - } - } -} diff --git a/cmd/statusd/debug/commands.go b/cmd/statusd/debug/commands.go deleted file mode 100644 index c0a60f7ec..000000000 --- a/cmd/statusd/debug/commands.go +++ /dev/null @@ -1,168 +0,0 @@ -package debug - -import ( - "fmt" - "go/ast" - "go/parser" - "go/token" - "reflect" - "strconv" - "strings" - - "github.com/status-im/status-go/api" - "github.com/status-im/status-go/params" -) - -// command contains the result of a parsed command line and -// is able to execute it on the command set. -type command struct { - funcName string - args []interface{} -} - -// newCommand parses a command line and returns the command -// for further usage. -func newCommand(commandLine string) (*command, error) { - expr, err := parser.ParseExpr(commandLine) - if err != nil { - return nil, err - } - switch expr := expr.(type) { - case *ast.CallExpr: - f, ok := expr.Fun.(*ast.Ident) - if !ok { - return nil, fmt.Errorf("invalid expression: %q", commandLine) - } - return &command{ - funcName: f.Name, - args: exprsToArgs(expr.Args), - }, nil - default: - return nil, fmt.Errorf("invalid command line: %q", commandLine) - } -} - -// execute calls the method on the passed command set value. -func (c *command) execute(commandSetValue reflect.Value) (replies []string, err error) { - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("invalid API call: %v", r) - } - }() - method := commandSetValue.MethodByName(c.funcName) - if !method.IsValid() { - return nil, fmt.Errorf("command %q not found", c.funcName) - } - argsV := make([]reflect.Value, len(c.args)) - for i, arg := range c.args { - argsV[i] = reflect.ValueOf(arg) - } - repliesV := method.Call(argsV) - replies = make([]string, len(repliesV)) - for i, replyV := range repliesV { - replies[i] = fmt.Sprintf("%v", replyV) - } - return replies, nil -} - -// exprsToArgs converts the argument expressions to arguments. -func exprsToArgs(exprs []ast.Expr) []interface{} { - args := make([]interface{}, len(exprs)) - for i, expr := range exprs { - switch expr := expr.(type) { - case *ast.BasicLit: - switch expr.Kind { - case token.INT: - args[i], _ = strconv.ParseInt(expr.Value, 10, 64) // nolint: gas - case token.FLOAT: - args[i], _ = strconv.ParseFloat(expr.Value, 64) // nolint: gas - case token.CHAR: - args[i] = expr.Value[1] - case token.STRING: - r := strings.NewReplacer("\\n", "\n", "\\t", "\t", "\\\"", "\"") - args[i] = strings.Trim(r.Replace(expr.Value), `"`) - } - case *ast.Ident: - switch expr.Name { - case "true": - args[i] = true - case "false": - args[i] = false - default: - args[i] = expr.Name - } - default: - args[i] = fmt.Sprintf("[unknown: %#v]", expr) - } - } - return args -} - -// commandSet implements the set of commands the debugger unterstands. -// In the beginning a subset of the Status API, may later grow to -// utility commands. -// -// Direct invocation of commands on the Status API sometimes sadly -// is not possible due to non-low-level arguments. Here this wrapper -// helps. -type commandSet struct { - statusBackend *api.StatusBackend -} - -// newCommandSet creates the command set for the passed Status API -// instance. -func newCommandSet(statusBackend *api.StatusBackend) *commandSet { - return &commandSet{ - statusBackend: statusBackend, - } -} - -// StartNode loads the configuration out of the passed string and -// starts a node with it. -func (cs *commandSet) StartNode(config string) error { - nodeConfig, err := params.NewConfigFromJSON(config) - if err != nil { - return err - } - return cs.statusBackend.StartNode(nodeConfig) -} - -// StopNode starts the stopped node. -func (cs *commandSet) StopNode() error { - return cs.statusBackend.StopNode() -} - -// ResetChainData removes chain data from data directory. -func (cs *commandSet) ResetChainData() error { - return cs.statusBackend.ResetChainData() -} - -// CallRPC calls status node via RPC. -func (cs *commandSet) CallRPC(inputJSON string) string { - return cs.statusBackend.CallRPC(inputJSON) -} - -// CreateAccount creates an internal geth account. -func (cs *commandSet) CreateAccount(password string) (string, string, string, error) { - return cs.statusBackend.AccountManager().CreateAccount(password) -} - -// CreateChildAccount creates a sub-account. -func (cs *commandSet) CreateChildAccount(parentAddress, password string) (string, string, error) { - return cs.statusBackend.AccountManager().CreateChildAccount(parentAddress, password) -} - -// RecoverAccount re-creates the master key using the given details. -func (cs *commandSet) RecoverAccount(password, mnemonic string) (string, string, error) { - return cs.statusBackend.AccountManager().RecoverAccount(password, mnemonic) -} - -// SelectAccount selects the addressed account. -func (cs *commandSet) SelectAccount(address, password string) error { - return cs.statusBackend.SelectAccount(address, password) -} - -// Logout clears the Whisper identities. -func (cs *commandSet) Logout() error { - return cs.statusBackend.Logout() -} diff --git a/cmd/statusd/debug/debug.go b/cmd/statusd/debug/debug.go deleted file mode 100644 index 0e1bdea94..000000000 --- a/cmd/statusd/debug/debug.go +++ /dev/null @@ -1,112 +0,0 @@ -package debug - -import ( - "bufio" - "fmt" - "net" - "reflect" - - "github.com/ethereum/go-ethereum/log" - "github.com/status-im/status-go/api" -) - -const ( - // CLIPort is the CLI port. - CLIPort = "51515" -) - -// Server provides a debug server receiving line based commands from -// a CLI via the debugging port and executing those on the Status API -// using reflection. The returned values will be rendered as -// string and returned to the CLI. -type Server struct { - commandSetValue reflect.Value - listener net.Listener - log log.Logger -} - -// New creates a debug server using the passed Status API. -// It also starts the server. -func New(statusBackend *api.StatusBackend, port string) (*Server, error) { - listener, err := net.Listen("tcp", fmt.Sprintf(":%s", port)) // nolint - if err != nil { - return nil, err - } - s := Server{ - commandSetValue: reflect.ValueOf(newCommandSet(statusBackend)), - listener: listener, - log: log.New("package", "status-go/cmd/statusd/debug.Server"), - } - go s.backend() - return &s, nil -} - -// backend receives the commands and executes them on -// the Status API. -func (s *Server) backend() { - for { - conn, err := s.listener.Accept() - if err != nil { - s.log.Error("cannot establish debug connection", "error", err) - continue - } - go s.handleConnection(conn) - } -} - -// handleConnection handles all commands of one connection. -func (s *Server) handleConnection(conn net.Conn) { - reader := bufio.NewReader(conn) - writer := bufio.NewWriter(conn) - defer func() { - if err := conn.Close(); err != nil { - s.log.Error("error while closing debug connection", "error", err) - } - }() - // Read, execute, and respond commands of a session. - for { - var ( - replies []string - err error - ) - command, err := s.readCommandLine(reader) - if err != nil { - replies = []string{fmt.Sprintf("cannot read command: %v", err)} - } else { - replies, err = command.execute(s.commandSetValue) - if err != nil { - replies = []string{fmt.Sprintf("cannot execute command: %v", err)} - } - } - err = s.writeReplies(writer, replies) - if err != nil { - s.log.Error("cannot write replies", "error", err) - return - } - } -} - -// readCommandLine receives a command line via network and -// parses it into an executable command. -func (s *Server) readCommandLine(reader *bufio.Reader) (*command, error) { - commandLine, err := reader.ReadString('\n') - if err != nil { - return nil, err - } - return newCommand(commandLine) -} - -// writeReplies sends the replies back to the CLI. -func (s *Server) writeReplies(writer *bufio.Writer, replies []string) error { - _, err := fmt.Fprintf(writer, "%d\n", len(replies)) - if err != nil { - return err - } - for i, reply := range replies { - _, err = fmt.Fprintf(writer, "[%d] %s\n", i, reply) - if err != nil { - return err - } - } - return writer.Flush() -} diff --git a/cmd/statusd/debug/debug_test.go b/cmd/statusd/debug/debug_test.go deleted file mode 100644 index 3c9ccc9af..000000000 --- a/cmd/statusd/debug/debug_test.go +++ /dev/null @@ -1,219 +0,0 @@ -package debug_test - -import ( - "bufio" - "fmt" - "io/ioutil" - "net" - "os" - "strconv" - "strings" - "sync" - "testing" - - "github.com/status-im/status-go/api" - "github.com/status-im/status-go/cmd/statusd/debug" - "github.com/status-im/status-go/params" - testifyAssert "github.com/stretchr/testify/assert" -) - -const stopNodeCommandLine = "StopNode()" - -// TestInvalidExpressions tests invalid expressions. -func TestInvalidExpressions(t *testing.T) { - assert := testifyAssert.New(t) - - startDebugging(assert) - - conn := connectDebug(assert) - tests := []struct { - commandLine string - replies []string - }{ - { - commandLine: "", - replies: []string{"[0] cannot read command: 1:2: expected operand, found 'EOF'"}, - }, { - commandLine: "1 + 1", - replies: []string{"[0] cannot read command: invalid command line: \"1 + 1\\n\""}, - }, { - commandLine: "func() { panic(42) }", - replies: []string{"[0] cannot read command: invalid command line: \"func() { panic(42) }\\n\""}, - }, { - commandLine: "DoesNotExist()", - replies: []string{"[0] cannot execute command: command \"DoesNotExist\" not found"}, - }, { - commandLine: "node.Start()", - replies: []string{"[0] cannot read command: invalid expression: \"node.Start()\\n\""}, - }, - } - - for _, test := range tests { - replies := sendCommandLine(assert, conn, test.commandLine) - assert.Equal(test.replies, replies) - } -} - -// TestStartStopNode tests starting and stopping a node remotely. -func TestStartStopNode(t *testing.T) { - assert := testifyAssert.New(t) - configJSON, cleanup, err := mkConfigJSON("start-stop-node") - assert.NoError(err) - defer cleanup() - - startDebugging(assert) - - conn := connectDebug(assert) - - commandLine := fmt.Sprintf("StartNode(%q)", configJSON) - replies := sendCommandLine(assert, conn, commandLine) - assert.Len(replies, 1) - assert.Equal("[0] ", replies[0]) - - replies = sendCommandLine(assert, conn, stopNodeCommandLine) - assert.Len(replies, 1) - assert.Equal("[0] ", replies[0]) -} - -// TestCreateAccount tests creating an account on the server. -func TestCreateAccount(t *testing.T) { - assert := testifyAssert.New(t) - configJSON, cleanup, err := mkConfigJSON("create-account") - assert.NoError(err) - defer cleanup() - - startDebugging(assert) - - conn := connectDebug(assert) - - commandLine := fmt.Sprintf("StartNode(%q)", configJSON) - replies := sendCommandLine(assert, conn, commandLine) - assert.Len(replies, 1) - assert.Equal("[0] ", replies[0]) - - commandLine = fmt.Sprintf("CreateAccount(%q)", "password") - replies = sendCommandLine(assert, conn, commandLine) - assert.Len(replies, 4) - assert.NotEqual("[0] ", replies[0]) - assert.NotEqual("[1] ", replies[1]) - assert.NotEqual("[2] ", replies[2]) - assert.Equal("[3] ", replies[3]) - - replies = sendCommandLine(assert, conn, stopNodeCommandLine) - assert.Len(replies, 1) - assert.Equal("[0] ", replies[0]) -} - -// TestSelectAccountLogout tests selecting an account on the server -// and logging out afterwards. -func TestSelectAccountLogout(t *testing.T) { - assert := testifyAssert.New(t) - configJSON, cleanup, err := mkConfigJSON("select-account") - assert.NoError(err) - defer cleanup() - - startDebugging(assert) - - conn := connectDebug(assert) - - commandLine := fmt.Sprintf("StartNode(%q)", configJSON) - replies := sendCommandLine(assert, conn, commandLine) - assert.Len(replies, 1) - assert.Equal("[0] ", replies[0]) - - commandLine = fmt.Sprintf("CreateAccount(%q)", "password") - replies = sendCommandLine(assert, conn, commandLine) - assert.Len(replies, 4) - assert.NotEqual("[0] ", replies[0]) - assert.NotEqual("[1] ", replies[1]) - assert.NotEqual("[2] ", replies[2]) - assert.Equal("[3] ", replies[3]) - - address := replies[0][4:] - - commandLine = fmt.Sprintf("SelectAccount(%q, %q)", address, "password") - replies = sendCommandLine(assert, conn, commandLine) - assert.Len(replies, 1) - assert.Equal("[0] ", replies[0]) - - commandLine = "Logout()" - replies = sendCommandLine(assert, conn, commandLine) - assert.Len(replies, 1) - assert.Equal("[0] ", replies[0]) - - replies = sendCommandLine(assert, conn, stopNodeCommandLine) - assert.Len(replies, 1) - assert.Equal("[0] ", replies[0]) -} - -//----- -// HELPERS -//----- - -var ( - mu sync.Mutex - d *debug.Server -) - -// startDebugging lazily creates or reuses a debug instance. -func startDebugging(assert *testifyAssert.Assertions) { - mu.Lock() - defer mu.Unlock() - if d == nil { - var err error - backend := api.NewStatusBackend() - d, err = debug.New(backend, debug.CLIPort) - assert.NoError(err) - } -} - -// mkConfigJSON creates a configuration matching to -// a temporary directory and a cleanup for that directory. -func mkConfigJSON(name string) (string, func(), error) { - tmpDir, err := ioutil.TempDir(os.TempDir(), name) - if err != nil { - return "", nil, err - } - cleanup := func() { - os.RemoveAll(tmpDir) //nolint: errcheck - } - configJSON := `{ - "NetworkId": ` + strconv.Itoa(params.RopstenNetworkID) + `, - "DataDir": "` + tmpDir + `", - "BackupDisabledDataDir": "` + tmpDir + `", - "KeyStoreDir": "` + tmpDir + `/keystore", - "LogLevel": "INFO", - "NoDiscovery": true, - "RPCEnabled": true - }` - return configJSON, cleanup, nil -} - -// connectDebug connects to the debug instance. -func connectDebug(assert *testifyAssert.Assertions) net.Conn { - conn, err := net.Dial("tcp", ":51515") - assert.NoError(err) - return conn -} - -// sendCommandLine sends a command line via the passed connection. -// nolint: interfacer -func sendCommandLine(assert *testifyAssert.Assertions, conn net.Conn, commandLine string) []string { - reader := bufio.NewReader(conn) - writer := bufio.NewWriter(conn) - _, err := writer.WriteString(commandLine + "\n") - assert.NoError(err) - err = writer.Flush() - assert.NoError(err) - countStr, err := reader.ReadString('\n') - assert.NoError(err) - count, err := strconv.Atoi(strings.TrimSuffix(countStr, "\n")) - assert.NoError(err) - replies := make([]string, count) - for i := 0; i < count; i++ { - reply, err := reader.ReadString('\n') - assert.NoError(err) - replies[i] = strings.TrimSuffix(reply, "\n") - } - return replies -} diff --git a/cmd/statusd/main.go b/cmd/statusd/main.go index 04edd4dbb..487cbee8b 100644 --- a/cmd/statusd/main.go +++ b/cmd/statusd/main.go @@ -16,7 +16,6 @@ import ( "github.com/ethereum/go-ethereum/log" gethmetrics "github.com/ethereum/go-ethereum/metrics" "github.com/status-im/status-go/api" - "github.com/status-im/status-go/cmd/statusd/debug" "github.com/status-im/status-go/logutils" nodemetrics "github.com/status-im/status-go/metrics/node" "github.com/status-im/status-go/node" @@ -37,8 +36,6 @@ var ( configFiles configFlags logLevel = flag.String("log", "", `Log level, one of: "ERROR", "WARN", "INFO", "DEBUG", and "TRACE"`) logWithoutColors = flag.Bool("log-without-color", false, "Disables log colors") - cliEnabled = flag.Bool("cli", false, "Enable debugging CLI server") - cliPort = flag.String("cli-port", debug.CLIPort, "CLI server's listening port") pprofEnabled = flag.Bool("pprof", false, "Enable runtime profiling via pprof") pprofPort = flag.Int("pprof-port", 52525, "Port for runtime profiling via pprof") version = flag.Bool("version", false, "Print version and dump configuration") @@ -129,15 +126,6 @@ func main() { // handle interrupt signals interruptCh := haltOnInterruptSignal(backend.StatusNode()) - // Check if debugging CLI connection shall be enabled. - if *cliEnabled { - err := startDebug(backend) - if err != nil { - logger.Error("Starting debugging CLI server failed", "error", err) - return - } - } - // Check if profiling shall be enabled. if *pprofEnabled { profiling.NewProfiler(*pprofPort).Go() @@ -193,12 +181,6 @@ func setupLogging(config *params.NodeConfig) { } } -// startDebug starts the debugging API server. -func startDebug(backend *api.StatusBackend) error { - _, err := debug.New(backend, *cliPort) - return err -} - // startCollectingStats collects various stats about the node and other protocols like Whisper. func startCollectingNodeMetrics(interruptCh <-chan struct{}, statusNode *node.StatusNode) { logger.Info("Starting collecting node metrics") @@ -278,7 +260,6 @@ Examples: statusd -c ./default.json # run node with configuration specified in ./default.json file statusd -c ./default.json -c ./standalone.json # run node with configuration specified in ./default.json file, after merging ./standalone.json file statusd -c ./default.json -metrics # run node with configuration specified in ./default.json file, and expose ethereum metrics with debug_metrics jsonrpc call - statusd -c ./default.json -cli # run node with configuration specified in ./default.json file, and enable connection by statusd-cli on default port Options: `