dht.go 17.4 KB
Newer Older
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
1 2
package dht

3
import (
4
	"bytes"
5
	"crypto/rand"
6
	"fmt"
7 8
	"sync"
	"time"
9

10
	inet "github.com/jbenet/go-ipfs/net"
11
	peer "github.com/jbenet/go-ipfs/peer"
Jeromy's avatar
Jeromy committed
12
	kb "github.com/jbenet/go-ipfs/routing/kbucket"
13
	u "github.com/jbenet/go-ipfs/util"
14

15
	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
Jeromy's avatar
Jeromy committed
16

17
	ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go"
Jeromy's avatar
Jeromy committed
18

19
	"github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto"
20 21
)

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
22 23 24 25 26
// TODO. SEE https://github.com/jbenet/node-ipfs/blob/master/submodules/ipfs-dht/index.js

// IpfsDHT is an implementation of Kademlia with Coral and S/Kademlia modifications.
// It is used to implement the base IpfsRouting module.
type IpfsDHT struct {
27 28
	// Array of routing tables for differently distanced nodes
	// NOTE: (currently, only a single table is used)
29
	routingTables []*kb.RoutingTable
30

31
	network inet.Network
32

Jeromy's avatar
Jeromy committed
33 34 35 36 37
	// Local peer (yourself)
	self *peer.Peer

	// Local data
	datastore ds.Datastore
38
	dslock    sync.Mutex
39

40
	providers *ProviderManager
41

42 43
	// Signal to shutdown dht
	shutdown chan struct{}
44 45 46

	// When this peer started up
	birth time.Time
47 48 49

	//lock to make diagnostics work better
	diaglock sync.Mutex
50 51
}

Jeromy's avatar
Jeromy committed
52
// NewDHT creates a new DHT object with the given peer as the 'local' host
Jeromy's avatar
Jeromy committed
53
func NewDHT(p *peer.Peer, net swarm.Network, dstore ds.Datastore) *IpfsDHT {
54
	dht := new(IpfsDHT)
Jeromy's avatar
Jeromy committed
55
	dht.network = net
56
	dht.netChan = net.GetChannel(swarm.PBWrapper_DHT_MESSAGE)
Jeromy's avatar
Jeromy committed
57
	dht.datastore = dstore
58
	dht.self = p
59
	dht.providers = NewProviderManager(p.ID)
Jeromy's avatar
Jeromy committed
60
	dht.shutdown = make(chan struct{})
61

62 63 64 65
	dht.routingTables = make([]*kb.RoutingTable, 3)
	dht.routingTables[0] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Millisecond*30)
	dht.routingTables[1] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Millisecond*100)
	dht.routingTables[2] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Hour)
66
	dht.listener = swarm.NewMessageListener()
67
	dht.birth = time.Now()
Jeromy's avatar
Jeromy committed
68
	return dht
69 70
}

71
// Start up background goroutines needed by the DHT
72 73 74 75
func (dht *IpfsDHT) Start() {
	go dht.handleMessages()
}

76
// Connect to a new peer at the given address, ping and add to the routing table
77
func (dht *IpfsDHT) Connect(addr *ma.Multiaddr) (*peer.Peer, error) {
78
	maddrstr, _ := addr.String()
79
	u.DOut("Connect to new peer: %s\n", maddrstr)
80
	npeer, err := dht.network.ConnectNew(addr)
81
	if err != nil {
82
		return nil, err
83 84
	}

Jeromy's avatar
Jeromy committed
85 86
	// Ping new peer to register in their routing table
	// NOTE: this should be done better...
87
	err = dht.Ping(npeer, time.Second*2)
Jeromy's avatar
Jeromy committed
88
	if err != nil {
89
		return nil, fmt.Errorf("failed to ping newly connected peer: %s\n", err)
Jeromy's avatar
Jeromy committed
90 91
	}

92 93
	dht.Update(npeer)

94
	return npeer, nil
Jeromy's avatar
Jeromy committed
95 96
}

