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