github.com/zignig/go-ipfs@v0.0.0-20141111235910-c9e5fdf55a52/routing/dht/routing.go (about)

     1  package dht
     2  
     3  import (
     4  	"sync"
     5  
     6  	context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
     7  
     8  	peer "github.com/jbenet/go-ipfs/peer"
     9  	"github.com/jbenet/go-ipfs/routing"
    10  	pb "github.com/jbenet/go-ipfs/routing/dht/pb"
    11  	kb "github.com/jbenet/go-ipfs/routing/kbucket"
    12  	u "github.com/jbenet/go-ipfs/util"
    13  )
    14  
    15  // This file implements the Routing interface for the IpfsDHT struct.
    16  
    17  // Basic Put/Get
    18  
    19  // PutValue adds value corresponding to given Key.
    20  // This is the top level "Store" operation of the DHT
    21  func (dht *IpfsDHT) PutValue(ctx context.Context, key u.Key, value []byte) error {
    22  	log.Debugf("PutValue %s", key)
    23  	err := dht.putLocal(key, value)
    24  	if err != nil {
    25  		return err
    26  	}
    27  
    28  	var peers []peer.Peer
    29  	for _, route := range dht.routingTables {
    30  		npeers := route.NearestPeers(kb.ConvertKey(key), KValue)
    31  		peers = append(peers, npeers...)
    32  	}
    33  
    34  	query := newQuery(key, dht.dialer, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) {
    35  		log.Debugf("%s PutValue qry part %v", dht.self, p)
    36  		err := dht.putValueToNetwork(ctx, p, string(key), value)
    37  		if err != nil {
    38  			return nil, err
    39  		}
    40  		return &dhtQueryResult{success: true}, nil
    41  	})
    42  
    43  	_, err = query.Run(ctx, peers)
    44  	return err
    45  }
    46  
    47  // GetValue searches for the value corresponding to given Key.
    48  // If the search does not succeed, a multiaddr string of a closer peer is
    49  // returned along with util.ErrSearchIncomplete
    50  func (dht *IpfsDHT) GetValue(ctx context.Context, key u.Key) ([]byte, error) {
    51  	log.Debugf("Get Value [%s]", key)
    52  
    53  	// If we have it local, dont bother doing an RPC!
    54  	// NOTE: this might not be what we want to do...
    55  	val, err := dht.getLocal(key)
    56  	if err == nil {
    57  		log.Debug("Got value locally!")
    58  		return val, nil
    59  	}
    60  
    61  	// get closest peers in the routing tables
    62  	routeLevel := 0
    63  	closest := dht.routingTables[routeLevel].NearestPeers(kb.ConvertKey(key), PoolSize)
    64  	if closest == nil || len(closest) == 0 {
    65  		log.Warning("Got no peers back from routing table!")
    66  		return nil, kb.ErrLookupFailure
    67  	}
    68  
    69  	// setup the Query
    70  	query := newQuery(key, dht.dialer, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) {
    71  
    72  		val, peers, err := dht.getValueOrPeers(ctx, p, key, routeLevel)
    73  		if err != nil {
    74  			return nil, err
    75  		}
    76  
    77  		res := &dhtQueryResult{value: val, closerPeers: peers}
    78  		if val != nil {
    79  			res.success = true
    80  		}
    81  
    82  		return res, nil
    83  	})
    84  
    85  	// run it!
    86  	result, err := query.Run(ctx, closest)
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  
    91  	log.Debugf("GetValue %v %v", key, result.value)
    92  	if result.value == nil {
    93  		return nil, routing.ErrNotFound
    94  	}
    95  
    96  	return result.value, nil
    97  }
    98  
    99  // Value provider layer of indirection.
   100  // This is what DSHTs (Coral and MainlineDHT) do to store large values in a DHT.
   101  
   102  // Provide makes this node announce that it can provide a value for the given key
   103  func (dht *IpfsDHT) Provide(ctx context.Context, key u.Key) error {
   104  
   105  	dht.providers.AddProvider(key, dht.self)
   106  	peers := dht.routingTables[0].NearestPeers(kb.ConvertKey(key), PoolSize)
   107  	if len(peers) == 0 {
   108  		return nil
   109  	}
   110  
   111  	//TODO FIX: this doesn't work! it needs to be sent to the actual nearest peers.
   112  	// `peers` are the closest peers we have, not the ones that should get the value.
   113  	for _, p := range peers {
   114  		err := dht.putProvider(ctx, p, string(key))
   115  		if err != nil {
   116  			return err
   117  		}
   118  	}
   119  	return nil
   120  }
   121  
   122  func (dht *IpfsDHT) FindProvidersAsync(ctx context.Context, key u.Key, count int) <-chan peer.Peer {
   123  	peerOut := make(chan peer.Peer, count)
   124  	go func() {
   125  		ps := newPeerSet()
   126  		provs := dht.providers.GetProviders(key)
   127  		for _, p := range provs {
   128  			count--
   129  			// NOTE: assuming that this list of peers is unique
   130  			ps.Add(p)
   131  			peerOut <- p
   132  			if count <= 0 {
   133  				return
   134  			}
   135  		}
   136  
   137  		wg := new(sync.WaitGroup)
   138  		peers := dht.routingTables[0].NearestPeers(kb.ConvertKey(key), AlphaValue)
   139  		for _, pp := range peers {
   140  			wg.Add(1)
   141  			go func(p peer.Peer) {
   142  				defer wg.Done()
   143  				pmes, err := dht.findProvidersSingle(ctx, p, key, 0)
   144  				if err != nil {
   145  					log.Error(err)
   146  					return
   147  				}
   148  				dht.addPeerListAsync(ctx, key, pmes.GetProviderPeers(), ps, count, peerOut)
   149  			}(pp)
   150  		}
   151  		wg.Wait()
   152  		close(peerOut)
   153  	}()
   154  	return peerOut
   155  }
   156  
   157  func (dht *IpfsDHT) addPeerListAsync(ctx context.Context, k u.Key, peers []*pb.Message_Peer, ps *peerSet, count int, out chan peer.Peer) {
   158  	done := make(chan struct{})
   159  	for _, pbp := range peers {
   160  		go func(mp *pb.Message_Peer) {
   161  			defer func() { done <- struct{}{} }()
   162  			// construct new peer
   163  			p, err := dht.ensureConnectedToPeer(ctx, mp)
   164  			if err != nil {
   165  				log.Error("%s", err)
   166  				return
   167  			}
   168  			if p == nil {
   169  				log.Error("Got nil peer from ensureConnectedToPeer")
   170  				return
   171  			}
   172  
   173  			dht.providers.AddProvider(k, p)
   174  			if ps.AddIfSmallerThan(p, count) {
   175  				out <- p
   176  			} else if ps.Size() >= count {
   177  				return
   178  			}
   179  		}(pbp)
   180  	}
   181  	for _ = range peers {
   182  		<-done
   183  	}
   184  }
   185  
   186  // Find specific Peer
   187  // FindPeer searches for a peer with given ID.
   188  func (dht *IpfsDHT) FindPeer(ctx context.Context, id peer.ID) (peer.Peer, error) {
   189  
   190  	// Check if were already connected to them
   191  	p, _ := dht.FindLocal(id)
   192  	if p != nil {
   193  		return p, nil
   194  	}
   195  
   196  	routeLevel := 0
   197  	closest := dht.routingTables[routeLevel].NearestPeers(kb.ConvertPeerID(id), AlphaValue)
   198  	if closest == nil || len(closest) == 0 {
   199  		return nil, kb.ErrLookupFailure
   200  	}
   201  
   202  	// Sanity...
   203  	for _, p := range closest {
   204  		if p.ID().Equal(id) {
   205  			log.Error("Found target peer in list of closest peers...")
   206  			return p, nil
   207  		}
   208  	}
   209  
   210  	// setup the Query
   211  	query := newQuery(u.Key(id), dht.dialer, func(ctx context.Context, p peer.Peer) (*dhtQueryResult, error) {
   212  
   213  		pmes, err := dht.findPeerSingle(ctx, p, id, routeLevel)
   214  		if err != nil {
   215  			return nil, err
   216  		}
   217  
   218  		closer := pmes.GetCloserPeers()
   219  		var clpeers []peer.Peer
   220  		for _, pbp := range closer {
   221  			np, err := dht.getPeer(peer.ID(pbp.GetId()))
   222  			if err != nil {
   223  				log.Warningf("Received invalid peer from query: %v", err)
   224  				continue
   225  			}
   226  			ma, err := pbp.Address()
   227  			if err != nil {
   228  				log.Warning("Received peer with bad or missing address.")
   229  				continue
   230  			}
   231  			np.AddAddress(ma)
   232  			if pbp.GetId() == string(id) {
   233  				return &dhtQueryResult{
   234  					peer:    np,
   235  					success: true,
   236  				}, nil
   237  			}
   238  			clpeers = append(clpeers, np)
   239  		}
   240  
   241  		return &dhtQueryResult{closerPeers: clpeers}, nil
   242  	})
   243  
   244  	// run it!
   245  	result, err := query.Run(ctx, closest)
   246  	if err != nil {
   247  		return nil, err
   248  	}
   249  
   250  	log.Debugf("FindPeer %v %v", id, result.success)
   251  	if result.peer == nil {
   252  		return nil, routing.ErrNotFound
   253  	}
   254  
   255  	return result.peer, nil
   256  }
   257  
   258  // Ping a peer, log the time it took
   259  func (dht *IpfsDHT) Ping(ctx context.Context, p peer.Peer) error {
   260  	// Thoughts: maybe this should accept an ID and do a peer lookup?
   261  	log.Infof("ping %s start", p)
   262  
   263  	pmes := pb.NewMessage(pb.Message_PING, "", 0)
   264  	_, err := dht.sendRequest(ctx, p, pmes)
   265  	log.Infof("ping %s end (err = %s)", p, err)
   266  	return err
   267  }