97 98
// Read in all messages from swarm and handle them appropriately
// NOTE: this function is just a quick sketch
99
func (dht *IpfsDHT) handleMessages() {
Jeromy's avatar
Jeromy committed
100
	u.DOut("Begin message handling routine\n")
101

102
	errs := dht.network.GetErrChan()
103 104
	for {
		select {
105
		case mes, ok := <-dht.netChan.Incoming:
106
			if !ok {
Jeromy's avatar
Jeromy committed
107
				u.DOut("handleMessages closing, bad recv on incoming\n")
108 109
				return
			}
110
			pmes := new(PBDHTMessage)
111 112
			err := proto.Unmarshal(mes.Data, pmes)
			if err != nil {
113
				u.PErr("Failed to decode protobuf message: %s\n", err)
114 115 116
				continue
			}

117
			dht.Update(mes.Peer)
118

119
			// Note: not sure if this is the correct place for this
120
			if pmes.GetResponse() {
121
				dht.listener.Respond(pmes.GetId(), mes)
122
				continue
123
			}
124 125
			//

126
			u.DOut("[peer: %s]\nGot message type: '%s' [id = %x, from = %s]\n",
Jeromy's avatar
Jeromy committed
127
				dht.self.ID.Pretty(),
128
				PBDHTMessage_MessageType_name[int32(pmes.GetType())],
129
				pmes.GetId(), mes.Peer.ID.Pretty())
130
			switch pmes.GetType() {
131
			case PBDHTMessage_GET_VALUE:
132
				go dht.handleGetValue(mes.Peer, pmes)
133
			case PBDHTMessage_PUT_VALUE:
134
				go dht.handlePutValue(mes.Peer, pmes)
135
			case PBDHTMessage_FIND_NODE:
136
				go dht.handleFindPeer(mes.Peer, pmes)
137
			case PBDHTMessage_ADD_PROVIDER:
138
				go dht.handleAddProvider(mes.Peer, pmes)
139
			case PBDHTMessage_GET_PROVIDERS:
140
				go dht.handleGetProviders(mes.Peer, pmes)
141
			case PBDHTMessage_PING:
142
				go dht.handlePing(mes.Peer, pmes)
143
			case PBDHTMessage_DIAGNOSTIC:
144
				go dht.handleDiagnostic(mes.Peer, pmes)
145 146
			default:
				u.PErr("Recieved invalid message type")
147 148
			}

149
		case err := <-errs:
150
			u.PErr("dht err: %s\n", err)
151 152
		case <-dht.shutdown:
			return
153 154 155 156
		}
	}
}

Jeromy's avatar
Jeromy committed
157
func (dht *IpfsDHT) putValueToNetwork(p *peer.Peer, key string, value []byte) error {
158
	pmes := Message{
159 160
		Type:  PBDHTMessage_PUT_VALUE,
		Key:   key,
161
		Value: value,
162
		ID:    swarm.GenerateMessageID(),
163 164 165
	}

	mes := swarm.NewMessage(p, pmes.ToProtobuf())
166
	dht.netChan.Outgoing <- mes
167 168 169
	return nil
}

170
func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *PBDHTMessage) {
171
	u.DOut("handleGetValue for key: %s\n", pmes.GetKey())
Jeromy's avatar
Jeromy committed
172
	dskey := ds.NewKey(pmes.GetKey())
173
	resp := &Message{
Jeromy's avatar
Jeromy committed
174
		Response: true,
175
		ID:       pmes.GetId(),
Jeromy's avatar
Jeromy committed
176 177 178
		Key:      pmes.GetKey(),
	}
	iVal, err := dht.datastore.Get(dskey)
Jeromy's avatar
Jeromy committed
179
	if err == nil {
Jeromy's avatar
Jeromy committed
180
		u.DOut("handleGetValue success!\n")
Jeromy's avatar
Jeromy committed
181 182
		resp.Success = true
		resp.Value = iVal.([]byte)
Jeromy's avatar
Jeromy committed
183
	} else if err == ds.ErrNotFound {
Jeromy's avatar
Jeromy committed
184
		// Check if we know any providers for the requested value
185 186
		provs := dht.providers.GetProviders(u.Key(pmes.GetKey()))
		if len(provs) > 0 {
187
			u.DOut("handleGetValue returning %d provider[s]\n", len(provs))
188
			resp.Peers = provs
Jeromy's avatar
Jeromy committed
189 190 191
			resp.Success = true
		} else {
			// No providers?
192 193
			// Find closest peer on given cluster to desired key and reply with that info

194 195 196
			level := 0
			if len(pmes.GetValue()) < 1 {
				// TODO: maybe return an error? Defaulting isnt a good idea IMO
Jeromy's avatar
Jeromy committed
197
				u.PErr("handleGetValue: no routing level specified, assuming 0\n")
198 199 200
			} else {
				level = int(pmes.GetValue()[0]) // Using value field to specify cluster level
			}
201
			u.DOut("handleGetValue searching level %d clusters\n", level)
202

203
			closer := dht.routingTables[level].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey())))
