github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/cmd/devp2p/discv4cmd.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // Copyright 2019 The go-aigar Authors 3 // This file is part of the go-aigar library. 4 // 5 // The go-aigar library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-aigar library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-aigar library. If not, see <http://www.gnu.org/licenses/>. 17 18 package main 19 20 import ( 21 "fmt" 22 "net" 23 "strings" 24 "time" 25 26 "github.com/AigarNetwork/aigar/common" 27 "github.com/AigarNetwork/aigar/crypto" 28 "github.com/AigarNetwork/aigar/p2p/discover" 29 "github.com/AigarNetwork/aigar/p2p/enode" 30 "github.com/AigarNetwork/aigar/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 }, 45 } 46 discv4PingCommand = cli.Command{ 47 Name: "ping", 48 Usage: "Sends ping to a node", 49 Action: discv4Ping, 50 ArgsUsage: "<node>", 51 } 52 discv4RequestRecordCommand = cli.Command{ 53 Name: "requestenr", 54 Usage: "Requests a node record using EIP-868 enrRequest", 55 Action: discv4RequestRecord, 56 ArgsUsage: "<node>", 57 } 58 discv4ResolveCommand = cli.Command{ 59 Name: "resolve", 60 Usage: "Finds a node in the DHT", 61 Action: discv4Resolve, 62 ArgsUsage: "<node>", 63 Flags: []cli.Flag{bootnodesFlag}, 64 } 65 discv4ResolveJSONCommand = cli.Command{ 66 Name: "resolve-json", 67 Usage: "Re-resolves nodes in a nodes.json file", 68 Action: discv4ResolveJSON, 69 Flags: []cli.Flag{bootnodesFlag}, 70 ArgsUsage: "<nodes.json file>", 71 } 72 discv4CrawlCommand = cli.Command{ 73 Name: "crawl", 74 Usage: "Updates a nodes.json file with random nodes found in the DHT", 75 Action: discv4Crawl, 76 Flags: []cli.Flag{bootnodesFlag, crawlTimeoutFlag}, 77 } 78 ) 79 80 var ( 81 bootnodesFlag = cli.StringFlag{ 82 Name: "bootnodes", 83 Usage: "Comma separated nodes used for bootstrapping", 84 } 85 crawlTimeoutFlag = cli.DurationFlag{ 86 Name: "timeout", 87 Usage: "Time limit for the crawl.", 88 Value: 30 * time.Minute, 89 } 90 ) 91 92 func discv4Ping(ctx *cli.Context) error { 93 n := getNodeArg(ctx) 94 disc := startV4(ctx) 95 defer disc.Close() 96 97 start := time.Now() 98 if err := disc.Ping(n); err != nil { 99 return fmt.Errorf("node didn't respond: %v", err) 100 } 101 fmt.Printf("node responded to ping (RTT %v).\n", time.Since(start)) 102 return nil 103 } 104 105 func discv4RequestRecord(ctx *cli.Context) error { 106 n := getNodeArg(ctx) 107 disc := startV4(ctx) 108 defer disc.Close() 109 110 respN, err := disc.RequestENR(n) 111 if err != nil { 112 return fmt.Errorf("can't retrieve record: %v", err) 113 } 114 fmt.Println(respN.String()) 115 return nil 116 } 117 118 func discv4Resolve(ctx *cli.Context) error { 119 n := getNodeArg(ctx) 120 disc := startV4(ctx) 121 defer disc.Close() 122 123 fmt.Println(disc.Resolve(n).String()) 124 return nil 125 } 126 127 func discv4ResolveJSON(ctx *cli.Context) error { 128 if ctx.NArg() < 1 { 129 return fmt.Errorf("need nodes file as argument") 130 } 131 nodesFile := ctx.Args().Get(0) 132 inputSet := make(nodeSet) 133 if common.FileExist(nodesFile) { 134 inputSet = loadNodesJSON(nodesFile) 135 } 136 137 // Add extra nodes from command line arguments. 138 var nodeargs []*enode.Node 139 for i := 1; i < ctx.NArg(); i++ { 140 n, err := parseNode(ctx.Args().Get(i)) 141 if err != nil { 142 exit(err) 143 } 144 nodeargs = append(nodeargs, n) 145 } 146 147 // Run the crawler. 148 disc := startV4(ctx) 149 defer disc.Close() 150 c := newCrawler(inputSet, disc, enode.IterNodes(nodeargs)) 151 c.revalidateInterval = 0 152 output := c.run(0) 153 writeNodesJSON(nodesFile, output) 154 return nil 155 } 156 157 func discv4Crawl(ctx *cli.Context) error { 158 if ctx.NArg() < 1 { 159 return fmt.Errorf("need nodes file as argument") 160 } 161 nodesFile := ctx.Args().First() 162 var inputSet nodeSet 163 if common.FileExist(nodesFile) { 164 inputSet = loadNodesJSON(nodesFile) 165 } 166 167 disc := startV4(ctx) 168 defer disc.Close() 169 c := newCrawler(inputSet, disc, disc.RandomNodes()) 170 c.revalidateInterval = 10 * time.Minute 171 output := c.run(ctx.Duration(crawlTimeoutFlag.Name)) 172 writeNodesJSON(nodesFile, output) 173 return nil 174 } 175 176 func parseBootnodes(ctx *cli.Context) ([]*enode.Node, error) { 177 s := params.RinkebyBootnodes 178 if ctx.IsSet(bootnodesFlag.Name) { 179 s = strings.Split(ctx.String(bootnodesFlag.Name), ",") 180 } 181 nodes := make([]*enode.Node, len(s)) 182 var err error 183 for i, record := range s { 184 nodes[i], err = parseNode(record) 185 if err != nil { 186 return nil, fmt.Errorf("invalid bootstrap node: %v", err) 187 } 188 } 189 return nodes, nil 190 } 191 192 // startV4 starts an ephemeral discovery V4 node. 193 func startV4(ctx *cli.Context) *discover.UDPv4 { 194 socket, ln, cfg, err := listen() 195 if err != nil { 196 exit(err) 197 } 198 if commandHasFlag(ctx, bootnodesFlag) { 199 bn, err := parseBootnodes(ctx) 200 if err != nil { 201 exit(err) 202 } 203 cfg.Bootnodes = bn 204 } 205 disc, err := discover.ListenV4(socket, ln, cfg) 206 if err != nil { 207 exit(err) 208 } 209 return disc 210 } 211 212 func listen() (*net.UDPConn, *enode.LocalNode, discover.Config, error) { 213 var cfg discover.Config 214 cfg.PrivateKey, _ = crypto.GenerateKey() 215 db, _ := enode.OpenDB("") 216 ln := enode.NewLocalNode(db, cfg.PrivateKey) 217 218 socket, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IP{0, 0, 0, 0}}) 219 if err != nil { 220 db.Close() 221 return nil, nil, cfg, err 222 } 223 addr := socket.LocalAddr().(*net.UDPAddr) 224 ln.SetFallbackIP(net.IP{127, 0, 0, 1}) 225 ln.SetFallbackUDP(addr.Port) 226 return socket, ln, cfg, nil 227 }