dht.go 13.8 KB
Newer Older
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
1
// Package dht implements a distributed hash table that satisfies the ipfs routing
2
// interface. This DHT is modeled after kademlia with Coral and S/Kademlia modifications.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
3 4
package dht

5
import (
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
6
	"bytes"
7
	"crypto/rand"
8
	"errors"
9
	"fmt"
10 11
	"sync"
	"time"
12

13
	inet "github.com/jbenet/go-ipfs/net"
14
	msg "github.com/jbenet/go-ipfs/net/message"
15
	peer "github.com/jbenet/go-ipfs/peer"
16
	routing "github.com/jbenet/go-ipfs/routing"
17
	pb "github.com/jbenet/go-ipfs/routing/dht/pb"
Jeromy's avatar
Jeromy committed
18
	kb "github.com/jbenet/go-ipfs/routing/kbucket"
19
	u "github.com/jbenet/go-ipfs/util"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
20
	ctxc "github.com/jbenet/go-ipfs/util/ctxcloser"
Brian Tiger Chow's avatar
Brian Tiger Chow committed
21
	"github.com/jbenet/go-ipfs/util/eventlog"
22

23
	context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
24
	ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore"
Jeromy's avatar
Jeromy committed
25

26
	"github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto"
27 28
)

29
var log = eventlog.Logger("dht")
30

31
const doPinging = false
32

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
33 34 35 36 37
// 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 {
38 39
	// Array of routing tables for differently distanced nodes
	// NOTE: (currently, only a single table is used)
40
	routingTable *kb.RoutingTable
41

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
42 43 44
	// the network services we need
	dialer inet.Dialer
	sender inet.Sender
45

Jeromy's avatar
Jeromy committed
46
	// Local peer (yourself)
47
	self peer.Peer
Jeromy's avatar
Jeromy committed
48

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
49 50 51
	// Other peers
	peerstore peer.Peerstore

Jeromy's avatar
Jeromy committed
52 53
	// Local data
	datastore ds.Datastore
54
	dslock    sync.Mutex
55

56
	providers *ProviderManager
57

58 59
	// When this peer started up
	birth time.Time
60 61 62

	//lock to make diagnostics work better
	diaglock sync.Mutex
63

64 65 66
	// record validator funcs
	Validators map[string]ValidatorFunc

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
67
	ctxc.ContextCloser
68 69
}

Jeromy's avatar
Jeromy committed
70
// NewDHT creates a new DHT object with the given peer as the 'local' host
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
71
func NewDHT(ctx context.Context, p peer.Peer, ps peer.Peerstore, dialer inet.Dialer, sender inet.Sender, dstore ds.Datastore) *IpfsDHT {
72
	dht := new(IpfsDHT)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
73
	dht.dialer = dialer
74
	dht.sender = sender
Jeromy's avatar
Jeromy committed
75
	dht.datastore = dstore
76
	dht.self = p
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
77
	dht.peerstore = ps
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
78
	dht.ContextCloser = ctxc.NewContextCloser(ctx, nil)
79

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
80 81
	dht.providers = NewProviderManager(dht.Context(), p.ID())
	dht.AddCloserChild(dht.providers)
82

83
	dht.routingTable = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID()), time.Minute)
84
	dht.birth = time.Now()
85

86
	dht.Validators = make(map[string]ValidatorFunc)
87
	dht.Validators["pk"] = ValidatePublicKeyRecord
88

89
	if doPinging {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
90
		dht.Children().Add(1)
91 92
		go dht.PingRoutine(time.Second * 10)
	}
Jeromy's avatar
Jeromy committed
93
	return dht
94 95
}

96
// Connect to a new peer at the given address, ping and add to the routing table
97
func (dht *IpfsDHT) Connect(ctx context.Context, npeer peer.Peer) error {
98
	err := dht.dialer.DialPeer(ctx, npeer)
99
	if err != nil {
100
		return err
101 102
	}

Jeromy's avatar
Jeromy committed
103 104
	// Ping new peer to register in their routing table
	// NOTE: this should be done better...
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
105
	err = dht.Ping(ctx, npeer)
Jeromy's avatar
Jeromy committed
106
	if err != nil {
107
		return fmt.Errorf("failed to ping newly connected peer: %s\n", err)
Jeromy's avatar
Jeromy committed
108
	}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
109
	log.Event(ctx, "connect", dht.self, npeer)
Jeromy's avatar
Jeromy committed
110

111
	dht.Update(ctx, npeer)
112

113
	return nil
Jeromy's avatar
Jeromy committed
114 115
}

