github.com/theQRL/go-zond@v0.1.1/cmd/devp2p/discv4cmd.go (about) 1 // Copyright 2019 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 package main 18 19 import ( 20 "errors" 21 "fmt" 22 "net" 23 "strconv" 24 "strings" 25 "time" 26 27 "github.com/theQRL/go-zond/cmd/devp2p/internal/v4test" 28 "github.com/theQRL/go-zond/common" 29 "github.com/theQRL/go-zond/crypto" 30 "github.com/theQRL/go-zond/internal/flags" 31 "github.com/theQRL/go-zond/p2p/discover" 32 "github.com/theQRL/go-zond/p2p/enode" 33 "github.com/theQRL/go-zond/params" 34 "github.com/urfave/cli/v2" 35 ) 36 37 var ( 38 discv4Command = &cli.Command{ 39 Name: "discv4", 40 Usage: "Node Discovery v4 tools", 41 Subcommands: []*cli.Command{ 42 discv4PingCommand, 43 discv4RequestRecordCommand, 44 discv4ResolveCommand, 45 discv4ResolveJSONCommand, 46 discv4CrawlCommand, 47 discv4TestCommand, 48 }, 49 } 50 discv4PingCommand = &cli.Command{ 51 Name: "ping", 52 Usage: "Sends ping to a node", 53 Action: discv4Ping, 54 ArgsUsage: "<node>", 55 Flags: discoveryNodeFlags, 56 } 57 discv4RequestRecordCommand = &cli.Command{ 58 Name: "requestenr", 59 Usage: "Requests a node record using EIP-868 enrRequest", 60 Action: discv4RequestRecord, 61 ArgsUsage: "<node>", 62 Flags: discoveryNodeFlags, 63 } 64 discv4ResolveCommand = &cli.Command{ 65 Name: "resolve", 66 Usage: "Finds a node in the DHT", 67 Action: discv4Resolve, 68 ArgsUsage: "<node>", 69 Flags: discoveryNodeFlags, 70 } 71 discv4ResolveJSONCommand = &cli.Command{ 72 Name: "resolve-json", 73 Usage: "Re-resolves nodes in a nodes.json file", 74 Action: discv4ResolveJSON, 75 Flags: discoveryNodeFlags, 76 ArgsUsage: "<nodes.json file>", 77 } 78 discv4CrawlCommand = &cli.Command{ 79 Name: "crawl", 80 Usage: "Updates a nodes.json file with random nodes found in the DHT", 81 Action: discv4Crawl, 82 Flags: flags.Merge(discoveryNodeFlags, []cli.Flag{crawlTimeoutFlag, crawlParallelismFlag}), 83 } 84 discv4TestCommand = &cli.Command{ 85 Name: "test", 86 Usage: "Runs tests against a node", 87 Action: discv4Test, 88 Flags: []cli.Flag{ 89 remoteEnodeFlag, 90 testPatternFlag, 91 testTAPFlag, 92 testListen1Flag, 93 testListen2Flag, 94 }, 95 } 96 ) 97 98 var ( 99 bootnodesFlag = &cli.StringFlag{ 100 Name: "bootnodes", 101 Usage: "Comma separated nodes used for bootstrapping", 102 } 103 nodekeyFlag = &cli.StringFlag{ 104 Name: "nodekey", 105 Usage: "Hex-encoded node key", 106 } 107 nodedbFlag = &cli.StringFlag{ 108 Name: "nodedb", 109 Usage: "Nodes database location", 110 } 111 listenAddrFlag = &cli.StringFlag{ 112 Name: "addr", 113 Usage: "Listening address", 114 } 115 extAddrFlag = &cli.StringFlag{ 116 Name: "extaddr", 117 Usage: "UDP endpoint announced in ENR. You can provide a bare IP address or IP:port as the value of this flag.", 118 } 119 crawlTimeoutFlag = &cli.DurationFlag{ 120 Name: "timeout", 121 Usage: "Time limit for the crawl.", 122 Value: 30 * time.Minute, 123 } 124 crawlParallelismFlag = &cli.IntFlag{ 125 Name: "parallel", 126 Usage: "How many parallel discoveries to attempt.", 127 Value: 16, 128 } 129 remoteEnodeFlag = &cli.StringFlag{ 130 Name: "remote", 131 Usage: "Enode of the remote node under test", 132 EnvVars: []string{"REMOTE_ENODE"}, 133 } 134 ) 135 136 var discoveryNodeFlags = []cli.Flag{ 137 bootnodesFlag, 138 nodekeyFlag, 139 nodedbFlag, 140 listenAddrFlag, 141 extAddrFlag, 142 } 143 144 func discv4Ping(ctx *cli.Context) error { 145 n := getNodeArg(ctx) 146 disc := startV4(ctx) 147 defer disc.Close() 148 149 start := time.Now() 150 if err := disc.Ping(n); err != nil { 151 return fmt.Errorf("node didn't respond: %v", err) 152 } 153 fmt.Printf("node responded to ping (RTT %v).\n", time.Since(start)) 154 return nil 155 } 156 157 func discv4RequestRecord(ctx *cli.Context) error { 158 n := getNodeArg(ctx) 159 disc := startV4(ctx) 160 defer disc.Close() 161 162 respN, err := disc.RequestENR(n) 163 if err != nil { 164 return fmt.Errorf("can't retrieve record: %v", err) 165 } 166 fmt.Println(respN.String()) 167 return nil 168 } 169 170 func discv4Resolve(ctx *cli.Context) error { 171 n := getNodeArg(ctx) 172 disc := startV4(ctx) 173 defer disc.Close() 174 175 fmt.Println(disc.Resolve(n).String()) 176 return nil 177 } 178 179 func discv4ResolveJSON(ctx *cli.Context) error { 180 if ctx.NArg() < 1 { 181 return errors.New("need nodes file as argument") 182 } 183 nodesFile := ctx.Args().Get(0) 184 inputSet := make(nodeSet) 185 if common.FileExist(nodesFile) { 186 inputSet = loadNodesJSON(nodesFile) 187 } 188 189 // Add extra nodes from command line arguments. 190 var nodeargs []*enode.Node 191 for i := 1; i < ctx.NArg(); i++ { 192 n, err := parseNode(ctx.Args().Get(i)) 193 if err != nil { 194 exit(err) 195 } 196 nodeargs = append(nodeargs, n) 197 } 198 199 // Run the crawler. 200 disc := startV4(ctx) 201 defer disc.Close() 202 c := newCrawler(inputSet, disc, enode.IterNodes(nodeargs)) 203 c.revalidateInterval = 0 204 output := c.run(0, 1) 205 writeNodesJSON(nodesFile, output) 206 return nil 207 } 208 209 func discv4Crawl(ctx *cli.Context) error { 210 if ctx.NArg() < 1 { 211 return errors.New("need nodes file as argument") 212 } 213 nodesFile := ctx.Args().First() 214 var inputSet nodeSet 215 if common.FileExist(nodesFile) { 216 inputSet = loadNodesJSON(nodesFile) 217 } 218 219 disc := startV4(ctx) 220 defer disc.Close() 221 c := newCrawler(inputSet, disc, disc.RandomNodes()) 222 c.revalidateInterval = 10 * time.Minute 223 output := c.run(ctx.Duration(crawlTimeoutFlag.Name), ctx.Int(crawlParallelismFlag.Name)) 224 writeNodesJSON(nodesFile, output) 225 return nil 226 } 227 228 // discv4Test runs the protocol test suite. 229 func discv4Test(ctx *cli.Context) error { 230 // Configure test package globals. 231 if !ctx.IsSet(remoteEnodeFlag.Name) { 232 return fmt.Errorf("Missing -%v", remoteEnodeFlag.Name) 233 } 234 v4test.Remote = ctx.String(remoteEnodeFlag.Name) 235 v4test.Listen1 = ctx.String(testListen1Flag.Name) 236 v4test.Listen2 = ctx.String(testListen2Flag.Name) 237 return runTests(ctx, v4test.AllTests) 238 } 239 240 // startV4 starts an ephemeral discovery V4 node. 241 func startV4(ctx *cli.Context) *discover.UDPv4 { 242 ln, config := makeDiscoveryConfig(ctx) 243 socket := listen(ctx, ln) 244 disc, err := discover.ListenV4(socket, ln, config) 245 if err != nil { 246 exit(err) 247 } 248 return disc 249 } 250 251 func makeDiscoveryConfig(ctx *cli.Context) (*enode.LocalNode, discover.Config) { 252 var cfg discover.Config 253 254 if ctx.IsSet(nodekeyFlag.Name) { 255 key, err := crypto.HexToECDSA(ctx.String(nodekeyFlag.Name)) 256 if err != nil { 257 exit(fmt.Errorf("-%s: %v", nodekeyFlag.Name, err)) 258 } 259 cfg.PrivateKey = key 260 } else { 261 cfg.PrivateKey, _ = crypto.GenerateKey() 262 } 263 264 if commandHasFlag(ctx, bootnodesFlag) { 265 bn, err := parseBootnodes(ctx) 266 if err != nil { 267 exit(err) 268 } 269 cfg.Bootnodes = bn 270 } 271 272 dbpath := ctx.String(nodedbFlag.Name) 273 db, err := enode.OpenDB(dbpath) 274 if err != nil { 275 exit(err) 276 } 277 ln := enode.NewLocalNode(db, cfg.PrivateKey) 278 return ln, cfg 279 } 280 281 func parseExtAddr(spec string) (ip net.IP, port int, ok bool) { 282 ip = net.ParseIP(spec) 283 if ip != nil { 284 return ip, 0, true 285 } 286 host, portstr, err := net.SplitHostPort(spec) 287 if err != nil { 288 return nil, 0, false 289 } 290 ip = net.ParseIP(host) 291 if ip == nil { 292 return nil, 0, false 293 } 294 port, err = strconv.Atoi(portstr) 295 if err != nil { 296 return nil, 0, false 297 } 298 return ip, port, true 299 } 300 301 func listen(ctx *cli.Context, ln *enode.LocalNode) *net.UDPConn { 302 addr := ctx.String(listenAddrFlag.Name) 303 if addr == "" { 304 addr = "0.0.0.0:0" 305 } 306 socket, err := net.ListenPacket("udp4", addr) 307 if err != nil { 308 exit(err) 309 } 310 311 // Configure UDP endpoint in ENR from listener address. 312 usocket := socket.(*net.UDPConn) 313 uaddr := socket.LocalAddr().(*net.UDPAddr) 314 if uaddr.IP.IsUnspecified() { 315 ln.SetFallbackIP(net.IP{127, 0, 0, 1}) 316 } else { 317 ln.SetFallbackIP(uaddr.IP) 318 } 319 ln.SetFallbackUDP(uaddr.Port) 320 321 // If an ENR endpoint is set explicitly on the command-line, override 322 // the information from the listening address. Note this is careful not 323 // to set the UDP port if the external address doesn't have it. 324 extAddr := ctx.String(extAddrFlag.Name) 325 if extAddr != "" { 326 ip, port, ok := parseExtAddr(extAddr) 327 if !ok { 328 exit(fmt.Errorf("-%s: invalid external address %q", extAddrFlag.Name, extAddr)) 329 } 330 ln.SetStaticIP(ip) 331 if port != 0 { 332 ln.SetFallbackUDP(port) 333 } 334 } 335 336 return usocket 337 } 338 339 func parseBootnodes(ctx *cli.Context) ([]*enode.Node, error) { 340 s := params.MainnetBootnodes 341 if ctx.IsSet(bootnodesFlag.Name) { 342 input := ctx.String(bootnodesFlag.Name) 343 if input == "" { 344 return nil, nil 345 } 346 s = strings.Split(input, ",") 347 } 348 nodes := make([]*enode.Node, len(s)) 349 var err error 350 for i, record := range s { 351 nodes[i], err = parseNode(record) 352 if err != nil { 353 return nil, fmt.Errorf("invalid bootstrap node: %v", err) 354 } 355 } 356 return nodes, nil 357 }