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 }