/ 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 }