204

Jeromy's avatar
Jeromy committed
205
			if closer.ID.Equal(dht.self.ID) {
Jeromy's avatar
Jeromy committed
206
				u.DOut("Attempted to return self! this shouldnt happen...\n")
Jeromy's avatar
Jeromy committed
207 208 209
				resp.Peers = nil
				goto out
			}
210 211 212
			// If this peer is closer than the one from the table, return nil
			if kb.Closer(dht.self.ID, closer.ID, u.Key(pmes.GetKey())) {
				resp.Peers = nil
Jeromy's avatar
Jeromy committed
213
				u.DOut("handleGetValue could not find a closer node than myself.\n")
214
			} else {
215
				u.DOut("handleGetValue returning a closer peer: '%s'\n", closer.ID.Pretty())
216 217
				resp.Peers = []*peer.Peer{closer}
			}
218
		}
Jeromy's avatar
Jeromy committed
219
	} else {
220
		//temp: what other errors can a datastore return?
Jeromy's avatar
Jeromy committed
221
		panic(err)
222
	}
223

Jeromy's avatar
Jeromy committed
224
out:
225
	mes := swarm.NewMessage(p, resp.ToProtobuf())
226
	dht.netChan.Outgoing <- mes
227 228
}

Jeromy's avatar
Jeromy committed
229
// Store a value in this peer local storage
230
func (dht *IpfsDHT) handlePutValue(p *peer.Peer, pmes *PBDHTMessage) {
231 232
	dht.dslock.Lock()
	defer dht.dslock.Unlock()
Jeromy's avatar
Jeromy committed
233 234 235 236 237 238
	dskey := ds.NewKey(pmes.GetKey())
	err := dht.datastore.Put(dskey, pmes.GetValue())
	if err != nil {
		// For now, just panic, handle this better later maybe
		panic(err)
	}
239 240
}

241
func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *PBDHTMessage) {
242
	u.DOut("[%s] Responding to ping from [%s]!\n", dht.self.ID.Pretty(), p.ID.Pretty())
243
	resp := Message{
244
		Type:     pmes.GetType(),
245
		Response: true,
246
		ID:       pmes.GetId(),
247
	}
248

249
	dht.netChan.Outgoing <- swarm.NewMessage(p, resp.ToProtobuf())
250 251
}

252
func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *PBDHTMessage) {
253
	resp := Message{
254
		Type:     pmes.GetType(),
255
		ID:       pmes.GetId(),
256 257 258 259
		Response: true,
	}
	defer func() {
		mes := swarm.NewMessage(p, resp.ToProtobuf())
260
		dht.netChan.Outgoing <- mes
261 262
	}()
	level := pmes.GetValue()[0]
263
	u.DOut("handleFindPeer: searching for '%s'\n", peer.ID(pmes.GetKey()).Pretty())
264
	closest := dht.routingTables[level].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey())))
Jeromy's avatar
Jeromy committed
265
	if closest == nil {
Jeromy's avatar
Jeromy committed
266
		u.PErr("handleFindPeer: could not find anything.\n")
267
		return
Jeromy's avatar
Jeromy committed
268 269 270
	}

	if len(closest.Addresses) == 0 {
Jeromy's avatar
Jeromy committed
271
		u.PErr("handleFindPeer: no addresses for connected peer...\n")
272
		return
Jeromy's avatar
Jeromy committed
273 274
	}

