github.com/qri-io/qri@v0.10.1-0.20220104210721-c771715036cb/p2p/bootstrap.go (about)

     1  package p2p
     2  
     3  import (
     4  	"context"
     5  	"math"
     6  	"math/rand"
     7  
     8  	"github.com/ipfs/go-ipfs/core/bootstrap"
     9  	peer "github.com/libp2p/go-libp2p-core/peer"
    10  	ma "github.com/multiformats/go-multiaddr"
    11  )
    12  
    13  // Bootstrap samples a subset of peers & requests their peers list
    14  // This is a naive version of IPFS bootstrapping, which we'll add in once
    15  // qri's settled on a shared-state implementation
    16  func (n *QriNode) Bootstrap(boostrapAddrs []string) {
    17  	peers, err := ParseMultiaddrs(boostrapAddrs)
    18  	if err != nil {
    19  		log.Info("error parsing bootstrap addresses:", err.Error())
    20  		return
    21  	}
    22  
    23  	pinfos := toPeerInfos(peers)
    24  	// TODO (ramfox): this randomSubsetOfPeers func is currently always
    25  	// returning the same 4 peers. Right now, I think it's okay to attempt to
    26  	// connect to all 7 of the bootstrap peers
    27  	// when we have more bootstraps in the future, then we can add back
    28  	// only dialing to a random subset
    29  	// for _, p := range randomSubsetOfPeers(pinfos, 4) {
    30  	for _, p := range pinfos {
    31  		go func(p peer.AddrInfo) {
    32  			log.Debugf("boostrapping to: %s", p.ID.Pretty())
    33  			if err := n.host.Connect(context.Background(), p); err != nil {
    34  				log.Infof("error connecting to host: %s", err.Error())
    35  			}
    36  		}(p)
    37  	}
    38  }
    39  
    40  // BootstrapIPFS connects this node to standard ipfs nodes for file exchange
    41  func (n *QriNode) BootstrapIPFS() {
    42  	if node, err := n.IPFS(); err == nil {
    43  		if err := node.Bootstrap(bootstrap.DefaultBootstrapConfig); err != nil {
    44  			log.Errorf("IPFS bootsrap error: %s", err.Error())
    45  		}
    46  	}
    47  }
    48  
    49  // ParseMultiaddrs turns a slice of strings into a slice of Multiaddrs
    50  func ParseMultiaddrs(addrs []string) (maddrs []ma.Multiaddr, err error) {
    51  	maddrs = make([]ma.Multiaddr, len(addrs))
    52  	for i, adr := range addrs {
    53  		maddrs[i], err = ma.NewMultiaddr(adr)
    54  		if err != nil {
    55  			return
    56  		}
    57  	}
    58  	return
    59  }
    60  
    61  // toPeerInfos turns a slice of multiaddrs into a slice of PeerInfos
    62  func toPeerInfos(addrs []ma.Multiaddr) []peer.AddrInfo {
    63  	pinfos := make(map[peer.ID]*peer.AddrInfo)
    64  	for _, addr := range addrs {
    65  		pid, err := addr.ValueForProtocol(ma.P_IPFS)
    66  		if err != nil {
    67  			return nil
    68  		}
    69  		peerid, err := peer.IDB58Decode(pid)
    70  		if err != nil {
    71  			return nil
    72  		}
    73  
    74  		pinfo, ok := pinfos[peerid]
    75  		if !ok {
    76  			pinfo = new(peer.AddrInfo)
    77  			pinfos[peerid] = pinfo
    78  			pinfo.ID = peerid
    79  		}
    80  
    81  		// TODO - support circuit-relay once it normalizes
    82  		split := ma.Split(addr)
    83  		maddr := ma.Join(split[:len(split)-1]...)
    84  		pinfo.Addrs = append(pinfo.Addrs, maddr)
    85  	}
    86  
    87  	var peers []peer.AddrInfo
    88  	for _, pinfo := range pinfos {
    89  		peers = append(peers, *pinfo)
    90  	}
    91  
    92  	return peers
    93  }
    94  
    95  // TODO (ramfox): this is always returning the same bootstrap peers
    96  // since the length of the list of peers that is given are always
    97  // the same
    98  // randomSubsetOfPeers samples up to max from a slice of PeerInfos
    99  func randomSubsetOfPeers(in []peer.AddrInfo, max int) []peer.AddrInfo {
   100  	n := int(math.Min(float64(max), float64(len(in))))
   101  	var out []peer.AddrInfo
   102  	for _, val := range rand.Perm(len(in)) {
   103  		out = append(out, in[val])
   104  		if len(out) >= n {
   105  			break
   106  		}
   107  	}
   108  	return out
   109  }