116
// HandleMessage implements the inet.Handler interface.
117
func (dht *IpfsDHT) HandleMessage(ctx context.Context, mes msg.NetMessage) msg.NetMessage {
118 119 120

	mData := mes.Data()
	if mData == nil {
121
		log.Error("Message contained nil data.")
122
		return nil
123 124 125 126
	}

	mPeer := mes.Peer()
	if mPeer == nil {
127
		log.Error("Message contained nil peer.")
128
		return nil
129 130 131
	}

	// deserialize msg
132
	pmes := new(pb.Message)
133 134
	err := proto.Unmarshal(mData, pmes)
	if err != nil {
135
		log.Error("Error unmarshaling data")
136
		return nil
137 138 139
	}

	// update the peer (on valid msgs only)
140
	dht.Update(ctx, mPeer)
141

142
	log.Event(ctx, "foo", dht.self, mPeer, pmes)
143 144 145 146

	// get handler for this msg type.
	handler := dht.handlerForMsgType(pmes.GetType())
	if handler == nil {
147
		log.Error("got back nil handler from handlerForMsgType")
148
		return nil
149 150 151
	}

	// dispatch handler.
152
	rpmes, err := handler(ctx, mPeer, pmes)
153
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
154
		log.Errorf("handle message error: %s", err)
155
		return nil
156 157
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
158 159
	// if nil response, return it before serializing
	if rpmes == nil {
160
		log.Warning("Got back nil response from request.")
161
		return nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
162 163
	}

164 165 166
	// serialize response msg
	rmes, err := msg.FromObject(mPeer, rpmes)
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
167
		log.Errorf("serialze response error: %s", err)
168
		return nil
169 170
	}

171
	return rmes
172 173
}

174 175
// sendRequest sends out a request using dht.sender, but also makes sure to
// measure the RTT for latency measurements.
176
func (dht *IpfsDHT) sendRequest(ctx context.Context, p peer.Peer, pmes *pb.Message) (*pb.Message, error) {
177 178 179 180 181 182 183 184

	mes, err := msg.FromObject(p, pmes)
	if err != nil {
		return nil, err
	}

	start := time.Now()

185
	rmes, err := dht.sender.SendRequest(ctx, mes) // respect?
186 187 188
	if err != nil {
		return nil, err
	}
189 190 191
	if rmes == nil {
		return nil, errors.New("no response to request")
	}
192
	log.Event(ctx, "sentMessage", dht.self, p, pmes)
193

194
	rmes.Peer().SetLatency(time.Since(start))
195

196
	rpmes := new(pb.Message)
197 198 199 200 201 202
	if err := proto.Unmarshal(rmes.Data(), rpmes); err != nil {
		return nil, err
	}
	return rpmes, nil
}

203
// putValueToNetwork stores the given key/value pair at the peer 'p'
204
func (dht *IpfsDHT) putValueToNetwork(ctx context.Context, p peer.Peer,
205
	key string, rec *pb.Record) error {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
206

207
	pmes := pb.NewMessage(pb.Message_PUT_VALUE, string(key), 0)
208
	pmes.Record = rec
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
209
	rpmes, err := dht.sendRequest(ctx, p, pmes)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
210 211 212
	if err != nil {
		return err
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
213

214
	if !bytes.Equal(rpmes.GetRecord().Value, pmes.GetRecord().Value) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
215 216 217
		return errors.New("value not put correctly")
	}
	return nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
218 219
}