275 276 277
	// If the found peer further away than this peer...
	if kb.Closer(dht.self.ID, closest.ID, u.Key(pmes.GetKey())) {
		return
Jeromy's avatar
Jeromy committed
278 279
	}

280
	u.DOut("handleFindPeer: sending back '%s'\n", closest.ID.Pretty())
281 282 283 284 285
	resp.Peers = []*peer.Peer{closest}
	resp.Success = true
}

func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *PBDHTMessage) {
286
	resp := Message{
287 288
		Type:     PBDHTMessage_GET_PROVIDERS,
		Key:      pmes.GetKey(),
289
		ID:       pmes.GetId(),
290
		Response: true,
Jeromy's avatar
Jeromy committed
291 292
	}

293 294 295 296 297
	has, err := dht.datastore.Has(ds.NewKey(pmes.GetKey()))
	if err != nil {
		dht.netChan.Errors <- err
	}

298
	providers := dht.providers.GetProviders(u.Key(pmes.GetKey()))
299 300 301
	if has {
		providers = append(providers, dht.self)
	}
302
	if providers == nil || len(providers) == 0 {
Jeromy's avatar
Jeromy committed
303 304 305 306 307
		level := 0
		if len(pmes.GetValue()) > 0 {
			level = int(pmes.GetValue()[0])
		}

308
		closer := dht.routingTables[level].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey())))
Jeromy's avatar
Jeromy committed
309 310 311 312 313
		if kb.Closer(dht.self.ID, closer.ID, u.Key(pmes.GetKey())) {
			resp.Peers = nil
		} else {
			resp.Peers = []*peer.Peer{closer}
		}
314
	} else {
315
		resp.Peers = providers
316
		resp.Success = true
317 318 319
	}

	mes := swarm.NewMessage(p, resp.ToProtobuf())
320
	dht.netChan.Outgoing <- mes
321 322
}

323 324
type providerInfo struct {
	Creation time.Time
325
	Value    *peer.Peer
326 327
}

328
func (dht *IpfsDHT) handleAddProvider(p *peer.Peer, pmes *PBDHTMessage) {
329
	key := u.Key(pmes.GetKey())
Jeromy's avatar
Jeromy committed
330
	u.DOut("[%s] Adding [%s] as a provider for '%s'\n", dht.self.ID.Pretty(), p.ID.Pretty(), peer.ID(key).Pretty())
331
	dht.providers.AddProvider(key, p)
332 333
}

334
// Halt stops all communications from this peer and shut down
335 336 337
func (dht *IpfsDHT) Halt() {
	dht.shutdown <- struct{}{}
	dht.network.Close()
338 339
	dht.providers.Halt()
	dht.listener.Halt()
340
}
341

Jeromy's avatar
Jeromy committed
342
// NOTE: not yet finished, low priority
343
func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *PBDHTMessage) {
344
	seq := dht.routingTables[0].NearestPeers(kb.ConvertPeerID(dht.self.ID), 10)
345
	listenChan := dht.listener.Listen(pmes.GetId(), len(seq), time.Second*30)
346

347
	for _, ps := range seq {
348
		mes := swarm.NewMessage(ps, pmes)
349
		dht.netChan.Outgoing <- mes
350 351 352
	}

	buf := new(bytes.Buffer)
353 354 355
	di := dht.getDiagInfo()
	buf.Write(di.Marshal())

356 357 358 359 360 361 362 363
	// NOTE: this shouldnt be a hardcoded value
	after := time.After(time.Second * 20)
	count := len(seq)
	for count > 0 {
		select {
		case <-after:
			//Timeout, return what we have
			goto out
364 365 366
		case reqResp := <-listenChan:
			pmesOut := new(PBDHTMessage)
			err := proto.Unmarshal(reqResp.Data, pmesOut)
367 368 369 370
			if err != nil {
				// It broke? eh, whatever, keep going
				continue
			}
371
			buf.Write(reqResp.Data)
372 373 374 375 376
			count--
		}
	}

out:
377
	resp := Message{
378
		Type:     PBDHTMessage_DIAGNOSTIC,
379
		ID:       pmes.GetId(),
380
		Value:    buf.Bytes(),
381 382 383 384
		Response: true,
	}

	mes := swarm.NewMessage(p, resp.ToProtobuf())
385
	dht.netChan.Outgoing <- mes
386
}
387

