// PutValue adds value corresponding to given Key. // This is the top level "Store" operation of the DHT func (dht *IpfsDHT) PutValue(key u.Key, value []byte) error { complete := make(chan struct{}) count := 0 for _, route := range dht.routingTables { peers := route.NearestPeers(kb.ConvertKey(key), KValue) for _, p := range peers { if p == nil { dht.network.Error(kb.ErrLookupFailure) continue } count++ go func(sp *peer.Peer) { err := dht.putValueToNetwork(sp, string(key), value) if err != nil { dht.network.Error(err) } complete <- struct{}{} }(p) } } for i := 0; i < count; i++ { <-complete } return nil }
func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *PBDHTMessage) { resp := Message{ Type: pmes.GetType(), ID: pmes.GetId(), Response: true, } defer func() { mes := swarm.NewMessage(p, resp.ToProtobuf()) dht.netChan.Outgoing <- mes }() level := pmes.GetValue()[0] u.DOut("handleFindPeer: searching for '%s'\n", peer.ID(pmes.GetKey()).Pretty()) closest := dht.routingTables[level].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey()))) if closest == nil { u.PErr("handleFindPeer: could not find anything.\n") return } if len(closest.Addresses) == 0 { u.PErr("handleFindPeer: no addresses for connected peer...\n") return } // If the found peer further away than this peer... if kb.Closer(dht.self.ID, closest.ID, u.Key(pmes.GetKey())) { return } u.DOut("handleFindPeer: sending back '%s'\n", closest.ID.Pretty()) resp.Peers = []*peer.Peer{closest} resp.Success = true }
func (dht *IpfsDHT) FindProvidersAsync(key u.Key, count int, timeout time.Duration) chan *peer.Peer { peerOut := make(chan *peer.Peer, count) go func() { ps := newPeerSet() provs := dht.providers.GetProviders(key) for _, p := range provs { count-- // NOTE: assuming that this list of peers is unique ps.Add(p) peerOut <- p if count <= 0 { return } } peers := dht.routingTables[0].NearestPeers(kb.ConvertKey(key), AlphaValue) for _, pp := range peers { go func() { pmes, err := dht.findProvidersSingle(pp, key, 0, timeout) if err != nil { u.PErr("%v\n", err) return } dht.addPeerListAsync(key, pmes.GetPeers(), ps, count, peerOut) }() } }() return peerOut }
func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *PBDHTMessage) { u.DOut("handleGetValue for key: %s\n", pmes.GetKey()) dskey := ds.NewKey(pmes.GetKey()) resp := &Message{ Response: true, ID: pmes.GetId(), Key: pmes.GetKey(), } iVal, err := dht.datastore.Get(dskey) if err == nil { u.DOut("handleGetValue success!\n") resp.Success = true resp.Value = iVal.([]byte) } else if err == ds.ErrNotFound { // Check if we know any providers for the requested value provs := dht.providers.GetProviders(u.Key(pmes.GetKey())) if len(provs) > 0 { u.DOut("handleGetValue returning %d provider[s]\n", len(provs)) resp.Peers = provs resp.Success = true } else { // No providers? // Find closest peer on given cluster to desired key and reply with that info level := 0 if len(pmes.GetValue()) < 1 { // TODO: maybe return an error? Defaulting isnt a good idea IMO u.PErr("handleGetValue: no routing level specified, assuming 0\n") } else { level = int(pmes.GetValue()[0]) // Using value field to specify cluster level } u.DOut("handleGetValue searching level %d clusters\n", level) closer := dht.routingTables[level].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey()))) if closer.ID.Equal(dht.self.ID) { u.DOut("Attempted to return self! this shouldnt happen...\n") resp.Peers = nil goto out } // If this peer is closer than the one from the table, return nil if kb.Closer(dht.self.ID, closer.ID, u.Key(pmes.GetKey())) { resp.Peers = nil u.DOut("handleGetValue could not find a closer node than myself.\n") } else { u.DOut("handleGetValue returning a closer peer: '%s'\n", closer.ID.Pretty()) resp.Peers = []*peer.Peer{closer} } } } else { //temp: what other errors can a datastore return? panic(err) } out: mes := swarm.NewMessage(p, resp.ToProtobuf()) dht.netChan.Outgoing <- mes }
// FindProviders searches for peers who can provide the value for given key. func (dht *IpfsDHT) FindProviders(key u.Key, timeout time.Duration) ([]*peer.Peer, error) { ll := startNewRPC("FindProviders") defer func() { ll.EndLog() ll.Print() }() u.DOut("Find providers for: '%s'\n", key) p := dht.routingTables[0].NearestPeer(kb.ConvertKey(key)) if p == nil { return nil, kb.ErrLookupFailure } for level := 0; level < len(dht.routingTables); { pmes, err := dht.findProvidersSingle(p, key, level, timeout) if err != nil { return nil, err } if pmes.GetSuccess() { u.DOut("Got providers back from findProviders call!\n") provs := dht.addPeerList(key, pmes.GetPeers()) ll.Success = true return provs, nil } u.DOut("Didnt get providers, just closer peers.\n") closer := pmes.GetPeers() if len(closer) == 0 { level++ continue } if peer.ID(closer[0].GetId()).Equal(dht.self.ID) { u.DOut("Got myself back as a closer peer.") return nil, u.ErrNotFound } maddr, err := ma.NewMultiaddr(closer[0].GetAddr()) if err != nil { // ??? Move up route level??? panic("not yet implemented") } np, err := dht.network.GetConnection(peer.ID(closer[0].GetId()), maddr) if err != nil { u.PErr("[%s] Failed to connect to: %s\n", dht.self.ID.Pretty(), closer[0].GetAddr()) level++ continue } p = np } return nil, u.ErrNotFound }
// Provide makes this node announce that it can provide a value for the given key func (dht *IpfsDHT) Provide(key u.Key) error { dht.providers.AddProvider(key, dht.self) peers := dht.routingTables[0].NearestPeers(kb.ConvertKey(key), PoolSize) if len(peers) == 0 { return kb.ErrLookupFailure } pmes := Message{ Type: PBDHTMessage_ADD_PROVIDER, Key: string(key), } pbmes := pmes.ToProtobuf() for _, p := range peers { mes := swarm.NewMessage(p, pbmes) dht.netChan.Outgoing <- mes } return nil }
func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *PBDHTMessage) { resp := Message{ Type: PBDHTMessage_GET_PROVIDERS, Key: pmes.GetKey(), ID: pmes.GetId(), Response: true, } has, err := dht.datastore.Has(ds.NewKey(pmes.GetKey())) if err != nil { dht.netChan.Errors <- err } providers := dht.providers.GetProviders(u.Key(pmes.GetKey())) if has { providers = append(providers, dht.self) } if providers == nil || len(providers) == 0 { level := 0 if len(pmes.GetValue()) > 0 { level = int(pmes.GetValue()[0]) } closer := dht.routingTables[level].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey()))) if kb.Closer(dht.self.ID, closer.ID, u.Key(pmes.GetKey())) { resp.Peers = nil } else { resp.Peers = []*peer.Peer{closer} } } else { resp.Peers = providers resp.Success = true } mes := swarm.NewMessage(p, resp.ToProtobuf()) dht.netChan.Outgoing <- mes }
// 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 } }