github.com/klaytn/klaytn@v1.12.1/cmd/utils/nodecmd/consolecmd.go (about)

     1  // Modifications Copyright 2018 The klaytn Authors
     2  // Copyright 2016 The go-ethereum Authors
     3  // This file is part of go-ethereum.
     4  //
     5  // go-ethereum is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // go-ethereum is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU General Public License
    16  // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  // This file is derived from cmd/geth/consolecmd.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package nodecmd
    22  
    23  import (
    24  	"fmt"
    25  	"path/filepath"
    26  	"strings"
    27  
    28  	"github.com/klaytn/klaytn/cmd/utils"
    29  	"github.com/klaytn/klaytn/console"
    30  	"github.com/klaytn/klaytn/log"
    31  	"github.com/klaytn/klaytn/networks/rpc"
    32  	"github.com/klaytn/klaytn/node"
    33  	"github.com/urfave/cli/v2"
    34  )
    35  
    36  var AttachCommand = &cli.Command{
    37  	Action:    remoteConsole,
    38  	Name:      "attach",
    39  	Usage:     "Start an interactive JavaScript environment (connect to node)",
    40  	ArgsUsage: "[endpoint]",
    41  	Flags:     append(utils.ConsoleFlags, utils.DataDirFlag),
    42  	Category:  "CONSOLE COMMANDS",
    43  	Description: `
    44  The Klaytn console is an interactive shell for the JavaScript runtime environment
    45  which exposes a node admin interface as well as the Ðapp JavaScript API.
    46  See https://github.com/ethereum/go-ethereum/wiki/JavaScript-Console.
    47  This command allows to open a console on a running Klaytn node.`,
    48  }
    49  
    50  // GetConsoleCommand returns cli.Command `console` whose flags are initialized with nodeFlags, rpcFlags, and ConsoleFlags.
    51  func GetConsoleCommand(nodeFlags, rpcFlags []cli.Flag) *cli.Command {
    52  	return &cli.Command{
    53  		Action:   localConsole,
    54  		Name:     "console",
    55  		Usage:    "Start an interactive JavaScript environment",
    56  		Flags:    append(append(nodeFlags, rpcFlags...), utils.ConsoleFlags...),
    57  		Category: "CONSOLE COMMANDS",
    58  		Description: `
    59  The Klaytn console is an interactive shell for the JavaScript runtime environment
    60  which exposes a node admin interface as well as the Ðapp JavaScript API.
    61  See https://github.com/ethereum/go-ethereum/wiki/JavaScript-Console.`,
    62  	}
    63  }
    64  
    65  // localConsole starts a new Klaytn node, attaching a JavaScript console to it at the
    66  // same time.
    67  func localConsole(ctx *cli.Context) error {
    68  	// Create and start the node based on the CLI flags
    69  	node := MakeFullNode(ctx)
    70  	startNode(ctx, node)
    71  	defer node.Stop()
    72  
    73  	// Attach to the newly started node and start the JavaScript console
    74  	client, err := node.Attach()
    75  	if err != nil {
    76  		log.Fatalf("Failed to attach to the inproc node: %v", err)
    77  	}
    78  	config := console.Config{
    79  		DataDir: utils.MakeDataDir(ctx),
    80  		DocRoot: ctx.String(utils.JSpathFlag.Name),
    81  		Client:  client,
    82  		Preload: utils.MakeConsolePreloads(ctx),
    83  	}
    84  
    85  	console, err := console.New(config)
    86  	if err != nil {
    87  		log.Fatalf("Failed to start the JavaScript console: %v", err)
    88  	}
    89  	defer console.Stop(false)
    90  
    91  	// If only a short execution was requested, evaluate and return
    92  	if script := ctx.String(utils.ExecFlag.Name); script != "" {
    93  		console.Evaluate(script)
    94  		return nil
    95  	}
    96  	// Otherwise print the welcome screen and enter interactive mode
    97  	console.Welcome()
    98  	console.Interactive()
    99  
   100  	return nil
   101  }
   102  
   103  // remoteConsole will connect to a remote node instance, attaching a JavaScript
   104  // console to it.
   105  func remoteConsole(ctx *cli.Context) error {
   106  	// Attach to a remotely running node instance and start the JavaScript console
   107  	endpoint := rpcEndpoint(ctx)
   108  	client, err := dialRPC(endpoint)
   109  	if err != nil {
   110  		log.Fatalf("Unable to attach to remote node: %v", err)
   111  	}
   112  	config := console.Config{
   113  		DataDir: utils.MakeDataDir(ctx),
   114  		DocRoot: ctx.String(utils.JSpathFlag.Name),
   115  		Client:  client,
   116  		Preload: utils.MakeConsolePreloads(ctx),
   117  	}
   118  
   119  	console, err := console.New(config)
   120  	if err != nil {
   121  		log.Fatalf("Failed to start the JavaScript console: %v", err)
   122  	}
   123  	defer console.Stop(false)
   124  
   125  	if script := ctx.String(utils.ExecFlag.Name); script != "" {
   126  		console.Evaluate(script)
   127  		return nil
   128  	}
   129  
   130  	// Otherwise print the welcome screen and enter interactive mode
   131  	console.Welcome()
   132  	console.Interactive()
   133  
   134  	return nil
   135  }
   136  
   137  func rpcEndpoint(ctx *cli.Context) string {
   138  	endpoint := ctx.Args().First()
   139  	if endpoint == "" {
   140  		path := node.DefaultDataDir()
   141  		if ctx.IsSet(utils.DataDirFlag.Name) {
   142  			path = ctx.String(utils.DataDirFlag.Name)
   143  		}
   144  		if path != "" {
   145  			if ctx.Bool(utils.BaobabFlag.Name) {
   146  				path = filepath.Join(path, "baobab")
   147  			}
   148  		}
   149  		endpoint = fmt.Sprintf("%s/klay.ipc", path)
   150  	}
   151  	return endpoint
   152  }
   153  
   154  // dialRPC returns a RPC client which connects to the given endpoint.
   155  // The check for empty endpoint implements the defaulting logic
   156  // for "ken attach" and "ken monitor" with no argument.
   157  func dialRPC(endpoint string) (*rpc.Client, error) {
   158  	if endpoint == "" {
   159  		endpoint = node.DefaultIPCEndpoint(utils.ClientIdentifier)
   160  	} else if strings.HasPrefix(endpoint, "rpc:") || strings.HasPrefix(endpoint, "ipc:") {
   161  		// TODO-Klaytn-RemoveLater: The below backward compatibility is not related to Klaytn.
   162  		// Backwards compatibility with klaytn < 1.5 which required
   163  		// these prefixes.
   164  		endpoint = endpoint[4:]
   165  	}
   166  	return rpc.Dial(endpoint)
   167  }