github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/cmd/devp2p/nodesetcmd.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 "time" 24 25 "github.com/AigarNetwork/aigar/core/forkid" 26 "github.com/AigarNetwork/aigar/p2p/enr" 27 "github.com/AigarNetwork/aigar/params" 28 "github.com/AigarNetwork/aigar/rlp" 29 "gopkg.in/urfave/cli.v1" 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 "-eth-network": {1, ethFilter}, 98 "-les-server": {0, lesFilter}, 99 } 100 101 func parseFilters(args []string) ([]nodeFilter, error) { 102 var filters []nodeFilter 103 for len(args) > 0 { 104 fc, ok := filterFlags[args[0]] 105 if !ok { 106 return nil, fmt.Errorf("invalid filter %q", args[0]) 107 } 108 if len(args) < fc.narg { 109 return nil, fmt.Errorf("filter %q wants %d arguments, have %d", args[0], fc.narg, len(args)) 110 } 111 filter, err := fc.fn(args[1:]) 112 if err != nil { 113 return nil, fmt.Errorf("%s: %v", args[0], err) 114 } 115 filters = append(filters, filter) 116 args = args[fc.narg+1:] 117 } 118 return filters, nil 119 } 120 121 func andFilter(args []string) (nodeFilter, error) { 122 checks, err := parseFilters(args) 123 if err != nil { 124 return nil, err 125 } 126 f := func(n nodeJSON) bool { 127 for _, filter := range checks { 128 if !filter(n) { 129 return false 130 } 131 } 132 return true 133 } 134 return f, nil 135 } 136 137 func ipFilter(args []string) (nodeFilter, error) { 138 _, cidr, err := net.ParseCIDR(args[0]) 139 if err != nil { 140 return nil, err 141 } 142 f := func(n nodeJSON) bool { return cidr.Contains(n.N.IP()) } 143 return f, nil 144 } 145 146 func minAgeFilter(args []string) (nodeFilter, error) { 147 minage, err := time.ParseDuration(args[0]) 148 if err != nil { 149 return nil, err 150 } 151 f := func(n nodeJSON) bool { 152 age := n.LastResponse.Sub(n.FirstResponse) 153 return age >= minage 154 } 155 return f, nil 156 } 157 158 func ethFilter(args []string) (nodeFilter, error) { 159 var filter forkid.Filter 160 switch args[0] { 161 case "mainnet": 162 filter = forkid.NewStaticFilter(params.MainnetChainConfig, params.MainnetGenesisHash) 163 case "rinkeby": 164 filter = forkid.NewStaticFilter(params.RinkebyChainConfig, params.RinkebyGenesisHash) 165 case "goerli": 166 filter = forkid.NewStaticFilter(params.GoerliChainConfig, params.GoerliGenesisHash) 167 case "ropsten": 168 filter = forkid.NewStaticFilter(params.TestnetChainConfig, params.TestnetGenesisHash) 169 default: 170 return nil, fmt.Errorf("unknown network %q", args[0]) 171 } 172 173 f := func(n nodeJSON) bool { 174 var eth struct { 175 ForkID forkid.ID 176 _ []rlp.RawValue `rlp:"tail"` 177 } 178 if n.N.Load(enr.WithEntry("eth", ð)) != nil { 179 return false 180 } 181 return filter(eth.ForkID) == nil 182 } 183 return f, nil 184 } 185 186 func lesFilter(args []string) (nodeFilter, error) { 187 f := func(n nodeJSON) bool { 188 var les struct { 189 _ []rlp.RawValue `rlp:"tail"` 190 } 191 return n.N.Load(enr.WithEntry("les", &les)) == nil 192 } 193 return f, nil 194 }