github.com/bearnetworkchain/go-bearnetwork@v1.10.19-0.20220604150648-d63890c2e42b/cmd/p2psim/main.go (about)

     1  // Copyright 2017 The go-ethereum Authors
     2  // This file is part of go-ethereum.
     3  //
     4  // go-ethereum is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // go-ethereum is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU General Public License
    15  // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  // p2psim provides a command-line client for a simulation HTTP API.
    18  //
    19  // Here is an example of creating a 2 node network with the first node
    20  // connected to the second:
    21  //
    22  //     $ p2psim node create
    23  //     Created node01
    24  //
    25  //     $ p2psim node start node01
    26  //     Started node01
    27  //
    28  //     $ p2psim node create
    29  //     Created node02
    30  //
    31  //     $ p2psim node start node02
    32  //     Started node02
    33  //
    34  //     $ p2psim node connect node01 node02
    35  //     Connected node01 to node02
    36  //
    37  package main
    38  
    39  import (
    40  	"context"
    41  	"encoding/json"
    42  	"fmt"
    43  	"io"
    44  	"os"
    45  	"strings"
    46  	"text/tabwriter"
    47  
    48  	"github.com/bearnetworkchain/go-bearnetwork/crypto"
    49  	"github.com/bearnetworkchain/go-bearnetwork/p2p"
    50  	"github.com/bearnetworkchain/go-bearnetwork/p2p/enode"
    51  	"github.com/bearnetworkchain/go-bearnetwork/p2p/simulations"
    52  	"github.com/bearnetworkchain/go-bearnetwork/p2p/simulations/adapters"
    53  	"github.com/bearnetworkchain/go-bearnetwork/rpc"
    54  	"gopkg.in/urfave/cli.v1"
    55  )
    56  
    57  var client *simulations.Client
    58  
    59  var (
    60  	// global command flags
    61  	apiFlag = cli.StringFlag{
    62  		Name:   "api",
    63  		Value:  "http://localhost:8888",
    64  		Usage:  "simulation API URL",
    65  		EnvVar: "P2PSIM_API_URL",
    66  	}
    67  
    68  	// events subcommand flags
    69  	currentFlag = cli.BoolFlag{
    70  		Name:  "current",
    71  		Usage: "get existing nodes and conns first",
    72  	}
    73  	filterFlag = cli.StringFlag{
    74  		Name:  "filter",
    75  		Value: "",
    76  		Usage: "message filter",
    77  	}
    78  
    79  	// node create subcommand flags
    80  	nameFlag = cli.StringFlag{
    81  		Name:  "name",
    82  		Value: "",
    83  		Usage: "node name",
    84  	}
    85  	servicesFlag = cli.StringFlag{
    86  		Name:  "services",
    87  		Value: "",
    88  		Usage: "node services (comma separated)",
    89  	}
    90  	keyFlag = cli.StringFlag{
    91  		Name:  "key",
    92  		Value: "",
    93  		Usage: "node private key (hex encoded)",
    94  	}
    95  
    96  	// node rpc subcommand flags
    97  	subscribeFlag = cli.BoolFlag{
    98  		Name:  "subscribe",
    99  		Usage: "method is a subscription",
   100  	}
   101  )
   102  
   103  func main() {
   104  	app := cli.NewApp()
   105  	app.Usage = "devp2p simulation command-line client"
   106  	app.Flags = []cli.Flag{
   107  		apiFlag,
   108  	}
   109  	app.Before = func(ctx *cli.Context) error {
   110  		client = simulations.NewClient(ctx.GlobalString(apiFlag.Name))
   111  		return nil
   112  	}
   113  	app.Commands = []cli.Command{
   114  		{
   115  			Name:   "show",
   116  			Usage:  "show network information",
   117  			Action: showNetwork,
   118  		},
   119  		{
   120  			Name:   "events",
   121  			Usage:  "stream network events",
   122  			Action: streamNetwork,
   123  			Flags: []cli.Flag{
   124  				currentFlag,
   125  				filterFlag,
   126  			},
   127  		},
   128  		{
   129  			Name:   "snapshot",
   130  			Usage:  "create a network snapshot to stdout",
   131  			Action: createSnapshot,
   132  		},
   133  		{
   134  			Name:   "load",
   135  			Usage:  "load a network snapshot from stdin",
   136  			Action: loadSnapshot,
   137  		},
   138  		{
   139  			Name:   "node",
   140  			Usage:  "manage simulation nodes",
   141  			Action: listNodes,
   142  			Subcommands: []cli.Command{
   143  				{
   144  					Name:   "list",
   145  					Usage:  "list nodes",
   146  					Action: listNodes,
   147  				},
   148  				{
   149  					Name:   "create",
   150  					Usage:  "create a node",
   151  					Action: createNode,
   152  					Flags: []cli.Flag{
   153  						nameFlag,
   154  						servicesFlag,
   155  						keyFlag,
   156  					},
   157  				},
   158  				{
   159  					Name:      "show",
   160  					ArgsUsage: "<node>",
   161  					Usage:     "show node information",
   162  					Action:    showNode,
   163  				},
   164  				{
   165  					Name:      "start",
   166  					ArgsUsage: "<node>",
   167  					Usage:     "start a node",
   168  					Action:    startNode,
   169  				},
   170  				{
   171  					Name:      "stop",
   172  					ArgsUsage: "<node>",
   173  					Usage:     "stop a node",
   174  					Action:    stopNode,
   175  				},
   176  				{
   177  					Name:      "connect",
   178  					ArgsUsage: "<node> <peer>",
   179  					Usage:     "connect a node to a peer node",
   180  					Action:    connectNode,
   181  				},
   182  				{
   183  					Name:      "disconnect",
   184  					ArgsUsage: "<node> <peer>",
   185  					Usage:     "disconnect a node from a peer node",
   186  					Action:    disconnectNode,
   187  				},
   188  				{
   189  					Name:      "rpc",
   190  					ArgsUsage: "<node> <method> [<args>]",
   191  					Usage:     "call a node RPC method",
   192  					Action:    rpcNode,
   193  					Flags: []cli.Flag{
   194  						subscribeFlag,
   195  					},
   196  				},
   197  			},
   198  		},
   199  	}
   200  	if err := app.Run(os.Args); err != nil {
   201  		fmt.Fprintln(os.Stderr, err)
   202  		os.Exit(1)
   203  	}
   204  }
   205  
   206  func showNetwork(ctx *cli.Context) error {
   207  	if len(ctx.Args()) != 0 {
   208  		return cli.ShowCommandHelp(ctx, ctx.Command.Name)
   209  	}
   210  	network, err := client.GetNetwork()
   211  	if err != nil {
   212  		return err
   213  	}
   214  	w := tabwriter.NewWriter(ctx.App.Writer, 1, 2, 2, ' ', 0)
   215  	defer w.Flush()
   216  	fmt.Fprintf(w, "NODES\t%d\n", len(network.Nodes))
   217  	fmt.Fprintf(w, "CONNS\t%d\n", len(network.Conns))
   218  	return nil
   219  }
   220  
   221  func streamNetwork(ctx *cli.Context) error {
   222  	if len(ctx.Args()) != 0 {
   223  		return cli.ShowCommandHelp(ctx, ctx.Command.Name)
   224  	}
   225  	events := make(chan *simulations.Event)
   226  	sub, err := client.SubscribeNetwork(events, simulations.SubscribeOpts{
   227  		Current: ctx.Bool(currentFlag.Name),
   228  		Filter:  ctx.String(filterFlag.Name),
   229  	})
   230  	if err != nil {
   231  		return err
   232  	}
   233  	defer sub.Unsubscribe()
   234  	enc := json.NewEncoder(ctx.App.Writer)
   235  	for {
   236  		select {
   237  		case event := <-events:
   238  			if err := enc.Encode(event); err != nil {
   239  				return err
   240  			}
   241  		case err := <-sub.Err():
   242  			return err
   243  		}
   244  	}
   245  }
   246  
   247  func createSnapshot(ctx *cli.Context) error {
   248  	if len(ctx.Args()) != 0 {
   249  		return cli.ShowCommandHelp(ctx, ctx.Command.Name)
   250  	}
   251  	snap, err := client.CreateSnapshot()
   252  	if err != nil {
   253  		return err
   254  	}
   255  	return json.NewEncoder(os.Stdout).Encode(snap)
   256  }
   257  
   258  func loadSnapshot(ctx *cli.Context) error {
   259  	if len(ctx.Args()) != 0 {
   260  		return cli.ShowCommandHelp(ctx, ctx.Command.Name)
   261  	}
   262  	snap := &simulations.Snapshot{}
   263  	if err := json.NewDecoder(os.Stdin).Decode(snap); err != nil {
   264  		return err
   265  	}
   266  	return client.LoadSnapshot(snap)
   267  }
   268  
   269  func listNodes(ctx *cli.Context) error {
   270  	if len(ctx.Args()) != 0 {
   271  		return cli.ShowCommandHelp(ctx, ctx.Command.Name)
   272  	}
   273  	nodes, err := client.GetNodes()
   274  	if err != nil {
   275  		return err
   276  	}
   277  	w := tabwriter.NewWriter(ctx.App.Writer, 1, 2, 2, ' ', 0)
   278  	defer w.Flush()
   279  	fmt.Fprintf(w, "NAME\tPROTOCOLS\tID\n")
   280  	for _, node := range nodes {
   281  		fmt.Fprintf(w, "%s\t%s\t%s\n", node.Name, strings.Join(protocolList(node), ","), node.ID)
   282  	}
   283  	return nil
   284  }
   285  
   286  func protocolList(node *p2p.NodeInfo) []string {
   287  	protos := make([]string, 0, len(node.Protocols))
   288  	for name := range node.Protocols {
   289  		protos = append(protos, name)
   290  	}
   291  	return protos
   292  }
   293  
   294  func createNode(ctx *cli.Context) error {
   295  	if len(ctx.Args()) != 0 {
   296  		return cli.ShowCommandHelp(ctx, ctx.Command.Name)
   297  	}
   298  	config := adapters.RandomNodeConfig()
   299  	config.Name = ctx.String(nameFlag.Name)
   300  	if key := ctx.String(keyFlag.Name); key != "" {
   301  		privKey, err := crypto.HexToECDSA(key)
   302  		if err != nil {
   303  			return err
   304  		}
   305  		config.ID = enode.PubkeyToIDV4(&privKey.PublicKey)
   306  		config.PrivateKey = privKey
   307  	}
   308  	if services := ctx.String(servicesFlag.Name); services != "" {
   309  		config.Lifecycles = strings.Split(services, ",")
   310  	}
   311  	node, err := client.CreateNode(config)
   312  	if err != nil {
   313  		return err
   314  	}
   315  	fmt.Fprintln(ctx.App.Writer, "Created", node.Name)
   316  	return nil
   317  }
   318  
   319  func showNode(ctx *cli.Context) error {
   320  	args := ctx.Args()
   321  	if len(args) != 1 {
   322  		return cli.ShowCommandHelp(ctx, ctx.Command.Name)
   323  	}
   324  	nodeName := args[0]
   325  	node, err := client.GetNode(nodeName)
   326  	if err != nil {
   327  		return err
   328  	}
   329  	w := tabwriter.NewWriter(ctx.App.Writer, 1, 2, 2, ' ', 0)
   330  	defer w.Flush()
   331  	fmt.Fprintf(w, "NAME\t%s\n", node.Name)
   332  	fmt.Fprintf(w, "PROTOCOLS\t%s\n", strings.Join(protocolList(node), ","))
   333  	fmt.Fprintf(w, "ID\t%s\n", node.ID)
   334  	fmt.Fprintf(w, "ENODE\t%s\n", node.Enode)
   335  	for name, proto := range node.Protocols {
   336  		fmt.Fprintln(w)
   337  		fmt.Fprintf(w, "--- PROTOCOL INFO: %s\n", name)
   338  		fmt.Fprintf(w, "%v\n", proto)
   339  		fmt.Fprintf(w, "---\n")
   340  	}
   341  	return nil
   342  }
   343  
   344  func startNode(ctx *cli.Context) error {
   345  	args := ctx.Args()
   346  	if len(args) != 1 {
   347  		return cli.ShowCommandHelp(ctx, ctx.Command.Name)
   348  	}
   349  	nodeName := args[0]
   350  	if err := client.StartNode(nodeName); err != nil {
   351  		return err
   352  	}
   353  	fmt.Fprintln(ctx.App.Writer, "Started", nodeName)
   354  	return nil
   355  }
   356  
   357  func stopNode(ctx *cli.Context) error {
   358  	args := ctx.Args()
   359  	if len(args) != 1 {
   360  		return cli.ShowCommandHelp(ctx, ctx.Command.Name)
   361  	}
   362  	nodeName := args[0]
   363  	if err := client.StopNode(nodeName); err != nil {
   364  		return err
   365  	}
   366  	fmt.Fprintln(ctx.App.Writer, "Stopped", nodeName)
   367  	return nil
   368  }
   369  
   370  func connectNode(ctx *cli.Context) error {
   371  	args := ctx.Args()
   372  	if len(args) != 2 {
   373  		return cli.ShowCommandHelp(ctx, ctx.Command.Name)
   374  	}
   375  	nodeName := args[0]
   376  	peerName := args[1]
   377  	if err := client.ConnectNode(nodeName, peerName); err != nil {
   378  		return err
   379  	}
   380  	fmt.Fprintln(ctx.App.Writer, "Connected", nodeName, "to", peerName)
   381  	return nil
   382  }
   383  
   384  func disconnectNode(ctx *cli.Context) error {
   385  	args := ctx.Args()
   386  	if len(args) != 2 {
   387  		return cli.ShowCommandHelp(ctx, ctx.Command.Name)
   388  	}
   389  	nodeName := args[0]
   390  	peerName := args[1]
   391  	if err := client.DisconnectNode(nodeName, peerName); err != nil {
   392  		return err
   393  	}
   394  	fmt.Fprintln(ctx.App.Writer, "Disconnected", nodeName, "from", peerName)
   395  	return nil
   396  }
   397  
   398  func rpcNode(ctx *cli.Context) error {
   399  	args := ctx.Args()
   400  	if len(args) < 2 {
   401  		return cli.ShowCommandHelp(ctx, ctx.Command.Name)
   402  	}
   403  	nodeName := args[0]
   404  	method := args[1]
   405  	rpcClient, err := client.RPCClient(context.Background(), nodeName)
   406  	if err != nil {
   407  		return err
   408  	}
   409  	if ctx.Bool(subscribeFlag.Name) {
   410  		return rpcSubscribe(rpcClient, ctx.App.Writer, method, args[3:]...)
   411  	}
   412  	var result interface{}
   413  	params := make([]interface{}, len(args[3:]))
   414  	for i, v := range args[3:] {
   415  		params[i] = v
   416  	}
   417  	if err := rpcClient.Call(&result, method, params...); err != nil {
   418  		return err
   419  	}
   420  	return json.NewEncoder(ctx.App.Writer).Encode(result)
   421  }
   422  
   423  func rpcSubscribe(client *rpc.Client, out io.Writer, method string, args ...string) error {
   424  	parts := strings.SplitN(method, "_", 2)
   425  	namespace := parts[0]
   426  	method = parts[1]
   427  	ch := make(chan interface{})
   428  	subArgs := make([]interface{}, len(args)+1)
   429  	subArgs[0] = method
   430  	for i, v := range args {
   431  		subArgs[i+1] = v
   432  	}
   433  	sub, err := client.Subscribe(context.Background(), namespace, ch, subArgs...)
   434  	if err != nil {
   435  		return err
   436  	}
   437  	defer sub.Unsubscribe()
   438  	enc := json.NewEncoder(out)
   439  	for {
   440  		select {
   441  		case v := <-ch:
   442  			if err := enc.Encode(v); err != nil {
   443  				return err
   444  			}
   445  		case err := <-sub.Err():
   446  			return err
   447  		}
   448  	}
   449  }