github.com/sbrajchuk/go-ethereum@v1.9.7/cmd/devp2p/nodesetcmd.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 "time" 23 24 "github.com/ethereum/go-ethereum/core/forkid" 25 "github.com/ethereum/go-ethereum/p2p/enr" 26 "github.com/ethereum/go-ethereum/params" 27 "github.com/ethereum/go-ethereum/rlp" 28 "gopkg.in/urfave/cli.v1" 29 ) 30 31 var ( 32 nodesetCommand = cli.Command{ 33 Name: "nodeset", 34 Usage: "Node set tools", 35 Subcommands: []cli.Command{ 36 nodesetInfoCommand, 37 nodesetFilterCommand, 38 }, 39 } 40 nodesetInfoCommand = cli.Command{ 41 Name: "info", 42 Usage: "Shows statistics about a node set", 43 Action: nodesetInfo, 44 ArgsUsage: "<nodes.json>", 45 } 46 nodesetFilterCommand = cli.Command{ 47 Name: "filter", 48 Usage: "Filters a node set", 49 Action: nodesetFilter, 50 ArgsUsage: "<nodes.json> filters..", 51 52 SkipFlagParsing: true, 53 } 54 ) 55 56 func nodesetInfo(ctx *cli.Context) error { 57 if ctx.NArg() < 1 { 58 return fmt.Errorf("need nodes file as argument") 59 } 60 61 ns := loadNodesJSON(ctx.Args().First()) 62 fmt.Printf("Set contains %d nodes.\n", len(ns)) 63 return nil 64 } 65 66 func nodesetFilter(ctx *cli.Context) error { 67 if ctx.NArg() < 1 { 68 return fmt.Errorf("need nodes file as argument") 69 } 70 ns := loadNodesJSON(ctx.Args().First()) 71 filter, err := andFilter(ctx.Args().Tail()) 72 if err != nil { 73 return err 74 } 75 76 result := make(nodeSet) 77 for id, n := range ns { 78 if filter(n) { 79 result[id] = n 80 } 81 } 82 writeNodesJSON("-", result) 83 return nil 84 } 85 86 type nodeFilter func(nodeJSON) bool 87 88 type nodeFilterC struct { 89 narg int 90 fn func([]string) (nodeFilter, error) 91 } 92 93 var filterFlags = map[string]nodeFilterC{ 94 "-ip": {1, ipFilter}, 95 "-min-age": {1, minAgeFilter}, 96 "-eth-network": {1, ethFilter}, 97 "-les-server": {0, lesFilter}, 98 } 99 100 func parseFilters(args []string) ([]nodeFilter, error) { 101 var filters []nodeFilter 102 for len(args) > 0 { 103 fc, ok := filterFlags[args[0]] 104 if !ok { 105 return nil, fmt.Errorf("invalid filter %q", args[0]) 106 } 107 if len(args) < fc.narg { 108 return nil, fmt.Errorf("filter %q wants %d arguments, have %d", args[0], fc.narg, len(args)) 109 } 110 filter, err := fc.fn(args[1:]) 111 if err != nil { 112 return nil, fmt.Errorf("%s: %v", args[0], err) 113 } 114 filters = append(filters, filter) 115 args = args[fc.narg+1:] 116 } 117 return filters, nil 118 } 119 120 func andFilter(args []string) (nodeFilter, error) { 121 checks, err := parseFilters(args) 122 if err != nil { 123 return nil, err 124 } 125 f := func(n nodeJSON) bool { 126 for _, filter := range checks { 127 if !filter(n) { 128 return false 129 } 130 } 131 return true 132 } 133 return f, nil 134 } 135 136 func ipFilter(args []string) (nodeFilter, error) { 137 _, cidr, err := net.ParseCIDR(args[0]) 138 if err != nil { 139 return nil, err 140 } 141 f := func(n nodeJSON) bool { return cidr.Contains(n.N.IP()) } 142 return f, nil 143 } 144 145 func minAgeFilter(args []string) (nodeFilter, error) { 146 minage, err := time.ParseDuration(args[0]) 147 if err != nil { 148 return nil, err 149 } 150 f := func(n nodeJSON) bool { 151 age := n.LastResponse.Sub(n.FirstResponse) 152 return age >= minage 153 } 154 return f, nil 155 } 156 157 func ethFilter(args []string) (nodeFilter, error) { 158 var filter forkid.Filter 159 switch args[0] { 160 case "mainnet": 161 filter = forkid.NewStaticFilter(params.MainnetChainConfig, params.MainnetGenesisHash) 162 case "rinkeby": 163 filter = forkid.NewStaticFilter(params.RinkebyChainConfig, params.RinkebyGenesisHash) 164 case "goerli": 165 filter = forkid.NewStaticFilter(params.GoerliChainConfig, params.GoerliGenesisHash) 166 case "ropsten": 167 filter = forkid.NewStaticFilter(params.TestnetChainConfig, params.TestnetGenesisHash) 168 default: 169 return nil, fmt.Errorf("unknown network %q", args[0]) 170 } 171 172 f := func(n nodeJSON) bool { 173 var eth struct { 174 ForkID forkid.ID 175 _ []rlp.RawValue `rlp:"tail"` 176 } 177 if n.N.Load(enr.WithEntry("eth", ð)) != nil { 178 return false 179 } 180 return filter(eth.ForkID) == nil 181 } 182 return f, nil 183 } 184 185 func lesFilter(args []string) (nodeFilter, error) { 186 f := func(n nodeJSON) bool { 187 var les struct { 188 _ []rlp.RawValue `rlp:"tail"` 189 } 190 return n.N.Load(enr.WithEntry("les", &les)) == nil 191 } 192 return f, nil 193 }