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 }