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