Jeromy's avatar
Jeromy committed
220 221
// putProvider sends a message to peer 'p' saying that the local node
// can provide the value of 'key'
222
func (dht *IpfsDHT) putProvider(ctx context.Context, p peer.Peer, key string) error {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
223

224
	pmes := pb.NewMessage(pb.Message_ADD_PROVIDER, string(key), 0)
225 226

	// add self as the provider
227
	pmes.ProviderPeers = pb.PeersToPBPeers(dht.dialer, []peer.Peer{dht.self})
228

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
229
	rpmes, err := dht.sendRequest(ctx, p, pmes)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
230 231 232
	if err != nil {
		return err
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
233

234
	log.Debugf("%s putProvider: %s for %s", dht.self, p, u.Key(key))
235
	if rpmes.GetKey() != pmes.GetKey() {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
236 237 238 239
		return errors.New("provider not added correctly")
	}

	return nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
240 241
}

242
func (dht *IpfsDHT) getValueOrPeers(ctx context.Context, p peer.Peer,
243
	key u.Key) ([]byte, []peer.Peer, error) {
244

245
	pmes, err := dht.getValueSingle(ctx, p, key)
246
	if err != nil {
247
		return nil, nil, err
248 249
	}

250
	if record := pmes.GetRecord(); record != nil {
251
		// Success! We were given the value
Jeromy's avatar
Jeromy committed
252
		log.Debug("getValueOrPeers: got value")
253 254 255 256

		// make sure record is still valid
		err = dht.verifyRecord(record)
		if err != nil {
Jeromy's avatar
Jeromy committed
257
			log.Error("Received invalid record!")
258 259 260
			return nil, nil, err
		}
		return record.GetValue(), nil, nil
261
	}
262

263
	// TODO decide on providers. This probably shouldn't be happening.
264
	if prv := pmes.GetProviderPeers(); prv != nil && len(prv) > 0 {
265
		val, err := dht.getFromPeerList(ctx, key, prv)
266 267 268
		if err != nil {
			return nil, nil, err
		}
Jeromy's avatar
Jeromy committed
269
		log.Debug("getValueOrPeers: get from providers")
270 271
		return val, nil, nil
	}
272 273

	// Perhaps we were given closer peers
274 275
	peers, errs := pb.PBPeersToPeers(dht.peerstore, pmes.GetCloserPeers())
	for _, err := range errs {
276
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
277
			log.Error(err)
278
		}
279
	}
280 281

	if len(peers) > 0 {
282
		log.Debug("getValueOrPeers: peers")
283 284 285
		return nil, peers, nil
	}

286 287
	log.Warning("getValueOrPeers: routing.ErrNotFound")
	return nil, nil, routing.ErrNotFound
288 289
}

290
// getValueSingle simply performs the get value RPC with the given parameters
291
func (dht *IpfsDHT) getValueSingle(ctx context.Context, p peer.Peer,
292
	key u.Key) (*pb.Message, error) {
293

294
	pmes := pb.NewMessage(pb.Message_GET_VALUE, string(key), 0)
295
	return dht.sendRequest(ctx, p, pmes)
Jeromy's avatar
Jeromy committed
296 297
}

298 299 300 301
// 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?
302
func (dht *IpfsDHT) getFromPeerList(ctx context.Context, key u.Key,
303
	peerlist []*pb.Message_Peer) ([]byte, error) {
304

305
	for _, pinfo := range peerlist {
306
		p, err := dht.ensureConnectedToPeer(ctx, pinfo)
307
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
308
			log.Errorf("getFromPeers error: %s", err)
309
			continue
Jeromy's avatar
Jeromy committed
310
		}
311

312
		pmes, err := dht.getValueSingle(ctx, p, key)
Jeromy's avatar
Jeromy committed
313
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
314
			log.Errorf("getFromPeers error: %s\n", err)
Jeromy's avatar
Jeromy committed
315 316 317
			continue
		}

318
		if record := pmes.GetRecord(); record != nil {
319
			// Success! We were given the value
320 321 322 323 324

			err := dht.verifyRecord(record)
			if err != nil {
				return nil, err
			}
325
			dht.providers.AddProvider(key, p)
326
			return record.GetValue(), nil
327
		}
Jeromy's avatar
Jeromy committed
328
	}
329
	return nil, routing.ErrNotFound
Jeromy's avatar
Jeromy committed
330 331
}