388 389 390
func (dht *IpfsDHT) getValueOrPeers(p *peer.Peer, key u.Key, timeout time.Duration, level int) ([]byte, []*peer.Peer, error) {
	pmes, err := dht.getValueSingle(p, key, timeout, level)
	if err != nil {
391
		return nil, nil, err
392 393 394 395 396 397 398 399 400 401 402 403 404
	}

	if pmes.GetSuccess() {
		if pmes.Value == nil { // We were given provider[s]
			val, err := dht.getFromPeerList(key, timeout, pmes.GetPeers(), level)
			if err != nil {
				return nil, nil, err
			}
			return val, nil, nil
		}

		// Success! We were given the value
		return pmes.GetValue(), nil, nil
405
	}
406

407 408 409 410 411 412 413 414
	// We were given a closer node
	var peers []*peer.Peer
	for _, pb := range pmes.GetPeers() {
		if peer.ID(pb.GetId()).Equal(dht.self.ID) {
			continue
		}
		addr, err := ma.NewMultiaddr(pb.GetAddr())
		if err != nil {
415
			u.PErr("%v\n", err.Error())
416 417
			continue
		}
418

419 420
		np, err := dht.network.GetConnection(peer.ID(pb.GetId()), addr)
		if err != nil {
421
			u.PErr("%v\n", err.Error())
422
			continue
423
		}
424 425

		peers = append(peers, np)
426
	}
427
	return nil, peers, nil
428 429
}

430 431
// getValueSingle simply performs the get value RPC with the given parameters
func (dht *IpfsDHT) getValueSingle(p *peer.Peer, key u.Key, timeout time.Duration, level int) (*PBDHTMessage, error) {
432
	pmes := Message{
433 434 435
		Type:  PBDHTMessage_GET_VALUE,
		Key:   string(key),
		Value: []byte{byte(level)},
436
		ID:    swarm.GenerateMessageID(),
Jeromy's avatar
Jeromy committed
437
	}
438
	responseChan := dht.listener.Listen(pmes.ID, 1, time.Minute)
Jeromy's avatar
Jeromy committed
439 440

	mes := swarm.NewMessage(p, pmes.ToProtobuf())
441
	t := time.Now()
442
	dht.netChan.Outgoing <- mes
Jeromy's avatar
Jeromy committed
443 444 445 446 447

	// Wait for either the response or a timeout
	timeup := time.After(timeout)
	select {
	case <-timeup:
448
		dht.listener.Unlisten(pmes.ID)
Jeromy's avatar
Jeromy committed
449
		return nil, u.ErrTimeout
450
	case resp, ok := <-responseChan:
Jeromy's avatar
Jeromy committed
451
		if !ok {
Jeromy's avatar
Jeromy committed
452
			u.PErr("response channel closed before timeout, please investigate.\n")
Jeromy's avatar
Jeromy committed
453 454
			return nil, u.ErrTimeout
		}
455 456
		roundtrip := time.Since(t)
		resp.Peer.SetLatency(roundtrip)
457 458
		pmesOut := new(PBDHTMessage)
		err := proto.Unmarshal(resp.Data, pmesOut)
Jeromy's avatar
Jeromy committed
459 460 461
		if err != nil {
			return nil, err
		}
462
		return pmesOut, nil
Jeromy's avatar
Jeromy committed
463 464 465
	}
}

