github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/cmd/p2psim/main.go (about)

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