github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/cmd/p2psim/main.go (about)

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