466 467 468 469 470 471 472 473 474 475
// TODO: Im not certain on this implementation, we get a list of peers/providers
// from someone what do we do with it? Connect to each of them? randomly pick
// one to get the value from? Or just connect to one at a time until we get a
// successful connection and request the value from it?
func (dht *IpfsDHT) getFromPeerList(key u.Key, timeout time.Duration,
	peerlist []*PBDHTMessage_PBPeer, level int) ([]byte, error) {
	for _, pinfo := range peerlist {
		p, _ := dht.Find(peer.ID(pinfo.GetId()))
		if p == nil {
			maddr, err := ma.NewMultiaddr(pinfo.GetAddr())
Jeromy's avatar
Jeromy committed
476
			if err != nil {
477
				u.PErr("getValue error: %s\n", err)
Jeromy's avatar
Jeromy committed
478 479
				continue
			}
480

481
			p, err = dht.network.GetConnection(peer.ID(pinfo.GetId()), maddr)
Jeromy's avatar
Jeromy committed
482
			if err != nil {
483
				u.PErr("getValue error: %s\n", err)
Jeromy's avatar
Jeromy committed
484 485 486
				continue
			}
		}
487
		pmes, err := dht.getValueSingle(p, key, timeout, level)
Jeromy's avatar
Jeromy committed
488
		if err != nil {
489
			u.DErr("getFromPeers error: %s\n", err)
Jeromy's avatar
Jeromy committed
490 491
			continue
		}
492
		dht.providers.AddProvider(key, p)
Jeromy's avatar
Jeromy committed
493

494 495 496 497
		// Make sure it was a successful get
		if pmes.GetSuccess() && pmes.Value != nil {
			return pmes.GetValue(), nil
		}
Jeromy's avatar
Jeromy committed
498 499 500 501
	}
	return nil, u.ErrNotFound
}

502
func (dht *IpfsDHT) getLocal(key u.Key) ([]byte, error) {
503 504
	dht.dslock.Lock()
	defer dht.dslock.Unlock()
505
	v, err := dht.datastore.Get(ds.NewKey(string(key)))
506 507 508 509 510 511
	if err != nil {
		return nil, err
	}
	return v.([]byte), nil
}

512
func (dht *IpfsDHT) putLocal(key u.Key, value []byte) error {
513 514
	return dht.datastore.Put(ds.NewKey(string(key)), value)
}
515

516
// Update TODO(chas) Document this function
517
func (dht *IpfsDHT) Update(p *peer.Peer) {
518
	for _, route := range dht.routingTables {
519
		removed := route.Update(p)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
520
		// Only close the connection if no tables refer to this peer
521 522
		if removed != nil {
			found := false
523
			for _, r := range dht.routingTables {
524 525 526 527 528 529
				if r.Find(removed.ID) != nil {
					found = true
					break
				}
			}
			if !found {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
530
				dht.network.CloseConnection(removed)
531 532
			}
		}
533 534
	}
}
Jeromy's avatar
Jeromy committed
535

536
// Find looks for a peer with a given ID connected to this dht and returns the peer and the table it was found in.
Jeromy's avatar
Jeromy committed
537
func (dht *IpfsDHT) Find(id peer.ID) (*peer.Peer, *kb.RoutingTable) {
538
	for _, table := range dht.routingTables {
Jeromy's avatar
Jeromy committed
539 540 541 542 543 544 545
		p := table.Find(id)
		if p != nil {
			return p, table
		}
	}
	return nil, nil
}
546 547

func (dht *IpfsDHT) findPeerSingle(p *peer.Peer, id peer.ID, timeout time.Duration, level int) (*PBDHTMessage, error) {
548
	pmes := Message{
549 550
		Type:  PBDHTMessage_FIND_NODE,
		Key:   string(id),
551
		ID:    swarm.GenerateMessageID(),
552 553 554 555
		Value: []byte{byte(level)},
	}

	mes := swarm.NewMessage(p, pmes.ToProtobuf())
556
	listenChan := dht.listener.Listen(pmes.ID, 1, time.Minute)
557
	t := time.Now()
558
	dht.netChan.Outgoing <- mes
559 560 561
	after := time.After(timeout)
	select {
	case <-after:
562
		dht.listener.Unlisten(pmes.ID)
563 564
		return nil, u.ErrTimeout
	case resp := <-listenChan:
565 566
		roundtrip := time.Since(t)
		resp.Peer.SetLatency(roundtrip)
567 568
		pmesOut := new(PBDHTMessage)
		err := proto.Unmarshal(resp.Data, pmesOut)
569 570 571 572
		if err != nil {
			return nil, err
		}

573
		return pmesOut, nil
574 575
	}
}
576

