From 800b1363ab471177a6d32da8fb2e319f63b82bff Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Tue, 8 May 2018 00:45:57 -0400 Subject: [PATCH] discovery: randomize order of different bootstrappers In this commit, we randomize the order of the different bootstrappers in order to prevent from always querying potentially unreliable bootstrappers first. --- discovery/bootstrapper.go | 41 ++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/discovery/bootstrapper.go b/discovery/bootstrapper.go index be192ef18..412dec658 100644 --- a/discovery/bootstrapper.go +++ b/discovery/bootstrapper.go @@ -4,10 +4,13 @@ import ( "bytes" "crypto/rand" "crypto/sha256" + "errors" "fmt" + prand "math/rand" "net" "strconv" "strings" + "time" "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd/autopilot" @@ -18,6 +21,10 @@ import ( "github.com/roasbeef/btcutil/bech32" ) +func init() { + prand.Seed(time.Now().Unix()) +} + // NetworkPeerBootstrapper is an interface that represents an initial peer // bootstrap mechanism. This interface is to be used to bootstrap a new peer to // the connection by providing it with the pubkey+address of a set of existing @@ -44,40 +51,64 @@ type NetworkPeerBootstrapper interface { // the ignore map is populated, then the bootstrappers will be instructed to // skip those nodes. func MultiSourceBootstrap(ignore map[autopilot.NodeID]struct{}, numAddrs uint32, - bootStrappers ...NetworkPeerBootstrapper) ([]*lnwire.NetAddress, error) { + bootstrappers ...NetworkPeerBootstrapper) ([]*lnwire.NetAddress, error) { + + // We'll randomly shuffle our bootstrappers before querying them in + // order to avoid from querying the same bootstrapper method over and + // over, as some of these might tend to provide better/worse results + // than others. + bootstrappers = shuffleBootstrappers(bootstrappers) var addrs []*lnwire.NetAddress - for _, bootStrapper := range bootStrappers { + for _, bootstrapper := range bootstrappers { // If we already have enough addresses, then we can exit early // w/o querying the additional bootstrappers. if uint32(len(addrs)) >= numAddrs { break } - log.Infof("Attempting to bootstrap with: %v", bootStrapper.Name()) + log.Infof("Attempting to bootstrap with: %v", bootstrapper.Name()) // If we still need additional addresses, then we'll compute // the number of address remaining that we need to fetch. numAddrsLeft := numAddrs - uint32(len(addrs)) log.Tracef("Querying for %v addresses", numAddrsLeft) - netAddrs, err := bootStrapper.SampleNodeAddrs(numAddrsLeft, ignore) + netAddrs, err := bootstrapper.SampleNodeAddrs(numAddrsLeft, ignore) if err != nil { // If we encounter an error with a bootstrapper, then // we'll continue on to the next available // bootstrapper. log.Errorf("Unable to query bootstrapper %v: %v", - bootStrapper.Name(), err) + bootstrapper.Name(), err) continue } addrs = append(addrs, netAddrs...) } + if len(addrs) == 0 { + return nil, errors.New("no addresses found") + } + log.Infof("Obtained %v addrs to bootstrap network with", len(addrs)) return addrs, nil } +// shuffleBootstrappers shuffles the set of bootstrappers in order to avoid +// querying the same bootstrapper over and over. To shuffle the set of +// candidates, we use a version of the Fisher–Yates shuffle algorithm. +func shuffleBootstrappers(candidates []NetworkPeerBootstrapper) []NetworkPeerBootstrapper { + shuffled := make([]NetworkPeerBootstrapper, len(candidates)) + perm := prand.Perm(len(candidates)) + + for i, v := range perm { + shuffled[v] = candidates[i] + } + + return shuffled +} + // ChannelGraphBootstrapper is an implementation of the NetworkPeerBootstrapper // which attempts to retrieve advertised peers directly from the active channel // graph. This instance requires a backing autopilot.ChannelGraph instance in