github.com/decred/dcrlnd@v0.7.6/discovery/bootstrapper.go (about)

     1  package discovery
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"crypto/rand"
     7  	"crypto/sha256"
     8  	"errors"
     9  	"fmt"
    10  	prand "math/rand"
    11  	"net"
    12  	"strconv"
    13  	"strings"
    14  	"time"
    15  
    16  	"github.com/miekg/dns"
    17  
    18  	"github.com/davecgh/go-spew/spew"
    19  	"github.com/decred/dcrd/bech32"
    20  	"github.com/decred/dcrd/dcrec/secp256k1/v4"
    21  	"github.com/decred/dcrlnd/autopilot"
    22  	"github.com/decred/dcrlnd/lnwire"
    23  	"github.com/decred/dcrlnd/tor"
    24  )
    25  
    26  // ErrNoAddressesFound is returned by the MultiSourceBootstrap to signal that
    27  // after attempting all available sub-bootstrappers, none returned a usable
    28  // address.
    29  var ErrNoAddressesFound = errors.New("no addresses found")
    30  
    31  func init() {
    32  	prand.Seed(time.Now().Unix())
    33  }
    34  
    35  // NetworkPeerBootstrapper is an interface that represents an initial peer
    36  // bootstrap mechanism. This interface is to be used to bootstrap a new peer to
    37  // the connection by providing it with the pubkey+address of a set of existing
    38  // peers on the network. Several bootstrap mechanisms can be implemented such
    39  // as DNS, in channel graph, DHT's, etc.
    40  type NetworkPeerBootstrapper interface {
    41  	// SampleNodeAddrs uniformly samples a set of specified address from
    42  	// the network peer bootstrapper source. The num addrs field passed in
    43  	// denotes how many valid peer addresses to return. The passed set of
    44  	// node nodes allows the caller to ignore a set of nodes perhaps
    45  	// because they already have connections established.
    46  	SampleNodeAddrs(ctx context.Context, numAddrs uint32,
    47  		ignore map[autopilot.NodeID]struct{}) ([]*lnwire.NetAddress, error)
    48  
    49  	// Name returns a human readable string which names the concrete
    50  	// implementation of the NetworkPeerBootstrapper.
    51  	Name() string
    52  }
    53  
    54  // MultiSourceBootstrap attempts to utilize a set of NetworkPeerBootstrapper
    55  // passed in to return the target (numAddrs) number of peer addresses that can
    56  // be used to bootstrap a peer just joining the Lightning Network. Each
    57  // bootstrapper will be queried successively until the target amount is met. If
    58  // the ignore map is populated, then the bootstrappers will be instructed to
    59  // skip those nodes.
    60  func MultiSourceBootstrap(ctx context.Context, ignore map[autopilot.NodeID]struct{}, numAddrs uint32,
    61  	bootstrappers ...NetworkPeerBootstrapper) ([]*lnwire.NetAddress, error) {
    62  
    63  	// We'll randomly shuffle our bootstrappers before querying them in
    64  	// order to avoid from querying the same bootstrapper method over and
    65  	// over, as some of these might tend to provide better/worse results
    66  	// than others.
    67  	bootstrappers = shuffleBootstrappers(bootstrappers)
    68  
    69  	var addrs []*lnwire.NetAddress
    70  	for _, bootstrapper := range bootstrappers {
    71  		// If we already have enough addresses, then we can exit early
    72  		// w/o querying the additional bootstrappers.
    73  		if uint32(len(addrs)) >= numAddrs {
    74  			break
    75  		}
    76  
    77  		log.Infof("Attempting to bootstrap with: %v", bootstrapper.Name())
    78  
    79  		// If we still need additional addresses, then we'll compute
    80  		// the number of address remaining that we need to fetch.
    81  		numAddrsLeft := numAddrs - uint32(len(addrs))
    82  		log.Tracef("Querying for %v addresses", numAddrsLeft)
    83  		netAddrs, err := bootstrapper.SampleNodeAddrs(ctx, numAddrsLeft, ignore)
    84  		if err != nil {
    85  			// If we encounter an error with a bootstrapper, then
    86  			// we'll continue on to the next available
    87  			// bootstrapper.
    88  			log.Errorf("Unable to query bootstrapper %v: %v",
    89  				bootstrapper.Name(), err)
    90  			continue
    91  		}
    92  
    93  		addrs = append(addrs, netAddrs...)
    94  	}
    95  
    96  	if len(addrs) == 0 {
    97  		return nil, ErrNoAddressesFound
    98  	}
    99  
   100  	log.Infof("Obtained %v addrs to bootstrap network with", len(addrs))
   101  
   102  	return addrs, nil
   103  }
   104  
   105  // shuffleBootstrappers shuffles the set of bootstrappers in order to avoid
   106  // querying the same bootstrapper over and over. To shuffle the set of
   107  // candidates, we use a version of the Fisher–Yates shuffle algorithm.
   108  func shuffleBootstrappers(candidates []NetworkPeerBootstrapper) []NetworkPeerBootstrapper {
   109  	shuffled := make([]NetworkPeerBootstrapper, len(candidates))
   110  	perm := prand.Perm(len(candidates))
   111  
   112  	for i, v := range perm {
   113  		shuffled[v] = candidates[i]
   114  	}
   115  
   116  	return shuffled
   117  }
   118  
   119  // ChannelGraphBootstrapper is an implementation of the NetworkPeerBootstrapper
   120  // which attempts to retrieve advertised peers directly from the active channel
   121  // graph. This instance requires a backing autopilot.ChannelGraph instance in
   122  // order to operate properly.
   123  type ChannelGraphBootstrapper struct {
   124  	chanGraph autopilot.ChannelGraph
   125  
   126  	// hashAccumulator is a set of 32 random bytes that are read upon the
   127  	// creation of the channel graph bootstrapper. We use this value to
   128  	// randomly select nodes within the known graph to connect to. After
   129  	// each selection, we rotate the accumulator by hashing it with itself.
   130  	hashAccumulator [32]byte
   131  
   132  	tried map[autopilot.NodeID]struct{}
   133  }
   134  
   135  // A compile time assertion to ensure that ChannelGraphBootstrapper meets the
   136  // NetworkPeerBootstrapper interface.
   137  var _ NetworkPeerBootstrapper = (*ChannelGraphBootstrapper)(nil)
   138  
   139  // NewGraphBootstrapper returns a new instance of a ChannelGraphBootstrapper
   140  // backed by an active autopilot.ChannelGraph instance. This type of network
   141  // peer bootstrapper will use the authenticated nodes within the known channel
   142  // graph to bootstrap connections.
   143  func NewGraphBootstrapper(cg autopilot.ChannelGraph) (NetworkPeerBootstrapper, error) {
   144  
   145  	c := &ChannelGraphBootstrapper{
   146  		chanGraph: cg,
   147  		tried:     make(map[autopilot.NodeID]struct{}),
   148  	}
   149  
   150  	if _, err := rand.Read(c.hashAccumulator[:]); err != nil {
   151  		return nil, err
   152  	}
   153  
   154  	return c, nil
   155  }
   156  
   157  // SampleNodeAddrs uniformly samples a set of specified address from the
   158  // network peer bootstrapper source. The num addrs field passed in denotes how
   159  // many valid peer addresses to return.
   160  //
   161  // NOTE: Part of the NetworkPeerBootstrapper interface.
   162  func (c *ChannelGraphBootstrapper) SampleNodeAddrs(ctx context.Context,
   163  	numAddrs uint32, ignore map[autopilot.NodeID]struct{}) ([]*lnwire.NetAddress, error) {
   164  
   165  	// We'll merge the ignore map with our currently selected map in order
   166  	// to ensure we don't return any duplicate nodes.
   167  	for n := range ignore {
   168  		log.Tracef("Ignored node %x for bootstrapping", n)
   169  		c.tried[n] = struct{}{}
   170  	}
   171  
   172  	// In order to bootstrap, we'll iterate all the nodes in the channel
   173  	// graph, accumulating nodes until either we go through all active
   174  	// nodes, or we reach our limit. We ensure that we meet the randomly
   175  	// sample constraint as we maintain an xor accumulator to ensure we
   176  	// randomly sample nodes independent of the iteration of the channel
   177  	// graph.
   178  	sampleAddrs := func() ([]*lnwire.NetAddress, error) {
   179  		var (
   180  			a []*lnwire.NetAddress
   181  
   182  			// We'll create a special error so we can return early
   183  			// and abort the transaction once we find a match.
   184  			errFound = fmt.Errorf("found node")
   185  		)
   186  
   187  		err := c.chanGraph.ForEachNode(func(node autopilot.Node) error {
   188  			nID := autopilot.NodeID(node.PubKey())
   189  			if _, ok := c.tried[nID]; ok {
   190  				return nil
   191  			}
   192  
   193  			// We'll select the first node we come across who's
   194  			// public key is less than our current accumulator
   195  			// value. When comparing, we skip the first byte as
   196  			// it's 50/50. If it isn't less, than then we'll
   197  			// continue forward.
   198  			nodePubKeyBytes := node.PubKey()
   199  			if bytes.Compare(c.hashAccumulator[:], nodePubKeyBytes[1:]) > 0 {
   200  				return nil
   201  			}
   202  
   203  			for _, nodeAddr := range node.Addrs() {
   204  				// If we haven't yet reached our limit, then
   205  				// we'll copy over the details of this node
   206  				// into the set of addresses to be returned.
   207  				switch nodeAddr.(type) {
   208  				case *net.TCPAddr, *tor.OnionAddr:
   209  				default:
   210  					// If this isn't a valid address
   211  					// supported by the protocol, then we'll
   212  					// skip this node.
   213  					return nil
   214  				}
   215  
   216  				nodePub, err := secp256k1.ParsePubKey(nodePubKeyBytes[:])
   217  				if err != nil {
   218  					return err
   219  				}
   220  
   221  				// At this point, we've found an eligible node,
   222  				// so we'll return early with our shibboleth
   223  				// error.
   224  				a = append(a, &lnwire.NetAddress{
   225  					IdentityKey: nodePub,
   226  					Address:     nodeAddr,
   227  				})
   228  			}
   229  
   230  			c.tried[nID] = struct{}{}
   231  
   232  			return errFound
   233  		})
   234  		if err != nil && err != errFound {
   235  			return nil, err
   236  		}
   237  
   238  		return a, nil
   239  	}
   240  
   241  	// We'll loop and sample new addresses from the graph source until
   242  	// we've reached our target number of outbound connections or we hit 50
   243  	// attempts, which ever comes first.
   244  	var (
   245  		addrs []*lnwire.NetAddress
   246  		tries uint32
   247  	)
   248  	for tries < 30 && uint32(len(addrs)) < numAddrs {
   249  		sampleAddrs, err := sampleAddrs()
   250  		if err != nil {
   251  			return nil, err
   252  		}
   253  
   254  		tries++
   255  
   256  		// We'll now rotate our hash accumulator one value forwards.
   257  		c.hashAccumulator = sha256.Sum256(c.hashAccumulator[:])
   258  
   259  		// If this attempt didn't yield any addresses, then we'll exit
   260  		// early.
   261  		if len(sampleAddrs) == 0 {
   262  			continue
   263  		}
   264  
   265  		addrs = append(addrs, sampleAddrs...)
   266  	}
   267  
   268  	log.Tracef("Ending hash accumulator state: %x", c.hashAccumulator)
   269  
   270  	return addrs, nil
   271  }
   272  
   273  // Name returns a human readable string which names the concrete implementation
   274  // of the NetworkPeerBootstrapper.
   275  //
   276  // NOTE: Part of the NetworkPeerBootstrapper interface.
   277  func (c *ChannelGraphBootstrapper) Name() string {
   278  	return "Authenticated Channel Graph"
   279  }
   280  
   281  // DNSSeedBootstrapper as an implementation of the NetworkPeerBootstrapper
   282  // interface which implements peer bootstrapping via a special DNS seed as
   283  // defined in BOLT-0010. For further details concerning Lightning's current DNS
   284  // boot strapping protocol, see this link:
   285  //   - https://github.com/lightningnetwork/lightning-rfc/blob/master/10-dns-bootstrap.md
   286  type DNSSeedBootstrapper struct {
   287  	// dnsSeeds is an array of two tuples we'll use for bootstrapping. The
   288  	// first item in the tuple is the primary host we'll use to attempt the
   289  	// SRV lookup we require. If we're unable to receive a response over
   290  	// UDP, then we'll fall back to manual TCP resolution. The second item
   291  	// in the tuple is a special A record that we'll query in order to
   292  	// receive the IP address of the current authoritative DNS server for
   293  	// the network seed.
   294  	dnsSeeds [][2]string
   295  	net      tor.Net
   296  
   297  	// timeout is the maximum amount of time a dial will wait for a connect to
   298  	// complete.
   299  	timeout time.Duration
   300  }
   301  
   302  // A compile time assertion to ensure that DNSSeedBootstrapper meets the
   303  // NetworkPeerjBootstrapper interface.
   304  var _ NetworkPeerBootstrapper = (*ChannelGraphBootstrapper)(nil)
   305  
   306  // NewDNSSeedBootstrapper returns a new instance of the DNSSeedBootstrapper.
   307  // The set of passed seeds should point to DNS servers that properly implement
   308  // Lightning's DNS peer bootstrapping protocol as defined in BOLT-0010. The set
   309  // of passed DNS seeds should come in pairs, with the second host name to be
   310  // used as a fallback for manual TCP resolution in the case of an error
   311  // receiving the UDP response. The second host should return a single A record
   312  // with the IP address of the authoritative name server.
   313  func NewDNSSeedBootstrapper(
   314  	seeds [][2]string, net tor.Net,
   315  	timeout time.Duration) NetworkPeerBootstrapper {
   316  	return &DNSSeedBootstrapper{dnsSeeds: seeds, net: net, timeout: timeout}
   317  }
   318  
   319  // fallBackSRVLookup attempts to manually query for SRV records we need to
   320  // properly bootstrap. We do this by querying the special record at the "soa."
   321  // sub-domain of supporting DNS servers. The retuned IP address will be the IP
   322  // address of the authoritative DNS server. Once we have this IP address, we'll
   323  // connect manually over TCP to request the SRV record. This is necessary as
   324  // the records we return are currently too large for a class of resolvers,
   325  // causing them to be filtered out. The targetEndPoint is the original end
   326  // point that was meant to be hit.
   327  func (d *DNSSeedBootstrapper) fallBackSRVLookup(soaShim string,
   328  	targetEndPoint string) ([]*net.SRV, error) {
   329  
   330  	log.Tracef("Attempting to query fallback DNS seed")
   331  
   332  	// First, we'll lookup the IP address of the server that will act as
   333  	// our shim.
   334  	addrs, err := d.net.LookupHost(soaShim)
   335  	if err != nil {
   336  		return nil, err
   337  	}
   338  
   339  	// Once we have the IP address, we'll establish a TCP connection using
   340  	// port 53.
   341  	dnsServer := net.JoinHostPort(addrs[0], "53")
   342  	conn, err := d.net.Dial("tcp", dnsServer, d.timeout)
   343  	if err != nil {
   344  		return nil, err
   345  	}
   346  
   347  	dnsHost := fmt.Sprintf("_nodes._tcp.%v.", targetEndPoint)
   348  	dnsConn := &dns.Conn{Conn: conn}
   349  	defer dnsConn.Close()
   350  
   351  	// With the connection established, we'll craft our SRV query, write
   352  	// toe request, then wait for the server to give our response.
   353  	msg := new(dns.Msg)
   354  	msg.SetQuestion(dnsHost, dns.TypeSRV)
   355  	if err := dnsConn.WriteMsg(msg); err != nil {
   356  		return nil, err
   357  	}
   358  	resp, err := dnsConn.ReadMsg()
   359  	if err != nil {
   360  		return nil, err
   361  	}
   362  
   363  	// If the message response code was not the success code, fail.
   364  	if resp.Rcode != dns.RcodeSuccess {
   365  		return nil, fmt.Errorf("unsuccessful SRV request, "+
   366  			"received: %v", resp.Rcode)
   367  	}
   368  
   369  	// Retrieve the RR(s) of the Answer section, and covert to the format
   370  	// that net.LookupSRV would normally return.
   371  	var rrs []*net.SRV
   372  	for _, rr := range resp.Answer {
   373  		srv := rr.(*dns.SRV)
   374  		rrs = append(rrs, &net.SRV{
   375  			Target:   srv.Target,
   376  			Port:     srv.Port,
   377  			Priority: srv.Priority,
   378  			Weight:   srv.Weight,
   379  		})
   380  	}
   381  
   382  	return rrs, nil
   383  }
   384  
   385  // SampleNodeAddrs uniformly samples a set of specified address from the
   386  // network peer bootstrapper source. The num addrs field passed in denotes how
   387  // many valid peer addresses to return. The set of DNS seeds are used
   388  // successively to retrieve eligible target nodes.
   389  func (d *DNSSeedBootstrapper) SampleNodeAddrs(ctx context.Context,
   390  	numAddrs uint32, ignore map[autopilot.NodeID]struct{}) ([]*lnwire.NetAddress, error) {
   391  
   392  	var netAddrs []*lnwire.NetAddress
   393  
   394  	if len(d.dnsSeeds) == 0 {
   395  		return nil, errors.New("empty dns seeder")
   396  	}
   397  
   398  	// We'll try all the registered DNS seeds, exiting early if one of them
   399  	// gives us all the peers we need.
   400  	//
   401  	// TODO(roasbeef): should combine results from both
   402  search:
   403  	for _, dnsSeedTuple := range d.dnsSeeds {
   404  		// We'll first query the seed with an SRV record so we can
   405  		// obtain a random sample of the encoded public keys of nodes.
   406  		// We use the lndLookupSRV function for this task.
   407  		primarySeed := dnsSeedTuple[0]
   408  		_, addrs, err := d.net.LookupSRV(
   409  			"nodes", "tcp", primarySeed, d.timeout,
   410  		)
   411  		if err != nil {
   412  			log.Tracef("Unable to lookup SRV records via "+
   413  				"primary seed (%v): %v", primarySeed, err)
   414  
   415  			log.Trace("Falling back to secondary")
   416  
   417  			// If the host of the secondary seed is blank, then
   418  			// we'll bail here as we can't proceed.
   419  			if dnsSeedTuple[1] == "" {
   420  				log.Tracef("DNS seed %v has no secondary, "+
   421  					"skipping fallback", primarySeed)
   422  				continue
   423  			}
   424  
   425  			// If we get an error when trying to query via the
   426  			// primary seed, we'll fallback to the secondary seed
   427  			// before concluding failure.
   428  			soaShim := dnsSeedTuple[1]
   429  			addrs, err = d.fallBackSRVLookup(
   430  				soaShim, primarySeed,
   431  			)
   432  			if err != nil {
   433  				log.Tracef("Unable to query fall "+
   434  					"back dns seed (%v): %v", soaShim, err)
   435  				continue
   436  			}
   437  
   438  			log.Tracef("Successfully queried fallback DNS seed")
   439  		}
   440  
   441  		log.Tracef("Retrieved SRV records from dns seed: %v",
   442  			newLogClosure(func() string {
   443  				return spew.Sdump(addrs)
   444  			}),
   445  		)
   446  
   447  		// Next, we'll need to issue an A record request for each of
   448  		// the nodes, skipping it if nothing comes back.
   449  		for _, nodeSrv := range addrs {
   450  			if uint32(len(netAddrs)) >= numAddrs {
   451  				break search
   452  			}
   453  
   454  			// With the SRV target obtained, we'll now perform
   455  			// another query to obtain the IP address for the
   456  			// matching bech32 encoded node key. We use the
   457  			// lndLookup function for this task.
   458  			bechNodeHost := nodeSrv.Target
   459  			addrs, err := d.net.LookupHost(bechNodeHost)
   460  			if err != nil {
   461  				return nil, err
   462  			}
   463  
   464  			if len(addrs) == 0 {
   465  				log.Tracef("No addresses for %v, skipping",
   466  					bechNodeHost)
   467  				continue
   468  			}
   469  
   470  			log.Tracef("Attempting to convert: %v", bechNodeHost)
   471  
   472  			// If the host isn't correctly formatted, then we'll
   473  			// skip it.
   474  			if len(bechNodeHost) == 0 ||
   475  				!strings.Contains(bechNodeHost, ".") {
   476  
   477  				continue
   478  			}
   479  
   480  			// If we have a set of valid addresses, then we'll need
   481  			// to parse the public key from the original bech32
   482  			// encoded string.
   483  			bechNode := strings.Split(bechNodeHost, ".")
   484  			_, nodeBytes5Bits, err := bech32.Decode(bechNode[0])
   485  			if err != nil {
   486  				return nil, err
   487  			}
   488  
   489  			// Once we have the bech32 decoded pubkey, we'll need
   490  			// to convert the 5-bit word grouping into our regular
   491  			// 8-bit word grouping so we can convert it into a
   492  			// public key.
   493  			nodeBytes, err := bech32.ConvertBits(
   494  				nodeBytes5Bits, 5, 8, false,
   495  			)
   496  			if err != nil {
   497  				return nil, err
   498  			}
   499  			nodeKey, err := secp256k1.ParsePubKey(
   500  				nodeBytes,
   501  			)
   502  			if err != nil {
   503  				return nil, err
   504  			}
   505  
   506  			// If we have an ignore list, and this node is in the
   507  			// ignore list, then we'll go to the next candidate.
   508  			if ignore != nil {
   509  				nID := autopilot.NewNodeID(nodeKey)
   510  				if _, ok := ignore[nID]; ok {
   511  					continue
   512  				}
   513  			}
   514  
   515  			// Finally we'll convert the host:port peer to a proper
   516  			// TCP address to use within the lnwire.NetAddress. We
   517  			// don't need to use the lndResolveTCP function here
   518  			// because we already have the host:port peer.
   519  			addr := net.JoinHostPort(
   520  				addrs[0],
   521  				strconv.FormatUint(uint64(nodeSrv.Port), 10),
   522  			)
   523  			tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
   524  			if err != nil {
   525  				return nil, err
   526  			}
   527  
   528  			// Finally, with all the information parsed, we'll
   529  			// return this fully valid address as a connection
   530  			// attempt.
   531  			lnAddr := &lnwire.NetAddress{
   532  				IdentityKey: nodeKey,
   533  				Address:     tcpAddr,
   534  			}
   535  
   536  			log.Tracef("Obtained %v as valid reachable "+
   537  				"node", lnAddr)
   538  
   539  			netAddrs = append(netAddrs, lnAddr)
   540  		}
   541  	}
   542  
   543  	return netAddrs, nil
   544  }
   545  
   546  // Name returns a human readable string which names the concrete
   547  // implementation of the NetworkPeerBootstrapper.
   548  func (d *DNSSeedBootstrapper) Name() string {
   549  	return fmt.Sprintf("BOLT-0010 DNS Seed: %v", d.dnsSeeds)
   550  }