577 578
func (dht *IpfsDHT) printTables() {
	for _, route := range dht.routingTables {
579 580 581
		route.Print()
	}
}
Jeromy's avatar
Jeromy committed
582 583

func (dht *IpfsDHT) findProvidersSingle(p *peer.Peer, key u.Key, level int, timeout time.Duration) (*PBDHTMessage, error) {
584
	pmes := Message{
Jeromy's avatar
Jeromy committed
585 586
		Type:  PBDHTMessage_GET_PROVIDERS,
		Key:   string(key),
587
		ID:    swarm.GenerateMessageID(),
Jeromy's avatar
Jeromy committed
588 589 590 591 592
		Value: []byte{byte(level)},
	}

	mes := swarm.NewMessage(p, pmes.ToProtobuf())

593
	listenChan := dht.listener.Listen(pmes.ID, 1, time.Minute)
594
	dht.netChan.Outgoing <- mes
Jeromy's avatar
Jeromy committed
595 596 597
	after := time.After(timeout)
	select {
	case <-after:
598
		dht.listener.Unlisten(pmes.ID)
Jeromy's avatar
Jeromy committed
599 600
		return nil, u.ErrTimeout
	case resp := <-listenChan:
Jeromy's avatar
Jeromy committed
601
		u.DOut("FindProviders: got response.\n")
602 603
		pmesOut := new(PBDHTMessage)
		err := proto.Unmarshal(resp.Data, pmesOut)
Jeromy's avatar
Jeromy committed
604 605 606 607
		if err != nil {
			return nil, err
		}

608
		return pmesOut, nil
Jeromy's avatar
Jeromy committed
609 610 611
	}
}

612
// TODO: Could be done async
Jeromy's avatar
Jeromy committed
613
func (dht *IpfsDHT) addPeerList(key u.Key, peers []*PBDHTMessage_PBPeer) []*peer.Peer {
614
	var provArr []*peer.Peer
Jeromy's avatar
Jeromy committed
615 616 617 618 619 620
	for _, prov := range peers {
		// Dont add outselves to the list
		if peer.ID(prov.GetId()).Equal(dht.self.ID) {
			continue
		}
		// Dont add someone who is already on the list
621
		p := dht.network.GetPeer(u.Key(prov.GetId()))
Jeromy's avatar
Jeromy committed
622
		if p == nil {
623
			u.DOut("given provider %s was not in our network already.\n", peer.ID(prov.GetId()).Pretty())
Jeromy's avatar
Jeromy committed
624 625
			var err error
			p, err = dht.peerFromInfo(prov)
Jeromy's avatar
Jeromy committed
626
			if err != nil {
627
				u.PErr("error connecting to new peer: %s\n", err)
Jeromy's avatar
Jeromy committed
628 629 630
				continue
			}
		}
631
		dht.providers.AddProvider(key, p)
632
		provArr = append(provArr, p)
Jeromy's avatar
Jeromy committed
633
	}
634
	return provArr
Jeromy's avatar
Jeromy committed
635
}
Jeromy's avatar
Jeromy committed
636 637 638 639 640 641 642 643 644

func (dht *IpfsDHT) peerFromInfo(pbp *PBDHTMessage_PBPeer) (*peer.Peer, error) {
	maddr, err := ma.NewMultiaddr(pbp.GetAddr())
	if err != nil {
		return nil, err
	}

	return dht.network.GetConnection(peer.ID(pbp.GetId()), maddr)
}
645 646

func (dht *IpfsDHT) loadProvidableKeys() error {
647 648 649 650
	kl, err := dht.datastore.KeyList()
	if err != nil {
		return err
	}
651 652 653 654 655 656 657 658 659 660 661 662
	for _, k := range kl {
		dht.providers.AddProvider(u.Key(k.Bytes()), dht.self)
	}
	return nil
}

// Builds up list of peers by requesting random peer IDs
func (dht *IpfsDHT) Bootstrap() {
	id := make([]byte, 16)
	rand.Read(id)
	dht.FindPeer(peer.ID(id), time.Second*10)
}