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 }