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

     1  package dht
     2  
     3  import (
     4  	"sync"
     5  
     6  	context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
     7  	key "github.com/ipfs/go-ipfs/blocks/key"
     8  	notif "github.com/ipfs/go-ipfs/notifications"
     9  	inet "github.com/ipfs/go-ipfs/p2p/net"
    10  	peer "github.com/ipfs/go-ipfs/p2p/peer"
    11  	"github.com/ipfs/go-ipfs/routing"
    12  	pb "github.com/ipfs/go-ipfs/routing/dht/pb"
    13  	kb "github.com/ipfs/go-ipfs/routing/kbucket"
    14  	record "github.com/ipfs/go-ipfs/routing/record"
    15  	pset "github.com/ipfs/go-ipfs/util/peerset"
    16  )
    17  
    18  // asyncQueryBuffer is the size of buffered channels in async queries. This
    19  // buffer allows multiple queries to execute simultaneously, return their
    20  // results and continue querying closer peers. Note that different query
    21  // results will wait for the channel to drain.
    22  var asyncQueryBuffer = 10
    23  
    24  // This file implements the Routing interface for the IpfsDHT struct.
    25  
    26  // Basic Put/Get
    27  
    28  // PutValue adds value corresponding to given Key.
    29  // This is the top level "Store" operation of the DHT
    30  func (dht *IpfsDHT) PutValue(ctx context.Context, key key.Key, value []byte) error {
    31  	log.Debugf("PutValue %s", key)
    32  	sk, err := dht.getOwnPrivateKey()
    33  	if err != nil {
    34  		return err
    35  	}
    36  
    37  	sign, err := dht.Validator.IsSigned(key)
    38  	if err != nil {
    39  		return err
    40  	}
    41  
    42  	rec, err := record.MakePutRecord(sk, key, value, sign)
    43  	if err != nil {
    44  		log.Debug("Creation of record failed!")
    45  		return err
    46  	}
    47  
    48  	err = dht.putLocal(key, rec)
    49  	if err != nil {
    50  		return err
    51  	}
    52  
    53  	pchan, err := dht.GetClosestPeers(ctx, key)
    54  	if err != nil {
    55  		return err
    56  	}
    57  
    58  	wg := sync.WaitGroup{}
    59  	for p := range pchan {
    60  		wg.Add(1)
    61  		go func(p peer.ID) {
    62  			defer wg.Done()
    63  			notif.PublishQueryEvent(ctx, &notif.QueryEvent{
    64  				Type: notif.Value,
    65  				ID:   p,
    66  			})
    67  
    68  			err := dht.putValueToPeer(ctx, p, key, rec)
    69  			if err != nil {
    70  				log.Debugf("failed putting value to peer: %s", err)
    71  			}
    72  		}(p)
    73  	}
    74  	wg.Wait()
    75  	return nil
    76  }
    77  
    78  // GetValue searches for the value corresponding to given Key.
    79  // If the search does not succeed, a multiaddr string of a closer peer is
    80  // returned along with util.ErrSearchIncomplete
    81  func (dht *IpfsDHT) GetValue(ctx context.Context, key key.Key) ([]byte, error) {
    82  	// If we have it local, dont bother doing an RPC!
    83  	val, err := dht.getLocal(key)
    84  	if err == nil {
    85  		log.Debug("have it locally")
    86  		return val, nil
    87  	} else {
    88  		log.Debug("failed to get value locally: %s", err)
    89  	}
    90  
    91  	// get closest peers in the routing table
    92  	rtp := dht.routingTable.NearestPeers(kb.ConvertKey(key), AlphaValue)
    93  	log.Debugf("peers in rt: %s", len(rtp), rtp)
    94  	if len(rtp) == 0 {
    95  		log.Warning("No peers from routing table!")
    96  		return nil, kb.ErrLookupFailure
    97  	}
    98  
    99  	// setup the Query
   100  	parent := ctx
   101  	query := dht.newQuery(key, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) {
   102  		notif.PublishQueryEvent(parent, &notif.QueryEvent{
   103  			Type: notif.SendingQuery,
   104  			ID:   p,
   105  		})
   106  
   107  		val, peers, err := dht.getValueOrPeers(ctx, p, key)
   108  		if err != nil {
   109  			return nil, err
   110  		}
   111  
   112  		res := &dhtQueryResult{value: val, closerPeers: peers}
   113  		if val != nil {
   114  			res.success = true
   115  		}
   116  
   117  		notif.PublishQueryEvent(parent, &notif.QueryEvent{
   118  			Type:      notif.PeerResponse,
   119  			ID:        p,
   120  			Responses: pointerizePeerInfos(peers),
   121  		})
   122  
   123  		return res, nil
   124  	})
   125  
   126  	// run it!
   127  	result, err := query.Run(ctx, rtp)
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  
   132  	log.Debugf("GetValue %v %v", key, result.value)
   133  	if result.value == nil {
   134  		return nil, routing.ErrNotFound
   135  	}
   136  
   137  	return result.value, nil
   138  }
   139  
   140  // Value provider layer of indirection.
   141  // This is what DSHTs (Coral and MainlineDHT) do to store large values in a DHT.
   142  
   143  // Provide makes this node announce that it can provide a value for the given key
   144  func (dht *IpfsDHT) Provide(ctx context.Context, key key.Key) error {
   145  	defer log.EventBegin(ctx, "provide", &key).Done()
   146  
   147  	// add self locally
   148  	dht.providers.AddProvider(ctx, key, dht.self)
   149  
   150  	peers, err := dht.GetClosestPeers(ctx, key)
   151  	if err != nil {
   152  		return err
   153  	}
   154  
   155  	wg := sync.WaitGroup{}
   156  	for p := range peers {
   157  		wg.Add(1)
   158  		go func(p peer.ID) {
   159  			defer wg.Done()
   160  			log.Debugf("putProvider(%s, %s)", key, p)
   161  			err := dht.putProvider(ctx, p, string(key))
   162  			if err != nil {
   163  				log.Debug(err)
   164  			}
   165  		}(p)
   166  	}
   167  	wg.Wait()
   168  	return nil
   169  }
   170  
   171  // FindProviders searches until the context expires.
   172  func (dht *IpfsDHT) FindProviders(ctx context.Context, key key.Key) ([]peer.PeerInfo, error) {
   173  	var providers []peer.PeerInfo
   174  	for p := range dht.FindProvidersAsync(ctx, key, KValue) {
   175  		providers = append(providers, p)
   176  	}
   177  	return providers, nil
   178  }
   179  
   180  // FindProvidersAsync is the same thing as FindProviders, but returns a channel.
   181  // Peers will be returned on the channel as soon as they are found, even before
   182  // the search query completes.
   183  func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key key.Key, count int) <-chan peer.PeerInfo {
   184  	log.Event(ctx, "findProviders", &key)
   185  	peerOut := make(chan peer.PeerInfo, count)
   186  	go dht.findProvidersAsyncRoutine(ctx, key, count, peerOut)
   187  	return peerOut
   188  }
   189  
   190  func (dht *IpfsDHT) findProvidersAsyncRoutine(ctx context.Context, key key.Key, count int, peerOut chan peer.PeerInfo) {
   191  	defer log.EventBegin(ctx, "findProvidersAsync", &key).Done()
   192  	defer close(peerOut)
   193  
   194  	ps := pset.NewLimited(count)
   195  	provs := dht.providers.GetProviders(ctx, key)
   196  	for _, p := range provs {
   197  		// NOTE: assuming that this list of peers is unique
   198  		if ps.TryAdd(p) {
   199  			select {
   200  			case peerOut <- dht.peerstore.PeerInfo(p):
   201  			case <-ctx.Done():
   202  				return
   203  			}
   204  		}
   205  
   206  		// If we have enough peers locally, dont bother with remote RPC
   207  		if ps.Size() >= count {
   208  			return
   209  		}
   210  	}
   211  
   212  	// setup the Query
   213  	parent := ctx
   214  	query := dht.newQuery(key, func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) {
   215  		notif.PublishQueryEvent(parent, &notif.QueryEvent{
   216  			Type: notif.SendingQuery,
   217  			ID:   p,
   218  		})
   219  		pmes, err := dht.findProvidersSingle(ctx, p, key)
   220  		if err != nil {
   221  			return nil, err
   222  		}
   223  
   224  		log.Debugf("%d provider entries", len(pmes.GetProviderPeers()))
   225  		provs := pb.PBPeersToPeerInfos(pmes.GetProviderPeers())
   226  		log.Debugf("%d provider entries decoded", len(provs))
   227  
   228  		// Add unique providers from request, up to 'count'
   229  		for _, prov := range provs {
   230  			log.Debugf("got provider: %s", prov)
   231  			if ps.TryAdd(prov.ID) {
   232  				log.Debugf("using provider: %s", prov)
   233  				select {
   234  				case peerOut <- prov:
   235  				case <-ctx.Done():
   236  					log.Debug("Context timed out sending more providers")
   237  					return nil, ctx.Err()
   238  				}
   239  			}
   240  			if ps.Size() >= count {
   241  				log.Debugf("got enough providers (%d/%d)", ps.Size(), count)
   242  				return &dhtQueryResult{success: true}, nil
   243  			}
   244  		}
   245  
   246  		// Give closer peers back to the query to be queried
   247  		closer := pmes.GetCloserPeers()
   248  		clpeers := pb.PBPeersToPeerInfos(closer)
   249  		log.Debugf("got closer peers: %d %s", len(clpeers), clpeers)
   250  
   251  		notif.PublishQueryEvent(parent, &notif.QueryEvent{
   252  			Type:      notif.PeerResponse,
   253  			ID:        p,
   254  			Responses: pointerizePeerInfos(clpeers),
   255  		})
   256  		return &dhtQueryResult{closerPeers: clpeers}, nil
   257  	})
   258  
   259  	peers := dht.routingTable.NearestPeers(kb.ConvertKey(key), AlphaValue)
   260  	_, err := query.Run(ctx, peers)
   261  	if err != nil {
   262  		log.Debugf("Query error: %s", err)
   263  		notif.PublishQueryEvent(ctx, &notif.QueryEvent{
   264  			Type:  notif.QueryError,
   265  			Extra: err.Error(),
   266  		})
   267  	}
   268  }
   269  
   270  // FindPeer searches for a peer with given ID.
   271  func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.PeerInfo, error) {
   272  	defer log.EventBegin(ctx, "FindPeer", id).Done()
   273  
   274  	// Check if were already connected to them
   275  	if pi := dht.FindLocal(id); pi.ID != "" {
   276  		return pi, nil
   277  	}
   278  
   279  	peers := dht.routingTable.NearestPeers(kb.ConvertPeerID(id), AlphaValue)
   280  	if len(peers) == 0 {
   281  		return peer.PeerInfo{}, kb.ErrLookupFailure
   282  	}
   283  
   284  	// Sanity...
   285  	for _, p := range peers {
   286  		if p == id {
   287  			log.Debug("Found target peer in list of closest peers...")
   288  			return dht.peerstore.PeerInfo(p), nil
   289  		}
   290  	}
   291  
   292  	// setup the Query
   293  	parent := ctx
   294  	query := dht.newQuery(key.Key(id), func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) {
   295  		notif.PublishQueryEvent(parent, &notif.QueryEvent{
   296  			Type: notif.SendingQuery,
   297  			ID:   p,
   298  		})
   299  
   300  		pmes, err := dht.findPeerSingle(ctx, p, id)
   301  		if err != nil {
   302  			return nil, err
   303  		}
   304  
   305  		closer := pmes.GetCloserPeers()
   306  		clpeerInfos := pb.PBPeersToPeerInfos(closer)
   307  
   308  		// see it we got the peer here
   309  		for _, npi := range clpeerInfos {
   310  			if npi.ID == id {
   311  				return &dhtQueryResult{
   312  					peer:    npi,
   313  					success: true,
   314  				}, nil
   315  			}
   316  		}
   317  
   318  		notif.PublishQueryEvent(parent, &notif.QueryEvent{
   319  			Type:      notif.PeerResponse,
   320  			Responses: pointerizePeerInfos(clpeerInfos),
   321  		})
   322  
   323  		return &dhtQueryResult{closerPeers: clpeerInfos}, nil
   324  	})
   325  
   326  	// run it!
   327  	result, err := query.Run(ctx, peers)
   328  	if err != nil {
   329  		return peer.PeerInfo{}, err
   330  	}
   331  
   332  	log.Debugf("FindPeer %v %v", id, result.success)
   333  	if result.peer.ID == "" {
   334  		return peer.PeerInfo{}, routing.ErrNotFound
   335  	}
   336  
   337  	return result.peer, nil
   338  }
   339  
   340  // FindPeersConnectedToPeer searches for peers directly connected to a given peer.
   341  func (dht *IpfsDHT) FindPeersConnectedToPeer(ctx context.Context, id peer.ID) (<-chan peer.PeerInfo, error) {
   342  
   343  	peerchan := make(chan peer.PeerInfo, asyncQueryBuffer)
   344  	peersSeen := peer.Set{}
   345  
   346  	peers := dht.routingTable.NearestPeers(kb.ConvertPeerID(id), AlphaValue)
   347  	if len(peers) == 0 {
   348  		return nil, kb.ErrLookupFailure
   349  	}
   350  
   351  	// setup the Query
   352  	query := dht.newQuery(key.Key(id), func(ctx context.Context, p peer.ID) (*dhtQueryResult, error) {
   353  
   354  		pmes, err := dht.findPeerSingle(ctx, p, id)
   355  		if err != nil {
   356  			return nil, err
   357  		}
   358  
   359  		var clpeers []peer.PeerInfo
   360  		closer := pmes.GetCloserPeers()
   361  		for _, pbp := range closer {
   362  			pi := pb.PBPeerToPeerInfo(pbp)
   363  
   364  			// skip peers already seen
   365  			if _, found := peersSeen[pi.ID]; found {
   366  				continue
   367  			}
   368  			peersSeen[pi.ID] = struct{}{}
   369  
   370  			// if peer is connected, send it to our client.
   371  			if pb.Connectedness(*pbp.Connection) == inet.Connected {
   372  				select {
   373  				case <-ctx.Done():
   374  					return nil, ctx.Err()
   375  				case peerchan <- pi:
   376  				}
   377  			}
   378  
   379  			// if peer is the peer we're looking for, don't bother querying it.
   380  			// TODO maybe query it?
   381  			if pb.Connectedness(*pbp.Connection) != inet.Connected {
   382  				clpeers = append(clpeers, pi)
   383  			}
   384  		}
   385  
   386  		return &dhtQueryResult{closerPeers: clpeers}, nil
   387  	})
   388  
   389  	// run it! run it asynchronously to gen peers as results are found.
   390  	// this does no error checking
   391  	go func() {
   392  		if _, err := query.Run(ctx, peers); err != nil {
   393  			log.Debug(err)
   394  		}
   395  
   396  		// close the peerchan channel when done.
   397  		close(peerchan)
   398  	}()
   399  
   400  	return peerchan, nil
   401  }