github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/cmd/p2psim/main.go (about)

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