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", &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  }