diff --git a/internal/cli/attach.go b/internal/cli/attach.go index 75c88d002f..b48bc5518b 100644 --- a/internal/cli/attach.go +++ b/internal/cli/attach.go @@ -6,6 +6,7 @@ import ( "path/filepath" "strings" + "github.com/ethereum/go-ethereum/internal/cli/flagset" "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/cmd/utils" @@ -14,18 +15,22 @@ import ( "github.com/mitchellh/cli" ) -// VersionCommand is the command to show the version of the agent +// AttachCommand is the command to Connect to remote Bor IPC console type AttachCommand struct { - UI cli.Ui - Meta *Meta - Meta2 *Meta2 + UI cli.Ui + Meta *Meta + Meta2 *Meta2 + ExecCMD string + Endpoint string + PreloadJSFlag string + JSpathFlag string } // Help implements the cli.Command interface func (c *AttachCommand) Help() string { return `Usage: bor attach - Connect to Bor IPC console.` + Connect to remote Bor IPC console.` } // Synopsis implements the cli.Command interface @@ -33,27 +38,65 @@ func (c *AttachCommand) Synopsis() string { return "Connect to Bor via IPC" } +func (c *AttachCommand) Flags() *flagset.Flagset { + + f := flagset.NewFlagSet("attach") + + f.StringFlag(&flagset.StringFlag{ + Name: "exec", + Usage: "Command to run in remote console", + Value: &c.ExecCMD, + }) + + f.StringFlag(&flagset.StringFlag{ + Name: "preload", + Usage: "Comma separated list of JavaScript files to preload into the console", + Value: &c.PreloadJSFlag, + }) + + f.StringFlag(&flagset.StringFlag{ + Name: "jspath", + Usage: "JavaScript root path for `loadScript`", + Value: &c.JSpathFlag, + }) + + return f +} + // Run implements the cli.Command interface func (c *AttachCommand) Run(args []string) int { - c.remoteConsole(args) + flags := c.Flags() + + //check if first arg is flag or IPC location + if len(args) == 0 { + args = append(args, "") + } + if args[0] != "" && string(args[0][0:2]) == "--" { + if err := flags.Parse(args); err != nil { + c.UI.Error(err.Error()) + return 1 + } + } else { + c.Endpoint = args[0] + if err := flags.Parse(args[1:]); err != nil { + c.UI.Error(err.Error()) + return 1 + } + } + c.remoteConsole() return 0 } // remoteConsole will connect to a remote bor instance, attaching a JavaScript // console to it. -func (c *AttachCommand) remoteConsole(args []string) error { +func (c *AttachCommand) remoteConsole() error { // Attach to a remotely running geth instance and start the JavaScript console - if len(args) < 1 { - args = append(args, "") - } - - endpoint := args[0] path := node.DefaultDataDir() - if endpoint == "" { + if c.Endpoint == "" { if c.Meta.dataDir != "" { path = c.Meta.dataDir } @@ -61,16 +104,17 @@ func (c *AttachCommand) remoteConsole(args []string) error { homeDir, _ := os.UserHomeDir() path = filepath.Join(homeDir, "/.bor/data") } - endpoint = fmt.Sprintf("%s/bor.ipc", path) + c.Endpoint = fmt.Sprintf("%s/bor.ipc", path) } - client, err := dialRPC(endpoint) + client, err := dialRPC(c.Endpoint) if err != nil { - utils.Fatalf("Unable to attach to remote geth: %v", err) + utils.Fatalf("Unable to attach to remote bor: %v", err) } config := console.Config{ DataDir: path, - DocRoot: utils.JSpathFlag.Name, + DocRoot: c.JSpathFlag, Client: client, + Preload: c.MakeConsolePreloads(), } console, err := console.New(config) @@ -79,11 +123,9 @@ func (c *AttachCommand) remoteConsole(args []string) error { } defer console.Stop(false) - if len(args) > 1 { - if script := args[1]; script == "--exec" { - console.Evaluate(args[2]) - return nil - } + if c.ExecCMD != "" { + console.Evaluate(c.ExecCMD) + return nil } // Otherwise print the welcome screen and enter interactive mode @@ -106,3 +148,19 @@ func dialRPC(endpoint string) (*rpc.Client, error) { } return rpc.Dial(endpoint) } + +// MakeConsolePreloads retrieves the absolute paths for the console JavaScript +// scripts to preload before starting. +func (c *AttachCommand) MakeConsolePreloads() []string { + // Skip preloading if there's nothing to preload + if c.PreloadJSFlag == "" { + return nil + } + // Otherwise resolve absolute paths and return them + var preloads []string + + for _, file := range strings.Split(c.PreloadJSFlag, ",") { + preloads = append(preloads, strings.TrimSpace(file)) + } + return preloads +} diff --git a/internal/cli/command.go b/internal/cli/command.go index 9e373a65cd..baa09368b6 100644 --- a/internal/cli/command.go +++ b/internal/cli/command.go @@ -137,6 +137,11 @@ func commands() map[string]cli.CommandFactory { Meta2: meta2, }, nil }, + "console": func() (cli.Command, error) { + return &server.ConsoleCommand{ + UI: ui, + }, nil + }, } } diff --git a/internal/cli/server/command_flags.go b/internal/cli/server/command_flags.go new file mode 100644 index 0000000000..f43535a459 --- /dev/null +++ b/internal/cli/server/command_flags.go @@ -0,0 +1,504 @@ +package server + +import ( + "github.com/ethereum/go-ethereum/internal/cli/flagset" +) + +func (c *ConsoleCommand) Flags() *flagset.Flagset { + c.cliConfig = DefaultConfig() + + f := flagset.NewFlagSet("console") + + // console flags + f.StringFlag(&flagset.StringFlag{ + Name: "exec", + Usage: "Command to run in remote console", + Value: &c.ExecCMD, + }) + + f.StringFlag(&flagset.StringFlag{ + Name: "preload", + Usage: "Comma separated list of JavaScript files to preload into the console", + Value: &c.PreloadJSFlag, + }) + + f.StringFlag(&flagset.StringFlag{ + Name: "jspath", + Usage: "JavaScript root path for `loadScript`", + Value: &c.JSpathFlag, + }) + + // server flags + f.StringFlag(&flagset.StringFlag{ + Name: "chain", + Usage: "Name of the chain to sync", + Value: &c.cliConfig.Chain, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "name", + Usage: "Name/Identity of the node", + Value: &c.cliConfig.Name, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "log-level", + Usage: "Set log level for the server", + Value: &c.cliConfig.LogLevel, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "datadir", + Usage: "Path of the data directory to store information", + Value: &c.cliConfig.DataDir, + }) + f.SliceStringFlag(&flagset.SliceStringFlag{ + Name: "config", + Usage: "File for the config file", + Value: &c.configFile, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "syncmode", + Usage: `Blockchain sync mode ("fast", "full", "snap" or "light")`, + Value: &c.cliConfig.SyncMode, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "gcmode", + Usage: `Blockchain garbage collection mode ("full", "archive")`, + Value: &c.cliConfig.GcMode, + }) + f.MapStringFlag(&flagset.MapStringFlag{ + Name: "whitelist", + Usage: "Comma separated block number-to-hash mappings to enforce (=)", + Value: &c.cliConfig.Whitelist, + }) + f.BoolFlag(&flagset.BoolFlag{ + Name: "snapshot", + Usage: `Enables snapshot-database mode (default = enable)`, + Value: &c.cliConfig.Snapshot, + }) + + // heimdall + f.StringFlag(&flagset.StringFlag{ + Name: "bor.heimdall", + Usage: "URL of Heimdall service", + Value: &c.cliConfig.Heimdall.URL, + }) + f.BoolFlag(&flagset.BoolFlag{ + Name: "bor.withoutheimdall", + Usage: "Run without Heimdall service (for testing purpose)", + Value: &c.cliConfig.Heimdall.Without, + }) + + // txpool options + f.SliceStringFlag(&flagset.SliceStringFlag{ + Name: "txpool.locals", + Usage: "Comma separated accounts to treat as locals (no flush, priority inclusion)", + Value: &c.cliConfig.TxPool.Locals, + }) + f.BoolFlag(&flagset.BoolFlag{ + Name: "txpool.nolocals", + Usage: "Disables price exemptions for locally submitted transactions", + Value: &c.cliConfig.TxPool.NoLocals, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "txpool.journal", + Usage: "Disk journal for local transaction to survive node restarts", + Value: &c.cliConfig.TxPool.Journal, + }) + f.DurationFlag(&flagset.DurationFlag{ + Name: "txpool.rejournal", + Usage: "Time interval to regenerate the local transaction journal", + Value: &c.cliConfig.TxPool.Rejournal, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "txpool.pricelimit", + Usage: "Minimum gas price limit to enforce for acceptance into the pool", + Value: &c.cliConfig.TxPool.PriceLimit, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "txpool.pricebump", + Usage: "Price bump percentage to replace an already existing transaction", + Value: &c.cliConfig.TxPool.PriceBump, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "txpool.accountslots", + Usage: "Minimum number of executable transaction slots guaranteed per account", + Value: &c.cliConfig.TxPool.AccountSlots, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "txpool.globalslots", + Usage: "Maximum number of executable transaction slots for all accounts", + Value: &c.cliConfig.TxPool.GlobalSlots, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "txpool.accountqueue", + Usage: "Maximum number of non-executable transaction slots permitted per account", + Value: &c.cliConfig.TxPool.AccountQueue, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "txpool.globalqueue", + Usage: "Maximum number of non-executable transaction slots for all accounts", + Value: &c.cliConfig.TxPool.GlobalQueue, + }) + f.DurationFlag(&flagset.DurationFlag{ + Name: "txpool.lifetime", + Usage: "Maximum amount of time non-executable transaction are queued", + Value: &c.cliConfig.TxPool.LifeTime, + }) + + // sealer options + f.BoolFlag(&flagset.BoolFlag{ + Name: "mine", + Usage: "Enable mining", + Value: &c.cliConfig.Sealer.Enabled, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "miner.etherbase", + Usage: "Public address for block mining rewards (default = first account)", + Value: &c.cliConfig.Sealer.Etherbase, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "miner.extradata", + Usage: "Block extra data set by the miner (default = client version)", + Value: &c.cliConfig.Sealer.ExtraData, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "miner.gaslimit", + Usage: "Target gas ceiling for mined blocks", + Value: &c.cliConfig.Sealer.GasCeil, + }) + f.BigIntFlag(&flagset.BigIntFlag{ + Name: "miner.gasprice", + Usage: "Minimum gas price for mining a transaction", + Value: c.cliConfig.Sealer.GasPrice, + }) + + // ethstats + f.StringFlag(&flagset.StringFlag{ + Name: "ethstats", + Usage: "Reporting URL of a ethstats service (nodename:secret@host:port)", + Value: &c.cliConfig.Ethstats, + }) + + // gas price oracle + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "gpo.blocks", + Usage: "Number of recent blocks to check for gas prices", + Value: &c.cliConfig.Gpo.Blocks, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "gpo.percentile", + Usage: "Suggested gas price is the given percentile of a set of recent transaction gas prices", + Value: &c.cliConfig.Gpo.Percentile, + }) + f.BigIntFlag(&flagset.BigIntFlag{ + Name: "gpo.maxprice", + Usage: "Maximum gas price will be recommended by gpo", + Value: c.cliConfig.Gpo.MaxPrice, + }) + f.BigIntFlag(&flagset.BigIntFlag{ + Name: "gpo.ignoreprice", + Usage: "Gas price below which gpo will ignore transactions", + Value: c.cliConfig.Gpo.IgnorePrice, + }) + + // cache options + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "cache", + Usage: "Megabytes of memory allocated to internal caching (default = 4096 mainnet full node)", + Value: &c.cliConfig.Cache.Cache, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "cache.database", + Usage: "Percentage of cache memory allowance to use for database io", + Value: &c.cliConfig.Cache.PercDatabase, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "cache.trie", + Usage: "Percentage of cache memory allowance to use for trie caching (default = 15% full mode, 30% archive mode)", + Value: &c.cliConfig.Cache.PercTrie, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "cache.trie.journal", + Usage: "Disk journal directory for trie cache to survive node restarts", + Value: &c.cliConfig.Cache.Journal, + }) + f.DurationFlag(&flagset.DurationFlag{ + Name: "cache.trie.rejournal", + Usage: "Time interval to regenerate the trie cache journal", + Value: &c.cliConfig.Cache.Rejournal, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "cache.gc", + Usage: "Percentage of cache memory allowance to use for trie pruning (default = 25% full mode, 0% archive mode)", + Value: &c.cliConfig.Cache.PercGc, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "cache.snapshot", + Usage: "Percentage of cache memory allowance to use for snapshot caching (default = 10% full mode, 20% archive mode)", + Value: &c.cliConfig.Cache.PercSnapshot, + }) + f.BoolFlag(&flagset.BoolFlag{ + Name: "cache.noprefetch", + Usage: "Disable heuristic state prefetch during block import (less CPU and disk IO, more time waiting for data)", + Value: &c.cliConfig.Cache.NoPrefetch, + }) + f.BoolFlag(&flagset.BoolFlag{ + Name: "cache.preimages", + Usage: "Enable recording the SHA3/keccak preimages of trie keys", + Value: &c.cliConfig.Cache.Preimages, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "txlookuplimit", + Usage: "Number of recent blocks to maintain transactions index for (default = about one year, 0 = entire chain)", + Value: &c.cliConfig.Cache.TxLookupLimit, + }) + + // rpc options + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "rpc.gascap", + Usage: "Sets a cap on gas that can be used in eth_call/estimateGas (0=infinite)", + Value: &c.cliConfig.JsonRPC.GasCap, + }) + f.Float64Flag(&flagset.Float64Flag{ + Name: "rpc.txfeecap", + Usage: "Sets a cap on transaction fee (in ether) that can be sent via the RPC APIs (0 = no cap)", + Value: &c.cliConfig.JsonRPC.TxFeeCap, + }) + f.BoolFlag(&flagset.BoolFlag{ + Name: "ipcdisable", + Usage: "Disable the IPC-RPC server", + Value: &c.cliConfig.JsonRPC.IPCDisable, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "ipcpath", + Usage: "Filename for IPC socket/pipe within the datadir (explicit paths escape it)", + Value: &c.cliConfig.JsonRPC.IPCPath, + }) + f.SliceStringFlag(&flagset.SliceStringFlag{ + Name: "jsonrpc.corsdomain", + Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)", + Value: &c.cliConfig.JsonRPC.Cors, + }) + f.SliceStringFlag(&flagset.SliceStringFlag{ + Name: "jsonrpc.vhosts", + Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.", + Value: &c.cliConfig.JsonRPC.VHost, + }) + + // http options + f.BoolFlag(&flagset.BoolFlag{ + Name: "http", + Usage: "Enable the HTTP-RPC server", + Value: &c.cliConfig.JsonRPC.Http.Enabled, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "http.addr", + Usage: "HTTP-RPC server listening interface", + Value: &c.cliConfig.JsonRPC.Http.Host, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "http.port", + Usage: "HTTP-RPC server listening port", + Value: &c.cliConfig.JsonRPC.Http.Port, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "http.rpcprefix", + Usage: "HTTP path path prefix on which JSON-RPC is served. Use '/' to serve on all paths.", + Value: &c.cliConfig.JsonRPC.Http.Prefix, + }) + f.SliceStringFlag(&flagset.SliceStringFlag{ + Name: "http.modules", + Usage: "API's offered over the HTTP-RPC interface", + Value: &c.cliConfig.JsonRPC.Http.Modules, + }) + + // ws options + f.BoolFlag(&flagset.BoolFlag{ + Name: "ws", + Usage: "Enable the WS-RPC server", + Value: &c.cliConfig.JsonRPC.Ws.Enabled, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "ws.addr", + Usage: "WS-RPC server listening interface", + Value: &c.cliConfig.JsonRPC.Ws.Host, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "ws.port", + Usage: "WS-RPC server listening port", + Value: &c.cliConfig.JsonRPC.Ws.Port, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "ws.rpcprefix", + Usage: "HTTP path prefix on which JSON-RPC is served. Use '/' to serve on all paths.", + Value: &c.cliConfig.JsonRPC.Ws.Prefix, + }) + f.SliceStringFlag(&flagset.SliceStringFlag{ + Name: "ws.modules", + Usage: "API's offered over the WS-RPC interface", + Value: &c.cliConfig.JsonRPC.Ws.Modules, + }) + + // graphql options + f.BoolFlag(&flagset.BoolFlag{ + Name: "graphql", + Usage: "Enable GraphQL on the HTTP-RPC server. Note that GraphQL can only be started if an HTTP server is started as well.", + Value: &c.cliConfig.JsonRPC.Graphql.Enabled, + }) + + // p2p options + f.StringFlag(&flagset.StringFlag{ + Name: "bind", + Usage: "Network binding address", + Value: &c.cliConfig.P2P.Bind, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "port", + Usage: "Network listening port", + Value: &c.cliConfig.P2P.Port, + }) + f.SliceStringFlag(&flagset.SliceStringFlag{ + Name: "bootnodes", + Usage: "Comma separated enode URLs for P2P discovery bootstrap", + Value: &c.cliConfig.P2P.Discovery.Bootnodes, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "maxpeers", + Usage: "Maximum number of network peers (network disabled if set to 0)", + Value: &c.cliConfig.P2P.MaxPeers, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "maxpendpeers", + Usage: "Maximum number of pending connection attempts (defaults used if set to 0)", + Value: &c.cliConfig.P2P.MaxPendPeers, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "nat", + Usage: "NAT port mapping mechanism (any|none|upnp|pmp|extip:)", + Value: &c.cliConfig.P2P.NAT, + }) + f.BoolFlag(&flagset.BoolFlag{ + Name: "nodiscover", + Usage: "Disables the peer discovery mechanism (manual peer addition)", + Value: &c.cliConfig.P2P.NoDiscover, + }) + f.BoolFlag(&flagset.BoolFlag{ + Name: "v5disc", + Usage: "Enables the experimental RLPx V5 (Topic Discovery) mechanism", + Value: &c.cliConfig.P2P.Discovery.V5Enabled, + }) + + // metrics + f.BoolFlag(&flagset.BoolFlag{ + Name: "metrics", + Usage: "Enable metrics collection and reporting", + Value: &c.cliConfig.Telemetry.Enabled, + }) + f.BoolFlag(&flagset.BoolFlag{ + Name: "metrics.expensive", + Usage: "Enable expensive metrics collection and reporting", + Value: &c.cliConfig.Telemetry.Expensive, + }) + f.BoolFlag(&flagset.BoolFlag{ + Name: "metrics.influxdb", + Usage: "Enable metrics export/push to an external InfluxDB database (v1)", + Value: &c.cliConfig.Telemetry.InfluxDB.V1Enabled, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "metrics.influxdb.endpoint", + Usage: "InfluxDB API endpoint to report metrics to", + Value: &c.cliConfig.Telemetry.InfluxDB.Endpoint, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "metrics.influxdb.database", + Usage: "InfluxDB database name to push reported metrics to", + Value: &c.cliConfig.Telemetry.InfluxDB.Database, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "metrics.influxdb.username", + Usage: "Username to authorize access to the database", + Value: &c.cliConfig.Telemetry.InfluxDB.Username, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "metrics.influxdb.password", + Usage: "Password to authorize access to the database", + Value: &c.cliConfig.Telemetry.InfluxDB.Password, + }) + f.MapStringFlag(&flagset.MapStringFlag{ + Name: "metrics.influxdb.tags", + Usage: "Comma-separated InfluxDB tags (key/values) attached to all measurements", + Value: &c.cliConfig.Telemetry.InfluxDB.Tags, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "metrics.prometheus-addr", + Usage: "Address for Prometheus Server", + Value: &c.cliConfig.Telemetry.PrometheusAddr, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "metrics.opencollector-endpoint", + Usage: "OpenCollector Endpoint (host:port)", + Value: &c.cliConfig.Telemetry.OpenCollectorEndpoint, + }) + // influx db v2 + f.BoolFlag(&flagset.BoolFlag{ + Name: "metrics.influxdbv2", + Usage: "Enable metrics export/push to an external InfluxDB v2 database", + Value: &c.cliConfig.Telemetry.InfluxDB.V2Enabled, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "metrics.influxdb.token", + Usage: "Token to authorize access to the database (v2 only)", + Value: &c.cliConfig.Telemetry.InfluxDB.Token, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "metrics.influxdb.bucket", + Usage: "InfluxDB bucket name to push reported metrics to (v2 only)", + Value: &c.cliConfig.Telemetry.InfluxDB.Bucket, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "metrics.influxdb.organization", + Usage: "InfluxDB organization name (v2 only)", + Value: &c.cliConfig.Telemetry.InfluxDB.Organization, + }) + + // account + f.SliceStringFlag(&flagset.SliceStringFlag{ + Name: "unlock", + Usage: "Comma separated list of accounts to unlock", + Value: &c.cliConfig.Accounts.Unlock, + }) + f.StringFlag(&flagset.StringFlag{ + Name: "password", + Usage: "Password file to use for non-interactive password input", + Value: &c.cliConfig.Accounts.PasswordFile, + }) + f.BoolFlag(&flagset.BoolFlag{ + Name: "allow-insecure-unlock", + Usage: "Allow insecure account unlocking when account-related RPCs are exposed by http", + Value: &c.cliConfig.Accounts.AllowInsecureUnlock, + }) + f.BoolFlag(&flagset.BoolFlag{ + Name: "lightkdf", + Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength", + Value: &c.cliConfig.Accounts.UseLightweightKDF, + }) + + // grpc + f.StringFlag(&flagset.StringFlag{ + Name: "grpc.addr", + Usage: "Address and port to bind the GRPC server", + Value: &c.cliConfig.GRPC.Addr, + }) + + // developer + f.BoolFlag(&flagset.BoolFlag{ + Name: "dev", + Usage: "Enable developer mode with ephemeral proof-of-authority network and a pre-funded developer account, mining enabled", + Value: &c.cliConfig.Developer.Enabled, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "dev.period", + Usage: "Block period to use in developer mode (0 = mine only if transaction pending)", + Value: &c.cliConfig.Developer.Period, + }) + return f +} diff --git a/internal/cli/server/console.go b/internal/cli/server/console.go new file mode 100644 index 0000000000..05b333111c --- /dev/null +++ b/internal/cli/server/console.go @@ -0,0 +1,163 @@ +package server + +import ( + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/console" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/rpc" + "github.com/mitchellh/cli" +) + +// ConsoleCommand is the command to Connect to remote Bor IPC console +type ConsoleCommand struct { + // cli configuration + cliConfig *Config + + // final configuration + config *Config + + configFile []string + UI cli.Ui + ExecCMD string + Endpoint string + PreloadJSFlag string + JSpathFlag string + srv *Server +} + +// Help implements the cli.Command interface +func (c *ConsoleCommand) Help() string { + return `Usage: bor console + + Connect to local Bor IPC console.` +} + +// Synopsis implements the cli.Command interface +func (c *ConsoleCommand) Synopsis() string { + return "Connect to Bor console" +} + +// Run implements the cli.Command interface +func (c *ConsoleCommand) Run(args []string) int { + flags := c.Flags() + if err := flags.Parse(args); err != nil { + c.UI.Error(err.Error()) + return 1 + } + + // read config file + config := DefaultConfig() + for _, configFile := range c.configFile { + cfg, err := readConfigFile(configFile) + if err != nil { + c.UI.Error(err.Error()) + return 1 + } + if err := config.Merge(cfg); err != nil { + c.UI.Error(err.Error()) + return 1 + } + } + if err := config.Merge(c.cliConfig); err != nil { + c.UI.Error(err.Error()) + return 1 + } + c.config = config + + srv, err := NewServer(config) + if err != nil { + c.UI.Error(err.Error()) + return 1 + } + c.srv = srv + + c.localConsole() + + return 0 +} + +// localConsole starts a new geth node, attaching a JavaScript console to it at the +// same time. +func (c *ConsoleCommand) localConsole() error { + // Create and start the node based on the CLI flags + stack := c.srv.node + stack.Start() + defer stack.Close() + + path := node.DefaultDataDir() + + if c.Endpoint == "" { + if c.config.DataDir != "" { + path = c.config.DataDir + } + if path != "" { + homeDir, _ := os.UserHomeDir() + path = filepath.Join(homeDir, "/.bor/data") + } + c.Endpoint = fmt.Sprintf("%s/bor.ipc", path) + } + + // Attach to the newly started node and start the JavaScript console + client, err := stack.Attach() + if err != nil { + utils.Fatalf("Failed to attach to the inproc geth: %v", err) + } + config := console.Config{ + DataDir: path, + DocRoot: c.JSpathFlag, + Client: client, + Preload: c.MakeConsolePreloads(), + } + + console, err := console.New(config) + if err != nil { + utils.Fatalf("Failed to start the JavaScript console: %v", err) + } + defer console.Stop(false) + + // If only a short execution was requested, evaluate and return + if script := c.ExecCMD; script != "" { + console.Evaluate(script) + return nil + } + // Otherwise print the welcome screen and enter interactive mode + console.Welcome() + console.Interactive() + + return nil +} + +// dialRPC returns a RPC client which connects to the given endpoint. +// The check for empty endpoint implements the defaulting logic +// for "geth attach" with no argument. +func dialRPC(endpoint string) (*rpc.Client, error) { + if endpoint == "" { + endpoint = node.DefaultIPCEndpoint("bor") + } else if strings.HasPrefix(endpoint, "rpc:") || strings.HasPrefix(endpoint, "ipc:") { + // Backwards compatibility with geth < 1.5 which required + // these prefixes. + endpoint = endpoint[4:] + } + return rpc.Dial(endpoint) +} + +// MakeConsolePreloads retrieves the absolute paths for the console JavaScript +// scripts to preload before starting. +func (c *ConsoleCommand) MakeConsolePreloads() []string { + // Skip preloading if there's nothing to preload + if c.PreloadJSFlag == "" { + return nil + } + // Otherwise resolve absolute paths and return them + var preloads []string + + for _, file := range strings.Split(c.PreloadJSFlag, ",") { + preloads = append(preloads, strings.TrimSpace(file)) + } + return preloads +}