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 }