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  }