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, ¬if.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, ¬if.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, ¬if.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, ¬if.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, ¬if.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, ¬if.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, ¬if.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, ¬if.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 }