// NewDHT creates a new DHT object with the given peer as the 'local' host func NewDHT(p *peer.Peer, net swarm.Network, dstore ds.Datastore) *IpfsDHT { dht := new(IpfsDHT) dht.network = net dht.netChan = net.GetChannel(swarm.PBWrapper_DHT_MESSAGE) dht.datastore = dstore dht.self = p dht.providers = NewProviderManager(p.ID) dht.shutdown = make(chan struct{}) dht.routingTables = make([]*kb.RoutingTable, 3) dht.routingTables[0] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Millisecond*30) dht.routingTables[1] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Millisecond*100) dht.routingTables[2] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Hour) dht.listener = swarm.NewMessageListener() dht.birth = time.Now() return dht }
func (dht *IpfsDHT) getDiagnostic(timeout time.Duration) ([]*diagInfo, error) { u.DOut("Begin Diagnostic") //Send to N closest peers targets := dht.routingTables[0].NearestPeers(kb.ConvertPeerID(dht.self.ID), 10) // TODO: Add timeout to this struct so nodes know when to return pmes := Message{ Type: PBDHTMessage_DIAGNOSTIC, ID: swarm.GenerateMessageID(), } listenChan := dht.listener.Listen(pmes.ID, len(targets), time.Minute*2) pbmes := pmes.ToProtobuf() for _, p := range targets { mes := swarm.NewMessage(p, pbmes) dht.netChan.Outgoing <- mes } var out []*diagInfo after := time.After(timeout) for count := len(targets); count > 0; { select { case <-after: u.DOut("Diagnostic request timed out.") return out, u.ErrTimeout case resp := <-listenChan: pmesOut := new(PBDHTMessage) err := proto.Unmarshal(resp.Data, pmesOut) if err != nil { // NOTE: here and elsewhere, need to audit error handling, // some errors should be continued on from return out, err } dec := json.NewDecoder(bytes.NewBuffer(pmesOut.GetValue())) for { di := new(diagInfo) err := dec.Decode(di) if err != nil { break } out = append(out, di) } } } return nil, nil }
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 } }
// NOTE: not yet finished, low priority func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *PBDHTMessage) { seq := dht.routingTables[0].NearestPeers(kb.ConvertPeerID(dht.self.ID), 10) listenChan := dht.listener.Listen(pmes.GetId(), len(seq), time.Second*30) for _, ps := range seq { mes := swarm.NewMessage(ps, pmes) dht.netChan.Outgoing <- mes } buf := new(bytes.Buffer) di := dht.getDiagInfo() buf.Write(di.Marshal()) // NOTE: this shouldnt be a hardcoded value after := time.After(time.Second * 20) count := len(seq) for count > 0 { select { case <-after: //Timeout, return what we have goto out case reqResp := <-listenChan: pmesOut := new(PBDHTMessage) err := proto.Unmarshal(reqResp.Data, pmesOut) if err != nil { // It broke? eh, whatever, keep going continue } buf.Write(reqResp.Data) count-- } } out: resp := Message{ Type: PBDHTMessage_DIAGNOSTIC, ID: pmes.GetId(), Value: buf.Bytes(), Response: true, } mes := swarm.NewMessage(p, resp.ToProtobuf()) dht.netChan.Outgoing <- mes }
// FindPeer searches for a peer with given ID. func (dht *IpfsDHT) FindPeer(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 p = dht.routingTables[routeLevel].NearestPeer(kb.ConvertPeerID(id)) if p == nil { return nil, kb.ErrLookupFailure } if p.ID.Equal(id) { return p, nil } for routeLevel < len(dht.routingTables) { pmes, err := dht.findPeerSingle(p, id, timeout, routeLevel) plist := pmes.GetPeers() if plist == nil || len(plist) == 0 { routeLevel++ continue } found := plist[0] addr, err := ma.NewMultiaddr(found.GetAddr()) if err != nil { return nil, err } nxtPeer, err := dht.network.GetConnection(peer.ID(found.GetId()), addr) if err != nil { return nil, err } if pmes.GetSuccess() { if !id.Equal(nxtPeer.ID) { return nil, errors.New("got back invalid peer from 'successful' response") } return nxtPeer, nil } p = nxtPeer } return nil, u.ErrNotFound }