github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/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 "fmt" 21 "net" 22 "strings" 23 "time" 24 25 "github.com/kisexp/xdchain/cmd/devp2p/internal/v4test" 26 "github.com/kisexp/xdchain/common" 27 "github.com/kisexp/xdchain/crypto" 28 "github.com/kisexp/xdchain/p2p/discover" 29 "github.com/kisexp/xdchain/p2p/enode" 30 "github.com/kisexp/xdchain/params" 31 "gopkg.in/urfave/cli.v1" 32 ) 33 34 var ( 35 discv4Command = cli.Command{ 36 Name: "discv4", 37 Usage: "Node Discovery v4 tools", 38 Subcommands: []cli.Command{ 39 discv4PingCommand, 40 discv4RequestRecordCommand, 41 discv4ResolveCommand, 42 discv4ResolveJSONCommand, 43 discv4CrawlCommand, 44 discv4TestCommand, 45 }, 46 } 47 discv4PingCommand = cli.Command{ 48 Name: "ping", 49 Usage: "Sends ping to a node", 50 Action: discv4Ping, 51 ArgsUsage: "<node>", 52 } 53 discv4RequestRecordCommand = cli.Command{ 54 Name: "requestenr", 55 Usage: "Requests a node record using EIP-868 enrRequest", 56 Action: discv4RequestRecord, 57 ArgsUsage: "<node>", 58 } 59 discv4ResolveCommand = cli.Command{ 60 Name: "resolve", 61 Usage: "Finds a node in the DHT", 62 Action: discv4Resolve, 63 ArgsUsage: "<node>", 64 Flags: []cli.Flag{bootnodesFlag}, 65 } 66 discv4ResolveJSONCommand = cli.Command{ 67 Name: "resolve-json", 68 Usage: "Re-resolves nodes in a nodes.json file", 69 Action: discv4ResolveJSON, 70 Flags: []cli.Flag{bootnodesFlag}, 71 ArgsUsage: "<nodes.json file>", 72 } 73 discv4CrawlCommand = cli.Command{ 74 Name: "crawl", 75 Usage: "Updates a nodes.json file with random nodes found in the DHT", 76 Action: discv4Crawl, 77 Flags: []cli.Flag{bootnodesFlag, crawlTimeoutFlag}, 78 } 79 discv4TestCommand = cli.Command{ 80 Name: "test", 81 Usage: "Runs tests against a node", 82 Action: discv4Test, 83 Flags: []cli.Flag{ 84 remoteEnodeFlag, 85 testPatternFlag, 86 testTAPFlag, 87 testListen1Flag, 88 testListen2Flag, 89 }, 90 } 91 ) 92 93 var ( 94 bootnodesFlag = cli.StringFlag{ 95 Name: "bootnodes", 96 Usage: "Comma separated nodes used for bootstrapping", 97 } 98 nodekeyFlag = cli.StringFlag{ 99 Name: "nodekey", 100 Usage: "Hex-encoded node key", 101 } 102 nodedbFlag = cli.StringFlag{ 103 Name: "nodedb", 104 Usage: "Nodes database location", 105 } 106 listenAddrFlag = cli.StringFlag{ 107 Name: "addr", 108 Usage: "Listening address", 109 } 110 crawlTimeoutFlag = cli.DurationFlag{ 111 Name: "timeout", 112 Usage: "Time limit for the crawl.", 113 Value: 30 * time.Minute, 114 } 115 remoteEnodeFlag = cli.StringFlag{ 116 Name: "remote", 117 Usage: "Enode of the remote node under test", 118 EnvVar: "REMOTE_ENODE", 119 } 120 ) 121 122 func discv4Ping(ctx *cli.Context) error { 123 n := getNodeArg(ctx) 124 disc := startV4(ctx) 125 defer disc.Close() 126 127 start := time.Now() 128 if err := disc.Ping(n); err != nil { 129 return fmt.Errorf("node didn't respond: %v", err) 130 } 131 fmt.Printf("node responded to ping (RTT %v).\n", time.Since(start)) 132 return nil 133 } 134 135 func discv4RequestRecord(ctx *cli.Context) error { 136 n := getNodeArg(ctx) 137 disc := startV4(ctx) 138 defer disc.Close() 139 140 respN, err := disc.RequestENR(n) 141 if err != nil { 142 return fmt.Errorf("can't retrieve record: %v", err) 143 } 144 fmt.Println(respN.String()) 145 return nil 146 } 147 148 func discv4Resolve(ctx *cli.Context) error { 149 n := getNodeArg(ctx) 150 disc := startV4(ctx) 151 defer disc.Close() 152 153 fmt.Println(disc.Resolve(n).String()) 154 return nil 155 } 156 157 func discv4ResolveJSON(ctx *cli.Context) error { 158 if ctx.NArg() < 1 { 159 return fmt.Errorf("need nodes file as argument") 160 } 161 nodesFile := ctx.Args().Get(0) 162 inputSet := make(nodeSet) 163 if common.FileExist(nodesFile) { 164 inputSet = loadNodesJSON(nodesFile) 165 } 166 167 // Add extra nodes from command line arguments. 168 var nodeargs []*enode.Node 169 for i := 1; i < ctx.NArg(); i++ { 170 n, err := parseNode(ctx.Args().Get(i)) 171 if err != nil { 172 exit(err) 173 } 174 nodeargs = append(nodeargs, n) 175 } 176 177 // Run the crawler. 178 disc := startV4(ctx) 179 defer disc.Close() 180 c := newCrawler(inputSet, disc, enode.IterNodes(nodeargs)) 181 c.revalidateInterval = 0 182 output := c.run(0) 183 writeNodesJSON(nodesFile, output) 184 return nil 185 } 186 187 func discv4Crawl(ctx *cli.Context) error { 188 if ctx.NArg() < 1 { 189 return fmt.Errorf("need nodes file as argument") 190 } 191 nodesFile := ctx.Args().First() 192 var inputSet nodeSet 193 if common.FileExist(nodesFile) { 194 inputSet = loadNodesJSON(nodesFile) 195 } 196 197 disc := startV4(ctx) 198 defer disc.Close() 199 c := newCrawler(inputSet, disc, disc.RandomNodes()) 200 c.revalidateInterval = 10 * time.Minute 201 output := c.run(ctx.Duration(crawlTimeoutFlag.Name)) 202 writeNodesJSON(nodesFile, output) 203 return nil 204 } 205 206 // discv4Test runs the protocol test suite. 207 func discv4Test(ctx *cli.Context) error { 208 // Configure test package globals. 209 if !ctx.IsSet(remoteEnodeFlag.Name) { 210 return fmt.Errorf("Missing -%v", remoteEnodeFlag.Name) 211 } 212 v4test.Remote = ctx.String(remoteEnodeFlag.Name) 213 v4test.Listen1 = ctx.String(testListen1Flag.Name) 214 v4test.Listen2 = ctx.String(testListen2Flag.Name) 215 return runTests(ctx, v4test.AllTests) 216 } 217 218 // startV4 starts an ephemeral discovery V4 node. 219 func startV4(ctx *cli.Context) *discover.UDPv4 { 220 ln, config := makeDiscoveryConfig(ctx) 221 socket := listen(ln, ctx.String(listenAddrFlag.Name)) 222 disc, err := discover.ListenV4(socket, ln, config) 223 if err != nil { 224 exit(err) 225 } 226 return disc 227 } 228 229 func makeDiscoveryConfig(ctx *cli.Context) (*enode.LocalNode, discover.Config) { 230 var cfg discover.Config 231 232 if ctx.IsSet(nodekeyFlag.Name) { 233 key, err := crypto.HexToECDSA(ctx.String(nodekeyFlag.Name)) 234 if err != nil { 235 exit(fmt.Errorf("-%s: %v", nodekeyFlag.Name, err)) 236 } 237 cfg.PrivateKey = key 238 } else { 239 cfg.PrivateKey, _ = crypto.GenerateKey() 240 } 241 242 if commandHasFlag(ctx, bootnodesFlag) { 243 bn, err := parseBootnodes(ctx) 244 if err != nil { 245 exit(err) 246 } 247 cfg.Bootnodes = bn 248 } 249 250 dbpath := ctx.String(nodedbFlag.Name) 251 db, err := enode.OpenDB(dbpath) 252 if err != nil { 253 exit(err) 254 } 255 ln := enode.NewLocalNode(db, cfg.PrivateKey) 256 return ln, cfg 257 } 258 259 func listen(ln *enode.LocalNode, addr string) *net.UDPConn { 260 if addr == "" { 261 addr = "0.0.0.0:0" 262 } 263 socket, err := net.ListenPacket("udp4", addr) 264 if err != nil { 265 exit(err) 266 } 267 usocket := socket.(*net.UDPConn) 268 uaddr := socket.LocalAddr().(*net.UDPAddr) 269 if uaddr.IP.IsUnspecified() { 270 ln.SetFallbackIP(net.IP{127, 0, 0, 1}) 271 } else { 272 ln.SetFallbackIP(uaddr.IP) 273 } 274 ln.SetFallbackUDP(uaddr.Port) 275 return usocket 276 } 277 278 func parseBootnodes(ctx *cli.Context) ([]*enode.Node, error) { 279 s := params.RinkebyBootnodes 280 if ctx.IsSet(bootnodesFlag.Name) { 281 input := ctx.String(bootnodesFlag.Name) 282 if input == "" { 283 return nil, nil 284 } 285 s = strings.Split(input, ",") 286 } 287 nodes := make([]*enode.Node, len(s)) 288 var err error 289 for i, record := range s { 290 nodes[i], err = parseNode(record) 291 if err != nil { 292 return nil, fmt.Errorf("invalid bootstrap node: %v", err) 293 } 294 } 295 return nodes, nil 296 }