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.
This commit is contained in:
1 file changed
+36
-5
@@ -4,10 +4,13 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
prand "math/rand"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/lightningnetwork/lnd/autopilot"
|
"github.com/lightningnetwork/lnd/autopilot"
|
||||||
@@ -18,6 +21,10 @@ import (
|
|||||||
"github.com/roasbeef/btcutil/bech32"
|
"github.com/roasbeef/btcutil/bech32"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
prand.Seed(time.Now().Unix())
|
||||||
|
}
|
||||||
|
|
||||||
// NetworkPeerBootstrapper is an interface that represents an initial peer
|
// NetworkPeerBootstrapper is an interface that represents an initial peer
|
||||||
// bootstrap mechanism. This interface is to be used to bootstrap a new peer to
|
// 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
|
// 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
|
// the ignore map is populated, then the bootstrappers will be instructed to
|
||||||
// skip those nodes.
|
// skip those nodes.
|
||||||
func MultiSourceBootstrap(ignore map[autopilot.NodeID]struct{}, numAddrs uint32,
|
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
|
var addrs []*lnwire.NetAddress
|
||||||
for _, bootStrapper := range bootStrappers {
|
for _, bootstrapper := range bootstrappers {
|
||||||
// If we already have enough addresses, then we can exit early
|
// If we already have enough addresses, then we can exit early
|
||||||
// w/o querying the additional bootstrappers.
|
// w/o querying the additional bootstrappers.
|
||||||
if uint32(len(addrs)) >= numAddrs {
|
if uint32(len(addrs)) >= numAddrs {
|
||||||
break
|
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
|
// If we still need additional addresses, then we'll compute
|
||||||
// the number of address remaining that we need to fetch.
|
// the number of address remaining that we need to fetch.
|
||||||
numAddrsLeft := numAddrs - uint32(len(addrs))
|
numAddrsLeft := numAddrs - uint32(len(addrs))
|
||||||
log.Tracef("Querying for %v addresses", numAddrsLeft)
|
log.Tracef("Querying for %v addresses", numAddrsLeft)
|
||||||
netAddrs, err := bootStrapper.SampleNodeAddrs(numAddrsLeft, ignore)
|
netAddrs, err := bootstrapper.SampleNodeAddrs(numAddrsLeft, ignore)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If we encounter an error with a bootstrapper, then
|
// If we encounter an error with a bootstrapper, then
|
||||||
// we'll continue on to the next available
|
// we'll continue on to the next available
|
||||||
// bootstrapper.
|
// bootstrapper.
|
||||||
log.Errorf("Unable to query bootstrapper %v: %v",
|
log.Errorf("Unable to query bootstrapper %v: %v",
|
||||||
bootStrapper.Name(), err)
|
bootstrapper.Name(), err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
addrs = append(addrs, netAddrs...)
|
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))
|
log.Infof("Obtained %v addrs to bootstrap network with", len(addrs))
|
||||||
|
|
||||||
return addrs, nil
|
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
|
// ChannelGraphBootstrapper is an implementation of the NetworkPeerBootstrapper
|
||||||
// which attempts to retrieve advertised peers directly from the active channel
|
// which attempts to retrieve advertised peers directly from the active channel
|
||||||
// graph. This instance requires a backing autopilot.ChannelGraph instance in
|
// graph. This instance requires a backing autopilot.ChannelGraph instance in
|
||||||
|
|||||||
Reference in new issue
Block a user