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