332
// getLocal attempts to retrieve the value from the datastore
333
func (dht *IpfsDHT) getLocal(key u.Key) ([]byte, error) {
334 335
	dht.dslock.Lock()
	defer dht.dslock.Unlock()
Jeromy's avatar
Jeromy committed
336
	log.Debug("getLocal %s", key)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
337
	v, err := dht.datastore.Get(key.DsKey())
338 339 340
	if err != nil {
		return nil, err
	}
Jeromy's avatar
Jeromy committed
341
	log.Debug("found in db")
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
342 343 344

	byt, ok := v.([]byte)
	if !ok {
345
		return nil, errors.New("value stored in datastore not []byte")
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
346
	}
347 348 349 350 351 352 353 354 355 356
	rec := new(pb.Record)
	err = proto.Unmarshal(byt, rec)
	if err != nil {
		return nil, err
	}

	// TODO: 'if paranoid'
	if u.Debug {
		err = dht.verifyRecord(rec)
		if err != nil {
Jeromy's avatar
Jeromy committed
357
			log.Errorf("local record verify failed: %s", err)
358 359 360 361 362
			return nil, err
		}
	}

	return rec.GetValue(), nil
363 364
}

365
// putLocal stores the key value pair in the datastore
366
func (dht *IpfsDHT) putLocal(key u.Key, value []byte) error {
367 368 369 370 371 372 373 374 375 376
	rec, err := dht.makePutRecord(key, value)
	if err != nil {
		return err
	}
	data, err := proto.Marshal(rec)
	if err != nil {
		return err
	}

	return dht.datastore.Put(key.DsKey(), data)
377
}
378

379
// Update signals the routingTable to Update its last-seen status
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
380
// on the given peer.
381 382
func (dht *IpfsDHT) Update(ctx context.Context, p peer.Peer) {
	log.Event(ctx, "updatePeer", p)
383
	dht.routingTable.Update(p)
384
}
Jeromy's avatar
Jeromy committed
385

Jeromy's avatar
Jeromy committed
386
// FindLocal looks for a peer with a given ID connected to this dht and returns the peer and the table it was found in.
387
func (dht *IpfsDHT) FindLocal(id peer.ID) (peer.Peer, *kb.RoutingTable) {
388 389 390
	p := dht.routingTable.Find(id)
	if p != nil {
		return p, dht.routingTable
Jeromy's avatar
Jeromy committed
391 392 393
	}
	return nil, nil
}
394

Jeromy's avatar
Jeromy committed
395
// findPeerSingle asks peer 'p' if they know where the peer with id 'id' is
396 397
func (dht *IpfsDHT) findPeerSingle(ctx context.Context, p peer.Peer, id peer.ID) (*pb.Message, error) {
	pmes := pb.NewMessage(pb.Message_FIND_NODE, string(id), 0)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
398
	return dht.sendRequest(ctx, p, pmes)
399
}
400

401 402
func (dht *IpfsDHT) findProvidersSingle(ctx context.Context, p peer.Peer, key u.Key) (*pb.Message, error) {
	pmes := pb.NewMessage(pb.Message_GET_PROVIDERS, string(key), 0)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
403
	return dht.sendRequest(ctx, p, pmes)
Jeromy's avatar
Jeromy committed
404 405
}

