/ main.go
main.go
  1  package main
  2  
  3  import (
  4  	"flag"
  5  	"net"
  6  	"os"
  7  	"time"
  8  
  9  	"github.com/ethereum/go-ethereum/crypto"
 10  	"github.com/ethereum/go-ethereum/log"
 11  	"github.com/ethereum/go-ethereum/p2p/discv5"
 12  )
 13  
 14  type bootnodes []*discv5.Node
 15  
 16  func (f *bootnodes) String() string {
 17  	return "discv5 nodes"
 18  }
 19  
 20  // Set unmarshals enode into discv5.Node.
 21  func (f *bootnodes) Set(value string) error {
 22  	n, err := discv5.ParseNode(value)
 23  	if err != nil {
 24  		return err
 25  	}
 26  	*f = append(*f, n)
 27  	return nil
 28  }
 29  
 30  func runBootnode(listenAddr string, nursery bootnodes) *discv5.Network {
 31  	key, err := crypto.GenerateKey()
 32  	if err != nil {
 33  		log.Crit("Failed to generate ecdsa key from", "error", err)
 34  	}
 35  	addr, err := net.ResolveUDPAddr("udp", listenAddr)
 36  	if err != nil {
 37  		log.Crit("Unable to resolve UDP", "address", listenAddr, "error", err)
 38  	}
 39  	conn, err := net.ListenUDP("udp", addr)
 40  	if err != nil {
 41  		log.Crit("Unable to listen on udp", "address", addr, "error", err)
 42  	}
 43  
 44  	realaddr := conn.LocalAddr().(*net.UDPAddr)
 45  	tab, err := discv5.ListenUDP(key, conn, realaddr, "", nil)
 46  	if err != nil {
 47  		log.Crit("Failed to create discovery v5 table:", "error", err)
 48  	}
 49  	if err := tab.SetFallbackNodes(nursery); err != nil {
 50  		log.Crit("Failed to set fallback", "nodes", nursery, "error", err)
 51  	}
 52  	return tab
 53  }
 54  
 55  func main() {
 56  	var (
 57  		listenAddr = flag.String("addr", ":0", "listen address")
 58  		verbosity  = flag.Int("verbosity", int(log.LvlInfo), "log verbosity (0-9)")
 59  		vmodule    = flag.String("vmodule", "", "log verbosity pattern")
 60  		nursery    = bootnodes{}
 61  		topic      = flag.String("topic", "whisper", "topic to search for")
 62  		limit      = flag.Int("limit", 5, "search no more than for 5 peers")
 63  		timer      = flag.Duration("timer", 2*time.Minute, "controls how often healthcheck runs")
 64  		period     = flag.Duration("period", 3*time.Second, "controls how often topic is searched")
 65  		statsPort  = flag.String("stats", ":8080", "listen addr for stats")
 66  	)
 67  	flag.Var(&nursery, "n", "These nodes are used to connect to the network if the table is empty and there are no known nodes in the database.")
 68  	flag.Parse()
 69  
 70  	glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
 71  	glogger.Verbosity(log.Lvl(*verbosity))
 72  	if err := glogger.Vmodule(*vmodule); err != nil {
 73  		log.Crit("Failed to set glog verbosity", "value", *vmodule, "err", err)
 74  	}
 75  	log.Root().SetHandler(glogger)
 76  	stats := NewStats(*statsPort)
 77  
 78  	healthcheck := func() {
 79  		tab := runBootnode(*listenAddr, nursery)
 80  		defer tab.Close()
 81  		setPeriod := make(chan time.Duration, 1)
 82  		setPeriod <- *period
 83  		found := make(chan *discv5.Node, *limit)
 84  		lookup := make(chan bool, 10)
 85  		log.Info("Started search.", "topic", *topic, "limit", *limit)
 86  		go tab.SearchTopic(discv5.Topic(*topic), setPeriod, found, lookup)
 87  		current := 0
 88  		failTimer := time.After(2 * time.Minute)
 89  		testStart := time.Now()
 90  		stats.Started()
 91  		for {
 92  			select {
 93  			case <-failTimer:
 94  				stats.Failed()
 95  				return
 96  			case <-lookup:
 97  			case n := <-found:
 98  				current++
 99  				latency := time.Since(testStart)
100  				log.Info("Discovered node", "total", current, "node", n, "latency", latency)
101  				stats.Discovered(current, latency)
102  				if current == *limit {
103  					return
104  				}
105  			}
106  		}
107  	}
108  	for {
109  		healthcheck()
110  		time.Sleep(*timer)
111  	}
112  }