github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/routing/dht/lookup.go (about)

     1  package dht
     2  
     3  import (
     4  	context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
     5  	key "github.com/ipfs/go-ipfs/blocks/key"
     6  	notif "github.com/ipfs/go-ipfs/notifications"
     7  	peer "github.com/ipfs/go-ipfs/p2p/peer"
     8  	kb "github.com/ipfs/go-ipfs/routing/kbucket"
     9  	pset "github.com/ipfs/go-ipfs/util/peerset"
    10  )
    11  
    12  // Required in order for proper JSON marshaling
    13  func pointerizePeerInfos(pis []peer.PeerInfo) []*peer.PeerInfo {
    14  	out := make([]*peer.PeerInfo, len(pis))
    15  	for i, p := range pis {
    16  		np := p
    17  		out[i] = &np
    18  	}
    19  	return out
    20  }
    21  
    22  // Kademlia 'node lookup' operation. Returns a channel of the K closest peers
    23  // to the given key
    24  func (dht *IpfsDHT) GetClosestPeers(ctx context.Context, key key.Key) (<-chan peer.ID, error) {
    25  	e := log.EventBegin(ctx, "getClosestPeers", &key)
    26  	tablepeers := dht.routingTable.NearestPeers(kb.ConvertKey(key), AlphaValue)
    27  	if len(tablepeers) == 0 {
    28  		return nil, kb.ErrLookupFailure
    29  	}
    30  
    31  	out := make(chan peer.ID, KValue)
    32  	peerset := pset.NewLimited(KValue)
    33  
    34  	for _, p := range tablepeers {
    35  		select {
    36  		case out <- p:
    37  		case <-ctx.Done():
    38  			return nil, ctx.Err()
    39  		}
    40  		peerset.Add(p)
    41  	}
    42  
    43  	// since the query doesnt actually pass our context down
    44  	// we have to hack this here. whyrusleeping isnt a huge fan of goprocess
    45  	parent := ctx
    46  	query := dht.newQuery(key, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) {
    47  		// For DHT query command
    48  		notif.PublishQueryEvent(parent, &notif.QueryEvent{
    49  			Type: notif.SendingQuery,
    50  			ID:   p,
    51  		})
    52  
    53  		closer, err := dht.closerPeersSingle(ctx, key, p)
    54  		if err != nil {
    55  			log.Debugf("error getting closer peers: %s", err)
    56  			return nil, err
    57  		}
    58  
    59  		var filtered []peer.PeerInfo
    60  		for _, clp := range closer {
    61  			if kb.Closer(clp, dht.self, key) && peerset.TryAdd(clp) {
    62  				select {
    63  				case out <- clp:
    64  				case <-ctx.Done():
    65  					return nil, ctx.Err()
    66  				}
    67  				filtered = append(filtered, dht.peerstore.PeerInfo(clp))
    68  			}
    69  		}
    70  
    71  		// For DHT query command
    72  		notif.PublishQueryEvent(parent, &notif.QueryEvent{
    73  			Type:      notif.PeerResponse,
    74  			ID:        p,
    75  			Responses: pointerizePeerInfos(filtered),
    76  		})
    77  
    78  		return &dhtQueryResult{closerPeers: filtered}, nil
    79  	})
    80  
    81  	go func() {
    82  		defer close(out)
    83  		defer e.Done()
    84  		// run it!
    85  		_, err := query.Run(ctx, tablepeers)
    86  		if err != nil {
    87  			log.Debugf("closestPeers query run error: %s", err)
    88  		}
    89  	}()
    90  
    91  	return out, nil
    92  }
    93  
    94  func (dht *IpfsDHT) closerPeersSingle(ctx context.Context, key key.Key, p peer.ID) ([]peer.ID, error) {
    95  	pmes, err := dht.findPeerSingle(ctx, p, peer.ID(key))
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  
   100  	var out []peer.ID
   101  	for _, pbp := range pmes.GetCloserPeers() {
   102  		pid := peer.ID(pbp.GetId())
   103  		if pid != dht.self { // dont add self
   104  			dht.peerstore.AddAddrs(pid, pbp.Addresses(), peer.TempAddrTTL)
   105  			out = append(out, pid)
   106  		}
   107  	}
   108  	return out, nil
   109  }