406 407 408 409 410
func (dht *IpfsDHT) addProviders(key u.Key, pbps []*pb.Message_Peer) []peer.Peer {
	peers, errs := pb.PBPeersToPeers(dht.peerstore, pbps)
	for _, err := range errs {
		log.Errorf("error converting peer: %v", err)
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
411

412 413
	var provArr []peer.Peer
	for _, p := range peers {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
414
		// Dont add outselves to the list
415
		if p.ID().Equal(dht.self.ID()) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
416
			continue
Jeromy's avatar
Jeromy committed
417
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
418

419
		log.Debugf("%s adding provider: %s for %s", dht.self, p, key)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
420
		// TODO(jbenet) ensure providers is idempotent
421
		dht.providers.AddProvider(key, p)
422
		provArr = append(provArr, p)
Jeromy's avatar
Jeromy committed
423
	}
424
	return provArr
Jeromy's avatar
Jeromy committed
425
}
Jeromy's avatar
Jeromy committed
426

427
// nearestPeersToQuery returns the routing tables closest peers.
428
func (dht *IpfsDHT) nearestPeersToQuery(pmes *pb.Message, count int) []peer.Peer {
429
	key := u.Key(pmes.GetKey())
430
	closer := dht.routingTable.NearestPeers(kb.ConvertKey(key), count)
431 432 433
	return closer
}

434
// betterPeerToQuery returns nearestPeersToQuery, but iff closer than self.
435
func (dht *IpfsDHT) betterPeersToQuery(pmes *pb.Message, count int) []peer.Peer {
436
	closer := dht.nearestPeersToQuery(pmes, count)
437 438 439 440 441 442

	// no node? nil
	if closer == nil {
		return nil
	}

443 444
	// == to self? thats bad
	for _, p := range closer {
445
		if p.ID().Equal(dht.self.ID()) {
446 447 448
			log.Error("Attempted to return self! this shouldnt happen...")
			return nil
		}
449 450
	}

451
	var filtered []peer.Peer
452 453 454
	for _, p := range closer {
		// must all be closer than self
		key := u.Key(pmes.GetKey())
455
		if !kb.Closer(dht.self.ID(), p.ID(), key) {
456 457
			filtered = append(filtered, p)
		}
458 459
	}

460 461
	// ok seems like closer nodes
	return filtered
462 463
}

Jeromy's avatar
Jeromy committed
464
// getPeer searches the peerstore for a peer with the given peer ID
465
func (dht *IpfsDHT) getPeer(id peer.ID) (peer.Peer, error) {
466
	p, err := dht.peerstore.FindOrCreate(id)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
467 468
	if err != nil {
		err = fmt.Errorf("Failed to get peer from peerstore: %s", err)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
469
		log.Error(err)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
470 471 472 473 474
		return nil, err
	}
	return p, nil
}

475 476
func (dht *IpfsDHT) ensureConnectedToPeer(ctx context.Context, pbp *pb.Message_Peer) (peer.Peer, error) {
	p, err := pb.PBPeerToPeer(dht.peerstore, pbp)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
477 478
	if err != nil {
		return nil, err
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
479 480
	}

481 482
	if dht.dialer.LocalPeer().ID().Equal(p.ID()) {
		return nil, errors.New("attempting to ensure connection to self")
Jeromy's avatar
Jeromy committed
483 484
	}

485
	// dial connection
486
	err = dht.dialer.DialPeer(ctx, p)
487
	return p, err
Jeromy's avatar
Jeromy committed
488
}
489

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
490
//TODO: this should be smarter about which keys it selects.
491
func (dht *IpfsDHT) loadProvidableKeys() error {
492 493 494 495
	kl, err := dht.datastore.KeyList()
	if err != nil {
		return err
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
496 497 498
	for _, dsk := range kl {
		k := u.KeyFromDsKey(dsk)
		if len(k) == 0 {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
499
			log.Errorf("loadProvidableKeys error: %v", dsk)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
500 501 502
		}

		dht.providers.AddProvider(k, dht.self)
503 504 505 506
	}
	return nil
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
507
// PingRoutine periodically pings nearest neighbors.
508
func (dht *IpfsDHT) PingRoutine(t time.Duration) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
509 510
	defer dht.Children().Done()

511 512 513 514 515 516
	tick := time.Tick(t)
	for {
		select {
		case <-tick:
			id := make([]byte, 16)
			rand.Read(id)
517
			peers := dht.routingTable.NearestPeers(kb.ConvertKey(u.Key(id)), 5)
518
			for _, p := range peers {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
519
				ctx, _ := context.WithTimeout(dht.Context(), time.Second*5)
520 521
				err := dht.Ping(ctx, p)
				if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
522
					log.Errorf("Ping error: %s", err)
523 524
				}
			}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
525
		case <-dht.Closing():
526 527 528 529 530
			return
		}
	}
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
531
// Bootstrap builds up list of peers by requesting random peer IDs
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
532
func (dht *IpfsDHT) Bootstrap(ctx context.Context) {
533 534
	id := make([]byte, 16)
	rand.Read(id)
Jeromy's avatar
Jeromy committed
535 536
	p, err := dht.FindPeer(ctx, peer.ID(id))
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
537
		log.Errorf("Bootstrap peer error: %s", err)
Jeromy's avatar
Jeromy committed
538
	}
539
	err = dht.dialer.DialPeer(ctx, p)
540
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
541
		log.Errorf("Bootstrap peer error: %s", err)
542
	}
543
}