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