func (dht *IpfsDHT) findPeerMultiple(id peer.ID, timeout time.Duration) (*peer.Peer, error) { // Check if were already connected to them p, _ := dht.Find(id) if p != nil { return p, nil } routeLevel := 0 peers := dht.routingTables[routeLevel].NearestPeers(kb.ConvertPeerID(id), AlphaValue) if len(peers) == 0 { return nil, kb.ErrLookupFailure } found := make(chan *peer.Peer) after := time.After(timeout) for _, p := range peers { go func(p *peer.Peer) { pmes, err := dht.findPeerSingle(p, id, timeout, routeLevel) if err != nil { u.DErr("getPeer error: %v\n", err) return } plist := pmes.GetPeers() if len(plist) == 0 { routeLevel++ } for _, fp := range plist { nxtp, err := dht.peerFromInfo(fp) if err != nil { u.DErr("findPeer error: %v\n", err) continue } if nxtp.ID.Equal(dht.self.ID) { found <- nxtp return } } }(p) } select { case p := <-found: return p, nil case <-after: return nil, u.ErrTimeout } }
// TODO: Im not certain on this implementation, we get a list of peers/providers // from someone what do we do with it? Connect to each of them? randomly pick // one to get the value from? Or just connect to one at a time until we get a // successful connection and request the value from it? func (dht *IpfsDHT) getFromPeerList(key u.Key, timeout time.Duration, peerlist []*PBDHTMessage_PBPeer, level int) ([]byte, error) { for _, pinfo := range peerlist { p, _ := dht.Find(peer.ID(pinfo.GetId())) if p == nil { maddr, err := ma.NewMultiaddr(pinfo.GetAddr()) if err != nil { u.PErr("getValue error: %s\n", err) continue } p, err = dht.network.GetConnection(peer.ID(pinfo.GetId()), maddr) if err != nil { u.PErr("getValue error: %s\n", err) continue } } pmes, err := dht.getValueSingle(p, key, timeout, level) if err != nil { u.DErr("getFromPeers error: %s\n", err) continue } dht.providers.AddProvider(key, p) // Make sure it was a successful get if pmes.GetSuccess() && pmes.Value != nil { return pmes.GetValue(), nil } } return nil, u.ErrNotFound }
// GetValue searches for the value corresponding to given Key. // If the search does not succeed, a multiaddr string of a closer peer is // returned along with util.ErrSearchIncomplete func (dht *IpfsDHT) GetValue(key u.Key, timeout time.Duration) ([]byte, error) { ll := startNewRPC("GET") defer func() { ll.EndLog() ll.Print() }() // If we have it local, dont bother doing an RPC! // NOTE: this might not be what we want to do... val, err := dht.getLocal(key) if err == nil { ll.Success = true u.DOut("Found local, returning.\n") return val, nil } routeLevel := 0 closest := dht.routingTables[routeLevel].NearestPeers(kb.ConvertKey(key), PoolSize) if closest == nil || len(closest) == 0 { return nil, kb.ErrLookupFailure } valChan := make(chan []byte) npeerChan := make(chan *peer.Peer, 30) procPeer := make(chan *peer.Peer, 30) errChan := make(chan error) after := time.After(timeout) pset := newPeerSet() for _, p := range closest { pset.Add(p) npeerChan <- p } c := counter{} count := 0 go func() { defer close(procPeer) for { select { case p, ok := <-npeerChan: if !ok { return } count++ if count >= KValue { errChan <- u.ErrNotFound return } c.Increment() procPeer <- p default: if c.Size() <= 0 { select { case errChan <- u.ErrNotFound: default: } return } } } }() process := func() { defer c.Decrement() for p := range procPeer { if p == nil { return } val, peers, err := dht.getValueOrPeers(p, key, timeout/4, routeLevel) if err != nil { u.DErr("%v\n", err.Error()) continue } if val != nil { select { case valChan <- val: default: u.DOut("Wasnt the first to return the value!") } return } for _, np := range peers { // TODO: filter out peers that arent closer if !pset.Contains(np) && pset.Size() < KValue { pset.Add(np) //This is racey... make a single function to do operation npeerChan <- np } } c.Decrement() } } for i := 0; i < AlphaValue; i++ { go process() } select { case val := <-valChan: return val, nil case err := <-errChan: return nil, err case <-after: return nil, u.ErrTimeout } }